diff options
author | Yoann Lopes <yoann.lopes@qt.io> | 2016-08-01 11:52:05 +0200 |
---|---|---|
committer | Yoann Lopes <yoann.lopes@qt.io> | 2016-08-01 11:52:05 +0200 |
commit | 339944b284e9dd11302dd013f9d9a10ad0d9055a (patch) | |
tree | c2952d186b1dbc6f2380448fa2cfd9ec55043da6 | |
parent | 53d0a1e5454f4e5beecdea19acb9df11c87cc375 (diff) | |
parent | d7d31d63db5f0029a4a5e24d998601baee8bade0 (diff) | |
download | qtmultimedia-339944b284e9dd11302dd013f9d9a10ad0d9055a.tar.gz |
Merge remote-tracking branch 'origin/5.6' into 5.7
Change-Id: Ic29cb09048003f18ff86d4546cd547be715eaec8
18 files changed, 184 insertions, 122 deletions
diff --git a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureListener.java b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureListener.java index 0404e2d75..a997dde90 100644 --- a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureListener.java +++ b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureListener.java @@ -43,18 +43,18 @@ import android.graphics.SurfaceTexture; public class QtSurfaceTextureListener implements SurfaceTexture.OnFrameAvailableListener { - private final int texID; + private final long m_id; - public QtSurfaceTextureListener(int texName) + public QtSurfaceTextureListener(long id) { - texID = texName; + m_id = id; } @Override public void onFrameAvailable(SurfaceTexture surfaceTexture) { - notifyFrameAvailable(texID); + notifyFrameAvailable(m_id); } - private static native void notifyFrameAvailable(int id); + private static native void notifyFrameAvailable(long id); } diff --git a/src/plugins/android/src/common/qandroidvideooutput.cpp b/src/plugins/android/src/common/qandroidvideooutput.cpp index 5c804ccc4..a5cd3580b 100644 --- a/src/plugins/android/src/common/qandroidvideooutput.cpp +++ b/src/plugins/android/src/common/qandroidvideooutput.cpp @@ -147,7 +147,13 @@ public: delete m_program; } - void setTexture(quint32 id) { m_textureID = id; } + void setTexture(quint32 id) { + if (m_textureID) + glDeleteTextures(1, &m_textureID); + + m_textureID = id; + } + void setFbo(QOpenGLFramebufferObject *fbo) { m_fbo = fbo; } void setShaderProgram(QOpenGLShaderProgram *prog) { m_program = prog; } diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp index 9137fbc05..0b082b02b 100644 --- a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp +++ b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp @@ -295,16 +295,27 @@ void QAndroidCameraSession::applyViewfinderSettings(const QSize &captureSize, bo adjustedViewfinderResolution = vfRes; } else if (validCaptureSize) { // search for viewfinder resolution with the same aspect ratio + qreal minAspectDiff = 1; + QSize closestResolution; for (int i = previewSizes.count() - 1; i >= 0; --i) { const QSize &size = previewSizes.at(i); - if (qAbs(captureAspectRatio - (qreal(size.width()) / size.height())) < 0.01) { + const qreal sizeAspect = qreal(size.width()) / size.height(); + if (qFuzzyCompare(captureAspectRatio, sizeAspect)) { adjustedViewfinderResolution = size; break; + } else if (minAspectDiff > qAbs(sizeAspect - captureAspectRatio)) { + closestResolution = size; + minAspectDiff = qAbs(sizeAspect - captureAspectRatio); } } if (!adjustedViewfinderResolution.isValid()) { qWarning("Cannot find a viewfinder resolution matching the capture aspect ratio."); - return; + if (closestResolution.isValid()) { + adjustedViewfinderResolution = closestResolution; + qWarning("Using closest viewfinder resolution."); + } else { + return; + } } } else { adjustedViewfinderResolution = previewSizes.last(); diff --git a/src/plugins/android/src/qandroidmediaserviceplugin.cpp b/src/plugins/android/src/qandroidmediaserviceplugin.cpp index 87de7d561..0de231c9d 100644 --- a/src/plugins/android/src/qandroidmediaserviceplugin.cpp +++ b/src/plugins/android/src/qandroidmediaserviceplugin.cpp @@ -150,6 +150,11 @@ QT_END_NAMESPACE Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/) { + static bool initialized = false; + if (initialized) + return JNI_VERSION_1_6; + initialized = true; + QT_USE_NAMESPACE typedef union { JNIEnv *nativeEnvironment; diff --git a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp index 82a8bffe3..df09d0819 100644 --- a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp +++ b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.cpp @@ -40,24 +40,32 @@ #include "androidsurfacetexture.h" #include <QtCore/private/qjni_p.h> #include <QtCore/private/qjnihelpers_p.h> +#include <QtCore/qmutex.h> QT_BEGIN_NAMESPACE static const char QtSurfaceTextureListenerClassName[] = "org/qtproject/qt5/android/multimedia/QtSurfaceTextureListener"; -static QMap<int, AndroidSurfaceTexture*> g_objectMap; +typedef QVector<jlong> SurfaceTextures; +Q_GLOBAL_STATIC(SurfaceTextures, g_surfaceTextures); +Q_GLOBAL_STATIC(QMutex, g_textureMutex); // native method for QtSurfaceTexture.java -static void notifyFrameAvailable(JNIEnv* , jobject, int id) +static void notifyFrameAvailable(JNIEnv* , jobject, jlong id) { - AndroidSurfaceTexture *obj = g_objectMap.value(id, 0); + const QMutexLocker lock(g_textureMutex); + const int idx = g_surfaceTextures->indexOf(id); + if (idx == -1) + return; + + AndroidSurfaceTexture *obj = reinterpret_cast<AndroidSurfaceTexture *>(g_surfaceTextures->at(idx)); if (obj) Q_EMIT obj->frameAvailable(); } AndroidSurfaceTexture::AndroidSurfaceTexture(unsigned int texName) : QObject() - , m_texID(int(texName)) { + Q_STATIC_ASSERT(sizeof (jlong) >= sizeof (void *)); // API level 11 or higher is required if (QtAndroidPrivate::androidSdkVersion() < 11) { qWarning("Camera preview and video playback require Android 3.0 (API level 11) or later."); @@ -73,13 +81,13 @@ AndroidSurfaceTexture::AndroidSurfaceTexture(unsigned int texName) env->ExceptionClear(); } - if (m_surfaceTexture.isValid()) - g_objectMap.insert(int(texName), this); + if (!m_surfaceTexture.isValid()) + return; - QJNIObjectPrivate listener(QtSurfaceTextureListenerClassName, "(I)V", jint(texName)); - m_surfaceTexture.callMethod<void>("setOnFrameAvailableListener", - "(Landroid/graphics/SurfaceTexture$OnFrameAvailableListener;)V", - listener.object()); + const QMutexLocker lock(g_textureMutex); + g_surfaceTextures->append(jlong(this)); + QJNIObjectPrivate listener(QtSurfaceTextureListenerClassName, "(J)V", jlong(this)); + setOnFrameAvailableListener(listener); } AndroidSurfaceTexture::~AndroidSurfaceTexture() @@ -89,7 +97,10 @@ AndroidSurfaceTexture::~AndroidSurfaceTexture() if (m_surfaceTexture.isValid()) { release(); - g_objectMap.remove(m_texID); + const QMutexLocker lock(g_textureMutex); + const int idx = g_surfaceTextures->indexOf(jlong(this)); + if (idx != -1) + g_surfaceTextures->remove(idx); } } @@ -178,7 +189,7 @@ bool AndroidSurfaceTexture::initJNI(JNIEnv *env) env); static const JNINativeMethod methods[] = { - {"notifyFrameAvailable", "(I)V", (void *)notifyFrameAvailable} + {"notifyFrameAvailable", "(J)V", (void *)notifyFrameAvailable} }; if (clazz && env->RegisterNatives(clazz, @@ -190,4 +201,11 @@ bool AndroidSurfaceTexture::initJNI(JNIEnv *env) return true; } +void AndroidSurfaceTexture::setOnFrameAvailableListener(const QJNIObjectPrivate &listener) +{ + m_surfaceTexture.callMethod<void>("setOnFrameAvailableListener", + "(Landroid/graphics/SurfaceTexture$OnFrameAvailableListener;)V", + listener.object()); +} + QT_END_NAMESPACE diff --git a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h index b45ee384c..0a271287a 100644 --- a/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h +++ b/src/plugins/android/src/wrappers/jni/androidsurfacetexture.h @@ -54,7 +54,6 @@ public: explicit AndroidSurfaceTexture(unsigned int texName); ~AndroidSurfaceTexture(); - int textureID() const { return m_texID; } jobject surfaceTexture(); jobject surface(); jobject surfaceHolder(); @@ -73,7 +72,8 @@ Q_SIGNALS: void frameAvailable(); private: - int m_texID; + void setOnFrameAvailableListener(const QJNIObjectPrivate &listener); + QJNIObjectPrivate m_surfaceTexture; QJNIObjectPrivate m_surface; QJNIObjectPrivate m_surfaceHolder; diff --git a/src/plugins/avfoundation/camera/avfmediaassetwriter.h b/src/plugins/avfoundation/camera/avfmediaassetwriter.h index fa1ec46a2..624906c85 100644 --- a/src/plugins/avfoundation/camera/avfmediaassetwriter.h +++ b/src/plugins/avfoundation/camera/avfmediaassetwriter.h @@ -44,7 +44,6 @@ #include <QtCore/qglobal.h> #include <QtCore/qatomic.h> -#include <QtCore/qmutex.h> #include <AVFoundation/AVFoundation.h> @@ -53,13 +52,10 @@ QT_BEGIN_NAMESPACE class AVFMediaRecorderControlIOS; class AVFCameraService; -typedef QAtomicInteger<bool> AVFAtomicBool; typedef QAtomicInteger<qint64> AVFAtomicInt64; QT_END_NAMESPACE -// TODO: any reasonable error handling requires smart pointers, otherwise it's getting crappy immediately. - @interface QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) : NSObject<AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate> { @@ -72,22 +68,20 @@ QT_END_NAMESPACE QT_PREPEND_NAMESPACE(AVFScopedPointer)<AVAssetWriterInput> m_audioWriterInput; AVCaptureDevice *m_audioCaptureDevice; + // Queue to write sample buffers: + QT_PREPEND_NAMESPACE(AVFScopedPointer)<dispatch_queue_t> m_writerQueue; // High priority serial queue for video output: QT_PREPEND_NAMESPACE(AVFScopedPointer)<dispatch_queue_t> m_videoQueue; // Serial queue for audio output: QT_PREPEND_NAMESPACE(AVFScopedPointer)<dispatch_queue_t> m_audioQueue; - // Queue to write sample buffers: - QT_PREPEND_NAMESPACE(AVFScopedPointer)<dispatch_queue_t> m_writerQueue; QT_PREPEND_NAMESPACE(AVFScopedPointer)<AVAssetWriter> m_assetWriter; QT_PREPEND_NAMESPACE(AVFMediaRecorderControlIOS) *m_delegate; bool m_setStartTime; - QT_PREPEND_NAMESPACE(AVFAtomicBool) m_stopped; - QT_PREPEND_NAMESPACE(AVFAtomicBool) m_aborted; - QT_PREPEND_NAMESPACE(QMutex) m_writerMutex; + QT_PREPEND_NAMESPACE(QAtomicInt) m_state; @public QT_PREPEND_NAMESPACE(AVFAtomicInt64) m_durationInMs; @private @@ -98,8 +92,7 @@ QT_END_NAMESPACE NSDictionary *m_videoSettings; } -- (id)initWithQueue:(dispatch_queue_t)writerQueue - delegate:(QT_PREPEND_NAMESPACE(AVFMediaRecorderControlIOS) *)delegate; +- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(AVFMediaRecorderControlIOS) *)delegate; - (bool)setupWithFileURL:(NSURL *)fileURL cameraService:(QT_PREPEND_NAMESPACE(AVFCameraService) *)service @@ -107,10 +100,10 @@ QT_END_NAMESPACE videoSettings:(NSDictionary *)videoSettings transform:(CGAffineTransform)transform; +// This to be called from the recorder control's thread: - (void)start; - (void)stop; -// This to be called if control's dtor gets called, -// on the control's thread. +// This to be called from the recorder control's dtor: - (void)abort; @end diff --git a/src/plugins/avfoundation/camera/avfmediaassetwriter.mm b/src/plugins/avfoundation/camera/avfmediaassetwriter.mm index 98c8f99ff..28735bc6a 100644 --- a/src/plugins/avfoundation/camera/avfmediaassetwriter.mm +++ b/src/plugins/avfoundation/camera/avfmediaassetwriter.mm @@ -46,7 +46,6 @@ #include "avfcameradebug.h" #include "avfmediacontainercontrol.h" -//#include <QtCore/qmutexlocker.h> #include <QtCore/qmetaobject.h> #include <QtCore/qsysinfo.h> @@ -74,6 +73,13 @@ bool qt_camera_service_isValid(AVFCameraService *service) return true; } +enum WriterState +{ + WriterStateIdle, + WriterStateActive, + WriterStateAborted +}; + } // unnamed namespace @interface QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) (PrivateAPI) @@ -85,21 +91,14 @@ bool qt_camera_service_isValid(AVFCameraService *service) @implementation QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) -- (id)initWithQueue:(dispatch_queue_t)writerQueue - delegate:(AVFMediaRecorderControlIOS *)delegate +- (id)initWithDelegate:(AVFMediaRecorderControlIOS *)delegate { - Q_ASSERT(writerQueue); Q_ASSERT(delegate); if (self = [super init]) { - // "Shared" queue: - dispatch_retain(writerQueue); - m_writerQueue.reset(writerQueue); - m_delegate = delegate; m_setStartTime = true; - m_stopped.store(true); - m_aborted.store(false); + m_state.store(WriterStateIdle); m_startTime = kCMTimeInvalid; m_lastTimeStamp = kCMTimeInvalid; m_durationInMs.store(0); @@ -127,6 +126,12 @@ bool qt_camera_service_isValid(AVFCameraService *service) m_audioSettings = audioSettings; m_videoSettings = videoSettings; + m_writerQueue.reset(dispatch_queue_create("asset-writer-queue", DISPATCH_QUEUE_SERIAL)); + if (!m_writerQueue) { + qDebugCamera() << Q_FUNC_INFO << "failed to create an asset writer's queue"; + return false; + } + m_videoQueue.reset(dispatch_queue_create("video-output-queue", DISPATCH_QUEUE_SERIAL)); if (!m_videoQueue) { qDebugCamera() << Q_FUNC_INFO << "failed to create video queue"; @@ -173,15 +178,12 @@ bool qt_camera_service_isValid(AVFCameraService *service) - (void)start { - // To be executed on a writer's queue. - const QMutexLocker lock(&m_writerMutex); - if (m_aborted.load()) - return; - [self setQueues]; m_setStartTime = true; - m_stopped.store(false); + + m_state.storeRelease(WriterStateActive); + [m_assetWriter startWriting]; AVCaptureSession *session = m_service->session()->captureSession(); if (!session.running) @@ -190,27 +192,36 @@ bool qt_camera_service_isValid(AVFCameraService *service) - (void)stop { - // To be executed on a writer's queue. - { - const QMutexLocker lock(&m_writerMutex); - if (m_aborted.load()) + if (m_state.loadAcquire() != WriterStateActive) return; - if (m_stopped.load()) + if ([m_assetWriter status] != AVAssetWriterStatusWriting) return; - m_stopped.store(true); - } - + // Do this here so that - + // 1. '-abort' should not try calling finishWriting again and + // 2. async block (see below) will know if recorder control was deleted + // before the block's execution: + m_state.storeRelease(WriterStateIdle); + // Now, since we have to ensure no sample buffers are + // appended after a call to finishWriting, we must + // ensure writer's queue sees this change in m_state + // _before_ we call finishWriting: + dispatch_sync(m_writerQueue, ^{}); + // Done, but now we also want to prevent video queue + // from updating our viewfinder: + dispatch_sync(m_videoQueue, ^{}); + + // Now we're safe to stop: [m_assetWriter finishWritingWithCompletionHandler:^{ // This block is async, so by the time it's executed, // it's possible that render control was deleted already ... - const QMutexLocker lock(&m_writerMutex); - if (m_aborted.load()) + if (m_state.loadAcquire() == WriterStateAborted) return; AVCaptureSession *session = m_service->session()->captureSession(); - [session stopRunning]; + if (session.running) + [session stopRunning]; [session removeOutput:m_audioOutput]; [session removeInput:m_audioInput]; QMetaObject::invokeMethod(m_delegate, "assetWriterFinished", Qt::QueuedConnection); @@ -219,12 +230,26 @@ bool qt_camera_service_isValid(AVFCameraService *service) - (void)abort { - // To be executed on any thread (presumably, it's the main thread), - // prevents writer from accessing any shared object. - const QMutexLocker lock(&m_writerMutex); - m_aborted.store(true); - if (m_stopped.load()) + // -abort is to be called from recorder control's dtor. + + if (m_state.fetchAndStoreRelease(WriterStateAborted) != WriterStateActive) { + // Not recording, nothing to stop. return; + } + + // From Apple's docs: + // "To guarantee that all sample buffers are successfully written, + // you must ensure that all calls to appendSampleBuffer: and + // appendPixelBuffer:withPresentationTime: have returned before + // invoking this method." + // + // The only way we can ensure this is: + dispatch_sync(m_writerQueue, ^{}); + // At this point next block (if any) on the writer's queue + // will see m_state preventing it from any further processing. + dispatch_sync(m_videoQueue, ^{}); + // After this point video queue will not try to modify our + // viewfider, so we're safe to delete now. [m_assetWriter finishWritingWithCompletionHandler:^{ }]; @@ -236,13 +261,12 @@ bool qt_camera_service_isValid(AVFCameraService *service) Q_ASSERT(m_setStartTime); Q_ASSERT(sampleBuffer); - const QMutexLocker lock(&m_writerMutex); - if (m_aborted.load() || m_stopped.load()) + if (m_state.loadAcquire() != WriterStateActive) return; QMetaObject::invokeMethod(m_delegate, "assetWriterStarted", Qt::QueuedConnection); - m_durationInMs.store(0); + m_durationInMs.storeRelease(0); m_startTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); m_lastTimeStamp = m_startTime; [m_assetWriter startSessionAtSourceTime:m_startTime]; @@ -251,10 +275,10 @@ bool qt_camera_service_isValid(AVFCameraService *service) - (void)writeVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer { + // This code is executed only on a writer's queue. Q_ASSERT(sampleBuffer); - // This code is executed only on a writer's queue. - if (!m_aborted.load() && !m_stopped.load()) { + if (m_state.loadAcquire() == WriterStateActive) { if (m_setStartTime) [self setStartTimeFrom:sampleBuffer]; @@ -264,17 +288,15 @@ bool qt_camera_service_isValid(AVFCameraService *service) } } - CFRelease(sampleBuffer); } - (void)writeAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer { - // This code is executed only on a writer's queue. - // it does not touch any shared/external data. Q_ASSERT(sampleBuffer); - if (!m_aborted.load() && !m_stopped.load()) { + // This code is executed only on a writer's queue. + if (m_state.loadAcquire() == WriterStateActive) { if (m_setStartTime) [self setStartTimeFrom:sampleBuffer]; @@ -293,10 +315,7 @@ bool qt_camera_service_isValid(AVFCameraService *service) { Q_UNUSED(connection) - // This method can be called on either video or audio queue, - // never on a writer's queue, it needs access to a shared data, so - // lock is required. - if (m_stopped.load()) + if (m_state.loadAcquire() != WriterStateActive) return; if (!CMSampleBufferDataIsReady(sampleBuffer)) { @@ -307,8 +326,7 @@ bool qt_camera_service_isValid(AVFCameraService *service) CFRetain(sampleBuffer); if (captureOutput != m_audioOutput.data()) { - const QMutexLocker lock(&m_writerMutex); - if (m_aborted.load() || m_stopped.load()) { + if (m_state.load() != WriterStateActive) { CFRelease(sampleBuffer); return; } @@ -447,7 +465,7 @@ bool qt_camera_service_isValid(AVFCameraService *service) if (!CMTimeCompare(duration, kCMTimeInvalid)) return; - m_durationInMs.store(CMTimeGetSeconds(duration) * 1000); + m_durationInMs.storeRelease(CMTimeGetSeconds(duration) * 1000); m_lastTimeStamp = newTimeStamp; } } diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h index 6736f4639..a0967efa3 100644 --- a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h +++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.h @@ -97,8 +97,6 @@ private: void stopWriter(); AVFCameraService *m_service; - - AVFScopedPointer<dispatch_queue_t> m_writerQueue; AVFScopedPointer<QT_MANGLE_NAMESPACE(AVFMediaAssetWriter)> m_writer; QUrl m_outputLocation; diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm index 7c8725260..5f604e9a0 100644 --- a/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm +++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol_ios.mm @@ -92,13 +92,7 @@ AVFMediaRecorderControlIOS::AVFMediaRecorderControlIOS(AVFCameraService *service { Q_ASSERT(service); - m_writerQueue.reset(dispatch_queue_create("asset-writer-queue", DISPATCH_QUEUE_SERIAL)); - if (!m_writerQueue) { - qDebugCamera() << Q_FUNC_INFO << "failed to create an asset writer's queue"; - return; - } - - m_writer.reset([[QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) alloc] initWithQueue:m_writerQueue delegate:this]); + m_writer.reset([[QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) alloc] initWithDelegate:this]); if (!m_writer) { qDebugCamera() << Q_FUNC_INFO << "failed to create an asset writer"; return; @@ -295,9 +289,15 @@ void AVFMediaRecorderControlIOS::setState(QMediaRecorder::State state) Q_EMIT stateChanged(m_state); Q_EMIT statusChanged(m_lastStatus); - dispatch_async(m_writerQueue, ^{ - [m_writer start]; - }); + // Apple recommends to call startRunning and do all + // setup on a special queue, and that's what we had + // initially (dispatch_async to writerQueue). Unfortunately, + // writer's queue is not the only queue/thread that can + // access/modify the session, and as a result we have + // all possible data/race-conditions with Obj-C exceptions + // at best and something worse in general. + // Now we try to only modify session on the same thread. + [m_writer start]; } else { [session startRunning]; Q_EMIT error(QMediaRecorder::FormatError, tr("Failed to start recording")); @@ -324,7 +324,7 @@ void AVFMediaRecorderControlIOS::setMuted(bool muted) void AVFMediaRecorderControlIOS::setVolume(qreal volume) { - Q_UNUSED(volume); + Q_UNUSED(volume) qDebugCamera() << Q_FUNC_INFO << "not implemented"; } @@ -409,9 +409,7 @@ void AVFMediaRecorderControlIOS::stopWriter() Q_EMIT stateChanged(m_state); Q_EMIT statusChanged(m_lastStatus); - dispatch_async(m_writerQueue, ^{ - [m_writer stop]; - }); + [m_writer stop]; } } diff --git a/src/plugins/directshow/player/directshowplayerservice.cpp b/src/plugins/directshow/player/directshowplayerservice.cpp index fa5ce3d55..ccc25d30b 100644 --- a/src/plugins/directshow/player/directshowplayerservice.cpp +++ b/src/plugins/directshow/player/directshowplayerservice.cpp @@ -552,8 +552,6 @@ void DirectShowPlayerService::doRender(QMutexLocker *locker) m_executedTasks |= Render; } - - m_loop->wake(); } void DirectShowPlayerService::doFinalizeLoad(QMutexLocker *locker) diff --git a/src/plugins/directshow/player/directshowvideorenderercontrol.cpp b/src/plugins/directshow/player/directshowvideorenderercontrol.cpp index e08ac1f9d..b86125df9 100644 --- a/src/plugins/directshow/player/directshowvideorenderercontrol.cpp +++ b/src/plugins/directshow/player/directshowvideorenderercontrol.cpp @@ -52,11 +52,20 @@ DirectShowVideoRendererControl::DirectShowVideoRendererControl(DirectShowEventLo , m_loop(loop) , m_surface(0) , m_filter(0) +#ifdef HAVE_EVR + , m_evrPresenter(0) +#endif { } DirectShowVideoRendererControl::~DirectShowVideoRendererControl() { +#ifdef HAVE_EVR + if (m_evrPresenter) { + m_evrPresenter->setSurface(Q_NULLPTR); + m_evrPresenter->Release(); + } +#endif if (m_filter) m_filter->Release(); } @@ -71,6 +80,14 @@ void DirectShowVideoRendererControl::setSurface(QAbstractVideoSurface *surface) if (m_surface == surface) return; +#ifdef HAVE_EVR + if (m_evrPresenter) { + m_evrPresenter->setSurface(Q_NULLPTR); + m_evrPresenter->Release(); + m_evrPresenter = 0; + } +#endif + if (m_filter) { m_filter->Release(); m_filter = 0; @@ -81,12 +98,13 @@ void DirectShowVideoRendererControl::setSurface(QAbstractVideoSurface *surface) if (m_surface) { #ifdef HAVE_EVR m_filter = com_new<IBaseFilter>(clsid_EnhancedVideoRenderer); - EVRCustomPresenter *evrPresenter = new EVRCustomPresenter(m_surface); - if (!evrPresenter->isValid() || !qt_evr_setCustomPresenter(m_filter, evrPresenter)) { + m_evrPresenter = new EVRCustomPresenter(m_surface); + if (!m_evrPresenter->isValid() || !qt_evr_setCustomPresenter(m_filter, m_evrPresenter)) { m_filter->Release(); m_filter = 0; + m_evrPresenter->Release(); + m_evrPresenter = 0; } - evrPresenter->Release(); if (!m_filter) #endif diff --git a/src/plugins/directshow/player/directshowvideorenderercontrol.h b/src/plugins/directshow/player/directshowvideorenderercontrol.h index 17dd4705b..9d1a23933 100644 --- a/src/plugins/directshow/player/directshowvideorenderercontrol.h +++ b/src/plugins/directshow/player/directshowvideorenderercontrol.h @@ -45,6 +45,9 @@ #include "qvideorenderercontrol.h" class DirectShowEventLoop; +#ifdef HAVE_EVR +class EVRCustomPresenter; +#endif QT_USE_NAMESPACE @@ -67,6 +70,9 @@ private: DirectShowEventLoop *m_loop; QAbstractVideoSurface *m_surface; IBaseFilter *m_filter; +#ifdef HAVE_EVR + EVRCustomPresenter *m_evrPresenter; +#endif }; #endif diff --git a/src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.cpp b/src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.cpp index d2be15e71..6ca1dbe94 100644 --- a/src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinv4limageprocessing.cpp @@ -200,8 +200,10 @@ void CameraBinV4LImageProcessing::setParameter( const QCameraImageProcessing::WhiteBalanceMode m = value.value<QCameraImageProcessing::WhiteBalanceMode>(); if (m != QCameraImageProcessing::WhiteBalanceAuto - && m != QCameraImageProcessing::WhiteBalanceManual) + && m != QCameraImageProcessing::WhiteBalanceManual) { + qt_safe_close(fd); return; + } control.value = (m == QCameraImageProcessing::WhiteBalanceAuto) ? true : false; } @@ -220,6 +222,7 @@ void CameraBinV4LImageProcessing::setParameter( break; default: + qt_safe_close(fd); return; } diff --git a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp b/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp index 87c2c26dc..5cd3bc3d2 100644 --- a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp +++ b/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp @@ -346,6 +346,8 @@ void MmRendererMediaPlayerControl::setState(QMediaPlayer::State state) void MmRendererMediaPlayerControl::stopInternal(StopCommand stopCommand) { + setPosition(0); + if (m_state != QMediaPlayer::StoppedState) { if (stopCommand == StopMmRenderer) { @@ -355,11 +357,6 @@ void MmRendererMediaPlayerControl::stopInternal(StopCommand stopCommand) setState(QMediaPlayer::StoppedState); } - - if (m_position != 0) { - m_position = 0; - emit positionChanged(0); - } } void MmRendererMediaPlayerControl::setVolume(int volume) diff --git a/src/plugins/winrt/qwinrtcameracontrol.cpp b/src/plugins/winrt/qwinrtcameracontrol.cpp index 390364eb8..6ce502b88 100644 --- a/src/plugins/winrt/qwinrtcameracontrol.cpp +++ b/src/plugins/winrt/qwinrtcameracontrol.cpp @@ -942,7 +942,8 @@ HRESULT QWinRTCameraControl::initialize() Q_ASSERT_SUCCEEDED(hr); if (isFocusSupported) { hr = advancedVideoDeviceController->get_RegionsOfInterestControl(&d->regionsOfInterestControl); - Q_ASSERT_SUCCEEDED(hr); + if (FAILED(hr)) + qCDebug(lcMMCamera) << "Focus supported, but no control for regions of interest available"; hr = initializeFocus(); Q_ASSERT_SUCCEEDED(hr); } else { diff --git a/tests/auto/integration/qsoundeffect/BLACKLIST b/tests/auto/integration/qsoundeffect/BLACKLIST deleted file mode 100644 index 3a358789a..000000000 --- a/tests/auto/integration/qsoundeffect/BLACKLIST +++ /dev/null @@ -1,8 +0,0 @@ -# QTBUG-46689 - -[testLooping] -ubuntu-14.04 64bit -redhatenterpriselinuxworkstation-6.6 -rhel-7.1 -opensuse-13.1 -rhel-7.2 diff --git a/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp b/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp index dd486a1a3..5be889096 100644 --- a/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp +++ b/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp @@ -183,7 +183,7 @@ void tst_QSoundEffect::testLooping() // wait for all the loops to be completed QTRY_COMPARE(sound->loopsRemaining(), 0); - QVERIFY(readSignal_Remaining.count() >= 6); + QTRY_VERIFY(readSignal_Remaining.count() >= 6); QTRY_VERIFY(!sound->isPlaying()); } @@ -198,7 +198,7 @@ void tst_QSoundEffect::testLooping() QCOMPARE(readSignal_Remaining.count(), 0); sound->play(); - QCOMPARE(sound->loopsRemaining(), int(QSoundEffect::Infinite)); + QTRY_COMPARE(sound->loopsRemaining(), int(QSoundEffect::Infinite)); QCOMPARE(readSignal_Remaining.count(), 1); QTest::qWait(1500); @@ -214,7 +214,7 @@ void tst_QSoundEffect::testLooping() QCOMPARE(readSignal_Remaining.count(), 1); QTRY_COMPARE(sound->loopsRemaining(), 0); - QVERIFY(readSignal_Remaining.count() >= 2); + QTRY_VERIFY(readSignal_Remaining.count() >= 2); QTRY_VERIFY(!sound->isPlaying()); } } |