From 14b2b53edc14f36ad15afadbd5254e9260118db5 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Fri, 13 Feb 2015 17:20:03 +0100 Subject: [PATCH 01/11] Compile Alsa and PulseAudio plugins only when enabled by configure. They are enabled by default, this simply makes sure -no-alsa and -no-pulseaudio work. Change-Id: I8e921381363064bb65b414152eab27c08a551dc4 Reviewed-by: Christian Stromme --- qtmultimedia.pro | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtmultimedia.pro b/qtmultimedia.pro index 3cec526e..cf97e642 100644 --- a/qtmultimedia.pro +++ b/qtmultimedia.pro @@ -15,8 +15,8 @@ win32 { } else:qnx { qtCompileTest(mmrenderer) } else { - qtCompileTest(alsa) - qtCompileTest(pulseaudio) + contains(QT_CONFIG, alsa):qtCompileTest(alsa) + contains(QT_CONFIG, pulseaudio):qtCompileTest(pulseaudio) qtCompileTest(gstreamer) { qtCompileTest(gstreamer_photography) qtCompileTest(gstreamer_encodingprofiles) From fcf5d826a0e2d3ea6a01bf4c962e0d4fe096d321 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Wed, 11 Feb 2015 12:40:10 +0100 Subject: [PATCH 02/11] GStreamer: improved logic for window and widget controls usage. Provide these controls only when the xvimagesink gstreamer element is available. This allows QVideoWidget to fallback to QVideoRendererControl when xvimagesink is not available. Task-number: QTBUG-41618 Change-Id: I59f90ea8857c7ec0ffa08be9804e5458d95b79c4 Reviewed-by: Christian Stromme --- src/gsttools/qgstreamervideowidget.cpp | 39 ++++++++----------- .../gstreamer/camerabin/camerabinservice.cpp | 14 +++++++ .../gstreamer/camerabin/camerabinservice.h | 3 +- .../mediacapture/qgstreamercaptureservice.cpp | 15 +++++++ .../mediacapture/qgstreamercaptureservice.h | 5 ++- .../mediaplayer/qgstreamerplayerservice.cpp | 14 +++++++ .../mediaplayer/qgstreamerplayerservice.h | 5 ++- 7 files changed, 68 insertions(+), 27 deletions(-) diff --git a/src/gsttools/qgstreamervideowidget.cpp b/src/gsttools/qgstreamervideowidget.cpp index aa2e2a30..49601874 100644 --- a/src/gsttools/qgstreamervideowidget.cpp +++ b/src/gsttools/qgstreamervideowidget.cpp @@ -93,6 +93,22 @@ QGstreamerVideoWidgetControl::QGstreamerVideoWidgetControl(QObject *parent) , m_widget(0) , m_fullScreen(false) { + m_videoSink = gst_element_factory_make ("xvimagesink", NULL); + + if (!m_videoSink) + m_videoSink = gst_element_factory_make ("ximagesink", NULL); + + if (m_videoSink) { + // Check if the xv sink is usable + if (gst_element_set_state(m_videoSink, GST_STATE_READY) != GST_STATE_CHANGE_SUCCESS) { + gst_object_unref(GST_OBJECT(m_videoSink)); + m_videoSink = 0; + } else { + gst_element_set_state(m_videoSink, GST_STATE_NULL); + g_object_set(G_OBJECT(m_videoSink), "force-aspect-ratio", 1, (const char*)NULL); + qt_gst_object_ref_sink(GST_OBJECT (m_videoSink)); //Take ownership + } + } } QGstreamerVideoWidgetControl::~QGstreamerVideoWidgetControl() @@ -105,38 +121,17 @@ QGstreamerVideoWidgetControl::~QGstreamerVideoWidgetControl() void QGstreamerVideoWidgetControl::createVideoWidget() { - if (m_widget) + if (!m_videoSink || m_widget) return; m_widget = new QGstreamerVideoWidget; m_widget->installEventFilter(this); m_windowId = m_widget->winId(); - - m_videoSink = gst_element_factory_make ("xvimagesink", NULL); - if (m_videoSink) { - // Check if the xv sink is usable - if (gst_element_set_state(m_videoSink, GST_STATE_READY) != GST_STATE_CHANGE_SUCCESS) { - gst_object_unref(GST_OBJECT(m_videoSink)); - m_videoSink = 0; - } else { - gst_element_set_state(m_videoSink, GST_STATE_NULL); - - g_object_set(G_OBJECT(m_videoSink), "force-aspect-ratio", 1, (const char*)NULL); - } - } - - if (!m_videoSink) - m_videoSink = gst_element_factory_make ("ximagesink", NULL); - - qt_gst_object_ref_sink(GST_OBJECT (m_videoSink)); //Take ownership - - } GstElement *QGstreamerVideoWidgetControl::videoSink() { - createVideoWidget(); return m_videoSink; } diff --git a/src/plugins/gstreamer/camerabin/camerabinservice.cpp b/src/plugins/gstreamer/camerabin/camerabinservice.cpp index 969955f8..e4aa594d 100644 --- a/src/plugins/gstreamer/camerabin/camerabinservice.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinservice.cpp @@ -121,9 +121,23 @@ CameraBinService::CameraBinService(GstElementFactory *sourceFactory, QObject *pa #else m_videoWindow = new QGstreamerVideoWindow(this); #endif + // If the GStreamer sink element is not available (xvimagesink), don't provide + // the video window control since it won't work anyway. + if (!m_videoWindow->videoSink()) { + delete m_videoWindow; + m_videoWindow = 0; + } #if defined(HAVE_WIDGETS) m_videoWidgetControl = new QGstreamerVideoWidgetControl(this); + + // If the GStreamer sink element is not available (xvimagesink or ximagesink), don't provide + // the video widget control since it won't work anyway. + // QVideoWidget will fall back to QVideoRendererControl in that case. + if (!m_videoWidgetControl->videoSink()) { + delete m_videoWidgetControl; + m_videoWidgetControl = 0; + } #endif m_audioInputSelector = new QGstreamerAudioInputSelector(this); diff --git a/src/plugins/gstreamer/camerabin/camerabinservice.h b/src/plugins/gstreamer/camerabin/camerabinservice.h index cadae6e8..a38e3603 100644 --- a/src/plugins/gstreamer/camerabin/camerabinservice.h +++ b/src/plugins/gstreamer/camerabin/camerabinservice.h @@ -48,6 +48,7 @@ class CameraBinControl; class QGstreamerMessage; class QGstreamerBusHelper; class QGstreamerVideoRenderer; +class QGstreamerVideoWindow; class QGstreamerVideoWidgetControl; class QGstreamerElementFactory; class CameraBinMetaData; @@ -79,7 +80,7 @@ private: QMediaControl *m_videoOutput; QMediaControl *m_videoRenderer; - QMediaControl *m_videoWindow; + QGstreamerVideoWindow *m_videoWindow; #if defined(HAVE_WIDGETS) QGstreamerVideoWidgetControl *m_videoWidgetControl; #endif diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp index 97a165dc..e77feebc 100644 --- a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp +++ b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp @@ -102,10 +102,25 @@ QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObje m_videoInput->setDevice(m_videoInputDevice->deviceName(m_videoInputDevice->selectedDevice())); m_videoRenderer = new QGstreamerVideoRenderer(this); + m_videoWindow = new QGstreamerVideoWindow(this); + // If the GStreamer sink element is not available (xvimagesink), don't provide + // the video window control since it won't work anyway. + if (!m_videoWindow->videoSink()) { + delete m_videoWindow; + m_videoWindow = 0; + } #if defined(HAVE_WIDGETS) m_videoWidgetControl = new QGstreamerVideoWidgetControl(this); + + // If the GStreamer sink element is not available (xvimagesink or ximagesink), don't provide + // the video widget control since it won't work anyway. + // QVideoWidget will fall back to QVideoRendererControl in that case. + if (!m_videoWidgetControl->videoSink()) { + delete m_videoWidgetControl; + m_videoWidgetControl = 0; + } #endif m_imageCaptureControl = new QGstreamerImageCaptureControl(m_captureSession); } diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h index 7ff8ce25..e5166d13 100644 --- a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h +++ b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h @@ -48,6 +48,7 @@ class QGstreamerCameraControl; class QGstreamerMessage; class QGstreamerBusHelper; class QGstreamerVideoRenderer; +class QGstreamerVideoWindow; class QGstreamerVideoWidgetControl; class QGstreamerElementFactory; class QGstreamerCaptureMetaDataControl; @@ -81,9 +82,9 @@ private: QMediaControl *m_videoOutput; QGstreamerVideoRenderer *m_videoRenderer; - QMediaControl *m_videoWindow; + QGstreamerVideoWindow *m_videoWindow; #if defined(HAVE_WIDGETS) - QMediaControl *m_videoWidgetControl; + QGstreamerVideoWidgetControl *m_videoWidgetControl; #endif QGstreamerImageCaptureControl *m_imageCaptureControl; }; diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp index ce267d73..ddc828eb 100644 --- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp @@ -91,9 +91,23 @@ QGstreamerPlayerService::QGstreamerPlayerService(QObject *parent): #else m_videoWindow = new QGstreamerVideoWindow(this); #endif + // If the GStreamer sink element is not available (xvimagesink), don't provide + // the video window control since it won't work anyway. + if (!m_videoWindow->videoSink()) { + delete m_videoWindow; + m_videoWindow = 0; + } #if defined(HAVE_WIDGETS) m_videoWidget = new QGstreamerVideoWidgetControl(this); + + // If the GStreamer sink element is not available (xvimagesink or ximagesink), don't provide + // the video widget control since it won't work anyway. + // QVideoWidget will fall back to QVideoRendererControl in that case. + if (!m_videoWidget->videoSink()) { + delete m_videoWidget; + m_videoWidget = 0; + } #endif } diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h index f3081e98..0c5b4064 100644 --- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h @@ -50,6 +50,7 @@ class QGstreamerPlayerSession; class QGstreamerMetaDataProvider; class QGstreamerStreamsControl; class QGstreamerVideoRenderer; +class QGstreamerVideoWindow; class QGstreamerVideoWidgetControl; class QGStreamerAvailabilityControl; @@ -72,9 +73,9 @@ private: QMediaControl *m_videoOutput; QMediaControl *m_videoRenderer; - QMediaControl *m_videoWindow; + QGstreamerVideoWindow *m_videoWindow; #if defined(HAVE_WIDGETS) - QMediaControl *m_videoWidget; + QGstreamerVideoWidgetControl *m_videoWidget; #endif void increaseVideoRef(); From 8c73595c023d41ec832a34988116d380a08955f8 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 20 Feb 2015 14:35:55 +0100 Subject: [PATCH 03/11] Fix some qdoc-warnings. qtmultimedia/src/multimedia/qmediaserviceprovider.cpp:850: warning: Cannot find 'defaultDevice(...)' in '\fn' QMediaServiceSupportedDevicesInterface::defaultDevice(const QByteArray &service) const qtmultimedia/src/multimedia/qmediaserviceprovider.cpp:850: warning: Cannot find 'defaultDevice(...)' in '\fn' QByteArray QMediaServiceSupportedDevicesInterface::defaultDevice(const QByteArray &service) const qtmultimedia/src/multimedia/video/qabstractvideofilter.cpp:261: warning: Undocumented parameter 'parent' in QAbstractVideoFilter::QAbstractVideoFilter() Change-Id: I72bfc1dc7697cd1323cb7aad16539932ace9d8ea Reviewed-by: Leena Miettinen --- src/multimedia/qmediaserviceprovider.cpp | 6 +++--- src/multimedia/video/qabstractvideofilter.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/multimedia/qmediaserviceprovider.cpp b/src/multimedia/qmediaserviceprovider.cpp index fe372ac5..563af846 100644 --- a/src/multimedia/qmediaserviceprovider.cpp +++ b/src/multimedia/qmediaserviceprovider.cpp @@ -850,19 +850,19 @@ QMediaServiceProvider *QMediaServiceProvider::defaultServiceProvider() /*! \since 5.3 - \fn QMediaServiceSupportedDevicesInterface::defaultDevice(const QByteArray &service) const + \fn QByteArray QMediaServiceSupportedDevicesInterface::defaultDevice(const QByteArray &service) const Returns the default device for a \a service type. */ /*! - \fn QMediaServiceSupportedDevicesInterface::devices(const QByteArray &service) const + \fn QList QMediaServiceSupportedDevicesInterface::devices(const QByteArray &service) const Returns a list of devices available for a \a service type. */ /*! - \fn QMediaServiceSupportedDevicesInterface::deviceDescription(const QByteArray &service, const QByteArray &device) + \fn QString QMediaServiceSupportedDevicesInterface::deviceDescription(const QByteArray &service, const QByteArray &device) Returns the description of a \a device available for a \a service type. */ diff --git a/src/multimedia/video/qabstractvideofilter.cpp b/src/multimedia/video/qabstractvideofilter.cpp index f26d3c34..ccfe9ccd 100644 --- a/src/multimedia/video/qabstractvideofilter.cpp +++ b/src/multimedia/video/qabstractvideofilter.cpp @@ -259,7 +259,7 @@ QVideoFilterRunnable::~QVideoFilterRunnable() } /*! - Constructs a new QAbstractVideoFilter instance. + Constructs a new QAbstractVideoFilter instance with parent object \a parent. */ QAbstractVideoFilter::QAbstractVideoFilter(QObject *parent) : QObject(parent), From f86a3b7bb3a601da9705ef178fda9658cdbfebb0 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 23 Feb 2015 14:50:11 +0100 Subject: [PATCH 04/11] Add Linux and AMD support to the OpenCL filter example Change-Id: I1ea91f93677c53322c3867db6069e4362c58cebd Reviewed-by: Yoann Lopes --- .../video/qmlvideofilter_opencl/README | 24 +++++++------- .../video/qmlvideofilter_opencl/main.cpp | 32 +++++++++++++++++-- .../qmlvideofilter_opencl.pro | 8 ++--- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/examples/multimedia/video/qmlvideofilter_opencl/README b/examples/multimedia/video/qmlvideofilter_opencl/README index c239bed2..96b812b8 100644 --- a/examples/multimedia/video/qmlvideofilter_opencl/README +++ b/examples/multimedia/video/qmlvideofilter_opencl/README @@ -1,18 +1,18 @@ -This example performs some simple OpenCL operations on camera or video input which -is assumed to be provided in RGB format. The OpenCL operation is done on an -OpenGL texture using CL-GL interop, without any further readbacks or copies +This example performs some simple OpenCL operations on camera or video input +which is assumed to be provided in RGB format. The OpenCL operation is done on +an OpenGL texture using CL-GL interop, without any further readbacks or copies (except for the initial texture upload, when necessary). -Currently only OS X and Windows with desktop OpenGL (opengl32.dll) are supported. -On Windows you may need to edit testplugin.pro to specify the location of the OpenCL -headers and libraries. +Currently OS X, Windows with real OpenGL (opengl32.dll) and Linux (GLX only) are +supported. Note that an OpenCL implementation with GPU support is required. The +platform and device selection logic supports NVIDIA, AMD and Intel. Porting to +other platforms is probably simple, see clCreateContextFromType. -Note that an OpenCL implementation with GPU support is required. -The platform and device selection logic supports NVIDIA and Intel. -Porting to other platforms is probably simple, see clCreateContextFromType. -Note however that YUV formats, that are commonly used also for camera input -on some platforms, are not supported in this example. +On Windows you may need to edit testplugin.pro to specify the location of the +OpenCL headers and libraries. + +YUV formats are not supported in this example. This is probably not an issue an +OS X and Windows, but will most likely disable the example on Linux. Pass the name of a video file to perform video playback or launch without arguments to use the camera. - diff --git a/examples/multimedia/video/qmlvideofilter_opencl/main.cpp b/examples/multimedia/video/qmlvideofilter_opencl/main.cpp index bdaddb8d..af5aa8f6 100644 --- a/examples/multimedia/video/qmlvideofilter_opencl/main.cpp +++ b/examples/multimedia/video/qmlvideofilter_opencl/main.cpp @@ -46,6 +46,10 @@ #include #endif +#ifdef Q_OS_LINUX +#include +#endif + #include "rgbframehelper.h" static const char *openclSrc = @@ -119,10 +123,17 @@ CLFilterRunnable::CLFilterRunnable(CLFilter *filter) : // Set up OpenCL. QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); - cl_int err; cl_uint n; - if (clGetPlatformIDs(0, 0, &n) != CL_SUCCESS) { - qWarning("Failed to get platform ID count"); + cl_int err = clGetPlatformIDs(0, 0, &n); + if (err != CL_SUCCESS) { + qWarning("Failed to get platform ID count (error %d)", err); + if (err == -1001) { + qDebug("Could not find OpenCL implementation. ICD missing?" +#ifdef Q_OS_LINUX + " Check /etc/OpenCL/vendors." +#endif + ); + } return; } if (n == 0) { @@ -140,6 +151,7 @@ CLFilterRunnable::CLFilterRunnable(CLFilter *filter) : qDebug("GL_VENDOR: %s", vendor); const bool isNV = vendor && strstr(vendor, "NVIDIA"); const bool isIntel = vendor && strstr(vendor, "Intel"); + const bool isAMD = vendor && strstr(vendor, "ATI"); qDebug("Found %u OpenCL platforms:", n); for (cl_uint i = 0; i < n; ++i) { QByteArray name; @@ -153,6 +165,8 @@ CLFilterRunnable::CLFilterRunnable(CLFilter *filter) : platform = platformIds[i]; else if (isIntel && name.contains(QByteArrayLiteral("Intel"))) platform = platformIds[i]; + else if (isAMD && name.contains(QByteArrayLiteral("AMD"))) + platform = platformIds[i]; } qDebug("Using platform %p", platform); @@ -166,6 +180,18 @@ CLFilterRunnable::CLFilterRunnable(CLFilter *filter) : CL_GL_CONTEXT_KHR, (cl_context_properties) wglGetCurrentContext(), CL_WGL_HDC_KHR, (cl_context_properties) wglGetCurrentDC(), 0 }; +#elif defined(Q_OS_LINUX) + // An elegant alternative to glXGetCurrentContext. This will even survive + // (without interop) when using something other than GLX. + QVariant nativeGLXHandle = QOpenGLContext::currentContext()->nativeHandle(); + QGLXNativeContext nativeGLXContext; + if (!nativeGLXHandle.isNull() && nativeGLXHandle.canConvert()) + nativeGLXContext = nativeGLXHandle.value(); + else + qWarning("Failed to get the underlying GLX context from the current QOpenGLContext"); + cl_context_properties contextProps[] = { CL_CONTEXT_PLATFORM, (cl_context_properties) platform, + CL_GL_CONTEXT_KHR, (cl_context_properties) nativeGLXContext.context(), + 0 }; #endif m_clContext = clCreateContextFromType(contextProps, CL_DEVICE_TYPE_GPU, 0, 0, &err); diff --git a/examples/multimedia/video/qmlvideofilter_opencl/qmlvideofilter_opencl.pro b/examples/multimedia/video/qmlvideofilter_opencl/qmlvideofilter_opencl.pro index b391bb8d..c83929f7 100644 --- a/examples/multimedia/video/qmlvideofilter_opencl/qmlvideofilter_opencl.pro +++ b/examples/multimedia/video/qmlvideofilter_opencl/qmlvideofilter_opencl.pro @@ -12,10 +12,10 @@ OTHER_FILES = main.qml target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/video/qmlvideofilter_opencl INSTALLS += target -# Edit these as necessary -osx { - LIBS += -framework OpenCL -} else { +osx: LIBS += -framework OpenCL +unix: !osx: LIBS += -lOpenCL +win32:!winrt { + # Edit these as necessary INCLUDEPATH += c:/cuda/include LIBPATH += c:/cuda/lib/x64 LIBS += -lopengl32 -lOpenCL From b0f283b65e8dc9e9dbfe81b3fd58e8898cbb6676 Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Sun, 22 Feb 2015 13:15:03 +0100 Subject: [PATCH 05/11] ios: Do not leak texture cache objects Do not recreate m_textureCache if it already exists. This changes the memory allocation behavior of a simple iOS example program from linearly increasing to constant over time. Change-Id: I6ff13b586c653fb7b4cadfa9f4ebf985b07ee455 Reviewed-by: Yoann Lopes --- .../mediaplayer/avfvideoframerenderer_ios.mm | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.mm b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.mm index 0c8e8c52..b5ad8538 100644 --- a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.mm +++ b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.mm @@ -240,14 +240,16 @@ void AVFVideoFrameRenderer::initRenderer() //Need current context m_glContext->makeCurrent(m_offscreenSurface); - // Create a new open gl texture cache - CVReturn err = CVOGLTextureCacheCreate(kCFAllocatorDefault, NULL, - [EAGLContext currentContext], - NULL, &m_textureCache); - - if (err) { -#ifdef QT_DEBUG_AVF - qWarning("Error at CVOGLTextureCacheCreate %d", err); -#endif + if (!m_textureCache) { + // Create a new open gl texture cache + CVReturn err = CVOGLTextureCacheCreate(kCFAllocatorDefault, NULL, + [EAGLContext currentContext], + NULL, &m_textureCache); + if (err) { + #ifdef QT_DEBUG_AVF + qWarning("Error at CVOGLTextureCacheCreate %d", err); + #endif + } } + } From 4e07ff99f6ed49da3853c3f34cce393820105f1c Mon Sep 17 00:00:00 2001 From: Frank Osterfeld Date: Tue, 24 Feb 2015 22:40:55 +0100 Subject: [PATCH 06/11] ios: Enable volume and mute functionality AVPlayer::setVolume and setMute also exist on iOS, so no need to ifdef them to be OS X-only. As they require iOS 7.0, add runtime checks to ensure that the methods exist. Task-number: QTBUG-39240 Change-Id: I10810705cef8e5d8c18e4c79a15fa06425ea57f9 Reviewed-by: Yoann Lopes Reviewed-by: James Turner --- .../mediaplayer/avfmediaplayersession.mm | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm index 8b87be87..005c00a1 100644 --- a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm @@ -719,14 +719,17 @@ void AVFMediaPlayerSession::setVolume(int volume) if (m_volume == volume) return; - m_volume = volume; - -#if defined(Q_OS_OSX) AVPlayer *player = [(AVFMediaPlayerSessionObserver*)m_observer player]; - if (player) { - [[(AVFMediaPlayerSessionObserver*)m_observer player] setVolume:m_volume / 100.0f]; + if (!player) + return; + + if (![player respondsToSelector:@selector(setVolume:)]) { + qWarning("%s not implemented, requires iOS 7 or later", Q_FUNC_INFO); + return; } -#endif + + [player setVolume:m_volume / 100.0f]; + m_volume = volume; Q_EMIT volumeChanged(m_volume); } @@ -739,10 +742,19 @@ void AVFMediaPlayerSession::setMuted(bool muted) if (m_muted == muted) return; + AVPlayer *player = [(AVFMediaPlayerSessionObserver*)m_observer player]; + if (!player) + return; + + // iOS: setMuted exists since iOS 7.0, thus check if it exists + if (![player respondsToSelector:@selector(setMuted:)]) { + qWarning("%s not implemented, requires iOS 7 or later", Q_FUNC_INFO); + return; + } + + [player setMuted:m_muted]; m_muted = muted; -#if defined(Q_OS_OSX) - [[(AVFMediaPlayerSessionObserver*)m_observer player] setMuted:m_muted]; -#endif + Q_EMIT mutedChanged(muted); } From 5a0a3791a1e5e97612ee11833aa084ae76a8b725 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Fri, 19 Dec 2014 20:08:58 +0100 Subject: [PATCH 07/11] DirectShow: implemented QCameraViewfinderSettingsControl2. Change-Id: I42ed49676e2fbc7207d8fe4579ad1fc0d62df138 Reviewed-by: Christian Stromme --- src/plugins/directshow/camera/camera.pri | 6 +- .../directshow/camera/dscameraservice.cpp | 6 + .../directshow/camera/dscameraservice.h | 3 +- .../directshow/camera/dscamerasession.cpp | 189 +++++++++++++----- .../directshow/camera/dscamerasession.h | 14 +- .../dscameraviewfindersettingscontrol.cpp | 60 ++++++ .../dscameraviewfindersettingscontrol.h | 59 ++++++ 7 files changed, 283 insertions(+), 54 deletions(-) create mode 100644 src/plugins/directshow/camera/dscameraviewfindersettingscontrol.cpp create mode 100644 src/plugins/directshow/camera/dscameraviewfindersettingscontrol.h diff --git a/src/plugins/directshow/camera/camera.pri b/src/plugins/directshow/camera/camera.pri index 75fca4aa..3a532f47 100644 --- a/src/plugins/directshow/camera/camera.pri +++ b/src/plugins/directshow/camera/camera.pri @@ -13,7 +13,8 @@ HEADERS += \ $$PWD/dsvideodevicecontrol.h \ $$PWD/dsimagecapturecontrol.h \ $$PWD/dscamerasession.h \ - $$PWD/directshowglobal.h + $$PWD/directshowglobal.h \ + $$PWD/dscameraviewfindersettingscontrol.h SOURCES += \ $$PWD/dscameraservice.cpp \ @@ -21,7 +22,8 @@ SOURCES += \ $$PWD/dsvideorenderer.cpp \ $$PWD/dsvideodevicecontrol.cpp \ $$PWD/dsimagecapturecontrol.cpp \ - $$PWD/dscamerasession.cpp + $$PWD/dscamerasession.cpp \ + $$PWD/dscameraviewfindersettingscontrol.cpp *-msvc*:INCLUDEPATH += $$(DXSDK_DIR)/include LIBS += -lstrmiids -ldmoguids -luuid -lmsdmo -lole32 -loleaut32 diff --git a/src/plugins/directshow/camera/dscameraservice.cpp b/src/plugins/directshow/camera/dscameraservice.cpp index a7072ed1..9fcd4de7 100644 --- a/src/plugins/directshow/camera/dscameraservice.cpp +++ b/src/plugins/directshow/camera/dscameraservice.cpp @@ -40,6 +40,7 @@ #include "dsvideorenderer.h" #include "dsvideodevicecontrol.h" #include "dsimagecapturecontrol.h" +#include "dscameraviewfindersettingscontrol.h" QT_BEGIN_NAMESPACE @@ -51,11 +52,13 @@ DSCameraService::DSCameraService(QObject *parent): m_control = new DSCameraControl(m_session); m_videoDevice = new DSVideoDeviceControl(m_session); m_imageCapture = new DSImageCaptureControl(m_session); + m_viewfinderSettings = new DSCameraViewfinderSettingsControl(m_session); } DSCameraService::~DSCameraService() { delete m_control; + delete m_viewfinderSettings; delete m_videoDevice; delete m_videoRenderer; delete m_imageCapture; @@ -80,6 +83,9 @@ QMediaControl* DSCameraService::requestControl(const char *name) if (qstrcmp(name,QVideoDeviceSelectorControl_iid) == 0) return m_videoDevice; + if (qstrcmp(name, QCameraViewfinderSettingsControl2_iid) == 0) + return m_viewfinderSettings; + return 0; } diff --git a/src/plugins/directshow/camera/dscameraservice.h b/src/plugins/directshow/camera/dscameraservice.h index e40cd02e..c3c881d0 100644 --- a/src/plugins/directshow/camera/dscameraservice.h +++ b/src/plugins/directshow/camera/dscameraservice.h @@ -45,7 +45,7 @@ class DSCameraSession; class DSVideoOutputControl; class DSVideoDeviceControl; class DSImageCaptureControl; - +class DSCameraViewfinderSettingsControl; class DSCameraService : public QMediaService { @@ -65,6 +65,7 @@ private: DSVideoDeviceControl *m_videoDevice; QMediaControl *m_videoRenderer; DSImageCaptureControl *m_imageCapture; + DSCameraViewfinderSettingsControl *m_viewfinderSettings; }; QT_END_NAMESPACE diff --git a/src/plugins/directshow/camera/dscamerasession.cpp b/src/plugins/directshow/camera/dscamerasession.cpp index 3682ec41..375c0511 100644 --- a/src/plugins/directshow/camera/dscamerasession.cpp +++ b/src/plugins/directshow/camera/dscamerasession.cpp @@ -78,9 +78,6 @@ void _FreeMediaType(AM_MEDIA_TYPE& mt) } } // end namespace -typedef QList SizeList; -Q_GLOBAL_STATIC(SizeList, commonPreviewResolutions) - static HRESULT getPin(IBaseFilter *filter, PIN_DIRECTION pinDir, IPin **pin); @@ -148,6 +145,42 @@ private: DSCameraSession *m_session; }; +QVideoFrame::PixelFormat pixelFormatFromMediaSubtype(GUID uid) +{ + if (uid == MEDIASUBTYPE_ARGB32) + return QVideoFrame::Format_ARGB32; + else if (uid == MEDIASUBTYPE_RGB32) + return QVideoFrame::Format_RGB32; + else if (uid == MEDIASUBTYPE_RGB24) + return QVideoFrame::Format_RGB24; + else if (uid == MEDIASUBTYPE_RGB565) + return QVideoFrame::Format_RGB565; + else if (uid == MEDIASUBTYPE_RGB555) + return QVideoFrame::Format_RGB555; + else if (uid == MEDIASUBTYPE_AYUV) + return QVideoFrame::Format_AYUV444; + else if (uid == MEDIASUBTYPE_I420 || uid == MEDIASUBTYPE_IYUV) + return QVideoFrame::Format_YUV420P; + else if (uid == MEDIASUBTYPE_YV12) + return QVideoFrame::Format_YV12; + else if (uid == MEDIASUBTYPE_UYVY) + return QVideoFrame::Format_UYVY; + else if (uid == MEDIASUBTYPE_YUYV || uid == MEDIASUBTYPE_YUY2) + return QVideoFrame::Format_YUYV; + else if (uid == MEDIASUBTYPE_NV12) + return QVideoFrame::Format_NV12; + else if (uid == MEDIASUBTYPE_IMC1) + return QVideoFrame::Format_IMC1; + else if (uid == MEDIASUBTYPE_IMC2) + return QVideoFrame::Format_IMC2; + else if (uid == MEDIASUBTYPE_IMC3) + return QVideoFrame::Format_IMC3; + else if (uid == MEDIASUBTYPE_IMC4) + return QVideoFrame::Format_IMC4; + else + return QVideoFrame::Format_Invalid; +} + DSCameraSession::DSCameraSession(QObject *parent) : QObject(parent) @@ -167,7 +200,7 @@ DSCameraSession::DSCameraSession(QObject *parent) , m_currentImageId(-1) , m_status(QCamera::UnloadedStatus) { - ZeroMemory(&m_sourcePreferredFormat, sizeof(m_sourcePreferredFormat)); + ZeroMemory(&m_sourceFormat, sizeof(m_sourceFormat)); connect(this, SIGNAL(statusChanged(QCamera::Status)), this, SLOT(updateReadyForCapture())); @@ -188,6 +221,16 @@ void DSCameraSession::setDevice(const QString &device) m_sourceDeviceName = device; } +QCameraViewfinderSettings DSCameraSession::viewfinderSettings() const +{ + return m_status == QCamera::ActiveStatus ? m_actualViewfinderSettings : m_viewfinderSettings; +} + +void DSCameraSession::setViewfinderSettings(const QCameraViewfinderSettings &settings) +{ + m_viewfinderSettings = settings; +} + bool DSCameraSession::load() { unload(); @@ -214,9 +257,10 @@ bool DSCameraSession::unload() setStatus(QCamera::UnloadingStatus); m_needsHorizontalMirroring = false; - m_sourcePreferredResolution = QSize(); - _FreeMediaType(m_sourcePreferredFormat); - ZeroMemory(&m_sourcePreferredFormat, sizeof(m_sourcePreferredFormat)); + m_supportedViewfinderSettings.clear(); + Q_FOREACH (AM_MEDIA_TYPE f, m_supportedFormats) + _FreeMediaType(f); + m_supportedFormats.clear(); SAFE_RELEASE(m_sourceFilter); SAFE_RELEASE(m_previewSampleGrabber); SAFE_RELEASE(m_previewFilter); @@ -302,6 +346,9 @@ bool DSCameraSession::stopPreview() disconnectGraph(); + _FreeMediaType(m_sourceFormat); + ZeroMemory(&m_sourceFormat, sizeof(m_sourceFormat)); + m_previewStarted = false; setStatus(QCamera::LoadedStatus); return true; @@ -581,9 +628,6 @@ bool DSCameraSession::createFilterGraph() failed: m_needsHorizontalMirroring = false; - m_sourcePreferredResolution = QSize(); - _FreeMediaType(m_sourcePreferredFormat); - ZeroMemory(&m_sourcePreferredFormat, sizeof(m_sourcePreferredFormat)); SAFE_RELEASE(m_sourceFilter); SAFE_RELEASE(m_previewSampleGrabber); SAFE_RELEASE(m_previewFilter); @@ -596,6 +640,34 @@ failed: bool DSCameraSession::configurePreviewFormat() { + // Resolve viewfinder settings + int settingsIndex = 0; + QCameraViewfinderSettings resolvedViewfinderSettings; + Q_FOREACH (const QCameraViewfinderSettings &s, m_supportedViewfinderSettings) { + if ((m_viewfinderSettings.resolution().isEmpty() || m_viewfinderSettings.resolution() == s.resolution()) + && (qFuzzyIsNull(m_viewfinderSettings.minimumFrameRate()) || qFuzzyCompare((float)m_viewfinderSettings.minimumFrameRate(), (float)s.minimumFrameRate())) + && (qFuzzyIsNull(m_viewfinderSettings.maximumFrameRate()) || qFuzzyCompare((float)m_viewfinderSettings.maximumFrameRate(), (float)s.maximumFrameRate())) + && (m_viewfinderSettings.pixelFormat() == QVideoFrame::Format_Invalid || m_viewfinderSettings.pixelFormat() == s.pixelFormat())) { + resolvedViewfinderSettings = s; + break; + } + ++settingsIndex; + } + + if (resolvedViewfinderSettings.isNull()) { + qWarning("Invalid viewfinder settings"); + return false; + } + + m_actualViewfinderSettings = resolvedViewfinderSettings; + + _CopyMediaType(&m_sourceFormat, &m_supportedFormats[settingsIndex]); + // Set frame rate. + // We don't care about the minimumFrameRate, DirectShow only allows to set an + // average frame rate, so set that to the maximumFrameRate. + VIDEOINFOHEADER *videoInfo = reinterpret_cast(m_sourceFormat.pbFormat); + videoInfo->AvgTimePerFrame = 10000000 / resolvedViewfinderSettings.maximumFrameRate(); + // We only support RGB32, if the capture source doesn't support // that format, the graph builder will automatically insert a // converter. @@ -607,7 +679,7 @@ bool DSCameraSession::configurePreviewFormat() } m_previewPixelFormat = QVideoFrame::Format_RGB32; - m_previewSize = m_sourcePreferredResolution; + m_previewSize = resolvedViewfinderSettings.resolution(); m_previewSurfaceFormat = QVideoSurfaceFormat(m_previewSize, m_previewPixelFormat, QAbstractVideoBuffer::NoHandle); @@ -624,7 +696,7 @@ bool DSCameraSession::configurePreviewFormat() return false; } - hr = pConfig->SetFormat(&m_sourcePreferredFormat); + hr = pConfig->SetFormat(&m_sourceFormat); pConfig->Release(); @@ -716,6 +788,11 @@ void DSCameraSession::disconnectGraph() m_filterGraph->RemoveFilter(m_sourceFilter); } +static bool qt_frameRateRangeGreaterThan(const QCamera::FrameRateRange &r1, const QCamera::FrameRateRange &r2) +{ + return r1.second > r2.second; +} + void DSCameraSession::updateSourceCapabilities() { HRESULT hr; @@ -724,10 +801,11 @@ void DSCameraSession::updateSourceCapabilities() VIDEO_STREAM_CONFIG_CAPS scc; IAMStreamConfig* pConfig = 0; + m_supportedViewfinderSettings.clear(); m_needsHorizontalMirroring = false; - m_sourcePreferredResolution = QSize(); - _FreeMediaType(m_sourcePreferredFormat); - ZeroMemory(&m_sourcePreferredFormat, sizeof(m_sourcePreferredFormat)); + Q_FOREACH (AM_MEDIA_TYPE f, m_supportedFormats) + _FreeMediaType(f); + m_supportedFormats.clear(); IAMVideoControl *pVideoControl = 0; hr = m_graphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, @@ -774,53 +852,68 @@ void DSCameraSession::updateSourceCapabilities() return; } - // Use preferred pixel format (first in the list) - // Then, pick the highest available resolution among the typical resolutions - // used for camera preview. - if (commonPreviewResolutions->isEmpty()) - populateCommonResolutions(); - - long maxPixelCount = 0; for (int iIndex = 0; iIndex < iCount; ++iIndex) { hr = pConfig->GetStreamCaps(iIndex, &pmt, reinterpret_cast(&scc)); if (hr == S_OK) { - if ((pmt->majortype == MEDIATYPE_Video) && - (pmt->formattype == FORMAT_VideoInfo) && - (!m_sourcePreferredFormat.cbFormat || - m_sourcePreferredFormat.subtype == pmt->subtype)) { + QVideoFrame::PixelFormat pixelFormat = pixelFormatFromMediaSubtype(pmt->subtype); + + if (pmt->majortype == MEDIATYPE_Video + && pmt->formattype == FORMAT_VideoInfo + && pixelFormat != QVideoFrame::Format_Invalid) { pvi = reinterpret_cast(pmt->pbFormat); - QSize resolution(pvi->bmiHeader.biWidth, pvi->bmiHeader.biHeight); - long pixelCount = resolution.width() * resolution.height(); - if (!m_sourcePreferredFormat.cbFormat || - (pixelCount > maxPixelCount && commonPreviewResolutions->contains(resolution))) { - _FreeMediaType(m_sourcePreferredFormat); - _CopyMediaType(&m_sourcePreferredFormat, pmt); - m_sourcePreferredResolution = resolution; - maxPixelCount = pixelCount; + QList frameRateRanges; + + if (pVideoControl) { + IPin *pPin = 0; + hr = getPin(m_sourceFilter, PINDIR_OUTPUT, &pPin); + if (FAILED(hr)) { + qWarning() << "Failed to get the pin for the video control"; + } else { + long listSize = 0; + LONGLONG *frameRates = 0; + SIZE size = { resolution.width(), resolution.height() }; + if (SUCCEEDED(pVideoControl->GetFrameRateList(pPin, iIndex, size, + &listSize, &frameRates))) { + for (long i = 0; i < listSize; ++i) { + qreal fr = qreal(10000000) / frameRates[i]; + frameRateRanges.append(QCamera::FrameRateRange(fr, fr)); + } + + // Make sure higher frame rates come first + std::sort(frameRateRanges.begin(), frameRateRanges.end(), qt_frameRateRangeGreaterThan); + } + pPin->Release(); + } } + + if (frameRateRanges.isEmpty()) { + frameRateRanges.append(QCamera::FrameRateRange(qreal(10000000) / scc.MaxFrameInterval, + qreal(10000000) / scc.MinFrameInterval)); + } + + Q_FOREACH (const QCamera::FrameRateRange &frameRateRange, frameRateRanges) { + QCameraViewfinderSettings settings; + settings.setResolution(resolution); + settings.setMinimumFrameRate(frameRateRange.first); + settings.setMaximumFrameRate(frameRateRange.second); + settings.setPixelFormat(pixelFormat); + m_supportedViewfinderSettings.append(settings); + + AM_MEDIA_TYPE format; + _CopyMediaType(&format, pmt); + m_supportedFormats.append(format); + } + + } _FreeMediaType(*pmt); } } pConfig->Release(); - - if (!m_sourcePreferredResolution.isValid()) - m_sourcePreferredResolution = QSize(640, 480); -} - -void DSCameraSession::populateCommonResolutions() -{ - commonPreviewResolutions->append(QSize(1920, 1080)); // 1080p - commonPreviewResolutions->append(QSize(1280, 720)); // 720p - commonPreviewResolutions->append(QSize(1024, 576)); // WSVGA - commonPreviewResolutions->append(QSize(720, 480)); // 480p (16:9) - commonPreviewResolutions->append(QSize(640, 480)); // 480p (4:3) - commonPreviewResolutions->append(QSize(352, 288)); // CIF - commonPreviewResolutions->append(QSize(320, 240)); // QVGA } HRESULT getPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin) diff --git a/src/plugins/directshow/camera/dscamerasession.h b/src/plugins/directshow/camera/dscamerasession.h index f2ff3925..9ac12146 100644 --- a/src/plugins/directshow/camera/dscamerasession.h +++ b/src/plugins/directshow/camera/dscamerasession.h @@ -91,6 +91,12 @@ public: void setSurface(QAbstractVideoSurface* surface); + QCameraViewfinderSettings viewfinderSettings() const; + void setViewfinderSettings(const QCameraViewfinderSettings &settings); + + QList supportedViewfinderSettings() const + { return m_supportedViewfinderSettings; } + Q_SIGNALS: void statusChanged(QCamera::Status); void imageExposed(int id); @@ -105,7 +111,6 @@ private Q_SLOTS: private: void setStatus(QCamera::Status status); - void populateCommonResolutions(); void onFrameAvailable(const char *frameData, long len); void saveCapturedImage(int id, const QImage &image, const QString &path); @@ -126,9 +131,10 @@ private: // Source (camera) QString m_sourceDeviceName; IBaseFilter* m_sourceFilter; - AM_MEDIA_TYPE m_sourcePreferredFormat; - QSize m_sourcePreferredResolution; bool m_needsHorizontalMirroring; + QList m_supportedFormats; + QList m_supportedViewfinderSettings; + AM_MEDIA_TYPE m_sourceFormat; // Preview IBaseFilter *m_previewFilter; @@ -140,6 +146,8 @@ private: QVideoSurfaceFormat m_previewSurfaceFormat; QVideoFrame::PixelFormat m_previewPixelFormat; QSize m_previewSize; + QCameraViewfinderSettings m_viewfinderSettings; + QCameraViewfinderSettings m_actualViewfinderSettings; // Image capture QString m_imageCaptureFileName; diff --git a/src/plugins/directshow/camera/dscameraviewfindersettingscontrol.cpp b/src/plugins/directshow/camera/dscameraviewfindersettingscontrol.cpp new file mode 100644 index 00000000..e3215a89 --- /dev/null +++ b/src/plugins/directshow/camera/dscameraviewfindersettingscontrol.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "dscameraviewfindersettingscontrol.h" +#include "dscamerasession.h" + +QT_BEGIN_NAMESPACE + +DSCameraViewfinderSettingsControl::DSCameraViewfinderSettingsControl(DSCameraSession *session) + : QCameraViewfinderSettingsControl2(session) + , m_session(session) +{ +} + +QList DSCameraViewfinderSettingsControl::supportedViewfinderSettings() const +{ + return m_session->supportedViewfinderSettings(); +} + +QCameraViewfinderSettings DSCameraViewfinderSettingsControl::viewfinderSettings() const +{ + return m_session->viewfinderSettings(); +} + +void DSCameraViewfinderSettingsControl::setViewfinderSettings(const QCameraViewfinderSettings &settings) +{ + m_session->setViewfinderSettings(settings); +} + +QT_END_NAMESPACE diff --git a/src/plugins/directshow/camera/dscameraviewfindersettingscontrol.h b/src/plugins/directshow/camera/dscameraviewfindersettingscontrol.h new file mode 100644 index 00000000..30d4122a --- /dev/null +++ b/src/plugins/directshow/camera/dscameraviewfindersettingscontrol.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DSCAMERAVIEWFINDERSETTINGSCONTROL_H +#define DSCAMERAVIEWFINDERSETTINGSCONTROL_H + +#include + +QT_BEGIN_NAMESPACE + +class DSCameraSession; + +class DSCameraViewfinderSettingsControl : public QCameraViewfinderSettingsControl2 +{ +public: + DSCameraViewfinderSettingsControl(DSCameraSession *session); + + QList supportedViewfinderSettings() const; + + QCameraViewfinderSettings viewfinderSettings() const; + void setViewfinderSettings(const QCameraViewfinderSettings &settings); + +private: + DSCameraSession *m_session; +}; + +QT_END_NAMESPACE + +#endif // DSCAMERAVIEWFINDERSETTINGSCONTROL_H From d27f493df06fb19125f5cd8010831a33b7f35ba8 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Fri, 20 Feb 2015 09:43:39 +0100 Subject: [PATCH 08/11] Add AVF camera utilities - general aux. functions/classes Move configuration lock class into avfutility.h + add several standalone functions to work with AVCaptureDeviceFormat/AVCaptureDevice etc. - utility functions that are not specific to any class. Change-Id: Idba544248772a3b2a4e00dddb377b1c0e62b8085 Reviewed-by: Yoann Lopes --- .../camera/avfcameraexposurecontrol.mm | 2 +- .../camera/avfcamerafocuscontrol.mm | 2 +- ...configurationlock.h => avfcamerautility.h} | 41 +- .../avfoundation/camera/avfcamerautility.mm | 386 ++++++++++++++++++ .../camera/avfcamerazoomcontrol.mm | 2 +- src/plugins/avfoundation/camera/camera.pro | 5 +- 6 files changed, 430 insertions(+), 8 deletions(-) rename src/plugins/avfoundation/camera/{avfconfigurationlock.h => avfcamerautility.h} (61%) create mode 100644 src/plugins/avfoundation/camera/avfcamerautility.mm diff --git a/src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm b/src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm index 9315e32a..9e56ce19 100644 --- a/src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm +++ b/src/plugins/avfoundation/camera/avfcameraexposurecontrol.mm @@ -32,7 +32,7 @@ ****************************************************************************/ #include "avfcameraexposurecontrol.h" -#include "avfconfigurationlock.h" +#include "avfcamerautility.h" #include "avfcamerasession.h" #include "avfcameraservice.h" #include "avfcameradebug.h" diff --git a/src/plugins/avfoundation/camera/avfcamerafocuscontrol.mm b/src/plugins/avfoundation/camera/avfcamerafocuscontrol.mm index da9b563a..25c69c74 100644 --- a/src/plugins/avfoundation/camera/avfcamerafocuscontrol.mm +++ b/src/plugins/avfoundation/camera/avfcamerafocuscontrol.mm @@ -32,7 +32,7 @@ ****************************************************************************/ #include "avfcamerafocuscontrol.h" -#include "avfconfigurationlock.h" +#include "avfcamerautility.h" #include "avfcameraservice.h" #include "avfcamerasession.h" #include "avfcameradebug.h" diff --git a/src/plugins/avfoundation/camera/avfconfigurationlock.h b/src/plugins/avfoundation/camera/avfcamerautility.h similarity index 61% rename from src/plugins/avfoundation/camera/avfconfigurationlock.h rename to src/plugins/avfoundation/camera/avfcamerautility.h index 8bb9428a..1c13736e 100644 --- a/src/plugins/avfoundation/camera/avfconfigurationlock.h +++ b/src/plugins/avfoundation/camera/avfcamerautility.h @@ -31,15 +31,20 @@ ** ****************************************************************************/ -#ifndef AVFCONFIGURATIONLOCK_H -#define AVFCONFIGURATIONLOCK_H +#ifndef AVFCAMERAUTILITY_H +#define AVFCAMERAUTILITY_H +#include #include +#include #include +#include +#include #include -@class AVCaptureDevice; +// In case we have SDK below 10.7/7.0: +@class AVCaptureDeviceFormat; QT_BEGIN_NAMESPACE @@ -73,6 +78,36 @@ private: bool m_locked; }; +inline QSysInfo::MacVersion qt_OS_limit(QSysInfo::MacVersion osxVersion, + QSysInfo::MacVersion iosVersion) +{ +#ifdef Q_OS_OSX + Q_UNUSED(iosVersion) + return osxVersion; +#else + Q_UNUSED(osxVersion) + return iosVersion; +#endif +} + +typedef QPair AVFPSRange; +AVFPSRange qt_connection_framerates(AVCaptureConnection *videoConnection); +typedef QPair AVFRational; +AVFRational qt_float_to_rational(qreal par, int limit); + +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0) + +bool qt_is_video_range_subtype(AVCaptureDeviceFormat *format); +QSize qt_device_format_resolution(AVCaptureDeviceFormat *format); +QSize qt_device_format_high_resolution(AVCaptureDeviceFormat *format); +QSize qt_device_format_pixel_aspect_ratio(AVCaptureDeviceFormat *format); +QVector qt_device_format_framerates(AVCaptureDeviceFormat *format); +AVCaptureDeviceFormat *qt_find_best_resolution_match(AVCaptureDevice *captureDevice, const QSize &res); +AVCaptureDeviceFormat *qt_find_best_framerate_match(AVCaptureDevice *captureDevice, Float64 fps); +AVFrameRateRange *qt_find_supported_framerate_range(AVCaptureDeviceFormat *format, Float64 fps); + +#endif + QT_END_NAMESPACE #endif diff --git a/src/plugins/avfoundation/camera/avfcamerautility.mm b/src/plugins/avfoundation/camera/avfcamerautility.mm new file mode 100644 index 00000000..554c25cb --- /dev/null +++ b/src/plugins/avfoundation/camera/avfcamerautility.mm @@ -0,0 +1,386 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "avfcamerautility.h" +#include "avfcameradebug.h" + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +AVFPSRange qt_connection_framerates(AVCaptureConnection *videoConnection) +{ + Q_ASSERT(videoConnection); + + AVFPSRange newRange; + // "The value in the videoMinFrameDuration is equivalent to the reciprocal + // of the maximum framerate, the value in the videoMaxFrameDuration is equivalent + // to the reciprocal of the minimum framerate." + if (videoConnection.supportsVideoMinFrameDuration) { + const CMTime cmMin = videoConnection.videoMinFrameDuration; + if (CMTimeCompare(cmMin, kCMTimeInvalid)) { // Has some non-default value: + if (const Float64 minSeconds = CMTimeGetSeconds(cmMin)) + newRange.second = 1. / minSeconds; + } + } + + if (videoConnection.supportsVideoMaxFrameDuration) { + const CMTime cmMax = videoConnection.videoMaxFrameDuration; + if (CMTimeCompare(cmMax, kCMTimeInvalid)) { + if (const Float64 maxSeconds = CMTimeGetSeconds(cmMax)) + newRange.first = 1. / maxSeconds; + } + } + + return newRange; +} + +AVFRational qt_float_to_rational(qreal par, int limit) +{ + Q_ASSERT(limit > 0); + + // In Qt we represent pixel aspect ratio + // as a rational number (we use QSize). + // AVFoundation describes dimensions in pixels + // and in pixels with width multiplied by PAR. + // Represent this PAR as a ratio. + int a = 0, b = 1, c = 1, d = 1; + qreal mid = 0.; + while (b <= limit && d <= limit) { + mid = qreal(a + c) / (b + d); + + if (qAbs(par - mid) < 0.000001) { + if (b + d <= limit) + return AVFRational(a + c, b + d); + else if (d > b) + return AVFRational(c, d); + else + return AVFRational(a, b); + } else if (par > mid) { + a = a + c; + b = b + d; + } else { + c = a + c; + d = b + d; + } + } + + if (b > limit) + return AVFRational(c, d); + + return AVFRational(a, b); +} + +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0) + +bool qt_is_video_range_subtype(AVCaptureDeviceFormat *format) +{ + Q_ASSERT(format); +#ifdef Q_OS_IOS + // Use only 420f on iOS, not 420v. + const FourCharCode subType = CMFormatDescriptionGetMediaSubType(format.formatDescription); + return subType == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; +#else + Q_UNUSED(format) +#endif + return false; +} + +namespace { + +inline bool qt_area_sane(const QSize &size) +{ + return !size.isNull() && size.isValid() + && std::numeric_limits::max() / size.width() >= size.height(); +} + +inline bool avf_format_compare(AVCaptureDeviceFormat *f1, AVCaptureDeviceFormat *f2) +{ + Q_ASSERT(f1); + Q_ASSERT(f2); + const QSize r1(qt_device_format_resolution(f1)); + const QSize r2(qt_device_format_resolution(f2)); + return r1.width() > r2.width() && r1.height() > r2.height(); +} + +QVector qt_sort_device_formats(AVCaptureDevice *captureDevice) +{ + // Select only formats with framerate ranges + sort them by resoluions, + Q_ASSERT(captureDevice); + + QVectorsorted; + + NSArray *formats = captureDevice.formats; + if (!formats || !formats.count) + return sorted; + + sorted.reserve(formats.count); + for (AVCaptureDeviceFormat *format in formats) { + if (qt_is_video_range_subtype(format)) + continue; + if (format.videoSupportedFrameRateRanges && format.videoSupportedFrameRateRanges.count) { + const QSize resolution(qt_device_format_resolution(format)); + if (!resolution.isNull() && resolution.isValid()) + sorted << format; + } + } + + std::sort(sorted.begin(), sorted.end(), avf_format_compare); + return sorted; +} + +Float64 qt_find_min_framerate_distance(AVCaptureDeviceFormat *format, Float64 fps) +{ + Q_ASSERT(format && format.videoSupportedFrameRateRanges + && format.videoSupportedFrameRateRanges.count); + + AVFrameRateRange *range = [format.videoSupportedFrameRateRanges objectAtIndex:0]; + Float64 distance = qAbs(range.maxFrameRate - fps); + for (NSUInteger i = 1, e = format.videoSupportedFrameRateRanges.count; i < e; ++i) { + range = [format.videoSupportedFrameRateRanges objectAtIndex:i]; + distance = qMin(distance, qAbs(range.maxFrameRate - fps)); + } + + return distance; +} + +} // Unnamed namespace. + +QSize qt_device_format_resolution(AVCaptureDeviceFormat *format) +{ + Q_ASSERT(format); + if (!format.formatDescription) + return QSize(); + + const CMVideoDimensions res = CMVideoFormatDescriptionGetDimensions(format.formatDescription); + return QSize(res.width, res.height); +} + +QSize qt_device_format_high_resolution(AVCaptureDeviceFormat *format) +{ + Q_ASSERT(format); + QSize res; +#if defined(Q_OS_IOS) && QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_8_0) { + const CMVideoDimensions hrDim(format.highResolutionStillImageDimensions); + res.setWidth(hrDim.width); + res.setHeight(hrDim.height); + } +#endif + return res; +} + +QVector qt_device_format_framerates(AVCaptureDeviceFormat *format) +{ + Q_ASSERT(format); + + QVector qtRanges; + + if (!format.videoSupportedFrameRateRanges || !format.videoSupportedFrameRateRanges.count) + return qtRanges; + + qtRanges.reserve(format.videoSupportedFrameRateRanges.count); + for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) + qtRanges << AVFPSRange(range.minFrameRate, range.maxFrameRate); + + return qtRanges; +} + +QSize qt_device_format_pixel_aspect_ratio(AVCaptureDeviceFormat *format) +{ + Q_ASSERT(format); + + if (!format.formatDescription) { + qDebugCamera() << Q_FUNC_INFO << "no format description found"; + return QSize(); + } + + const CMVideoDimensions res = CMVideoFormatDescriptionGetDimensions(format.formatDescription); + const CGSize resPAR = CMVideoFormatDescriptionGetPresentationDimensions(format.formatDescription, true, false); + + if (qAbs(resPAR.width - res.width) < 1.) { + // "Pixel aspect ratio is used to adjust the width, leaving the height alone." + return QSize(1, 1); + } + + if (!res.width || !resPAR.width) + return QSize(); + + const AVFRational asRatio(qt_float_to_rational(resPAR.width > res.width + ? res.width / qreal(resPAR.width) + : resPAR.width / qreal(res.width), 200)); + return QSize(asRatio.first, asRatio.second); +} + +AVCaptureDeviceFormat *qt_find_best_resolution_match(AVCaptureDevice *captureDevice, const QSize &request) +{ + Q_ASSERT(captureDevice); + Q_ASSERT(!request.isNull() && request.isValid()); + + if (!captureDevice.formats || !captureDevice.formats.count) + return 0; + + for (AVCaptureDeviceFormat *format in captureDevice.formats) { + if (qt_is_video_range_subtype(format)) + continue; + if (qt_device_format_resolution(format) == request) + return format; + // iOS only (still images). + if (qt_device_format_high_resolution(format) == request) + return format; + } + + if (!qt_area_sane(request)) + return 0; + + typedef QPair FormatPair; + + QVector formats; + formats.reserve(captureDevice.formats.count); + + for (AVCaptureDeviceFormat *format in captureDevice.formats) { + if (qt_is_video_range_subtype(format)) + continue; + const QSize res(qt_device_format_resolution(format)); + if (!res.isNull() && res.isValid() && qt_area_sane(res)) + formats << FormatPair(res, format); + const QSize highRes(qt_device_format_high_resolution(format)); + if (!highRes.isNull() && highRes.isValid() && qt_area_sane(highRes)) + formats << FormatPair(highRes, format); + } + + if (!formats.size()) + return 0; + + AVCaptureDeviceFormat *best = formats[0].second; + QSize next(formats[0].first); + int wDiff = qAbs(request.width() - next.width()); + int hDiff = qAbs(request.height() - next.height()); + const int area = request.width() * request.height(); + int areaDiff = qAbs(area - next.width() * next.height()); + for (int i = 1; i < formats.size(); ++i) { + next = formats[i].first; + const int newWDiff = qAbs(next.width() - request.width()); + const int newHDiff = qAbs(next.height() - request.height()); + const int newAreaDiff = qAbs(area - next.width() * next.height()); + + if ((newWDiff < wDiff && newHDiff < hDiff) + || ((newWDiff <= wDiff || newHDiff <= hDiff) && newAreaDiff <= areaDiff)) { + wDiff = newWDiff; + hDiff = newHDiff; + best = formats[i].second; + areaDiff = newAreaDiff; + } + } + + return best; +} + +AVCaptureDeviceFormat *qt_find_best_framerate_match(AVCaptureDevice *captureDevice, Float64 fps) +{ + Q_ASSERT(captureDevice); + Q_ASSERT(fps > 0.); + + const qreal epsilon = 0.1; + + // Sort formats by their resolution. + const QVector sorted(qt_sort_device_formats(captureDevice)); + if (!sorted.size()) + return nil; + + for (int i = 0; i < sorted.size(); ++i) { + AVCaptureDeviceFormat *format = sorted[i]; + for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) { + if (range.maxFrameRate - range.minFrameRate < epsilon) { + // On OS X ranges are points (built-in camera). + if (qAbs(fps - range.maxFrameRate) < epsilon) + return format; + } + + if (fps >= range.minFrameRate && fps <= range.maxFrameRate) + return format; + } + } + + Float64 distance = qt_find_min_framerate_distance(sorted[0], fps); + AVCaptureDeviceFormat *match = sorted[0]; + for (int i = 1; i < sorted.size(); ++i) { + const Float64 newDistance = qt_find_min_framerate_distance(sorted[i], fps); + if (newDistance < distance) { + distance = newDistance; + match = sorted[i]; + } + } + + return match; +} + +AVFrameRateRange *qt_find_supported_framerate_range(AVCaptureDeviceFormat *format, Float64 fps) +{ + Q_ASSERT(format && format.videoSupportedFrameRateRanges + && format.videoSupportedFrameRateRanges.count); + + const qreal epsilon = 0.1; + + for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) { + if (range.maxFrameRate - range.minFrameRate < epsilon) { + // On OS X ranges are points (built-in camera). + if (qAbs(fps - range.maxFrameRate) < epsilon) + return range; + } + + if (fps >= range.minFrameRate && fps <= range.maxFrameRate) + return range; + } + + AVFrameRateRange *match = [format.videoSupportedFrameRateRanges objectAtIndex:0]; + Float64 distance = qAbs(match.maxFrameRate - fps); + for (NSUInteger i = 1, e = format.videoSupportedFrameRateRanges.count; i < e; ++i) { + AVFrameRateRange *range = [format.videoSupportedFrameRateRanges objectAtIndex:i]; + const Float64 newDistance = qAbs(range.maxFrameRate - fps); + if (newDistance < distance) { + distance = newDistance; + match = range; + } + } + + return match; +} + +#endif // SDK + +QT_END_NAMESPACE diff --git a/src/plugins/avfoundation/camera/avfcamerazoomcontrol.mm b/src/plugins/avfoundation/camera/avfcamerazoomcontrol.mm index 3328b315..8206112b 100644 --- a/src/plugins/avfoundation/camera/avfcamerazoomcontrol.mm +++ b/src/plugins/avfoundation/camera/avfcamerazoomcontrol.mm @@ -32,8 +32,8 @@ ****************************************************************************/ #include "avfcamerazoomcontrol.h" -#include "avfconfigurationlock.h" #include "avfcameraservice.h" +#include "avfcamerautility.h" #include "avfcamerasession.h" #include "avfcameracontrol.h" #include "avfcameradebug.h" diff --git a/src/plugins/avfoundation/camera/camera.pro b/src/plugins/avfoundation/camera/camera.pro index 88bb8c37..82084a1b 100644 --- a/src/plugins/avfoundation/camera/camera.pro +++ b/src/plugins/avfoundation/camera/camera.pro @@ -39,7 +39,7 @@ HEADERS += \ avfcameradevicecontrol.h \ avfcamerafocuscontrol.h \ avfcameraexposurecontrol.h \ - avfconfigurationlock.h + avfcamerautility.h OBJECTIVE_SOURCES += \ avfcameraserviceplugin.mm \ @@ -57,7 +57,8 @@ OBJECTIVE_SOURCES += \ avfcameradevicecontrol.mm \ avfcamerarenderercontrol.mm \ avfcamerafocuscontrol.mm \ - avfcameraexposurecontrol.mm + avfcameraexposurecontrol.mm \ + avfcamerautility.mm ios { From 0d783b730372ffaeebc22e882a3c65d83445932f Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Wed, 28 Jan 2015 17:51:19 +0100 Subject: [PATCH 09/11] Viewfinder settings control (2) - version for iOS/OS X QCameraViewfinderSettingsControl2 - version for AV foundation plugin (the new settings control interface implemented). Change-Id: I3fbfb87925e57c914d43eb711fa5422e26981207 Reviewed-by: Yoann Lopes --- .../camera/avfcamerarenderercontrol.h | 2 + .../camera/avfcamerarenderercontrol.mm | 18 +- .../avfoundation/camera/avfcameraservice.h | 4 + .../avfoundation/camera/avfcameraservice.mm | 7 + .../avfoundation/camera/avfcamerasession.h | 1 + .../avfoundation/camera/avfcamerasession.mm | 11 + .../avfcameraviewfindersettingscontrol.h | 91 +++ .../avfcameraviewfindersettingscontrol.mm | 579 ++++++++++++++++++ src/plugins/avfoundation/camera/camera.pro | 6 +- 9 files changed, 716 insertions(+), 3 deletions(-) create mode 100644 src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h create mode 100644 src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm diff --git a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.h b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.h index 5fbbcb67..92ab75bd 100644 --- a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.h +++ b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.h @@ -61,6 +61,8 @@ public: void configureAVCaptureSession(AVFCameraSession *cameraSession); void syncHandleViewfinderFrame(const QVideoFrame &frame); + AVCaptureVideoDataOutput *videoDataOutput() const; + Q_SIGNALS: void surfaceChanged(QAbstractVideoSurface *surface); diff --git a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm index 05edd0a9..87bfeb82 100644 --- a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm +++ b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm @@ -31,6 +31,7 @@ ** ****************************************************************************/ +#include "avfcameraviewfindersettingscontrol.h" #include "avfcamerarenderercontrol.h" #include "avfcamerasession.h" #include "avfcameraservice.h" @@ -129,7 +130,17 @@ private: int height = CVPixelBufferGetHeight(imageBuffer); QAbstractVideoBuffer *buffer = new CVPixelBufferVideoBuffer(imageBuffer); - QVideoFrame frame(buffer, QSize(width, height), QVideoFrame::Format_RGB32); + + QVideoFrame::PixelFormat format = QVideoFrame::Format_RGB32; + if ([captureOutput isKindOfClass:[AVCaptureVideoDataOutput class]]) { + NSDictionary *settings = ((AVCaptureVideoDataOutput *)captureOutput).videoSettings; + if (settings && [settings objectForKey:(id)kCVPixelBufferPixelFormatTypeKey]) { + NSNumber *avf = [settings objectForKey:(id)kCVPixelBufferPixelFormatTypeKey]; + format = AVFCameraViewfinderSettingsControl2::QtPixelFormatFromCVFormat([avf unsignedIntValue]); + } + } + + QVideoFrame frame(buffer, QSize(width, height), format); m_renderer->syncHandleViewfinderFrame(frame); } @end @@ -236,6 +247,11 @@ void AVFCameraRendererControl::syncHandleViewfinderFrame(const QVideoFrame &fram m_cameraSession->onCameraFrameFetched(m_lastViewfinderFrame); } +AVCaptureVideoDataOutput *AVFCameraRendererControl::videoDataOutput() const +{ + return m_videoDataOutput; +} + void AVFCameraRendererControl::handleViewfinderFrame() { QVideoFrame frame; diff --git a/src/plugins/avfoundation/camera/avfcameraservice.h b/src/plugins/avfoundation/camera/avfcameraservice.h index 752065bf..fffa1442 100644 --- a/src/plugins/avfoundation/camera/avfcameraservice.h +++ b/src/plugins/avfoundation/camera/avfcameraservice.h @@ -55,6 +55,7 @@ class AVFAudioInputSelectorControl; class AVFCameraFocusControl; class AVFCameraExposureControl; class AVFCameraZoomControl; +class AVFCameraViewfinderSettingsControl2; class AVFCameraService : public QMediaService { @@ -76,6 +77,8 @@ public: AVFCameraFocusControl *cameraFocusControl() const { return m_cameraFocusControl; } AVFCameraExposureControl *cameraExposureControl() const {return m_cameraExposureControl; } AVFCameraZoomControl *cameraZoomControl() const {return m_cameraZoomControl; } + AVFCameraRendererControl *videoOutput() const {return m_videoOutput; } + AVFCameraViewfinderSettingsControl2 *viewfinderSettingsControl2() const {return m_viewfinderSettingsControl2; } private: AVFCameraSession *m_session; @@ -90,6 +93,7 @@ private: AVFCameraFocusControl *m_cameraFocusControl; AVFCameraExposureControl *m_cameraExposureControl; AVFCameraZoomControl *m_cameraZoomControl; + AVFCameraViewfinderSettingsControl2 *m_viewfinderSettingsControl2; }; QT_END_NAMESPACE diff --git a/src/plugins/avfoundation/camera/avfcameraservice.mm b/src/plugins/avfoundation/camera/avfcameraservice.mm index 5b4a90bb..188dc821 100644 --- a/src/plugins/avfoundation/camera/avfcameraservice.mm +++ b/src/plugins/avfoundation/camera/avfcameraservice.mm @@ -50,6 +50,7 @@ #include "avfmediavideoprobecontrol.h" #include "avfcamerafocuscontrol.h" #include "avfcameraexposurecontrol.h" +#include "avfcameraviewfindersettingscontrol.h" #ifdef Q_OS_IOS #include "avfcamerazoomcontrol.h" @@ -84,6 +85,7 @@ AVFCameraService::AVFCameraService(QObject *parent): #ifdef Q_OS_IOS m_cameraZoomControl = new AVFCameraZoomControl(this); #endif + m_viewfinderSettingsControl2 = new AVFCameraViewfinderSettingsControl2(this); } AVFCameraService::~AVFCameraService() @@ -107,6 +109,8 @@ AVFCameraService::~AVFCameraService() #ifdef Q_OS_IOS delete m_cameraZoomControl; #endif + delete m_viewfinderSettingsControl2; + delete m_session; } @@ -140,6 +144,9 @@ QMediaControl *AVFCameraService::requestControl(const char *name) if (qstrcmp(name, QCameraFocusControl_iid) == 0) return m_cameraFocusControl; + if (qstrcmp(name, QCameraViewfinderSettingsControl2_iid) == 0) + return m_viewfinderSettingsControl2; + if (qstrcmp(name,QMediaVideoProbeControl_iid) == 0) { AVFMediaVideoProbeControl *videoProbe = 0; videoProbe = new AVFMediaVideoProbeControl(this); diff --git a/src/plugins/avfoundation/camera/avfcamerasession.h b/src/plugins/avfoundation/camera/avfcamerasession.h index 8598d7c0..c5bae660 100644 --- a/src/plugins/avfoundation/camera/avfcamerasession.h +++ b/src/plugins/avfoundation/camera/avfcamerasession.h @@ -98,6 +98,7 @@ Q_SIGNALS: private: static void updateCameraDevices(); void attachInputDevices(); + void applyViewfinderSettings(); static QByteArray m_defaultCameraDevice; static QList m_cameraDevices; diff --git a/src/plugins/avfoundation/camera/avfcamerasession.mm b/src/plugins/avfoundation/camera/avfcamerasession.mm index e7e88648..5570aa83 100644 --- a/src/plugins/avfoundation/camera/avfcamerasession.mm +++ b/src/plugins/avfoundation/camera/avfcamerasession.mm @@ -39,6 +39,7 @@ #include "avfcameradevicecontrol.h" #include "avfaudioinputselectorcontrol.h" #include "avfmediavideoprobecontrol.h" +#include "avfcameraviewfindersettingscontrol.h" #include #include @@ -275,6 +276,7 @@ void AVFCameraSession::setState(QCamera::State newState) Q_EMIT readyToConfigureConnections(); [m_captureSession commitConfiguration]; [m_captureSession startRunning]; + applyViewfinderSettings(); } if (oldState == QCamera::ActiveState) { @@ -364,6 +366,15 @@ void AVFCameraSession::attachInputDevices() } } +void AVFCameraSession::applyViewfinderSettings() +{ + if (AVFCameraViewfinderSettingsControl2 *control = m_service->viewfinderSettingsControl2()) { + QCameraViewfinderSettings settings(control->requestedSettings()); + // TODO: Adjust the resolution (from image encoder control), updating 'settings'. + control->setViewfinderSettings(settings); + } +} + void AVFCameraSession::addProbe(AVFMediaVideoProbeControl *probe) { m_videoProbesMutex.lock(); diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h new file mode 100644 index 00000000..d2864c62 --- /dev/null +++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AVFCAMERAVIEWFINDERSETTINGSCONTROL_H +#define AVFCAMERAVIEWFINDERSETTINGSCONTROL_H + +#include +#include +#include + +#include +#include + +@class AVCaptureDevice; +@class AVCaptureVideoDataOutput; +@class AVCaptureConnection; +@class AVCaptureDeviceFormat; + +QT_BEGIN_NAMESPACE + +class AVFCameraSession; +class AVFCameraService; + +class AVFCameraViewfinderSettingsControl2 : public QCameraViewfinderSettingsControl2 +{ + Q_OBJECT + + friend class AVFCameraSession; +public: + AVFCameraViewfinderSettingsControl2(AVFCameraService *service); + + QList supportedViewfinderSettings() const Q_DECL_OVERRIDE; + QCameraViewfinderSettings viewfinderSettings() const Q_DECL_OVERRIDE; + void setViewfinderSettings(const QCameraViewfinderSettings &settings) Q_DECL_OVERRIDE; + + // "Converters": + static QVideoFrame::PixelFormat QtPixelFormatFromCVFormat(unsigned avPixelFormat); + static bool CVPixelFormatFromQtFormat(QVideoFrame::PixelFormat qtFormat, unsigned &conv); + +private: + void setResolution(const QSize &resolution); + void setFramerate(qreal minFPS, qreal maxFPS, bool useActive); + void setPixelFormat(QVideoFrame::PixelFormat newFormat); + AVCaptureDeviceFormat *findBestFormatMatch(const QCameraViewfinderSettings &settings) const; + bool convertPixelFormatIfSupported(QVideoFrame::PixelFormat format, unsigned &avfFormat) const; + void applySettings(); + QCameraViewfinderSettings requestedSettings() const; + // Aux. function to extract things like captureDevice, videoOutput, etc. + bool updateAVFoundationObjects() const; + + AVFCameraService *m_service; + mutable AVFCameraSession *m_session; + QCameraViewfinderSettings m_settings; + mutable AVCaptureDevice *m_captureDevice; + mutable AVCaptureVideoDataOutput *m_videoOutput; + mutable AVCaptureConnection *m_videoConnection; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm new file mode 100644 index 00000000..c9d04f62 --- /dev/null +++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm @@ -0,0 +1,579 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "avfcameraviewfindersettingscontrol.h" +#include "avfcamerarenderercontrol.h" +#include "avfcamerautility.h" +#include "avfcamerasession.h" +#include "avfcameraservice.h" +#include "avfcameradebug.h" + +#include +#include +#include +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace { + +QVector qt_viewfinder_pixel_formats(AVCaptureVideoDataOutput *videoOutput) +{ + Q_ASSERT(videoOutput); + + QVector qtFormats; + + NSArray *pixelFormats = [videoOutput availableVideoCVPixelFormatTypes]; + for (NSObject *obj in pixelFormats) { + if (![obj isKindOfClass:[NSNumber class]]) + continue; + + NSNumber *formatAsNSNumber = static_cast(obj); + // It's actually FourCharCode (== UInt32): + const QVideoFrame::PixelFormat qtFormat(AVFCameraViewfinderSettingsControl2:: + QtPixelFormatFromCVFormat([formatAsNSNumber unsignedIntValue])); + if (qtFormat != QVideoFrame::Format_Invalid) + qtFormats << qtFormat; + } + + return qtFormats; +} + +bool qt_framerates_sane(const QCameraViewfinderSettings &settings) +{ + const qreal minFPS = settings.minimumFrameRate(); + const qreal maxFPS = settings.maximumFrameRate(); + + if (minFPS < 0. || maxFPS < 0.) + return false; + + return !maxFPS || maxFPS >= minFPS; +} + +void qt_set_framerate_limits(AVCaptureConnection *videoConnection, + const QCameraViewfinderSettings &settings) +{ + Q_ASSERT(videoConnection); + + if (!qt_framerates_sane(settings)) { + qDebugCamera() << Q_FUNC_INFO << "invalid framerate (min, max):" + << settings.minimumFrameRate() << settings.maximumFrameRate(); + return; + } + + const qreal minFPS = settings.minimumFrameRate(); + const qreal maxFPS = settings.maximumFrameRate(); + + CMTime minDuration = kCMTimeInvalid; + CMTime maxDuration = kCMTimeInvalid; + if (minFPS > 0. || maxFPS > 0.) { + if (maxFPS) { + if (!videoConnection.supportsVideoMinFrameDuration) + qDebugCamera() << Q_FUNC_INFO << "maximum framerate is not supported"; + else + minDuration = CMTimeMake(1, maxFPS); + } + + if (minFPS) { + if (!videoConnection.supportsVideoMaxFrameDuration) + qDebugCamera() << Q_FUNC_INFO << "minimum framerate is not supported"; + else + maxDuration = CMTimeMake(1, minFPS); + } + } + + if (videoConnection.supportsVideoMinFrameDuration) + videoConnection.videoMinFrameDuration = minDuration; + if (videoConnection.supportsVideoMaxFrameDuration) + videoConnection.videoMaxFrameDuration = maxDuration; +} + +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0) + +CMTime qt_adjusted_frame_duration(AVFrameRateRange *range, qreal fps) +{ + Q_ASSERT(range); + Q_ASSERT(fps > 0.); + + if (range.maxFrameRate - range.minFrameRate < 0.1) { + // Can happen on OS X. + return range.minFrameDuration; + } + + if (fps <= range.minFrameRate) + return range.maxFrameDuration; + if (fps >= range.maxFrameRate) + return range.minFrameDuration; + + const AVFRational timeAsRational(qt_float_to_rational(1. / fps, 1000)); + return CMTimeMake(timeAsRational.first, timeAsRational.second); +} + +void qt_set_framerate_limits(AVCaptureDevice *captureDevice, + const QCameraViewfinderSettings &settings) +{ + Q_ASSERT(captureDevice); + if (!captureDevice.activeFormat) { + qDebugCamera() << Q_FUNC_INFO << "no active capture device format"; + return; + } + + const qreal minFPS = settings.minimumFrameRate(); + const qreal maxFPS = settings.maximumFrameRate(); + if (!qt_framerates_sane(settings)) { + qDebugCamera() << Q_FUNC_INFO << "invalid framerates (min, max):" + << minFPS << maxFPS; + return; + } + + CMTime minFrameDuration = kCMTimeInvalid; + CMTime maxFrameDuration = kCMTimeInvalid; + if (maxFPS || minFPS) { + AVFrameRateRange *range = qt_find_supported_framerate_range(captureDevice.activeFormat, + maxFPS ? maxFPS : minFPS); + if (!range) { + qDebugCamera() << Q_FUNC_INFO << "no framerate range found, (min, max):" + << minFPS << maxFPS; + return; + } + + if (maxFPS) + minFrameDuration = qt_adjusted_frame_duration(range, maxFPS); + if (minFPS) + maxFrameDuration = qt_adjusted_frame_duration(range, minFPS); + } + + const AVFConfigurationLock lock(captureDevice); + if (!lock) { + qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration"; + return; + } + + // While Apple's docs say kCMTimeInvalid will end in default + // settings for this format, kCMTimeInvalid on OS X ends with a runtime + // exception: + // "The activeVideoMinFrameDuration passed is not supported by the device." +#ifdef Q_OS_IOS + [captureDevice setActiveVideoMinFrameDuration:minFrameDuration]; + [captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration]; +#else + if (CMTimeCompare(minFrameDuration, kCMTimeInvalid)) + [captureDevice setActiveVideoMinFrameDuration:minFrameDuration]; + if (CMTimeCompare(maxFrameDuration, kCMTimeInvalid)) + [captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration]; +#endif +} + +#endif // Platform SDK >= 10.9, >= 7.0. + +// 'Dispatchers': + +AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection) +{ + Q_ASSERT(captureDevice); + Q_ASSERT(videoConnection); + + AVFPSRange fps; +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0) + if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) { + const CMTime minDuration = captureDevice.activeVideoMinFrameDuration; + if (CMTimeCompare(minDuration, kCMTimeInvalid)) { + if (const Float64 minSeconds = CMTimeGetSeconds(minDuration)) + fps.second = 1. / minSeconds; // Max FPS = 1 / MinDuration. + } + + const CMTime maxDuration = captureDevice.activeVideoMaxFrameDuration; + if (CMTimeCompare(maxDuration, kCMTimeInvalid)) { + if (const Float64 maxSeconds = CMTimeGetSeconds(maxDuration)) + fps.first = 1. / maxSeconds; // Min FPS = 1 / MaxDuration. + } + } else { +#else + { +#endif + fps = qt_connection_framerates(videoConnection); + } + + return fps; +} + +void qt_set_framerate_limits(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection, + const QCameraViewfinderSettings &settings) +{ + Q_ASSERT(captureDevice); + Q_ASSERT(videoConnection); +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0) + if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_9, QSysInfo::MV_IOS_7_0)) + qt_set_framerate_limits(captureDevice, settings); + else + qt_set_framerate_limits(videoConnection, settings); +#else + qt_set_framerate_limits(videoConnection, settings); +#endif +} + +} // Unnamed namespace. + +AVFCameraViewfinderSettingsControl2::AVFCameraViewfinderSettingsControl2(AVFCameraService *service) + : m_service(service), + m_captureDevice(0), + m_videoOutput(0), + m_videoConnection(0) +{ + Q_ASSERT(service); +} + +QList AVFCameraViewfinderSettingsControl2::supportedViewfinderSettings() const +{ + QList supportedSettings; + + if (!updateAVFoundationObjects()) { + qDebugCamera() << Q_FUNC_INFO << "no capture device or video output found"; + return supportedSettings; + } + + QVector framerates; + + QVector pixelFormats(qt_viewfinder_pixel_formats(m_videoOutput)); + if (!pixelFormats.size()) + pixelFormats << QVideoFrame::Format_Invalid; // The default value. +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0) + if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) { + if (!m_captureDevice.formats || !m_captureDevice.formats.count) { + qDebugCamera() << Q_FUNC_INFO << "no capture device formats found"; + return supportedSettings; + } + + for (AVCaptureDeviceFormat *format in m_captureDevice.formats) { + if (qt_is_video_range_subtype(format)) + continue; + const QSize res(qt_device_format_resolution(format)); + if (res.isNull() || !res.isValid()) + continue; + const QSize par(qt_device_format_pixel_aspect_ratio(format)); + if (par.isNull() || !par.isValid()) + continue; + + framerates = qt_device_format_framerates(format); + if (!framerates.size()) + framerates << AVFPSRange(); // The default value. + + for (int i = 0; i < pixelFormats.size(); ++i) { + for (int j = 0; j < framerates.size(); ++j) { + QCameraViewfinderSettings newSet; + newSet.setResolution(res); + newSet.setPixelAspectRatio(par); + newSet.setPixelFormat(pixelFormats[i]); + newSet.setMinimumFrameRate(framerates[j].first); + newSet.setMaximumFrameRate(framerates[j].second); + supportedSettings << newSet; + } + } + } + } else { +#else + { +#endif + // TODO: resolution and PAR. + framerates << qt_connection_framerates(m_videoConnection); + for (int i = 0; i < pixelFormats.size(); ++i) { + for (int j = 0; j < framerates.size(); ++j) { + QCameraViewfinderSettings newSet; + newSet.setPixelFormat(pixelFormats[i]); + newSet.setMinimumFrameRate(framerates[j].first); + newSet.setMaximumFrameRate(framerates[j].second); + supportedSettings << newSet; + } + } + } + + return supportedSettings; +} + +QCameraViewfinderSettings AVFCameraViewfinderSettingsControl2::viewfinderSettings() const +{ + QCameraViewfinderSettings settings; + + if (!updateAVFoundationObjects()) { + qDebugCamera() << Q_FUNC_INFO << "no capture device or video output found"; + return settings; + } + +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0) + if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) { + if (!m_captureDevice.activeFormat) { + qDebugCamera() << Q_FUNC_INFO << "no active capture device format"; + return settings; + } + + const QSize res(qt_device_format_resolution(m_captureDevice.activeFormat)); + const QSize par(qt_device_format_pixel_aspect_ratio(m_captureDevice.activeFormat)); + if (res.isNull() || !res.isValid() || par.isNull() || !par.isValid()) { + qDebugCamera() << Q_FUNC_INFO << "failed to obtain resolution/pixel aspect ratio"; + return settings; + } + + settings.setResolution(res); + settings.setPixelAspectRatio(par); + } +#endif + // TODO: resolution and PAR before 7.0. + const AVFPSRange fps = qt_current_framerates(m_captureDevice, m_videoConnection); + settings.setMinimumFrameRate(fps.first); + settings.setMaximumFrameRate(fps.second); + + if (NSObject *obj = [m_videoOutput.videoSettings objectForKey:(id)kCVPixelBufferPixelFormatTypeKey]) { + if ([obj isKindOfClass:[NSNumber class]]) { + NSNumber *nsNum = static_cast(obj); + settings.setPixelFormat(QtPixelFormatFromCVFormat([nsNum unsignedIntValue])); + } + } + + return settings; +} + +void AVFCameraViewfinderSettingsControl2::setViewfinderSettings(const QCameraViewfinderSettings &settings) +{ + if (settings.isNull()) { + qDebugCamera() << Q_FUNC_INFO << "empty viewfinder settings"; + return; + } + + if (m_settings == settings) + return; + + m_settings = settings; + applySettings(); +} + +QVideoFrame::PixelFormat AVFCameraViewfinderSettingsControl2::QtPixelFormatFromCVFormat(unsigned avPixelFormat) +{ + // BGRA <-> ARGB "swap" is intentional: + // to work correctly with GL_RGBA, color swap shaders + // (in QSG node renderer etc.). + switch (avPixelFormat) { + case kCVPixelFormatType_32ARGB: + return QVideoFrame::Format_BGRA32; + case kCVPixelFormatType_32BGRA: + return QVideoFrame::Format_ARGB32; + case kCVPixelFormatType_24RGB: + return QVideoFrame::Format_RGB24; + case kCVPixelFormatType_24BGR: + return QVideoFrame::Format_BGR24; + default: + return QVideoFrame::Format_Invalid; + } +} + +bool AVFCameraViewfinderSettingsControl2::CVPixelFormatFromQtFormat(QVideoFrame::PixelFormat qtFormat, unsigned &conv) +{ + // BGRA <-> ARGB "swap" is intentional: + // to work correctly with GL_RGBA, color swap shaders + // (in QSG node renderer etc.). + switch (qtFormat) { + case QVideoFrame::Format_ARGB32: + conv = kCVPixelFormatType_32BGRA; + break; + case QVideoFrame::Format_BGRA32: + conv = kCVPixelFormatType_32ARGB; + break; + // These two formats below are not supported + // by QSGVideoNodeFactory_RGB, so for now I have to + // disable them. + /* + case QVideoFrame::Format_RGB24: + conv = kCVPixelFormatType_24RGB; + break; + case QVideoFrame::Format_BGR24: + conv = kCVPixelFormatType_24BGR; + break; + */ + default: + return false; + } + + return true; +} + +AVCaptureDeviceFormat *AVFCameraViewfinderSettingsControl2::findBestFormatMatch(const QCameraViewfinderSettings &settings) const +{ +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0) + if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) { + Q_ASSERT(m_captureDevice); + + const QSize &resolution = settings.resolution(); + if (!resolution.isNull() && resolution.isValid()) { + // Either the exact match (including high resolution for images on iOS) + // or a format with a resolution close to the requested one. + return qt_find_best_resolution_match(m_captureDevice, resolution); + } + + // No resolution requested, what about framerates? + if (!qt_framerates_sane(settings)) { + qDebugCamera() << Q_FUNC_INFO << "invalid framerate requested (min/max):" + << settings.minimumFrameRate() << settings.maximumFrameRate(); + return nil; + } + + const qreal minFPS(settings.minimumFrameRate()); + const qreal maxFPS(settings.maximumFrameRate()); + if (minFPS || maxFPS) + return qt_find_best_framerate_match(m_captureDevice, maxFPS ? maxFPS : minFPS); + // Ignore PAR for the moment (PAR without resolution can + // pick a format with really bad resolution). + // No need to test pixel format, just return settings. + } +#endif + return nil; +} + +bool AVFCameraViewfinderSettingsControl2::convertPixelFormatIfSupported(QVideoFrame::PixelFormat qtFormat, unsigned &avfFormat)const +{ + Q_ASSERT(m_videoOutput); + + unsigned conv = 0; + if (!CVPixelFormatFromQtFormat(qtFormat, conv)) + return false; + + NSArray *formats = [m_videoOutput availableVideoCVPixelFormatTypes]; + if (!formats || !formats.count) + return false; + + for (NSObject *obj in formats) { + if (![obj isKindOfClass:[NSNumber class]]) + continue; + NSNumber *nsNum = static_cast(obj); + if ([nsNum unsignedIntValue] == conv) { + avfFormat = conv; + return true; + } + } + + return false; +} + +void AVFCameraViewfinderSettingsControl2::applySettings() +{ + if (m_settings.isNull()) + return; + + if (!updateAVFoundationObjects()) + return; + + if (m_session->state() != QCamera::LoadedState && + m_session->state() != QCamera::ActiveState) { + return; + } + + NSMutableDictionary *videoSettings = [NSMutableDictionary dictionaryWithCapacity:1]; +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0) + AVCaptureDeviceFormat *match = findBestFormatMatch(m_settings); + if (match) { + if (match != m_captureDevice.activeFormat) { + const AVFConfigurationLock lock(m_captureDevice); + if (!lock) { + qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration"; + return; + } + + m_captureDevice.activeFormat = match; + } + } else { + qDebugCamera() << Q_FUNC_INFO << "matching device format not found"; + // We still can update the pixel format at least. + } +#endif + + unsigned avfPixelFormat = 0; + if (m_settings.pixelFormat() != QVideoFrame::Format_Invalid && + convertPixelFormatIfSupported(m_settings.pixelFormat(), avfPixelFormat)) { + [videoSettings setObject:[NSNumber numberWithUnsignedInt:avfPixelFormat] + forKey:(id)kCVPixelBufferPixelFormatTypeKey]; + } else { + // We have to set the pixel format, otherwise AVFoundation can change it to something we do not support. + if (NSObject *oldFormat = [m_videoOutput.videoSettings objectForKey:(id)kCVPixelBufferPixelFormatTypeKey]) { + [videoSettings setObject:oldFormat forKey:(id)kCVPixelBufferPixelFormatTypeKey]; + } else { + [videoSettings setObject:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA] + forKey:(id)kCVPixelBufferPixelFormatTypeKey]; + } + } + + if (videoSettings.count) + m_videoOutput.videoSettings = videoSettings; + + qt_set_framerate_limits(m_captureDevice, m_videoConnection, m_settings); +} + +QCameraViewfinderSettings AVFCameraViewfinderSettingsControl2::requestedSettings() const +{ + return m_settings; +} + +bool AVFCameraViewfinderSettingsControl2::updateAVFoundationObjects() const +{ + m_session = 0; + m_captureDevice = 0; + m_videoOutput = 0; + m_videoConnection = 0; + + if (!m_service->session()) + return false; + + if (!m_service->session()->videoCaptureDevice()) + return false; + + if (!m_service->videoOutput() || !m_service->videoOutput()->videoDataOutput()) + return false; + + AVCaptureVideoDataOutput *output = m_service->videoOutput()->videoDataOutput(); + AVCaptureConnection *connection = [output connectionWithMediaType:AVMediaTypeVideo]; + if (!connection) + return false; + + m_session = m_service->session(); + m_captureDevice = m_session->videoCaptureDevice(); + m_videoOutput = output; + m_videoConnection = connection; + + return true; +} + +QT_END_NAMESPACE + +#include "moc_avfcameraviewfindersettingscontrol.cpp" diff --git a/src/plugins/avfoundation/camera/camera.pro b/src/plugins/avfoundation/camera/camera.pro index 82084a1b..19e3e631 100644 --- a/src/plugins/avfoundation/camera/camera.pro +++ b/src/plugins/avfoundation/camera/camera.pro @@ -39,7 +39,8 @@ HEADERS += \ avfcameradevicecontrol.h \ avfcamerafocuscontrol.h \ avfcameraexposurecontrol.h \ - avfcamerautility.h + avfcamerautility.h \ + avfcameraviewfindersettingscontrol.h OBJECTIVE_SOURCES += \ avfcameraserviceplugin.mm \ @@ -58,7 +59,8 @@ OBJECTIVE_SOURCES += \ avfcamerarenderercontrol.mm \ avfcamerafocuscontrol.mm \ avfcameraexposurecontrol.mm \ - avfcamerautility.mm + avfcamerautility.mm \ + avfcameraviewfindersettingscontrol.mm ios { From 985ee3261b3c82ddad87124657ee3fb61e311a4d Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Tue, 10 Feb 2015 09:29:29 +0100 Subject: [PATCH 10/11] Viewfinder settings control (1) for OS X/iOS QCameraViewfinderSettingsControl - version for AVFoundation plugin ('obsolete' viewfinder settings control interface, camera session uses v2 instead). v1 is implemented using v2 (the v2 object from camera service). Change-Id: I81207b52b0ba5a67e64465f0e5e0c80d7267df3e Reviewed-by: Yoann Lopes --- .../avfoundation/camera/avfcameraservice.h | 3 + .../avfoundation/camera/avfcameraservice.mm | 5 + .../avfcameraviewfindersettingscontrol.h | 23 +++ .../avfcameraviewfindersettingscontrol.mm | 153 ++++++++++++++++++ 4 files changed, 184 insertions(+) diff --git a/src/plugins/avfoundation/camera/avfcameraservice.h b/src/plugins/avfoundation/camera/avfcameraservice.h index fffa1442..babee3dc 100644 --- a/src/plugins/avfoundation/camera/avfcameraservice.h +++ b/src/plugins/avfoundation/camera/avfcameraservice.h @@ -56,6 +56,7 @@ class AVFCameraFocusControl; class AVFCameraExposureControl; class AVFCameraZoomControl; class AVFCameraViewfinderSettingsControl2; +class AVFCameraViewfinderSettingsControl; class AVFCameraService : public QMediaService { @@ -79,6 +80,7 @@ public: AVFCameraZoomControl *cameraZoomControl() const {return m_cameraZoomControl; } AVFCameraRendererControl *videoOutput() const {return m_videoOutput; } AVFCameraViewfinderSettingsControl2 *viewfinderSettingsControl2() const {return m_viewfinderSettingsControl2; } + AVFCameraViewfinderSettingsControl *viewfinderSettingsControl() const {return m_viewfinderSettingsControl; } private: AVFCameraSession *m_session; @@ -94,6 +96,7 @@ private: AVFCameraExposureControl *m_cameraExposureControl; AVFCameraZoomControl *m_cameraZoomControl; AVFCameraViewfinderSettingsControl2 *m_viewfinderSettingsControl2; + AVFCameraViewfinderSettingsControl *m_viewfinderSettingsControl; }; QT_END_NAMESPACE diff --git a/src/plugins/avfoundation/camera/avfcameraservice.mm b/src/plugins/avfoundation/camera/avfcameraservice.mm index 188dc821..f9c7e5ae 100644 --- a/src/plugins/avfoundation/camera/avfcameraservice.mm +++ b/src/plugins/avfoundation/camera/avfcameraservice.mm @@ -86,6 +86,7 @@ AVFCameraService::AVFCameraService(QObject *parent): m_cameraZoomControl = new AVFCameraZoomControl(this); #endif m_viewfinderSettingsControl2 = new AVFCameraViewfinderSettingsControl2(this); + m_viewfinderSettingsControl = new AVFCameraViewfinderSettingsControl(this); } AVFCameraService::~AVFCameraService() @@ -110,6 +111,7 @@ AVFCameraService::~AVFCameraService() delete m_cameraZoomControl; #endif delete m_viewfinderSettingsControl2; + delete m_viewfinderSettingsControl; delete m_session; } @@ -147,6 +149,9 @@ QMediaControl *AVFCameraService::requestControl(const char *name) if (qstrcmp(name, QCameraViewfinderSettingsControl2_iid) == 0) return m_viewfinderSettingsControl2; + if (qstrcmp(name, QCameraViewfinderSettingsControl_iid) == 0) + return m_viewfinderSettingsControl; + if (qstrcmp(name,QMediaVideoProbeControl_iid) == 0) { AVFMediaVideoProbeControl *videoProbe = 0; videoProbe = new AVFMediaVideoProbeControl(this); diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h index d2864c62..fccc938a 100644 --- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h +++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h @@ -38,6 +38,7 @@ #include #include +#include #include #include @@ -56,6 +57,7 @@ class AVFCameraViewfinderSettingsControl2 : public QCameraViewfinderSettingsCont Q_OBJECT friend class AVFCameraSession; + friend class AVFCameraViewfinderSettingsControl; public: AVFCameraViewfinderSettingsControl2(AVFCameraService *service); @@ -86,6 +88,27 @@ private: mutable AVCaptureConnection *m_videoConnection; }; +class AVFCameraViewfinderSettingsControl : public QCameraViewfinderSettingsControl +{ + Q_OBJECT +public: + AVFCameraViewfinderSettingsControl(AVFCameraService *service); + + bool isViewfinderParameterSupported(ViewfinderParameter parameter) const Q_DECL_OVERRIDE; + QVariant viewfinderParameter(ViewfinderParameter parameter) const Q_DECL_OVERRIDE; + void setViewfinderParameter(ViewfinderParameter parameter, const QVariant &value) Q_DECL_OVERRIDE; + +private: + void setResolution(const QVariant &resolution); + void setAspectRatio(const QVariant &aspectRatio); + void setFrameRate(const QVariant &fps, bool max); + void setPixelFormat(const QVariant &pf); + bool initSettingsControl() const; + + AVFCameraService *m_service; + mutable QPointer m_settingsControl; +}; + QT_END_NAMESPACE #endif diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm index c9d04f62..c5aa5733 100644 --- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm +++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm @@ -574,6 +574,159 @@ bool AVFCameraViewfinderSettingsControl2::updateAVFoundationObjects() const return true; } +AVFCameraViewfinderSettingsControl::AVFCameraViewfinderSettingsControl(AVFCameraService *service) + : m_service(service) +{ + // Legacy viewfinder settings control. + Q_ASSERT(service); + initSettingsControl(); +} + +bool AVFCameraViewfinderSettingsControl::isViewfinderParameterSupported(ViewfinderParameter parameter) const +{ + return parameter == Resolution + || parameter == PixelAspectRatio + || parameter == MinimumFrameRate + || parameter == MaximumFrameRate + || parameter == PixelFormat; +} + +QVariant AVFCameraViewfinderSettingsControl::viewfinderParameter(ViewfinderParameter parameter) const +{ + if (!isViewfinderParameterSupported(parameter)) { + qDebugCamera() << Q_FUNC_INFO << "parameter is not supported"; + return QVariant(); + } + + if (!initSettingsControl()) { + qDebugCamera() << Q_FUNC_INFO << "initialization failed"; + return QVariant(); + } + + const QCameraViewfinderSettings settings(m_settingsControl->viewfinderSettings()); + if (parameter == Resolution) + return settings.resolution(); + if (parameter == PixelAspectRatio) + return settings.pixelAspectRatio(); + if (parameter == MinimumFrameRate) + return settings.minimumFrameRate(); + if (parameter == MaximumFrameRate) + return settings.maximumFrameRate(); + if (parameter == PixelFormat) + return QVariant::fromValue(settings.pixelFormat()); + + return QVariant(); +} + +void AVFCameraViewfinderSettingsControl::setViewfinderParameter(ViewfinderParameter parameter, const QVariant &value) +{ + if (!isViewfinderParameterSupported(parameter)) { + qDebugCamera() << Q_FUNC_INFO << "parameter is not supported"; + return; + } + + if (parameter == Resolution) + setResolution(value); + if (parameter == PixelAspectRatio) + setAspectRatio(value); + if (parameter == MinimumFrameRate) + setFrameRate(value, false); + if (parameter == MaximumFrameRate) + setFrameRate(value, true); + if (parameter == PixelFormat) + setPixelFormat(value); +} + +void AVFCameraViewfinderSettingsControl::setResolution(const QVariant &newValue) +{ + if (!newValue.canConvert()) { + qDebugCamera() << Q_FUNC_INFO << "QSize type expected"; + return; + } + + if (!initSettingsControl()) { + qDebugCamera() << Q_FUNC_INFO << "initialization failed"; + return; + } + + const QSize res(newValue.toSize()); + if (res.isNull() || !res.isValid()) { + qDebugCamera() << Q_FUNC_INFO << "invalid resolution:" << res; + return; + } + + QCameraViewfinderSettings settings(m_settingsControl->viewfinderSettings()); + settings.setResolution(res); + m_settingsControl->setViewfinderSettings(settings); +} + +void AVFCameraViewfinderSettingsControl::setAspectRatio(const QVariant &newValue) +{ + if (!newValue.canConvert()) { + qDebugCamera() << Q_FUNC_INFO << "QSize type expected"; + return; + } + + if (!initSettingsControl()) { + qDebugCamera() << Q_FUNC_INFO << "initialization failed"; + return; + } + + const QSize par(newValue.value()); + if (par.isNull() || !par.isValid()) { + qDebugCamera() << Q_FUNC_INFO << "invalid pixel aspect ratio:" << par; + return; + } + + QCameraViewfinderSettings settings(m_settingsControl->viewfinderSettings()); + settings.setPixelAspectRatio(par); + m_settingsControl->setViewfinderSettings(settings); +} + +void AVFCameraViewfinderSettingsControl::setFrameRate(const QVariant &newValue, bool max) +{ + if (!newValue.canConvert()) { + qDebugCamera() << Q_FUNC_INFO << "qreal type expected"; + return; + } + + if (!initSettingsControl()) { + qDebugCamera() << Q_FUNC_INFO << "initialization failed"; + return; + } + + const qreal fps(newValue.toReal()); + QCameraViewfinderSettings settings(m_settingsControl->viewfinderSettings()); + max ? settings.setMaximumFrameRate(fps) : settings.setMinimumFrameRate(fps); + m_settingsControl->setViewfinderSettings(settings); +} + +void AVFCameraViewfinderSettingsControl::setPixelFormat(const QVariant &newValue) +{ + if (!newValue.canConvert()) { + qDebugCamera() << Q_FUNC_INFO + << "QVideoFrame::PixelFormat type expected"; + return; + } + + if (!initSettingsControl()) { + qDebugCamera() << Q_FUNC_INFO << "initialization failed"; + return; + } + + QCameraViewfinderSettings settings(m_settingsControl->viewfinderSettings()); + settings.setPixelFormat(newValue.value()); + m_settingsControl->setViewfinderSettings(settings); +} + +bool AVFCameraViewfinderSettingsControl::initSettingsControl()const +{ + if (!m_settingsControl) + m_settingsControl = m_service->viewfinderSettingsControl2(); + + return !m_settingsControl.isNull(); +} + QT_END_NAMESPACE #include "moc_avfcameraviewfindersettingscontrol.cpp" From 33b27c3c15aaca611d5ed0164afa3cd2e9f891a7 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Thu, 12 Feb 2015 10:17:09 +0100 Subject: [PATCH 11/11] Image encoder control - version for OS X/iOS QImageEncoderControl - implementation for AVFoundation plugin (OS X/iOS, at the moment iOS >= 7.0). Change-Id: Ibc2c3ae48252dd4698e263f5abca5c328482d5e7 Reviewed-by: Yoann Lopes --- .../avfoundation/camera/avfcameraservice.h | 3 + .../avfoundation/camera/avfcameraservice.mm | 6 + .../avfoundation/camera/avfcamerasession.h | 1 + .../avfoundation/camera/avfcamerasession.mm | 25 +- .../camera/avfimagecapturecontrol.h | 1 + .../camera/avfimageencodercontrol.h | 77 +++++ .../camera/avfimageencodercontrol.mm | 273 ++++++++++++++++++ src/plugins/avfoundation/camera/camera.pro | 6 +- 8 files changed, 386 insertions(+), 6 deletions(-) create mode 100644 src/plugins/avfoundation/camera/avfimageencodercontrol.h create mode 100644 src/plugins/avfoundation/camera/avfimageencodercontrol.mm diff --git a/src/plugins/avfoundation/camera/avfcameraservice.h b/src/plugins/avfoundation/camera/avfcameraservice.h index babee3dc..d4feada9 100644 --- a/src/plugins/avfoundation/camera/avfcameraservice.h +++ b/src/plugins/avfoundation/camera/avfcameraservice.h @@ -57,6 +57,7 @@ class AVFCameraExposureControl; class AVFCameraZoomControl; class AVFCameraViewfinderSettingsControl2; class AVFCameraViewfinderSettingsControl; +class AVFImageEncoderControl; class AVFCameraService : public QMediaService { @@ -81,6 +82,7 @@ public: AVFCameraRendererControl *videoOutput() const {return m_videoOutput; } AVFCameraViewfinderSettingsControl2 *viewfinderSettingsControl2() const {return m_viewfinderSettingsControl2; } AVFCameraViewfinderSettingsControl *viewfinderSettingsControl() const {return m_viewfinderSettingsControl; } + AVFImageEncoderControl *imageEncoderControl() const {return m_imageEncoderControl; } private: AVFCameraSession *m_session; @@ -97,6 +99,7 @@ private: AVFCameraZoomControl *m_cameraZoomControl; AVFCameraViewfinderSettingsControl2 *m_viewfinderSettingsControl2; AVFCameraViewfinderSettingsControl *m_viewfinderSettingsControl; + AVFImageEncoderControl *m_imageEncoderControl; }; QT_END_NAMESPACE diff --git a/src/plugins/avfoundation/camera/avfcameraservice.mm b/src/plugins/avfoundation/camera/avfcameraservice.mm index f9c7e5ae..0485abdc 100644 --- a/src/plugins/avfoundation/camera/avfcameraservice.mm +++ b/src/plugins/avfoundation/camera/avfcameraservice.mm @@ -51,6 +51,7 @@ #include "avfcamerafocuscontrol.h" #include "avfcameraexposurecontrol.h" #include "avfcameraviewfindersettingscontrol.h" +#include "avfimageencodercontrol.h" #ifdef Q_OS_IOS #include "avfcamerazoomcontrol.h" @@ -87,6 +88,7 @@ AVFCameraService::AVFCameraService(QObject *parent): #endif m_viewfinderSettingsControl2 = new AVFCameraViewfinderSettingsControl2(this); m_viewfinderSettingsControl = new AVFCameraViewfinderSettingsControl(this); + m_imageEncoderControl = new AVFImageEncoderControl(this); } AVFCameraService::~AVFCameraService() @@ -112,6 +114,7 @@ AVFCameraService::~AVFCameraService() #endif delete m_viewfinderSettingsControl2; delete m_viewfinderSettingsControl; + delete m_imageEncoderControl; delete m_session; } @@ -152,6 +155,9 @@ QMediaControl *AVFCameraService::requestControl(const char *name) if (qstrcmp(name, QCameraViewfinderSettingsControl_iid) == 0) return m_viewfinderSettingsControl; + if (qstrcmp(name, QImageEncoderControl_iid) == 0) + return m_imageEncoderControl; + if (qstrcmp(name,QMediaVideoProbeControl_iid) == 0) { AVFMediaVideoProbeControl *videoProbe = 0; videoProbe = new AVFMediaVideoProbeControl(this); diff --git a/src/plugins/avfoundation/camera/avfcamerasession.h b/src/plugins/avfoundation/camera/avfcamerasession.h index c5bae660..4f418cb1 100644 --- a/src/plugins/avfoundation/camera/avfcamerasession.h +++ b/src/plugins/avfoundation/camera/avfcamerasession.h @@ -98,6 +98,7 @@ Q_SIGNALS: private: static void updateCameraDevices(); void attachInputDevices(); + void applyImageEncoderSettings(); void applyViewfinderSettings(); static QByteArray m_defaultCameraDevice; diff --git a/src/plugins/avfoundation/camera/avfcamerasession.mm b/src/plugins/avfoundation/camera/avfcamerasession.mm index 5570aa83..98fbb986 100644 --- a/src/plugins/avfoundation/camera/avfcamerasession.mm +++ b/src/plugins/avfoundation/camera/avfcamerasession.mm @@ -40,6 +40,7 @@ #include "avfaudioinputselectorcontrol.h" #include "avfmediavideoprobecontrol.h" #include "avfcameraviewfindersettingscontrol.h" +#include "avfimageencodercontrol.h" #include #include @@ -276,6 +277,7 @@ void AVFCameraSession::setState(QCamera::State newState) Q_EMIT readyToConfigureConnections(); [m_captureSession commitConfiguration]; [m_captureSession startRunning]; + applyImageEncoderSettings(); applyViewfinderSettings(); } @@ -366,12 +368,27 @@ void AVFCameraSession::attachInputDevices() } } +void AVFCameraSession::applyImageEncoderSettings() +{ + if (AVFImageEncoderControl *control = m_service->imageEncoderControl()) + control->applySettings(); +} + void AVFCameraSession::applyViewfinderSettings() { - if (AVFCameraViewfinderSettingsControl2 *control = m_service->viewfinderSettingsControl2()) { - QCameraViewfinderSettings settings(control->requestedSettings()); - // TODO: Adjust the resolution (from image encoder control), updating 'settings'. - control->setViewfinderSettings(settings); + if (AVFCameraViewfinderSettingsControl2 *vfControl = m_service->viewfinderSettingsControl2()) { + QCameraViewfinderSettings vfSettings(vfControl->requestedSettings()); + if (AVFImageEncoderControl *imControl = m_service->imageEncoderControl()) { + const QSize imageResolution(imControl->imageSettings().resolution()); + if (!imageResolution.isNull() && imageResolution.isValid()) { + vfSettings.setResolution(imageResolution); + vfControl->setViewfinderSettings(vfSettings); + return; + } + } + + if (!vfSettings.isNull()) + vfControl->applySettings(); } } diff --git a/src/plugins/avfoundation/camera/avfimagecapturecontrol.h b/src/plugins/avfoundation/camera/avfimagecapturecontrol.h index c4cb2fa5..c27abd39 100644 --- a/src/plugins/avfoundation/camera/avfimagecapturecontrol.h +++ b/src/plugins/avfoundation/camera/avfimagecapturecontrol.h @@ -56,6 +56,7 @@ public: QCameraImageCapture::DriveMode driveMode() const { return QCameraImageCapture::SingleImageCapture; } void setDriveMode(QCameraImageCapture::DriveMode ) {} + AVCaptureStillImageOutput *stillImageOutput() const {return m_stillImageOutput;} int capture(const QString &fileName); void cancelCapture(); diff --git a/src/plugins/avfoundation/camera/avfimageencodercontrol.h b/src/plugins/avfoundation/camera/avfimageencodercontrol.h new file mode 100644 index 00000000..fcb665a0 --- /dev/null +++ b/src/plugins/avfoundation/camera/avfimageencodercontrol.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AVFIMAGEENCODERCONTROL_H +#define AVFIMAGEENCODERCONTROL_H + +#include +#include + +#include +#include +#include + +@class AVCaptureDeviceFormat; + +QT_BEGIN_NAMESPACE + +class AVFCameraService; + +class AVFImageEncoderControl : public QImageEncoderControl +{ + Q_OBJECT + + friend class AVFCameraSession; +public: + AVFImageEncoderControl(AVFCameraService *service); + + QStringList supportedImageCodecs() const Q_DECL_OVERRIDE; + QString imageCodecDescription(const QString &codecName) const Q_DECL_OVERRIDE; + QList supportedResolutions(const QImageEncoderSettings &settings, + bool *continuous) const Q_DECL_OVERRIDE; + QImageEncoderSettings imageSettings() const Q_DECL_OVERRIDE; + void setImageSettings(const QImageEncoderSettings &settings) Q_DECL_OVERRIDE; + +private: + AVFCameraService *m_service; + QImageEncoderSettings m_settings; + + void applySettings(); + bool videoCaptureDeviceIsValid() const; +}; + +QSize qt_image_high_resolution(AVCaptureDeviceFormat *fomat); + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/avfoundation/camera/avfimageencodercontrol.mm b/src/plugins/avfoundation/camera/avfimageencodercontrol.mm new file mode 100644 index 00000000..ea25665e --- /dev/null +++ b/src/plugins/avfoundation/camera/avfimageencodercontrol.mm @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "avfcameraviewfindersettingscontrol.h" +#include "avfimageencodercontrol.h" +#include "avfimagecapturecontrol.h" +#include "avfcamerautility.h" +#include "avfcamerasession.h" +#include "avfcameraservice.h" +#include "avfcameradebug.h" + +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +QSize qt_image_high_resolution(AVCaptureDeviceFormat *format) +{ + Q_ASSERT(format); + QSize res; +#if defined(Q_OS_IOS) && QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_8_0) { + const CMVideoDimensions hrDim(format.highResolutionStillImageDimensions); + res.setWidth(hrDim.width); + res.setHeight(hrDim.height); + } +#endif + return res; +} + +AVFImageEncoderControl::AVFImageEncoderControl(AVFCameraService *service) + : m_service(service) +{ + Q_ASSERT(service); +} + +QStringList AVFImageEncoderControl::supportedImageCodecs() const +{ + return QStringList() << QLatin1String("jpeg"); +} + +QString AVFImageEncoderControl::imageCodecDescription(const QString &codecName) const +{ + if (codecName == QLatin1String("jpeg")) + return tr("JPEG image"); + + return QString(); +} + +QList AVFImageEncoderControl::supportedResolutions(const QImageEncoderSettings &settings, + bool *continuous) const +{ + Q_UNUSED(settings) + + QList resolutions; + + if (!videoCaptureDeviceIsValid()) + return resolutions; + +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0) + if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) { + AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice(); + for (AVCaptureDeviceFormat *format in captureDevice.formats) { + if (qt_is_video_range_subtype(format)) + continue; + + const QSize res(qt_device_format_resolution(format)); + if (!res.isNull() && res.isValid()) + resolutions << res; +#if defined(Q_OS_IOS) && QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_8_0) { + // From Apple's docs (iOS): + // By default, AVCaptureStillImageOutput emits images with the same dimensions as + // its source AVCaptureDevice instance’s activeFormat.formatDescription. However, + // if you set this property to YES, the receiver emits still images at the capture + // device’s highResolutionStillImageDimensions value. + const QSize hrRes(qt_image_high_resolution(format)); + if (!hrRes.isNull() && hrRes.isValid()) + resolutions << res; + } +#endif + } + } else { +#else + { +#endif + // TODO: resolutions without AVCaptureDeviceFormat ... + } + + if (continuous) + *continuous = false; + + return resolutions; +} + +QImageEncoderSettings AVFImageEncoderControl::imageSettings() const +{ + QImageEncoderSettings settings; + + if (!videoCaptureDeviceIsValid()) + return settings; + +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0) + if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) { + AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice(); + if (!captureDevice.activeFormat) { + qDebugCamera() << Q_FUNC_INFO << "no active format"; + return settings; + } + + QSize res(qt_device_format_resolution(captureDevice.activeFormat)); +#if defined(Q_OS_IOS) && QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_8_0) { + if (!m_service->imageCaptureControl() || !m_service->imageCaptureControl()->stillImageOutput()) { + qDebugCamera() << Q_FUNC_INFO << "no still image output"; + return settings; + } + + AVCaptureStillImageOutput *stillImageOutput = m_service->imageCaptureControl()->stillImageOutput(); + if (stillImageOutput.highResolutionStillImageOutputEnabled) + res = qt_image_high_resolution(captureDevice.activeFormat); + } +#endif + if (res.isNull() || !res.isValid()) { + qDebugCamera() << Q_FUNC_INFO << "failed to exctract the image resolution"; + return settings; + } + + settings.setResolution(res); + } else { +#else + { +#endif + // TODO: resolution without AVCaptureDeviceFormat. + } + + settings.setCodec(QLatin1String("jpeg")); + + return settings; +} + +void AVFImageEncoderControl::setImageSettings(const QImageEncoderSettings &settings) +{ + if (m_settings == settings || settings.isNull()) + return; + + m_settings = settings; + + applySettings(); +} + +void AVFImageEncoderControl::applySettings() +{ + if (!videoCaptureDeviceIsValid()) + return; + + AVFCameraSession *session = m_service->session(); + if (!session || (session->state() != QCamera::ActiveState + && session->state() != QCamera::LoadedState)) { + return; + } + + if (!m_service->imageCaptureControl() + || !m_service->imageCaptureControl()->stillImageOutput()) { + qDebugCamera() << Q_FUNC_INFO << "no still image output"; + return; + } + + if (m_settings.codec().size() + && m_settings.codec() != QLatin1String("jpeg")) { + qDebugCamera() << Q_FUNC_INFO << "unsupported codec:" << m_settings.codec(); + return; + } + + QSize res(m_settings.resolution()); + if (res.isNull()) { + qDebugCamera() << Q_FUNC_INFO << "invalid resolution:" << res; + return; + } + + if (!res.isValid()) { + // Invalid == default value. + // Here we could choose the best format available, but + // activeFormat is already equal to 'preset high' by default, + // which is good enough, otherwise we can end in some format with low framerates. + return; + } + +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0) + if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_7, QSysInfo::MV_IOS_7_0)) { + AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice(); + AVCaptureDeviceFormat *match = qt_find_best_resolution_match(captureDevice, res); + + if (!match) { + qDebugCamera() << Q_FUNC_INFO << "unsupported resolution:" << res; + return; + } + + if (match != captureDevice.activeFormat) { + const AVFConfigurationLock lock(captureDevice); + if (!lock) { + qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration"; + return; + } + captureDevice.activeFormat = match; + } + +#if defined(Q_OS_IOS) && QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_8_0) { + AVCaptureStillImageOutput *imageOutput = m_service->imageCaptureControl()->stillImageOutput(); + if (res == qt_image_high_resolution(captureDevice.activeFormat)) + imageOutput.highResolutionStillImageOutputEnabled = YES; + else + imageOutput.highResolutionStillImageOutputEnabled = NO; + } +#endif + } else { +#else + { +#endif + // TODO: resolution without capture device format ... + } +} + +bool AVFImageEncoderControl::videoCaptureDeviceIsValid() const +{ + if (!m_service->session() || !m_service->session()->videoCaptureDevice()) + return false; + + AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice(); + if (!captureDevice.formats || !captureDevice.formats.count) + return false; + + return true; +} + +QT_END_NAMESPACE + +#include "moc_avfimageencodercontrol.cpp" diff --git a/src/plugins/avfoundation/camera/camera.pro b/src/plugins/avfoundation/camera/camera.pro index 19e3e631..729932ee 100644 --- a/src/plugins/avfoundation/camera/camera.pro +++ b/src/plugins/avfoundation/camera/camera.pro @@ -40,7 +40,8 @@ HEADERS += \ avfcamerafocuscontrol.h \ avfcameraexposurecontrol.h \ avfcamerautility.h \ - avfcameraviewfindersettingscontrol.h + avfcameraviewfindersettingscontrol.h \ + avfimageencodercontrol.h OBJECTIVE_SOURCES += \ avfcameraserviceplugin.mm \ @@ -60,7 +61,8 @@ OBJECTIVE_SOURCES += \ avfcamerafocuscontrol.mm \ avfcameraexposurecontrol.mm \ avfcamerautility.mm \ - avfcameraviewfindersettingscontrol.mm + avfcameraviewfindersettingscontrol.mm \ + avfimageencodercontrol.mm ios {