summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/imports/multimedia/multimedia.cpp6
-rw-r--r--src/imports/multimedia/plugins.qmltypes17
-rw-r--r--src/imports/multimedia/qdeclarativecamera.cpp9
-rw-r--r--src/imports/multimedia/qdeclarativecamera_p.h3
-rw-r--r--src/imports/multimedia/qdeclarativeradiodata.cpp38
-rw-r--r--src/multimedia/audio/qsoundeffect_qaudio_p.cpp1
-rw-r--r--src/plugins/android/src/common/qandroidvideorendercontrol.cpp47
-rw-r--r--src/plugins/avfoundation/camera/avfcamerarenderercontrol.h9
-rw-r--r--src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm137
-rw-r--r--src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm31
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.mm4
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h8
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm161
-rw-r--r--tests/auto/unit/qdeclarativecamera/tst_qdeclarativecamera.qml154
14 files changed, 491 insertions, 134 deletions
diff --git a/src/imports/multimedia/multimedia.cpp b/src/imports/multimedia/multimedia.cpp
index f61f97253..7d34672a1 100644
--- a/src/imports/multimedia/multimedia.cpp
+++ b/src/imports/multimedia/multimedia.cpp
@@ -98,7 +98,7 @@ public:
trUtf8("CameraImageProcessing is provided by Camera"));
// 5.2 types
- qmlRegisterRevision<QDeclarativeVideoOutput, 2>(uri, 5, 2);
+ qmlRegisterType<QDeclarativeVideoOutput, 2>(uri, 5, 2, "VideoOutput");
// 5.3 types
// Nothing changed, but adding "import QtMultimedia 5.3" in QML will fail unless at
@@ -107,13 +107,13 @@ public:
// 5.4 types
qmlRegisterSingletonType<QDeclarativeMultimediaGlobal>(uri, 5, 4, "QtMultimedia", multimedia_global_object);
- qmlRegisterRevision<QDeclarativeCamera, 1>(uri, 5, 4);
+ qmlRegisterType<QDeclarativeCamera, 1>(uri, 5, 4, "Camera");
qmlRegisterUncreatableType<QDeclarativeCameraViewfinder>(uri, 5, 4, "CameraViewfinder",
trUtf8("CameraViewfinder is provided by Camera"));
// 5.5 types
qmlRegisterUncreatableType<QDeclarativeCameraImageProcessing, 1>(uri, 5, 5, "CameraImageProcessing", trUtf8("CameraImageProcessing is provided by Camera"));
- qmlRegisterRevision<QDeclarativeCamera, 2>(uri, 5, 5);
+ qmlRegisterType<QDeclarativeCamera, 2>(uri, 5, 5, "Camera");
// 5.6 types
qmlRegisterType<QDeclarativeAudio, 1>(uri, 5, 6, "Audio");
diff --git a/src/imports/multimedia/plugins.qmltypes b/src/imports/multimedia/plugins.qmltypes
index 4d529dece..fe1c68af2 100644
--- a/src/imports/multimedia/plugins.qmltypes
+++ b/src/imports/multimedia/plugins.qmltypes
@@ -270,8 +270,12 @@ Module {
Component {
name: "QDeclarativeCamera"
prototype: "QObject"
- exports: ["QtMultimedia/Camera 5.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "QtMultimedia/Camera 5.0",
+ "QtMultimedia/Camera 5.4",
+ "QtMultimedia/Camera 5.5"
+ ]
+ exportMetaObjectRevisions: [0, 1, 2]
Enum {
name: "Position"
values: {
@@ -543,7 +547,7 @@ Module {
name: "supportedViewfinderFrameRateRanges"
revision: 2
type: "QJSValue"
- Parameter { name: "resolution"; type: "QSize" }
+ Parameter { name: "resolution"; type: "QJSValue" }
}
Method { name: "supportedViewfinderFrameRateRanges"; revision: 2; type: "QJSValue" }
}
@@ -1654,8 +1658,11 @@ Module {
name: "QDeclarativeVideoOutput"
defaultProperty: "data"
prototype: "QQuickItem"
- exports: ["QtMultimedia/VideoOutput 5.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "QtMultimedia/VideoOutput 5.0",
+ "QtMultimedia/VideoOutput 5.2"
+ ]
+ exportMetaObjectRevisions: [0, 2]
Enum {
name: "FillMode"
values: {
diff --git a/src/imports/multimedia/qdeclarativecamera.cpp b/src/imports/multimedia/qdeclarativecamera.cpp
index 5930b20fb..eac1c0c9b 100644
--- a/src/imports/multimedia/qdeclarativecamera.cpp
+++ b/src/imports/multimedia/qdeclarativecamera.cpp
@@ -1013,12 +1013,17 @@ QJSValue QDeclarativeCamera::supportedViewfinderResolutions(qreal minimumFrameRa
\since 5.5
*/
-QJSValue QDeclarativeCamera::supportedViewfinderFrameRateRanges(const QSize &resolution)
+QJSValue QDeclarativeCamera::supportedViewfinderFrameRateRanges(const QJSValue &resolution)
{
QQmlEngine *engine = qmlEngine(this);
QCameraViewfinderSettings settings;
- settings.setResolution(resolution);
+ if (!resolution.isUndefined()) {
+ QJSValue width = resolution.property(QStringLiteral("width"));
+ QJSValue height = resolution.property(QStringLiteral("height"));
+ if (width.isNumber() && height.isNumber())
+ settings.setResolution(width.toInt(), height.toInt());
+ }
QList<QCamera::FrameRateRange> frameRateRanges = m_camera->supportedViewfinderFrameRateRanges(settings);
QJSValue supportedFrameRateRanges = engine->newArray(frameRateRanges.count());
diff --git a/src/imports/multimedia/qdeclarativecamera_p.h b/src/imports/multimedia/qdeclarativecamera_p.h
index 623460c59..b3f199a57 100644
--- a/src/imports/multimedia/qdeclarativecamera_p.h
+++ b/src/imports/multimedia/qdeclarativecamera_p.h
@@ -57,6 +57,7 @@
#include <QtCore/qdatetime.h>
#include <QtQml/qqmlparserstatus.h>
#include <QtQml/qqml.h>
+#include <QtQml/qjsvalue.h>
QT_BEGIN_NAMESPACE
@@ -297,7 +298,7 @@ public Q_SLOTS:
Q_REVISION(2) QJSValue supportedViewfinderResolutions(qreal minimumFrameRate = 0.0,
qreal maximumFrameRate = 0.0);
- Q_REVISION(2) QJSValue supportedViewfinderFrameRateRanges(const QSize &resolution = QSize());
+ Q_REVISION(2) QJSValue supportedViewfinderFrameRateRanges(const QJSValue &resolution = QJSValue());
Q_SIGNALS:
void errorChanged();
diff --git a/src/imports/multimedia/qdeclarativeradiodata.cpp b/src/imports/multimedia/qdeclarativeradiodata.cpp
index 51d3bb877..f272e8d09 100644
--- a/src/imports/multimedia/qdeclarativeradiodata.cpp
+++ b/src/imports/multimedia/qdeclarativeradiodata.cpp
@@ -129,7 +129,10 @@ QDeclarativeRadioData::~QDeclarativeRadioData()
*/
QDeclarativeRadioData::Availability QDeclarativeRadioData::availability() const
{
- return Availability(m_radioData->availability());
+ if (m_radioData)
+ return Availability(m_radioData->availability());
+
+ return Unavailable;
}
@@ -141,7 +144,10 @@ QDeclarativeRadioData::Availability QDeclarativeRadioData::availability() const
*/
QString QDeclarativeRadioData::stationId() const
{
- return m_radioData->stationId();
+ if (m_radioData)
+ return m_radioData->stationId();
+
+ return QString();
}
/*!
@@ -206,7 +212,10 @@ QString QDeclarativeRadioData::stationId() const
*/
QDeclarativeRadioData::ProgramType QDeclarativeRadioData::programType() const
{
- return static_cast<QDeclarativeRadioData::ProgramType>(m_radioData->programType());
+ if (m_radioData)
+ return static_cast<QDeclarativeRadioData::ProgramType>(m_radioData->programType());
+
+ return Undefined;
}
/*!
@@ -216,7 +225,10 @@ QDeclarativeRadioData::ProgramType QDeclarativeRadioData::programType() const
*/
QString QDeclarativeRadioData::programTypeName() const
{
- return m_radioData->programTypeName();
+ if (m_radioData)
+ return m_radioData->programTypeName();
+
+ return QString();
}
/*!
@@ -226,7 +238,10 @@ QString QDeclarativeRadioData::programTypeName() const
*/
QString QDeclarativeRadioData::stationName() const
{
- return m_radioData->stationName();
+ if (m_radioData)
+ return m_radioData->stationName();
+
+ return QString();
}
/*!
@@ -238,7 +253,10 @@ QString QDeclarativeRadioData::stationName() const
*/
QString QDeclarativeRadioData::radioText() const
{
- return m_radioData->radioText();
+ if (m_radioData)
+ return m_radioData->radioText();
+
+ return QString();
}
/*!
@@ -250,12 +268,16 @@ QString QDeclarativeRadioData::radioText() const
*/
bool QDeclarativeRadioData::alternativeFrequenciesEnabled() const
{
- return m_radioData->isAlternativeFrequenciesEnabled();
+ if (m_radioData)
+ return m_radioData->isAlternativeFrequenciesEnabled();
+
+ return false;
}
void QDeclarativeRadioData::setAlternativeFrequenciesEnabled(bool enabled)
{
- m_radioData->setAlternativeFrequenciesEnabled(enabled);
+ if (m_radioData)
+ m_radioData->setAlternativeFrequenciesEnabled(enabled);
}
void QDeclarativeRadioData::_q_programTypeChanged(QRadioData::ProgramType programType)
diff --git a/src/multimedia/audio/qsoundeffect_qaudio_p.cpp b/src/multimedia/audio/qsoundeffect_qaudio_p.cpp
index c4776b32d..e95e4e521 100644
--- a/src/multimedia/audio/qsoundeffect_qaudio_p.cpp
+++ b/src/multimedia/audio/qsoundeffect_qaudio_p.cpp
@@ -297,6 +297,7 @@ void QSoundEffectPrivate::setCategory(const QString &category)
}
PrivateSoundSource::PrivateSoundSource(QSoundEffectPrivate* s):
+ QIODevice(s),
m_loopCount(1),
m_runningCount(0),
m_playing(false),
diff --git a/src/plugins/android/src/common/qandroidvideorendercontrol.cpp b/src/plugins/android/src/common/qandroidvideorendercontrol.cpp
index bf95c42f6..cd9c9d1d6 100644
--- a/src/plugins/android/src/common/qandroidvideorendercontrol.cpp
+++ b/src/plugins/android/src/common/qandroidvideorendercontrol.cpp
@@ -73,29 +73,60 @@ public:
: QAbstractVideoBuffer(GLTextureHandle)
, m_control(control)
, m_textureUpdated(false)
+ , m_mapMode(NotMapped)
{
}
virtual ~AndroidTextureVideoBuffer() {}
- MapMode mapMode() const { return NotMapped; }
- uchar *map(MapMode, int*, int*) { return 0; }
- void unmap() {}
+ MapMode mapMode() const { return m_mapMode; }
+
+ uchar *map(MapMode mode, int *numBytes, int *bytesPerLine)
+ {
+ if (m_mapMode == NotMapped && mode == ReadOnly) {
+ updateFrame();
+ m_mapMode = mode;
+ m_image = m_control->m_fbo->toImage();
+
+ if (numBytes)
+ *numBytes = m_image.byteCount();
+
+ if (bytesPerLine)
+ *bytesPerLine = m_image.bytesPerLine();
+
+ return m_image.bits();
+ } else {
+ return 0;
+ }
+ }
+
+ void unmap()
+ {
+ m_image = QImage();
+ m_mapMode = NotMapped;
+ }
QVariant handle() const
{
+ AndroidTextureVideoBuffer *that = const_cast<AndroidTextureVideoBuffer*>(this);
+ that->updateFrame();
+ return m_control->m_fbo->texture();
+ }
+
+private:
+ void updateFrame()
+ {
if (!m_textureUpdated) {
// update the video texture (called from the render thread)
m_control->renderFrameToFbo();
m_textureUpdated = true;
}
-
- return m_control->m_fbo->texture();
}
-private:
- mutable QAndroidVideoRendererControl *m_control;
- mutable bool m_textureUpdated;
+ QAndroidVideoRendererControl *m_control;
+ bool m_textureUpdated;
+ MapMode m_mapMode;
+ QImage m_image;
};
QAndroidVideoRendererControl::QAndroidVideoRendererControl(QObject *parent)
diff --git a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.h b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.h
index b8f92d9ca..2d74e2511 100644
--- a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.h
+++ b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.h
@@ -63,6 +63,8 @@ public:
AVCaptureVideoDataOutput *videoDataOutput() const;
+ bool supportsTextures() const { return m_supportsTextures; }
+
#ifdef Q_OS_IOS
AVFCaptureFramesDelegate *captureDelegate() const;
void resetCaptureDelegate() const;
@@ -81,11 +83,18 @@ private:
AVFCameraSession *m_cameraSession;
AVCaptureVideoDataOutput *m_videoDataOutput;
+ bool m_supportsTextures;
bool m_needsHorizontalMirroring;
+#ifdef Q_OS_IOS
+ CVOpenGLESTextureCacheRef m_textureCache;
+#endif
+
QVideoFrame m_lastViewfinderFrame;
QMutex m_vfMutex;
dispatch_queue_t m_delegateQueue;
+
+ friend class CVImageVideoBuffer;
};
QT_END_NAMESPACE
diff --git a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm
index 529541285..924c62d97 100644
--- a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm
+++ b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm
@@ -38,6 +38,10 @@
#include "avfcameraservice.h"
#include "avfcameradebug.h"
+#ifdef Q_OS_IOS
+#include <QtGui/qopengl.h>
+#endif
+
#include <QtMultimedia/qabstractvideosurface.h>
#include <QtMultimedia/qabstractvideobuffer.h>
@@ -45,20 +49,32 @@
QT_USE_NAMESPACE
-class CVPixelBufferVideoBuffer : public QAbstractPlanarVideoBuffer
+class CVImageVideoBuffer : public QAbstractPlanarVideoBuffer
{
- friend class CVPixelBufferVideoBufferPrivate;
public:
- CVPixelBufferVideoBuffer(CVPixelBufferRef buffer)
+ CVImageVideoBuffer(CVImageBufferRef buffer, AVFCameraRendererControl *renderer)
+#ifndef Q_OS_IOS
: QAbstractPlanarVideoBuffer(NoHandle)
+#else
+ : QAbstractPlanarVideoBuffer(renderer->supportsTextures()
+ && CVPixelBufferGetPixelFormatType(buffer) == kCVPixelFormatType_32BGRA
+ ? GLTextureHandle : NoHandle)
+ , m_texture(0)
+#endif
, m_buffer(buffer)
+ , m_renderer(renderer)
, m_mode(NotMapped)
{
CVPixelBufferRetain(m_buffer);
}
- virtual ~CVPixelBufferVideoBuffer()
+ ~CVImageVideoBuffer()
{
+ CVImageVideoBuffer::unmap();
+#ifdef Q_OS_IOS
+ if (m_texture)
+ CFRelease(m_texture);
+#endif
CVPixelBufferRelease(m_buffer);
}
@@ -78,7 +94,9 @@ public:
// For a bi-planar format we have to set the parameters correctly:
if (mode != QAbstractVideoBuffer::NotMapped && m_mode == QAbstractVideoBuffer::NotMapped) {
- CVPixelBufferLockBaseAddress(m_buffer, 0);
+ CVPixelBufferLockBaseAddress(m_buffer, mode == QAbstractVideoBuffer::ReadOnly
+ ? kCVPixelBufferLock_ReadOnly
+ : 0);
if (numBytes)
*numBytes = CVPixelBufferGetDataSize(m_buffer);
@@ -103,8 +121,9 @@ public:
uchar *map(MapMode mode, int *numBytes, int *bytesPerLine)
{
if (mode != NotMapped && m_mode == NotMapped) {
- CVPixelBufferLockBaseAddress(m_buffer, 0);
-
+ CVPixelBufferLockBaseAddress(m_buffer, mode == QAbstractVideoBuffer::ReadOnly
+ ? kCVPixelBufferLock_ReadOnly
+ : 0);
if (numBytes)
*numBytes = CVPixelBufferGetDataSize(m_buffer);
@@ -121,13 +140,63 @@ public:
void unmap()
{
if (m_mode != NotMapped) {
+ CVPixelBufferUnlockBaseAddress(m_buffer, m_mode == QAbstractVideoBuffer::ReadOnly
+ ? kCVPixelBufferLock_ReadOnly
+ : 0);
m_mode = NotMapped;
- CVPixelBufferUnlockBaseAddress(m_buffer, 0);
}
}
+ QVariant handle() const
+ {
+#ifdef Q_OS_IOS
+ // Called from the render thread, so there is a current OpenGL context
+
+ if (!m_renderer->m_textureCache) {
+ CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault,
+ NULL,
+ [EAGLContext currentContext],
+ NULL,
+ &m_renderer->m_textureCache);
+
+ if (err != kCVReturnSuccess)
+ qWarning("Error creating texture cache");
+ }
+
+ if (m_renderer->m_textureCache && !m_texture) {
+ CVOpenGLESTextureCacheFlush(m_renderer->m_textureCache, 0);
+
+ CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
+ m_renderer->m_textureCache,
+ m_buffer,
+ NULL,
+ GL_TEXTURE_2D,
+ GL_RGBA,
+ CVPixelBufferGetWidth(m_buffer),
+ CVPixelBufferGetHeight(m_buffer),
+ GL_BGRA,
+ GL_UNSIGNED_BYTE,
+ 0,
+ &m_texture);
+ if (err != kCVReturnSuccess)
+ qWarning("Error creating texture from buffer");
+ }
+
+ if (m_texture)
+ return CVOpenGLESTextureGetName(m_texture);
+ else
+ return 0;
+#else
+ return QVariant();
+#endif
+ }
+
private:
- CVPixelBufferRef m_buffer;
+#ifdef Q_OS_IOS
+ mutable CVOpenGLESTextureRef m_texture;
+#endif
+ CVImageBufferRef m_buffer;
+ AVFCameraRendererControl *m_renderer;
MapMode m_mode;
};
@@ -171,13 +240,25 @@ private:
int width = CVPixelBufferGetWidth(imageBuffer);
int height = CVPixelBufferGetHeight(imageBuffer);
- QVideoFrame::PixelFormat format =
- AVFCameraViewfinderSettingsControl2::QtPixelFormatFromCVFormat(CVPixelBufferGetPixelFormatType(imageBuffer));
+ QVideoFrame::PixelFormat format;
+
+#ifdef Q_OS_IOS
+ bool useTexture = m_renderer->supportsTextures()
+ && CVPixelBufferGetPixelFormatType(imageBuffer) == kCVPixelFormatType_32BGRA;
+
+ if (useTexture)
+ format = QVideoFrame::Format_BGRA32;
+ else
+#endif
+ format = AVFCameraViewfinderSettingsControl2::QtPixelFormatFromCVFormat(CVPixelBufferGetPixelFormatType(imageBuffer));
if (format == QVideoFrame::Format_Invalid)
return;
- QVideoFrame frame(new CVPixelBufferVideoBuffer(imageBuffer), QSize(width, height), format);
+ QVideoFrame frame(new CVImageVideoBuffer(imageBuffer, m_renderer),
+ QSize(width, height),
+ format);
+
m_renderer->syncHandleViewfinderFrame(frame);
}
@@ -187,7 +268,11 @@ private:
AVFCameraRendererControl::AVFCameraRendererControl(QObject *parent)
: QVideoRendererControl(parent)
, m_surface(0)
+ , m_supportsTextures(false)
, m_needsHorizontalMirroring(false)
+#ifdef Q_OS_IOS
+ , m_textureCache(0)
+#endif
{
m_viewfinderFramesDelegate = [[AVFCaptureFramesDelegate alloc] initWithRenderer:this];
}
@@ -198,6 +283,10 @@ AVFCameraRendererControl::~AVFCameraRendererControl()
[m_viewfinderFramesDelegate release];
if (m_delegateQueue)
dispatch_release(m_delegateQueue);
+#ifdef Q_OS_IOS
+ if (m_textureCache)
+ CFRelease(m_textureCache);
+#endif
}
QAbstractVideoSurface *AVFCameraRendererControl::surface() const
@@ -209,6 +298,11 @@ void AVFCameraRendererControl::setSurface(QAbstractVideoSurface *surface)
{
if (m_surface != surface) {
m_surface = surface;
+#ifdef Q_OS_IOS
+ m_supportsTextures = m_surface
+ ? m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).contains(QVideoFrame::Format_BGRA32)
+ : false;
+#endif
Q_EMIT surfaceChanged(surface);
}
}
@@ -261,21 +355,6 @@ void AVFCameraRendererControl::syncHandleViewfinderFrame(const QVideoFrame &fram
m_lastViewfinderFrame = frame;
- if (m_needsHorizontalMirroring) {
- m_lastViewfinderFrame.map(QAbstractVideoBuffer::ReadOnly);
-
- // no deep copy
- QImage image(m_lastViewfinderFrame.bits(),
- m_lastViewfinderFrame.size().width(),
- m_lastViewfinderFrame.size().height(),
- m_lastViewfinderFrame.bytesPerLine(),
- QImage::Format_RGB32);
-
- QImage mirrored = image.mirrored(true, false);
-
- m_lastViewfinderFrame.unmap();
- m_lastViewfinderFrame = QVideoFrame(mirrored);
- }
if (m_cameraSession && m_lastViewfinderFrame.isValid())
m_cameraSession->onCameraFrameFetched(m_lastViewfinderFrame);
}
@@ -315,7 +394,9 @@ void AVFCameraRendererControl::handleViewfinderFrame()
}
if (!m_surface->isActive()) {
- QVideoSurfaceFormat format(frame.size(), frame.pixelFormat());
+ QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), frame.handleType());
+ if (m_needsHorizontalMirroring)
+ format.setProperty("mirrored", true);
if (!m_surface->start(format)) {
qWarning() << "Failed to start viewfinder m_surface, format:" << format;
diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm
index b32c0cd63..05f28898d 100644
--- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm
+++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm
@@ -573,22 +573,29 @@ void AVFCameraViewfinderSettingsControl2::applySettings()
if (!convertPixelFormatIfSupported(m_settings.pixelFormat(), avfPixelFormat)) {
// If the the pixel format is not specified or invalid, pick the preferred video surface
// format, or if no surface is set, the preferred capture device format
- const QVector<QVideoFrame::PixelFormat> deviceFormats = viewfinderPixelFormats();
- QList<QVideoFrame::PixelFormat> surfaceFormats;
- if (m_service->videoOutput() && m_service->videoOutput()->surface())
- surfaceFormats = m_service->videoOutput()->surface()->supportedPixelFormats();
-
- QVideoFrame::PixelFormat format = deviceFormats.first();
- for (int i = 0; i < surfaceFormats.count(); ++i) {
- const QVideoFrame::PixelFormat surfaceFormat = surfaceFormats.at(i);
- if (deviceFormats.contains(surfaceFormat)) {
- format = surfaceFormat;
- break;
+ const QVector<QVideoFrame::PixelFormat> deviceFormats = viewfinderPixelFormats();
+ QVideoFrame::PixelFormat pickedFormat = deviceFormats.first();
+
+ QAbstractVideoSurface *surface = m_service->videoOutput() ? m_service->videoOutput()->surface()
+ : 0;
+ if (surface) {
+ if (m_service->videoOutput()->supportsTextures()) {
+ pickedFormat = QVideoFrame::Format_ARGB32;
+ } else {
+ QList<QVideoFrame::PixelFormat> surfaceFormats = m_service->videoOutput()->surface()->supportedPixelFormats();
+
+ for (int i = 0; i < surfaceFormats.count(); ++i) {
+ const QVideoFrame::PixelFormat surfaceFormat = surfaceFormats.at(i);
+ if (deviceFormats.contains(surfaceFormat)) {
+ pickedFormat = surfaceFormat;
+ break;
+ }
+ }
}
}
- CVPixelFormatFromQtFormat(format, avfPixelFormat);
+ CVPixelFormatFromQtFormat(pickedFormat, avfPixelFormat);
}
if (avfPixelFormat != 0) {
diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.mm
index 1a4defac5..1f4b42bf1 100644
--- a/src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.mm
+++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayercontrol.mm
@@ -63,6 +63,10 @@ void AVFMediaPlayerControl::setSession(AVFMediaPlayerSession *session)
connect(m_session, SIGNAL(audioAvailableChanged(bool)), this, SIGNAL(audioAvailableChanged(bool)));
connect(m_session, SIGNAL(videoAvailableChanged(bool)), this, SIGNAL(videoAvailableChanged(bool)));
connect(m_session, SIGNAL(error(int,QString)), this, SIGNAL(error(int,QString)));
+ connect(m_session, &AVFMediaPlayerSession::playbackRateChanged,
+ this, &AVFMediaPlayerControl::playbackRateChanged);
+ connect(m_session, &AVFMediaPlayerSession::seekableChanged,
+ this, &AVFMediaPlayerControl::seekableChanged);
}
QMediaPlayer::State AVFMediaPlayerControl::state() const
diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h
index 5e224bb50..18bcb23e1 100644
--- a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h
+++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.h
@@ -80,6 +80,8 @@ public:
qreal playbackRate() const;
+ inline bool isVolumeSupported() const { return m_volumeSupported; }
+
public Q_SLOTS:
void setPlaybackRate(qreal rate);
@@ -106,6 +108,8 @@ Q_SIGNALS:
void mutedChanged(bool muted);
void audioAvailableChanged(bool audioAvailable);
void videoAvailableChanged(bool videoAvailable);
+ void playbackRateChanged(qreal rate);
+ void seekableChanged(bool seekable);
void error(int error, const QString &errorString);
private:
@@ -148,6 +152,7 @@ private:
void setAudioAvailable(bool available);
void setVideoAvailable(bool available);
+ void setSeekable(bool seekable);
AVFMediaPlayerService *m_service;
AVFVideoOutput *m_videoOutput;
@@ -158,14 +163,17 @@ private:
QMediaContent m_resources;
ResourceHandler m_resourceHandler;
+ const bool m_volumeSupported;
bool m_muted;
bool m_tryingAsync;
int m_volume;
qreal m_rate;
+ qint64 m_requestedPosition;
qint64 m_duration;
bool m_videoAvailable;
bool m_audioAvailable;
+ bool m_seekable;
void *m_observer;
};
diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm
index 5ef1f9baf..53cdebd82 100644
--- a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm
+++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm
@@ -121,19 +121,28 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe
- (void) unloadMedia
{
- if (m_player)
- [m_player setRate:0.0];
if (m_playerItem) {
[m_playerItem removeObserver:self forKeyPath:AVF_STATUS_KEY];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:AVPlayerItemDidPlayToEndTimeNotification
- object:m_playerItem];
+ object:m_playerItem];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:AVPlayerItemTimeJumpedNotification
object:m_playerItem];
m_playerItem = 0;
}
+ if (m_player) {
+ [m_player setRate:0.0];
+ [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_KEY];
+ [m_player removeObserver:self forKeyPath:AVF_RATE_KEY];
+ [m_player release];
+ m_player = 0;
+ }
+ if (m_playerLayer) {
+ [m_playerLayer release];
+ m_playerLayer = 0;
+ }
}
- (void) prepareToPlayAsset:(AVURLAsset *)asset
@@ -203,30 +212,15 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe
name:AVPlayerItemTimeJumpedNotification
object:m_playerItem];
-
- //Clean up old player if we have one
- if (m_player) {
- [m_player setRate:0.0];
- [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_KEY];
- [m_player removeObserver:self forKeyPath:AVF_RATE_KEY];
- [m_player release];
- m_player = 0;
-
- if (m_playerLayer) {
- [m_playerLayer release];
- m_playerLayer = 0; //Will have been released
- }
- }
-
//Get a new AVPlayer initialized to play the specified player item.
m_player = [AVPlayer playerWithPlayerItem:m_playerItem];
[m_player retain];
-#if defined(Q_OS_OSX)
//Set the initial volume on new player object
- if (self.session)
- m_player.volume = m_session->volume() / 100.0f;
-#endif
+ if (self.session && self.session->isVolumeSupported()) {
+ [m_player setVolume:m_session->volume() / 100.0f];
+ [m_player setMuted:m_session->isMuted()];
+ }
//Create a new player layer if we don't have one already
if (!m_playerLayer)
@@ -354,18 +348,6 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe
#endif
[self unloadMedia];
- if (m_player) {
- [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_KEY];
- [m_player removeObserver:self forKeyPath:AVF_RATE_KEY];
- [m_player release];
- m_player = 0;
- }
-
- if (m_playerLayer) {
- [m_playerLayer release];
- m_playerLayer = 0;
- }
-
if (m_URL) {
[m_URL release];
}
@@ -382,13 +364,20 @@ AVFMediaPlayerSession::AVFMediaPlayerSession(AVFMediaPlayerService *service, QOb
, m_state(QMediaPlayer::StoppedState)
, m_mediaStatus(QMediaPlayer::NoMedia)
, m_mediaStream(0)
+#ifdef Q_OS_IOS
+ , m_volumeSupported(QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_7_0)
+#else
+ , m_volumeSupported(true)
+#endif
, m_muted(false)
, m_tryingAsync(false)
, m_volume(100)
, m_rate(1.0)
+ , m_requestedPosition(-1)
, m_duration(0)
, m_videoAvailable(false)
, m_audioAvailable(false)
+ , m_seekable(false)
{
m_observer = [[AVFMediaPlayerSessionObserver alloc] initWithMediaPlayerSession:this];
}
@@ -458,23 +447,26 @@ void AVFMediaPlayerSession::setMedia(const QMediaContent &content, QIODevice *st
qDebug() << Q_FUNC_INFO << content.canonicalUrl();
#endif
+ [(AVFMediaPlayerSessionObserver*)m_observer unloadMedia];
+
m_resources = content;
m_mediaStream = stream;
setAudioAvailable(false);
setVideoAvailable(false);
+ setSeekable(false);
+ m_requestedPosition = -1;
+ Q_EMIT positionChanged(position());
QMediaPlayer::MediaStatus oldMediaStatus = m_mediaStatus;
if (content.isNull() || content.canonicalUrl().isEmpty()) {
- [(AVFMediaPlayerSessionObserver*)m_observer unloadMedia];
m_mediaStatus = QMediaPlayer::NoMedia;
if (m_state != QMediaPlayer::StoppedState)
Q_EMIT stateChanged(m_state = QMediaPlayer::StoppedState);
if (m_mediaStatus != oldMediaStatus)
Q_EMIT mediaStatusChanged(m_mediaStatus);
- Q_EMIT positionChanged(position());
return;
} else {
@@ -482,6 +474,7 @@ void AVFMediaPlayerSession::setMedia(const QMediaContent &content, QIODevice *st
if (m_mediaStatus != oldMediaStatus)
Q_EMIT mediaStatusChanged(m_mediaStatus);
}
+
//Load AVURLAsset
//initialize asset using content's URL
NSString *urlString = [NSString stringWithUTF8String:content.canonicalUrl().toEncoded().constData()];
@@ -494,7 +487,7 @@ qint64 AVFMediaPlayerSession::position() const
AVPlayerItem *playerItem = [(AVFMediaPlayerSessionObserver*)m_observer playerItem];
if (!playerItem)
- return 0;
+ return m_requestedPosition != -1 ? m_requestedPosition : 0;
CMTime time = [playerItem currentTime];
return static_cast<quint64>(float(time.value) / float(time.timescale) * 1000.0f);
@@ -563,7 +556,16 @@ bool AVFMediaPlayerSession::isVideoAvailable() const
bool AVFMediaPlayerSession::isSeekable() const
{
- return true;
+ return m_seekable;
+}
+
+void AVFMediaPlayerSession::setSeekable(bool seekable)
+{
+ if (m_seekable == seekable)
+ return;
+
+ m_seekable = seekable;
+ Q_EMIT seekableChanged(seekable);
}
QMediaTimeRange AVFMediaPlayerSession::availablePlaybackRanges() const
@@ -602,10 +604,10 @@ void AVFMediaPlayerSession::setPlaybackRate(qreal rate)
m_rate = rate;
AVPlayer *player = [(AVFMediaPlayerSessionObserver*)m_observer player];
-
- if (player != 0 && m_state == QMediaPlayer::PlayingState) {
+ if (player && m_state == QMediaPlayer::PlayingState)
[player setRate:m_rate];
- }
+
+ Q_EMIT playbackRateChanged(m_rate);
}
void AVFMediaPlayerSession::setPosition(qint64 pos)
@@ -614,14 +616,23 @@ void AVFMediaPlayerSession::setPosition(qint64 pos)
qDebug() << Q_FUNC_INFO << pos;
#endif
- if ( !isSeekable() || pos == position())
+ if (pos == position())
return;
AVPlayerItem *playerItem = [(AVFMediaPlayerSessionObserver*)m_observer playerItem];
-
- if (!playerItem)
+ if (!playerItem) {
+ m_requestedPosition = pos;
+ Q_EMIT positionChanged(m_requestedPosition);
return;
+ } else if (!isSeekable()) {
+ if (m_requestedPosition != -1) {
+ m_requestedPosition = -1;
+ Q_EMIT positionChanged(position());
+ }
+ return;
+ }
+ pos = qMax(qint64(0), pos);
if (duration() > 0)
pos = qMin(pos, duration());
@@ -655,8 +666,10 @@ void AVFMediaPlayerSession::play()
processLoadStateChange();
}
- if (m_mediaStatus == QMediaPlayer::LoadedMedia || m_mediaStatus == QMediaPlayer::BufferedMedia)
- [[(AVFMediaPlayerSessionObserver*)m_observer player] play];
+ if (m_mediaStatus == QMediaPlayer::LoadedMedia || m_mediaStatus == QMediaPlayer::BufferedMedia) {
+ // Setting the rate starts playback
+ [[(AVFMediaPlayerSessionObserver*)m_observer player] setRate:m_rate];
+ }
//processLoadStateChange();
Q_EMIT stateChanged(m_state);
@@ -697,8 +710,8 @@ void AVFMediaPlayerSession::stop()
return;
m_state = QMediaPlayer::StoppedState;
- m_rate = 0.0f;
- [[(AVFMediaPlayerSessionObserver*)m_observer player] setRate:m_rate];
+ // AVPlayer doesn't have stop(), only pause() and play().
+ [[(AVFMediaPlayerSessionObserver*)m_observer player] pause];
setPosition(0);
if (m_videoOutput) {
@@ -716,21 +729,20 @@ void AVFMediaPlayerSession::setVolume(int volume)
qDebug() << Q_FUNC_INFO << volume;
#endif
- if (m_volume == volume)
- return;
-
- AVPlayer *player = [(AVFMediaPlayerSessionObserver*)m_observer player];
- if (!player)
- return;
-
- if (![player respondsToSelector:@selector(setVolume:)]) {
+ if (!m_volumeSupported) {
qWarning("%s not implemented, requires iOS 7 or later", Q_FUNC_INFO);
return;
}
- [player setVolume:volume / 100.0f];
+ if (m_volume == volume)
+ return;
+
m_volume = volume;
+ AVPlayer *player = [(AVFMediaPlayerSessionObserver*)m_observer player];
+ if (player)
+ [player setVolume:volume / 100.0f];
+
Q_EMIT volumeChanged(m_volume);
}
@@ -739,22 +751,21 @@ void AVFMediaPlayerSession::setMuted(bool muted)
#ifdef QT_DEBUG_AVF
qDebug() << Q_FUNC_INFO << muted;
#endif
- 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:)]) {
+ if (!m_volumeSupported) {
qWarning("%s not implemented, requires iOS 7 or later", Q_FUNC_INFO);
return;
}
- [player setMuted:muted];
+ if (m_muted == muted)
+ return;
+
m_muted = muted;
+ AVPlayer *player = [(AVFMediaPlayerSessionObserver*)m_observer player];
+ if (player)
+ [player setMuted:muted];
+
Q_EMIT mutedChanged(muted);
}
@@ -767,6 +778,11 @@ void AVFMediaPlayerSession::processEOS()
Q_EMIT positionChanged(position());
m_mediaStatus = QMediaPlayer::EndOfMedia;
+ // At this point, frames should not be rendered anymore.
+ // Clear the output layer to make sure of that.
+ if (m_videoOutput)
+ m_videoOutput->setLayer(0);
+
Q_EMIT stateChanged(m_state = QMediaPlayer::StoppedState);
Q_EMIT mediaStatusChanged(m_mediaStatus);
}
@@ -801,6 +817,8 @@ void AVFMediaPlayerSession::processLoadStateChange()
}
}
+ setSeekable([[playerItem seekableTimeRanges] count] > 0);
+
// Get the native size of the video, and reset the bounds of the player layer
AVPlayerLayer *playerLayer = [(AVFMediaPlayerSessionObserver*)m_observer playerLayer];
if (videoTrack && playerLayer) {
@@ -818,11 +836,16 @@ void AVFMediaPlayerSession::processLoadStateChange()
if (m_duration != currentDuration)
Q_EMIT durationChanged(m_duration = currentDuration);
+ if (m_requestedPosition != -1) {
+ setPosition(m_requestedPosition);
+ m_requestedPosition = -1;
+ }
+
newStatus = isPlaying ? QMediaPlayer::BufferedMedia : QMediaPlayer::LoadedMedia;
if (m_state == QMediaPlayer::PlayingState && [(AVFMediaPlayerSessionObserver*)m_observer player]) {
+ // Setting the rate is enough to start playback, no need to call play()
[[(AVFMediaPlayerSessionObserver*)m_observer player] setRate:m_rate];
- [[(AVFMediaPlayerSessionObserver*)m_observer player] play];
}
}
@@ -837,6 +860,10 @@ void AVFMediaPlayerSession::processPositionChange()
void AVFMediaPlayerSession::processMediaLoadError()
{
+ if (m_requestedPosition != -1) {
+ m_requestedPosition = -1;
+ Q_EMIT positionChanged(position());
+ }
Q_EMIT error(QMediaPlayer::FormatError, tr("Failed to load media"));
Q_EMIT mediaStatusChanged(m_mediaStatus = QMediaPlayer::InvalidMedia);
Q_EMIT stateChanged(m_state = QMediaPlayer::StoppedState);
diff --git a/tests/auto/unit/qdeclarativecamera/tst_qdeclarativecamera.qml b/tests/auto/unit/qdeclarativecamera/tst_qdeclarativecamera.qml
index 833ca522b..02e32f4c9 100644
--- a/tests/auto/unit/qdeclarativecamera/tst_qdeclarativecamera.qml
+++ b/tests/auto/unit/qdeclarativecamera/tst_qdeclarativecamera.qml
@@ -144,4 +144,158 @@ TestCase {
cameraLoader.sourceComponent = undefined;
}
+
+ function test_supportedViewfinderResolutions_data() {
+ // see mockcameraviewfindersettingscontrol.h for expected values
+
+ return [
+ {
+ tag: "all",
+ minimumFrameRate: 0, maximumFrameRate: 0,
+ expectedResolutions: [
+ { width: 320, height: 240 },
+ { width: 640, height: 480 },
+ { width: 1280, height: 720 },
+ { width: 1920, height: 1080 }
+ ]
+ },
+ {
+ tag: "invalid minimumFrameRate",
+ minimumFrameRate: 2, maximumFrameRate: 0,
+ expectedResolutions: [ ]
+ },
+ {
+ tag: "minimumFrameRate=5",
+ minimumFrameRate: 5, maximumFrameRate: 0,
+ expectedResolutions: [
+ { width: 1920, height: 1080 }
+ ]
+ },
+ {
+ tag: "minimumFrameRate=10",
+ minimumFrameRate: 10, maximumFrameRate: 0,
+ expectedResolutions: [
+ { width: 1280, height: 720 }
+ ]
+ },
+ {
+ tag: "minimumFrameRate=30",
+ minimumFrameRate: 30, maximumFrameRate: 0,
+ expectedResolutions: [
+ { width: 320, height: 240 },
+ { width: 640, height: 480 },
+ { width: 1280, height: 720 }
+ ]
+ },
+ {
+ tag: "invalid maximumFrameRate",
+ minimumFrameRate: 0, maximumFrameRate: 2,
+ expectedResolutions: [ ]
+ },
+ {
+ tag: "maximumFrameRate=10",
+ minimumFrameRate: 0, maximumFrameRate: 10,
+ expectedResolutions: [
+ { width: 1280, height: 720 },
+ { width: 1920, height: 1080 }
+ ]
+ },
+ {
+ tag: "minimumFrameRate=10, maximumFrameRate=10",
+ minimumFrameRate: 10, maximumFrameRate: 10,
+ expectedResolutions: [
+ { width: 1280, height: 720 }
+ ]
+ },
+ {
+ tag: "minimumFrameRate=30, maximumFrameRate=30",
+ minimumFrameRate: 30, maximumFrameRate: 30,
+ expectedResolutions: [
+ { width: 320, height: 240 },
+ { width: 640, height: 480 },
+ { width: 1280, height: 720 }
+ ]
+ }
+ ]
+ }
+
+ function test_supportedViewfinderResolutions(data) {
+ cameraLoader.sourceComponent = cameraComponent;
+ var camera = cameraLoader.item;
+
+ var actualResolutions = camera.supportedViewfinderResolutions(data.minimumFrameRate, data.maximumFrameRate);
+ compare(actualResolutions.length, data.expectedResolutions.length);
+ for (var i = 0; i < actualResolutions.length; ++i) {
+ compare(actualResolutions[i].width, data.expectedResolutions[i].width);
+ compare(actualResolutions[i].height, data.expectedResolutions[i].height);
+ }
+
+ cameraLoader.sourceComponent = undefined;
+ }
+
+ function test_supportedViewfinderFrameRateRanges_data() {
+ // see mockcameraviewfindersettingscontrol.h for expected values
+ return [
+ {
+ tag: "all",
+ expectedFrameRateRanges: [
+ { minimumFrameRate: 5, maximumFrameRate: 10 },
+ { minimumFrameRate: 10, maximumFrameRate: 10 },
+ { minimumFrameRate: 30, maximumFrameRate: 30 }
+ ]
+ },
+ {
+ tag: "invalid",
+ resolution: { width: 452472, height: 444534 },
+ expectedFrameRateRanges: [ ]
+ },
+ {
+ tag: "320, 240",
+ resolution: { width: 320, height: 240 },
+ expectedFrameRateRanges: [
+ { minimumFrameRate: 30, maximumFrameRate: 30 }
+ ]
+ },
+ {
+ tag: "1280, 720",
+ resolution: { width: 1280, height: 720 },
+ expectedFrameRateRanges: [
+ { minimumFrameRate: 10, maximumFrameRate: 10 },
+ { minimumFrameRate: 30, maximumFrameRate: 30 }
+ ]
+ },
+ {
+ tag: "1920, 1080",
+ resolution: { width: 1920, height: 1080 },
+ expectedFrameRateRanges: [
+ { minimumFrameRate: 5, maximumFrameRate: 10 }
+ ]
+ }
+ ]
+ }
+
+ function test_supportedViewfinderFrameRateRanges(data) {
+ cameraLoader.sourceComponent = cameraComponent;
+ var camera = cameraLoader.item;
+
+ // Pass the resolution as an object
+ var actualFrameRateRanges = camera.supportedViewfinderFrameRateRanges(data.resolution);
+ compare(actualFrameRateRanges.length, data.expectedFrameRateRanges.length);
+ for (var i = 0; i < actualFrameRateRanges.length; ++i) {
+ compare(actualFrameRateRanges[i].minimumFrameRate, data.expectedFrameRateRanges[i].minimumFrameRate);
+ compare(actualFrameRateRanges[i].maximumFrameRate, data.expectedFrameRateRanges[i].maximumFrameRate);
+ }
+
+ // Pass the resolution as a size
+ if (typeof data.resolution !== 'undefined') {
+ actualFrameRateRanges = camera.supportedViewfinderFrameRateRanges(Qt.size(data.resolution.width, data.resolution.height));
+ compare(actualFrameRateRanges.length, data.expectedFrameRateRanges.length);
+ for (i = 0; i < actualFrameRateRanges.length; ++i) {
+ compare(actualFrameRateRanges[i].minimumFrameRate, data.expectedFrameRateRanges[i].minimumFrameRate);
+ compare(actualFrameRateRanges[i].maximumFrameRate, data.expectedFrameRateRanges[i].maximumFrameRate);
+ }
+ }
+
+ cameraLoader.sourceComponent = undefined;
+ }
}