AVFoundation: fix QCameraInfo::availableCameras() on OS X.

Cameras can be dynamically added or removed on OS X. Make sure
the cache is updated often enough so QCameraInfo::availableCameras()
return an up to date list.

Task-number: QTBUG-39708
Change-Id: Id806d52278e1a29163fcc6707da7f86c0f3e7c0d
Reviewed-by: Timur Pocheptsov <Timur.Pocheptsov@digia.com>
This commit is contained in:
Yoann Lopes
2015-04-09 15:14:36 +02:00
committed by Timur Pocheptsov
parent fa9e829a60
commit def89d7171
4 changed files with 59 additions and 38 deletions

View File

@@ -71,18 +71,26 @@ void AVFServicePlugin::release(QMediaService *service)
QByteArray AVFServicePlugin::defaultDevice(const QByteArray &service) const QByteArray AVFServicePlugin::defaultDevice(const QByteArray &service) const
{ {
if (service == Q_MEDIASERVICE_CAMERA) if (service == Q_MEDIASERVICE_CAMERA) {
return AVFCameraSession::defaultCameraDevice(); int i = AVFCameraSession::defaultCameraIndex();
if (i != -1)
return AVFCameraSession::availableCameraDevices().at(i).deviceId;
}
return QByteArray(); return QByteArray();
} }
QList<QByteArray> AVFServicePlugin::devices(const QByteArray &service) const QList<QByteArray> AVFServicePlugin::devices(const QByteArray &service) const
{ {
if (service == Q_MEDIASERVICE_CAMERA) QList<QByteArray> devs;
return AVFCameraSession::availableCameraDevices();
return QList<QByteArray>(); if (service == Q_MEDIASERVICE_CAMERA) {
const QList<AVFCameraInfo> &cameras = AVFCameraSession::availableCameraDevices();
Q_FOREACH (const AVFCameraInfo &info, cameras)
devs.append(info.deviceId);
}
return devs;
} }
QString AVFServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device) QString AVFServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device)

View File

@@ -52,6 +52,7 @@ struct AVFCameraInfo
AVFCameraInfo() : position(QCamera::UnspecifiedPosition), orientation(0) AVFCameraInfo() : position(QCamera::UnspecifiedPosition), orientation(0)
{ } { }
QByteArray deviceId;
QString description; QString description;
QCamera::Position position; QCamera::Position position;
int orientation; int orientation;
@@ -64,8 +65,8 @@ public:
AVFCameraSession(AVFCameraService *service, QObject *parent = 0); AVFCameraSession(AVFCameraService *service, QObject *parent = 0);
~AVFCameraSession(); ~AVFCameraSession();
static const QByteArray &defaultCameraDevice(); static int defaultCameraIndex();
static const QList<QByteArray> &availableCameraDevices(); static const QList<AVFCameraInfo> &availableCameraDevices();
static AVFCameraInfo cameraDeviceInfo(const QByteArray &device); static AVFCameraInfo cameraDeviceInfo(const QByteArray &device);
void setVideoOutput(AVFVideoRendererControl *output); void setVideoOutput(AVFVideoRendererControl *output);
@@ -93,9 +94,8 @@ private:
static void updateCameraDevices(); static void updateCameraDevices();
void attachInputDevices(); void attachInputDevices();
static QByteArray m_defaultCameraDevice; static int m_defaultCameraIndex;
static QList<QByteArray> m_cameraDevices; static QList<AVFCameraInfo> m_cameraDevices;
static QMap<QByteArray, AVFCameraInfo> m_cameraInfo;
AVFCameraService *m_service; AVFCameraService *m_service;
AVFVideoRendererControl *m_videoOutput; AVFVideoRendererControl *m_videoOutput;

View File

@@ -52,14 +52,14 @@
#include <QtCore/qdatetime.h> #include <QtCore/qdatetime.h>
#include <QtCore/qurl.h> #include <QtCore/qurl.h>
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qdebug.h> #include <QtCore/qdebug.h>
QT_USE_NAMESPACE QT_USE_NAMESPACE
QByteArray AVFCameraSession::m_defaultCameraDevice; int AVFCameraSession::m_defaultCameraIndex;
QList<QByteArray> AVFCameraSession::m_cameraDevices; QList<AVFCameraInfo> AVFCameraSession::m_cameraDevices;
QMap<QByteArray, AVFCameraInfo> AVFCameraSession::m_cameraInfo;
@interface AVFCameraSessionObserver : NSObject @interface AVFCameraSessionObserver : NSObject
{ {
@@ -172,45 +172,55 @@ AVFCameraSession::~AVFCameraSession()
[m_captureSession release]; [m_captureSession release];
} }
const QByteArray &AVFCameraSession::defaultCameraDevice() int AVFCameraSession::defaultCameraIndex()
{ {
if (m_cameraDevices.isEmpty()) updateCameraDevices();
updateCameraDevices(); return m_defaultCameraIndex;
return m_defaultCameraDevice;
} }
const QList<QByteArray> &AVFCameraSession::availableCameraDevices() const QList<AVFCameraInfo> &AVFCameraSession::availableCameraDevices()
{ {
if (m_cameraDevices.isEmpty()) updateCameraDevices();
updateCameraDevices();
return m_cameraDevices; return m_cameraDevices;
} }
AVFCameraInfo AVFCameraSession::cameraDeviceInfo(const QByteArray &device) AVFCameraInfo AVFCameraSession::cameraDeviceInfo(const QByteArray &device)
{ {
if (m_cameraDevices.isEmpty()) updateCameraDevices();
updateCameraDevices();
return m_cameraInfo.value(device); Q_FOREACH (const AVFCameraInfo &info, m_cameraDevices) {
if (info.deviceId == device)
return info;
}
return AVFCameraInfo();
} }
void AVFCameraSession::updateCameraDevices() void AVFCameraSession::updateCameraDevices()
{ {
m_defaultCameraDevice.clear(); #ifdef Q_OS_IOS
// Cameras can't change dynamically on iOS. Update only once.
if (!m_cameraDevices.isEmpty())
return;
#else
// On OS X, cameras can be added or removed. Update the list every time, but not more than
// once every 500 ms
static QElapsedTimer timer;
if (timer.isValid() && timer.elapsed() < 500) // ms
return;
#endif
m_defaultCameraIndex = -1;
m_cameraDevices.clear(); m_cameraDevices.clear();
m_cameraInfo.clear();
AVCaptureDevice *defaultDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; AVCaptureDevice *defaultDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if (defaultDevice)
m_defaultCameraDevice = QByteArray([[defaultDevice uniqueID] UTF8String]);
NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *device in videoDevices) { for (AVCaptureDevice *device in videoDevices) {
QByteArray deviceId([[device uniqueID] UTF8String]); if (defaultDevice && [defaultDevice.uniqueID isEqualToString:device.uniqueID])
m_defaultCameraIndex = m_cameraDevices.count();
AVFCameraInfo info; AVFCameraInfo info;
info.deviceId = QByteArray([[device uniqueID] UTF8String]);
info.description = QString::fromNSString([device localizedName]); info.description = QString::fromNSString([device localizedName]);
// There is no API to get the camera sensor orientation, however, cameras are always // There is no API to get the camera sensor orientation, however, cameras are always
@@ -235,9 +245,12 @@ void AVFCameraSession::updateCameraDevices()
break; break;
} }
m_cameraDevices << deviceId; m_cameraDevices.append(info);
m_cameraInfo.insert(deviceId, info);
} }
#ifndef Q_OS_IOS
timer.restart();
#endif
} }
void AVFCameraSession::setVideoOutput(AVFVideoRendererControl *output) void AVFCameraSession::setVideoOutput(AVFVideoRendererControl *output)

View File

@@ -65,25 +65,25 @@ int AVFVideoDeviceControl::deviceCount() const
QString AVFVideoDeviceControl::deviceName(int index) const QString AVFVideoDeviceControl::deviceName(int index) const
{ {
const QList<QByteArray> &devices = AVFCameraSession::availableCameraDevices(); const QList<AVFCameraInfo> &devices = AVFCameraSession::availableCameraDevices();
if (index < 0 || index >= devices.count()) if (index < 0 || index >= devices.count())
return QString(); return QString();
return QString::fromUtf8(devices.at(index)); return QString::fromUtf8(devices.at(index).deviceId);
} }
QString AVFVideoDeviceControl::deviceDescription(int index) const QString AVFVideoDeviceControl::deviceDescription(int index) const
{ {
const QList<QByteArray> &devices = AVFCameraSession::availableCameraDevices(); const QList<AVFCameraInfo> &devices = AVFCameraSession::availableCameraDevices();
if (index < 0 || index >= devices.count()) if (index < 0 || index >= devices.count())
return QString(); return QString();
return AVFCameraSession::cameraDeviceInfo(devices.at(index)).description; return devices.at(index).description;
} }
int AVFVideoDeviceControl::defaultDevice() const int AVFVideoDeviceControl::defaultDevice() const
{ {
return AVFCameraSession::availableCameraDevices().indexOf(AVFCameraSession::defaultCameraDevice()); return AVFCameraSession::defaultCameraIndex();
} }
int AVFVideoDeviceControl::selectedDevice() const int AVFVideoDeviceControl::selectedDevice() const