summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorYoann Lopes <yoann.lopes@theqtcompany.com>2016-09-01 12:29:35 +0200
committerYoann Lopes <yoann.lopes@theqtcompany.com>2016-09-01 12:29:35 +0200
commit9fb4597d5c0d3fa3340ae74ed2eb6bb8e3ee2d17 (patch)
treecc82491fc856e3d184b3b00727a06139d73c550b /src
parent29232a2bc207fb7dc44532f64bbe35d9181340c6 (diff)
parentda7d462e315fb101fc9112a294b5ca2e3bd35a75 (diff)
Merge remote-tracking branch 'origin/5.7' into 5.8
Conflicts: src/multimedia/audio/qaudiosystemplugin.cpp src/plugins/directshow/helpers/directshowobject.cpp src/plugins/directshow/player/directshowiosource.cpp src/plugins/directshow/player/directshowiosource.h Change-Id: I0e4632c7705128f81429ddbcb0d4abbc04858a8b
Diffstat (limited to 'src')
-rw-r--r--src/multimedia/audio/qaudiosystemplugin.cpp2
-rw-r--r--src/multimedia/audio/qsoundeffect_pulse_p.cpp49
-rw-r--r--src/multimedia/audio/qsoundeffect_pulse_p.h10
-rw-r--r--src/multimedia/doc/src/qtmultimedia-examples.qdoc2
-rw-r--r--src/multimedia/video/qabstractvideobuffer.cpp2
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm16
-rw-r--r--src/plugins/avfoundation/mediaplayer/avfvideowidget.mm6
-rw-r--r--src/plugins/directshow/player/directshowiosource.cpp77
-rw-r--r--src/plugins/directshow/player/directshowiosource.h2
-rw-r--r--src/plugins/opensles/qopenslesaudiooutput.cpp31
10 files changed, 168 insertions, 29 deletions
diff --git a/src/multimedia/audio/qaudiosystemplugin.cpp b/src/multimedia/audio/qaudiosystemplugin.cpp
index 438b8b7bb..636b614d4 100644
--- a/src/multimedia/audio/qaudiosystemplugin.cpp
+++ b/src/multimedia/audio/qaudiosystemplugin.cpp
@@ -77,7 +77,7 @@ QAudioSystemPluginExtension::~QAudioSystemPluginExtension()
\sa QAbstractAudioDeviceInfo, QAbstractAudioOutput, QAbstractAudioInput
- Qt comes with plugins for Windows (WinMM and WASAPI), Linux (ALSA and PulseAudio), OS X / iOS
+ Qt comes with plugins for Windows (WinMM and WASAPI), Linux (ALSA and PulseAudio), \macos / iOS
(CoreAudio), Android (OpenSL ES) and QNX.
If no audio plugins are available, a fallback dummy backend will be used.
diff --git a/src/multimedia/audio/qsoundeffect_pulse_p.cpp b/src/multimedia/audio/qsoundeffect_pulse_p.cpp
index 79d1d96ab..6e4aadd0b 100644
--- a/src/multimedia/audio/qsoundeffect_pulse_p.cpp
+++ b/src/multimedia/audio/qsoundeffect_pulse_p.cpp
@@ -419,7 +419,13 @@ void QSoundEffectPrivate::setSource(const QUrl &url)
#ifdef QT_PA_DEBUG
qDebug() << this << "setSource =" << url;
#endif
+
+ // Make sure the stream is empty before loading a new source (otherwise whatever is there will
+ // be played before the new source)
+ emptyStream();
+
stop();
+
if (m_sample) {
if (!m_sampleReady) {
disconnect(m_sample, SIGNAL(error()), this, SLOT(decoderError()));
@@ -594,7 +600,7 @@ void QSoundEffectPrivate::playAvailable()
setLoopsRemaining(0);
m_playQueued = true;
Q_ASSERT(m_pulseStream);
- emptyStream();
+ emptyStream(ReloadSampleWhenDone);
return;
}
setLoopsRemaining(m_loopCount);
@@ -604,18 +610,25 @@ void QSoundEffectPrivate::playAvailable()
setPlaying(true);
}
-void QSoundEffectPrivate::emptyStream()
+void QSoundEffectPrivate::emptyStream(EmptyStreamOptions options)
{
#ifdef QT_PA_DEBUG
qDebug() << this << "emptyStream";
#endif
+ if (!m_pulseStream || m_emptying)
+ return;
+
+ const bool reloadSample = options.testFlag(ReloadSampleWhenDone);
+ pa_stream_success_cb_t flushCompleteCb = reloadSample ? stream_flush_reload_callback
+ : stream_flush_callback;
+
m_emptying = true;
pa_stream_set_write_callback(m_pulseStream, 0, 0);
pa_stream_set_underflow_callback(m_pulseStream, 0, 0);
- pa_operation_unref(pa_stream_flush(m_pulseStream, stream_flush_callback, m_ref->getRef()));
+ pa_operation_unref(pa_stream_flush(m_pulseStream, flushCompleteCb, m_ref->getRef()));
}
-void QSoundEffectPrivate::emptyComplete(void *stream)
+void QSoundEffectPrivate::emptyComplete(void *stream, bool reload)
{
PulseDaemonLocker locker;
#ifdef QT_PA_DEBUG
@@ -625,7 +638,7 @@ void QSoundEffectPrivate::emptyComplete(void *stream)
m_emptying = false;
if ((pa_stream *)stream == m_pulseStream)
- pa_operation_unref(pa_stream_cork(m_pulseStream, 1, stream_cork_callback, m_ref->getRef()));
+ pa_operation_unref(pa_stream_cork(m_pulseStream, 1, reload ? stream_cork_callback : 0, m_ref->getRef()));
}
void QSoundEffectPrivate::sampleReady()
@@ -729,6 +742,10 @@ void QSoundEffectPrivate::prepare()
if (!m_pulseStream || !m_sampleReady)
return;
PulseDaemonLocker locker;
+
+ if (pa_stream_get_state(m_pulseStream) != PA_STREAM_READY)
+ return;
+
pa_stream_set_write_callback(m_pulseStream, stream_write_callback, this);
pa_stream_set_underflow_callback(m_pulseStream, stream_underrun_callback, this);
m_stopping = false;
@@ -857,7 +874,7 @@ void QSoundEffectPrivate::stop()
PulseDaemonLocker locker;
m_stopping = true;
if (m_pulseStream) {
- emptyStream();
+ emptyStream(ReloadSampleWhenDone);
if (m_reloadCategory) {
unloadPulseStream(); // upon play we reconnect anyway
}
@@ -1100,10 +1117,26 @@ void QSoundEffectPrivate::stream_flush_callback(pa_stream *s, int success, void
if (!success)
qWarning("QSoundEffect(pulseaudio): faild to drain");
+
+ QMetaObject::invokeMethod(self, "emptyComplete", Qt::QueuedConnection, Q_ARG(void*, s), Q_ARG(bool, false));
+}
+
+void QSoundEffectPrivate::stream_flush_reload_callback(pa_stream *s, int success, void *userdata)
+{
#ifdef QT_PA_DEBUG
- qDebug() << self << "stream_flush_callback";
+ qDebug() << "stream_flush_reload_callback";
#endif
- QMetaObject::invokeMethod(self, "emptyComplete", Qt::QueuedConnection, Q_ARG(void*, s));
+ Q_UNUSED(s);
+ QSoundEffectRef *ref = reinterpret_cast<QSoundEffectRef*>(userdata);
+ QSoundEffectPrivate *self = ref->soundEffect();
+ ref->release();
+ if (!self)
+ return;
+
+ if (!success)
+ qWarning("QSoundEffect(pulseaudio): faild to drain");
+
+ QMetaObject::invokeMethod(self, "emptyComplete", Qt::QueuedConnection, Q_ARG(void*, s), Q_ARG(bool, true));
}
void QSoundEffectPrivate::stream_write_done_callback(void *p)
diff --git a/src/multimedia/audio/qsoundeffect_pulse_p.h b/src/multimedia/audio/qsoundeffect_pulse_p.h
index 6bf2416cf..020aa031a 100644
--- a/src/multimedia/audio/qsoundeffect_pulse_p.h
+++ b/src/multimedia/audio/qsoundeffect_pulse_p.h
@@ -117,7 +117,7 @@ private Q_SLOTS:
void underRun();
void prepare();
void streamReady();
- void emptyComplete(void *stream);
+ void emptyComplete(void *stream, bool reload);
void handleAvailabilityChanged(bool available);
@@ -125,7 +125,12 @@ private:
void playAvailable();
void playSample();
- void emptyStream();
+ enum EmptyStreamOption {
+ ReloadSampleWhenDone = 0x1
+ };
+ Q_DECLARE_FLAGS(EmptyStreamOptions, EmptyStreamOption)
+ void emptyStream(EmptyStreamOptions options = EmptyStreamOptions());
+
void createPulseStream();
void unloadPulseStream();
@@ -140,6 +145,7 @@ private:
static void stream_underrun_callback(pa_stream *s, void *userdata);
static void stream_cork_callback(pa_stream *s, int success, void *userdata);
static void stream_flush_callback(pa_stream *s, int success, void *userdata);
+ static void stream_flush_reload_callback(pa_stream *s, int success, void *userdata);
static void stream_write_done_callback(void *p);
static void stream_adjust_prebuffer_callback(pa_stream *s, int success, void *userdata);
static void stream_reset_buffer_callback(pa_stream *s, int success, void *userdata);
diff --git a/src/multimedia/doc/src/qtmultimedia-examples.qdoc b/src/multimedia/doc/src/qtmultimedia-examples.qdoc
index 582efc5e1..dd35aeb1a 100644
--- a/src/multimedia/doc/src/qtmultimedia-examples.qdoc
+++ b/src/multimedia/doc/src/qtmultimedia-examples.qdoc
@@ -32,6 +32,6 @@
\brief Demonstrates the multimedia functionality provided by Qt.
The \l{Qt Multimedia} module provides low-level audio support on Linux,
- Windows and OS X. It also provides audio plugin API to allow developers
+ Windows and \macos. It also provides audio plugin API to allow developers
implement their own audio support for custom devices and platforms.
*/
diff --git a/src/multimedia/video/qabstractvideobuffer.cpp b/src/multimedia/video/qabstractvideobuffer.cpp
index 877fe3544..50e38a98c 100644
--- a/src/multimedia/video/qabstractvideobuffer.cpp
+++ b/src/multimedia/video/qabstractvideobuffer.cpp
@@ -98,7 +98,7 @@ int QAbstractVideoBufferPrivate::map(
\value NoHandle The buffer has no handle, its data can only be accessed by mapping the buffer.
\value GLTextureHandle The handle of the buffer is an OpenGL texture ID.
\value XvShmImageHandle The handle contains pointer to shared memory XVideo image.
- \value CoreImageHandle The handle contains pointer to OS X CIImage.
+ \value CoreImageHandle The handle contains pointer to \macos CIImage.
\value QPixmapHandle The handle of the buffer is a QPixmap.
\value EGLImageHandle The handle of the buffer is an EGLImageKHR.
\value UserHandle Start value for user defined handle types.
diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm
index 87bb08e5c..473a18884 100644
--- a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm
+++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm
@@ -41,6 +41,8 @@
#include "avfmediaplayerservice.h"
#include "avfvideooutput.h"
+#include <qpointer.h>
+
#import <AVFoundation/AVFoundation.h>
QT_USE_NAMESPACE
@@ -111,15 +113,23 @@ static void *AVFMediaPlayerSessionObserverCurrentItemObservationContext = &AVFMe
//Create an asset for inspection of a resource referenced by a given URL.
//Load the values for the asset keys "tracks", "playable".
- AVURLAsset *asset = [AVURLAsset URLAssetWithURL:m_URL options:nil];
- NSArray *requestedKeys = [NSArray arrayWithObjects:AVF_TRACKS_KEY, AVF_PLAYABLE_KEY, nil];
+ // use __block to avoid maintaining strong references on variables captured by the
+ // following block callback
+ __block AVURLAsset *asset = [[AVURLAsset URLAssetWithURL:m_URL options:nil] retain];
+ __block NSArray *requestedKeys = [[NSArray arrayWithObjects:AVF_TRACKS_KEY, AVF_PLAYABLE_KEY, nil] retain];
+
+ __block AVFMediaPlayerSessionObserver *blockSelf = self;
+ QPointer<AVFMediaPlayerSession> session(m_session);
// Tells the asset to load the values of any of the specified keys that are not already loaded.
[asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:
^{
dispatch_async( dispatch_get_main_queue(),
^{
- [self prepareToPlayAsset:asset withKeys:requestedKeys];
+ if (session)
+ [blockSelf prepareToPlayAsset:asset withKeys:requestedKeys];
+ [asset release];
+ [requestedKeys release];
});
}];
}
diff --git a/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm b/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm
index 3b270f9b6..7eb5a71cf 100644
--- a/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm
+++ b/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm
@@ -45,6 +45,12 @@
#include <QtGui/QPaintEvent>
#include <QtGui/QPainter>
+#if defined(Q_OS_MACOS)
+#import <AppKit/AppKit.h>
+#else
+#import <UIKit/UIKit.h>
+#endif
+
QT_USE_NAMESPACE
AVFVideoWidget::AVFVideoWidget(QWidget *parent)
diff --git a/src/plugins/directshow/player/directshowiosource.cpp b/src/plugins/directshow/player/directshowiosource.cpp
index 4dc8bec3d..3c44dd1ed 100644
--- a/src/plugins/directshow/player/directshowiosource.cpp
+++ b/src/plugins/directshow/player/directshowiosource.cpp
@@ -47,6 +47,22 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qurl.h>
+static const GUID directshow_subtypes[] =
+{
+ MEDIASUBTYPE_NULL,
+ MEDIASUBTYPE_Avi,
+ MEDIASUBTYPE_Asf,
+ MEDIASUBTYPE_MPEG1Video,
+ MEDIASUBTYPE_QTMovie,
+ MEDIASUBTYPE_WAVE,
+ MEDIASUBTYPE_AIFF,
+ MEDIASUBTYPE_AU,
+ MEDIASUBTYPE_DssVideo,
+ MEDIASUBTYPE_MPEG1Audio,
+ MEDIASUBTYPE_MPEG1System,
+ MEDIASUBTYPE_MPEG1VideoCD
+};
+
DirectShowIOSource::DirectShowIOSource(DirectShowEventLoop *loop)
: m_ref(1)
, m_state(State_Stopped)
@@ -65,13 +81,26 @@ DirectShowIOSource::DirectShowIOSource(DirectShowEventLoop *loop)
//
// The filter works in pull mode, the downstream filter is responsible for requesting
// samples from this one.
-
- m_outputType.majortype = MEDIATYPE_Stream;
- m_outputType.subtype = MEDIASUBTYPE_NULL; // Wildcard
- m_outputType.bFixedSizeSamples = TRUE;
- m_outputType.lSampleSize = 1;
-
- m_supportedMediaTypes.append(m_outputType);
+ //
+ AM_MEDIA_TYPE type =
+ {
+ MEDIATYPE_Stream, // majortype
+ MEDIASUBTYPE_NULL, // subtype
+ TRUE, // bFixedSizeSamples
+ FALSE, // bTemporalCompression
+ 1, // lSampleSize
+ GUID_NULL, // formattype
+ 0, // pUnk
+ 0, // cbFormat
+ 0, // pbFormat
+ };
+
+ static const int count = sizeof(directshow_subtypes) / sizeof(GUID);
+
+ for (int i = 0; i < count; ++i) {
+ type.subtype = directshow_subtypes[i];
+ m_supportedMediaTypes.append(type);
+ }
}
DirectShowIOSource::~DirectShowIOSource()
@@ -328,14 +357,40 @@ HRESULT DirectShowIOSource::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
return VFW_E_ALREADY_CONNECTED;
// If we get a type from the graph manager, check that we support that
- if (pmt && (pmt->majortype != MEDIATYPE_Stream || pmt->subtype != MEDIASUBTYPE_NULL))
+ if (pmt && pmt->majortype != MEDIATYPE_Stream)
return VFW_E_TYPE_NOT_ACCEPTED;
// This filter only works in pull mode, the downstream filter must query for the
// AsyncReader interface during ReceiveConnection().
// If it doesn't, we can't connect to it.
m_queriedForAsyncReader = false;
- HRESULT hr = pReceivePin->ReceiveConnection(this, pmt ? pmt : &m_outputType);
+ HRESULT hr = 0;
+ // Negotiation of media type
+ // - Complete'ish type (Stream with subtype specified).
+ if (pmt && pmt->subtype != MEDIASUBTYPE_NULL /* aka. GUID_NULL */) {
+ hr = pReceivePin->ReceiveConnection(this, pmt);
+ // Update the media type for the current connection.
+ if (SUCCEEDED(hr))
+ m_connectionMediaType = *pmt;
+ } else if (pmt && pmt->subtype == MEDIATYPE_NULL) { // - Partial type (Stream, but no subtype specified).
+ m_connectionMediaType = *pmt;
+ // Check if the receiving pin accepts any of the streaming subtypes.
+ for (const DirectShowMediaType &t : qAsConst(m_supportedMediaTypes)) {
+ m_connectionMediaType.subtype = t.subtype;
+ hr = pReceivePin->ReceiveConnection(this, &m_connectionMediaType);
+ if (SUCCEEDED(hr))
+ break;
+ }
+ } else { // - No media type specified.
+ // Check if the receiving pin accepts any of the streaming types.
+ for (const DirectShowMediaType &t : qAsConst(m_supportedMediaTypes)) {
+ hr = pReceivePin->ReceiveConnection(this, &t);
+ if (SUCCEEDED(hr)) {
+ m_connectionMediaType = t;
+ break;
+ }
+ }
+ }
if (SUCCEEDED(hr) && m_queriedForAsyncReader) {
m_peerPin = pReceivePin;
@@ -348,6 +403,8 @@ HRESULT DirectShowIOSource::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
}
if (!m_queriedForAsyncReader)
hr = VFW_E_NO_TRANSPORT;
+
+ m_connectionMediaType.clear();
}
return hr;
@@ -420,7 +477,7 @@ HRESULT DirectShowIOSource::ConnectionMediaType(AM_MEDIA_TYPE *pmt)
return VFW_E_NOT_CONNECTED;
} else {
- DirectShowMediaType::copy(pmt, m_outputType);
+ DirectShowMediaType::copy(pmt, m_connectionMediaType);
return S_OK;
}
diff --git a/src/plugins/directshow/player/directshowiosource.h b/src/plugins/directshow/player/directshowiosource.h
index efcde7a27..702bfed61 100644
--- a/src/plugins/directshow/player/directshowiosource.h
+++ b/src/plugins/directshow/player/directshowiosource.h
@@ -123,7 +123,7 @@ private:
IReferenceClock *m_clock;
IMemAllocator *m_allocator;
IPin *m_peerPin;
- DirectShowMediaType m_outputType;
+ DirectShowMediaType m_connectionMediaType;
QList<DirectShowMediaType> m_supportedMediaTypes;
QString m_filterName;
const QString m_pinId;
diff --git a/src/plugins/opensles/qopenslesaudiooutput.cpp b/src/plugins/opensles/qopenslesaudiooutput.cpp
index 26f2380b2..70d77a380 100644
--- a/src/plugins/opensles/qopenslesaudiooutput.cpp
+++ b/src/plugins/opensles/qopenslesaudiooutput.cpp
@@ -114,11 +114,18 @@ QAudio::State QOpenSLESAudioOutput::state() const
void QOpenSLESAudioOutput::start(QIODevice *device)
{
Q_ASSERT(device);
+
+ if (m_state != QAudio::StoppedState)
+ stop();
+
if (!preparePlayer())
return;
m_pullMode = true;
m_audioSource = device;
+ m_nextBuffer = 0;
+ m_processedBytes = 0;
+ m_availableBuffers = BUFFER_COUNT;
setState(QAudio::ActiveState);
setError(QAudio::NoError);
@@ -136,6 +143,9 @@ void QOpenSLESAudioOutput::start(QIODevice *device)
m_processedBytes += readSize;
}
+ if (m_processedBytes < 1)
+ onEOSEvent();
+
// Change the state to playing.
// We need to do this after filling the buffers or processedBytes might get corrupted.
startPlayer();
@@ -143,10 +153,15 @@ void QOpenSLESAudioOutput::start(QIODevice *device)
QIODevice *QOpenSLESAudioOutput::start()
{
+ if (m_state != QAudio::StoppedState)
+ stop();
+
if (!preparePlayer())
return Q_NULLPTR;
m_pullMode = false;
+ m_processedBytes = 0;
+ m_availableBuffers = BUFFER_COUNT;
m_audioSource = new SLIODevicePrivate(this);
m_audioSource->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
@@ -372,7 +387,10 @@ void QOpenSLESAudioOutput::bufferAvailable(quint32 count, quint32 playIndex)
if (!m_pullMode) { // We're in push mode.
// Signal that there is a new open slot in the buffer and return
- m_availableBuffers.fetchAndAddRelease(1);
+ const int val = m_availableBuffers.fetchAndAddRelease(1) + 1;
+ if (val == BUFFER_COUNT)
+ QMetaObject::invokeMethod(this, "onEOSEvent", Qt::QueuedConnection);
+
return;
}
@@ -380,8 +398,11 @@ void QOpenSLESAudioOutput::bufferAvailable(quint32 count, quint32 playIndex)
const int index = m_nextBuffer * m_bufferSize;
const qint64 readSize = m_audioSource->read(m_buffers + index, m_bufferSize);
- if (1 > readSize)
+ if (readSize < 1) {
+ QMetaObject::invokeMethod(this, "onEOSEvent", Qt::QueuedConnection);
return;
+ }
+
if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Enqueue(m_bufferQueueItf,
m_buffers + index,
@@ -606,6 +627,12 @@ void QOpenSLESAudioOutput::stopPlayer()
{
setState(QAudio::StoppedState);
+ if (m_audioSource && !m_pullMode) {
+ m_audioSource->close();
+ delete m_audioSource;
+ m_audioSource = Q_NULLPTR;
+ }
+
// We need to change the state manually...
if (m_playItf)
(*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_STOPPED);