diff --git a/examples/multimedia/spectrum/app/spectrumanalyser.cpp b/examples/multimedia/spectrum/app/spectrumanalyser.cpp index e8a9f8dc..6199221d 100644 --- a/examples/multimedia/spectrum/app/spectrumanalyser.cpp +++ b/examples/multimedia/spectrum/app/spectrumanalyser.cpp @@ -135,8 +135,8 @@ void SpectrumAnalyserThread::calculateSpectrum(const QByteArray &buffer, if (i>0 && i 1.0); diff --git a/examples/multimedia/video/qmlvideo/trace.h b/examples/multimedia/video/qmlvideo/trace.h index 3bfb1d79..02ba6476 100644 --- a/examples/multimedia/video/qmlvideo/trace.h +++ b/examples/multimedia/video/qmlvideo/trace.h @@ -62,8 +62,9 @@ struct PtrWrapper template inline QDebug& operator<<(QDebug &debug, const Trace::PtrWrapper &wrapper) { - debug.nospace() << "[" << (void*)wrapper.m_ptr << "]"; - return debug.space(); + QDebugStateSaver saver(debug); + debug.nospace() << '[' << static_cast(wrapper.m_ptr) << ']'; + return debug; } template diff --git a/examples/multimedia/video/qmlvideofx/trace.h b/examples/multimedia/video/qmlvideofx/trace.h index 86e46b33..b251deb3 100644 --- a/examples/multimedia/video/qmlvideofx/trace.h +++ b/examples/multimedia/video/qmlvideofx/trace.h @@ -62,8 +62,9 @@ struct PtrWrapper template inline QDebug &operator<<(QDebug &debug, const Trace::PtrWrapper &wrapper) { - debug.nospace() << "[" << (void*)wrapper.m_ptr << "]"; - return debug.space(); + QDebugStateSaver saver(debug); + debug.nospace() << '[' << static_cast(wrapper.m_ptr) << ']'; + return debug; } #ifdef ENABLE_TRACE diff --git a/src/gsttools/qgstappsrc.cpp b/src/gsttools/qgstappsrc.cpp index 5057d65c..66c38499 100644 --- a/src/gsttools/qgstappsrc.cpp +++ b/src/gsttools/qgstappsrc.cpp @@ -34,7 +34,6 @@ #include #include "qgstappsrc_p.h" -#include QGstAppSrc::QGstAppSrc(QObject *parent) :QObject(parent) diff --git a/src/gsttools/qgstvideorenderersink.cpp b/src/gsttools/qgstvideorenderersink.cpp index ff489b77..615348c1 100644 --- a/src/gsttools/qgstvideorenderersink.cpp +++ b/src/gsttools/qgstvideorenderersink.cpp @@ -201,6 +201,14 @@ void QVideoSurfaceGstDelegate::stop() waitForAsyncEvent(&locker, &m_setupCondition, 500); } +void QVideoSurfaceGstDelegate::unlock() +{ + QMutexLocker locker(&m_mutex); + + m_setupCondition.wakeAll(); + m_renderCondition.wakeAll(); +} + bool QVideoSurfaceGstDelegate::proposeAllocation(GstQuery *query) { QMutexLocker locker(&m_mutex); @@ -218,6 +226,7 @@ GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer) { QMutexLocker locker(&m_mutex); + m_renderReturn = GST_FLOW_OK; m_renderBuffer = buffer; GstFlowReturn flowReturn = waitForAsyncEvent(&locker, &m_renderCondition, 300) @@ -291,8 +300,11 @@ bool QVideoSurfaceGstDelegate::handleEvent(QMutexLocker *locker) gst_caps_unref(startCaps); } else if (m_renderBuffer) { + GstBuffer *buffer = m_renderBuffer; + m_renderBuffer = 0; + m_renderReturn = GST_FLOW_ERROR; + if (m_activeRenderer && m_surface) { - GstBuffer *buffer = m_renderBuffer; gst_buffer_ref(buffer); locker->unlock(); @@ -303,15 +315,11 @@ bool QVideoSurfaceGstDelegate::handleEvent(QMutexLocker *locker) locker->relock(); - m_renderReturn = rendered - ? GST_FLOW_OK - : GST_FLOW_ERROR; - - m_renderCondition.wakeAll(); - } else { - m_renderReturn = GST_FLOW_ERROR; - m_renderCondition.wakeAll(); + if (rendered) + m_renderReturn = GST_FLOW_OK; } + + m_renderCondition.wakeAll(); } else { m_setupCondition.wakeAll(); @@ -415,12 +423,15 @@ void QGstVideoRendererSink::class_init(gpointer g_class, gpointer class_data) sink_parent_class = reinterpret_cast(g_type_class_peek_parent(g_class)); + GstVideoSinkClass *video_sink_class = reinterpret_cast(g_class); + video_sink_class->show_frame = QGstVideoRendererSink::show_frame; + GstBaseSinkClass *base_sink_class = reinterpret_cast(g_class); base_sink_class->get_caps = QGstVideoRendererSink::get_caps; base_sink_class->set_caps = QGstVideoRendererSink::set_caps; base_sink_class->propose_allocation = QGstVideoRendererSink::propose_allocation; base_sink_class->stop = QGstVideoRendererSink::stop; - base_sink_class->render = QGstVideoRendererSink::render; + base_sink_class->unlock = QGstVideoRendererSink::unlock; GstElementClass *element_class = reinterpret_cast(g_class); element_class->change_state = QGstVideoRendererSink::change_state; @@ -517,7 +528,14 @@ gboolean QGstVideoRendererSink::stop(GstBaseSink *base) return TRUE; } -GstFlowReturn QGstVideoRendererSink::render(GstBaseSink *base, GstBuffer *buffer) +gboolean QGstVideoRendererSink::unlock(GstBaseSink *base) +{ + VO_SINK(base); + sink->delegate->unlock(); + return TRUE; +} + +GstFlowReturn QGstVideoRendererSink::show_frame(GstVideoSink *base, GstBuffer *buffer) { VO_SINK(base); return sink->delegate->render(buffer); diff --git a/src/gsttools/qvideosurfacegstsink.cpp b/src/gsttools/qvideosurfacegstsink.cpp index 36644581..4a786ea1 100644 --- a/src/gsttools/qvideosurfacegstsink.cpp +++ b/src/gsttools/qvideosurfacegstsink.cpp @@ -162,6 +162,15 @@ void QVideoSurfaceGstDelegate::stop() m_started = false; } +void QVideoSurfaceGstDelegate::unlock() +{ + QMutexLocker locker(&m_mutex); + + m_startCanceled = true; + m_setupCondition.wakeAll(); + m_renderCondition.wakeAll(); +} + bool QVideoSurfaceGstDelegate::isActive() { QMutexLocker locker(&m_mutex); @@ -218,8 +227,9 @@ GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer) void QVideoSurfaceGstDelegate::queuedStart() { + QMutexLocker locker(&m_mutex); + if (!m_startCanceled) { - QMutexLocker locker(&m_mutex); m_started = m_surface->start(m_format); m_setupCondition.wakeAll(); } @@ -238,6 +248,9 @@ void QVideoSurfaceGstDelegate::queuedRender() { QMutexLocker locker(&m_mutex); + if (!m_frame.isValid()) + return; + if (m_surface.isNull()) { qWarning() << "Rendering video frame to deleted surface, skip the frame"; m_renderReturn = GST_FLOW_OK; @@ -347,6 +360,7 @@ void QVideoSurfaceGstSink::class_init(gpointer g_class, gpointer class_data) base_sink_class->buffer_alloc = QVideoSurfaceGstSink::buffer_alloc; base_sink_class->start = QVideoSurfaceGstSink::start; base_sink_class->stop = QVideoSurfaceGstSink::stop; + base_sink_class->unlock = QVideoSurfaceGstSink::unlock; GstElementClass *element_class = reinterpret_cast(g_class); element_class->change_state = QVideoSurfaceGstSink::change_state; @@ -601,6 +615,13 @@ gboolean QVideoSurfaceGstSink::stop(GstBaseSink *base) return TRUE; } +gboolean QVideoSurfaceGstSink::unlock(GstBaseSink *base) +{ + VO_SINK(base); + sink->delegate->unlock(); + return TRUE; +} + GstFlowReturn QVideoSurfaceGstSink::show_frame(GstVideoSink *base, GstBuffer *buffer) { VO_SINK(base); diff --git a/src/imports/audioengine/qdeclarative_audioengine_p.cpp b/src/imports/audioengine/qdeclarative_audioengine_p.cpp index cd6405c1..cf0a2264 100644 --- a/src/imports/audioengine/qdeclarative_audioengine_p.cpp +++ b/src/imports/audioengine/qdeclarative_audioengine_p.cpp @@ -373,6 +373,7 @@ void QDeclarativeAudioEngine::appendFunction(QQmlListProperty *property if (category->name() == QLatin1String("default")) { engine->m_defaultCategory = category; } + return; } QDeclarativeAttenuationModel *attenModel = qobject_cast(value); diff --git a/src/multimedia/audio/qaudio.cpp b/src/multimedia/audio/qaudio.cpp index d32b4efe..8b452a11 100644 --- a/src/multimedia/audio/qaudio.cpp +++ b/src/multimedia/audio/qaudio.cpp @@ -86,59 +86,62 @@ Q_CONSTRUCTOR_FUNCTION(qRegisterAudioMetaTypes) #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, QAudio::Error error) { - QDebug nospace = dbg.nospace(); + QDebugStateSaver saver(dbg); + dbg.nospace(); switch (error) { case QAudio::NoError: - nospace << "NoError"; + dbg << "NoError"; break; case QAudio::OpenError: - nospace << "OpenError"; + dbg << "OpenError"; break; case QAudio::IOError: - nospace << "IOError"; + dbg << "IOError"; break; case QAudio::UnderrunError: - nospace << "UnderrunError"; + dbg << "UnderrunError"; break; case QAudio::FatalError: - nospace << "FatalError"; + dbg << "FatalError"; break; } - return nospace; + return dbg; } QDebug operator<<(QDebug dbg, QAudio::State state) { - QDebug nospace = dbg.nospace(); + QDebugStateSaver saver(dbg); + dbg.nospace(); switch (state) { case QAudio::ActiveState: - nospace << "ActiveState"; + dbg << "ActiveState"; break; case QAudio::SuspendedState: - nospace << "SuspendedState"; + dbg << "SuspendedState"; break; case QAudio::StoppedState: - nospace << "StoppedState"; + dbg << "StoppedState"; break; case QAudio::IdleState: - nospace << "IdleState"; + dbg << "IdleState"; break; } - return nospace; + return dbg; } QDebug operator<<(QDebug dbg, QAudio::Mode mode) { - QDebug nospace = dbg.nospace(); + QDebugStateSaver saver(dbg); + dbg.nospace(); switch (mode) { case QAudio::AudioInput: - nospace << "AudioInput"; + dbg << "AudioInput"; break; case QAudio::AudioOutput: - nospace << "AudioOutput"; + dbg << "AudioOutput"; break; } - return nospace; + return dbg; } #endif diff --git a/src/multimedia/audio/qaudioformat.cpp b/src/multimedia/audio/qaudioformat.cpp index 2c9aafb8..1249ea99 100644 --- a/src/multimedia/audio/qaudioformat.cpp +++ b/src/multimedia/audio/qaudioformat.cpp @@ -459,49 +459,50 @@ int QAudioFormat::bytesPerFrame() const #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, QAudioFormat::Endian endian) { - QDebug nospace = dbg.nospace(); + QDebugStateSaver saver(dbg); + dbg.nospace(); switch (endian) { case QAudioFormat::BigEndian: - nospace << "BigEndian"; + dbg << "BigEndian"; break; case QAudioFormat::LittleEndian: - nospace << "LittleEndian"; + dbg << "LittleEndian"; break; } - return nospace; + return dbg; } QDebug operator<<(QDebug dbg, QAudioFormat::SampleType type) { - QDebug nospace = dbg.nospace(); + QDebugStateSaver saver(dbg); + dbg.nospace(); switch (type) { case QAudioFormat::SignedInt: - nospace << "SignedInt"; + dbg << "SignedInt"; break; case QAudioFormat::UnSignedInt: - nospace << "UnSignedInt"; + dbg << "UnSignedInt"; break; case QAudioFormat::Float: - nospace << "Float"; + dbg << "Float"; break; default: - nospace << "Unknown"; + dbg << "Unknown"; break; } - return nospace; + return dbg; } QDebug operator<<(QDebug dbg, const QAudioFormat &f) { - dbg.nospace() << "QAudioFormat(" << f.sampleRate(); - dbg.nospace() << "Hz, " << f.sampleSize(); - dbg.nospace() << "bit, channelCount=" << f.channelCount(); - dbg.nospace() << ", sampleType=" << f.sampleType(); - dbg.nospace() << ", byteOrder=" << f.byteOrder(); - dbg.nospace() << ", codec=" << f.codec(); - dbg.nospace() << ")"; + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "QAudioFormat(" << f.sampleRate() << "Hz, " + << f.sampleSize() << "bit, channelCount=" << f.channelCount() + << ", sampleType=" << f.sampleType() << ", byteOrder=" << f.byteOrder() + << ", codec=" << f.codec() << ')'; - return dbg.space(); + return dbg; } #endif diff --git a/src/multimedia/audio/qsamplecache_p.cpp b/src/multimedia/audio/qsamplecache_p.cpp index fbb53ffc..8043b770 100644 --- a/src/multimedia/audio/qsamplecache_p.cpp +++ b/src/multimedia/audio/qsamplecache_p.cpp @@ -33,8 +33,12 @@ #include "qsamplecache_p.h" #include "qwavedecoder_p.h" -#include +#include +#include +#include + +#include //#define QT_SAMPLECACHE_DEBUG QT_BEGIN_NAMESPACE diff --git a/src/multimedia/audio/qsoundeffect_pulse_p.cpp b/src/multimedia/audio/qsoundeffect_pulse_p.cpp index 0a509a8b..ecc42cac 100644 --- a/src/multimedia/audio/qsoundeffect_pulse_p.cpp +++ b/src/multimedia/audio/qsoundeffect_pulse_p.cpp @@ -44,8 +44,8 @@ #include #include -#include #include +#include #include "qsoundeffect_pulse_p.h" diff --git a/src/multimedia/controls/qmediaplayercontrol.cpp b/src/multimedia/controls/qmediaplayercontrol.cpp index 1eccb762..9ea6fde8 100644 --- a/src/multimedia/controls/qmediaplayercontrol.cpp +++ b/src/multimedia/controls/qmediaplayercontrol.cpp @@ -315,6 +315,11 @@ QMediaPlayerControl::QMediaPlayerControl(QObject *parent): Setting the media to a null QMediaContent will cause the control to discard all information relating to the current media source and to cease all I/O operations related to that media. + + Qt resource files are never passed as is. If the service supports + QMediaServiceProviderHint::StreamPlayback, a \a stream is supplied, pointing to an opened + QFile. Otherwise, the resource is copied into a temporary file and \a media contains the + url to that file. */ /*! diff --git a/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h b/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h index 48b14108..18670887 100644 --- a/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h +++ b/src/multimedia/gsttools_headers/qgstvideorenderersink_p.h @@ -96,6 +96,7 @@ public: bool start(GstCaps *caps); void stop(); + void unlock(); bool proposeAllocation(GstQuery *query); GstFlowReturn render(GstBuffer *buffer); @@ -153,7 +154,9 @@ private: static gboolean stop(GstBaseSink *sink); - static GstFlowReturn render(GstBaseSink *sink, GstBuffer *buffer); + static gboolean unlock(GstBaseSink *sink); + + static GstFlowReturn show_frame(GstVideoSink *sink, GstBuffer *buffer); private: QVideoSurfaceGstDelegate *delegate; diff --git a/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h b/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h index 0b253462..e8f61afe 100644 --- a/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h +++ b/src/multimedia/gsttools_headers/qvideosurfacegstsink_p.h @@ -88,6 +88,8 @@ public: bool start(const QVideoSurfaceFormat &format, int bytesPerLine); void stop(); + void unlock(); + bool isActive(); QGstBufferPoolInterface *pool() { return m_pool; } @@ -148,6 +150,8 @@ private: static gboolean start(GstBaseSink *sink); static gboolean stop(GstBaseSink *sink); + static gboolean unlock(GstBaseSink *sink); + static GstFlowReturn show_frame(GstVideoSink *sink, GstBuffer *buffer); private: diff --git a/src/multimedia/playback/playlistfileparser.cpp b/src/multimedia/playback/playlistfileparser.cpp index 55835b4e..a40bdd9f 100644 --- a/src/multimedia/playback/playlistfileparser.cpp +++ b/src/multimedia/playback/playlistfileparser.cpp @@ -33,7 +33,9 @@ #include "playlistfileparser_p.h" #include +#include #include +#include #include "qmediaobject_p.h" #include "qmediametadata.h" diff --git a/src/multimedia/playback/playlistfileparser_p.h b/src/multimedia/playback/playlistfileparser_p.h index 272fac68..3e28b96c 100644 --- a/src/multimedia/playback/playlistfileparser_p.h +++ b/src/multimedia/playback/playlistfileparser_p.h @@ -45,8 +45,8 @@ // We mean it. // -#include #include "qtmultimediadefs.h" +#include QT_BEGIN_NAMESPACE diff --git a/src/multimedia/playback/qmediaplayer.cpp b/src/multimedia/playback/qmediaplayer.cpp index aae4c7ef..b43faa2b 100644 --- a/src/multimedia/playback/qmediaplayer.cpp +++ b/src/multimedia/playback/qmediaplayer.cpp @@ -48,6 +48,8 @@ #include #include #include +#include +#include QT_BEGIN_NAMESPACE @@ -103,22 +105,30 @@ public: : provider(0) , control(0) , state(QMediaPlayer::StoppedState) + , status(QMediaPlayer::UnknownMediaStatus) , error(QMediaPlayer::NoError) + , ignoreNextStatusChange(-1) , playlist(0) , networkAccessControl(0) + , hasStreamPlaybackFeature(false) , nestedPlaylists(0) {} QMediaServiceProvider *provider; QMediaPlayerControl* control; QMediaPlayer::State state; + QMediaPlayer::MediaStatus status; QMediaPlayer::Error error; QString errorString; + int ignoreNextStatusChange; QPointer videoOutput; QMediaPlaylist *playlist; QMediaNetworkAccessControl *networkAccessControl; QVideoSurfaceOutput surfaceOutput; + bool hasStreamPlaybackFeature; + QMediaContent qrcMedia; + QScopedPointer qrcFile; QMediaContent rootMedia; QMediaContent pendingPlaylist; @@ -126,6 +136,8 @@ public: bool isInChain(QUrl url); int nestedPlaylists; + void setMedia(const QMediaContent &media, QIODevice *stream = 0); + void setPlaylist(QMediaPlaylist *playlist); void setPlaylistMedia(); void loadPlaylist(); @@ -137,6 +149,7 @@ public: void _q_error(int error, const QString &errorString); void _q_updateMedia(const QMediaContent&); void _q_playlistDestroyed(); + void _q_handleMediaChanged(const QMediaContent&); void _q_handlePlaylistLoaded(); void _q_handlePlaylistLoadFailed(); }; @@ -196,22 +209,30 @@ void QMediaPlayerPrivate::_q_stateChanged(QMediaPlayer::State ps) } } -void QMediaPlayerPrivate::_q_mediaStatusChanged(QMediaPlayer::MediaStatus status) +void QMediaPlayerPrivate::_q_mediaStatusChanged(QMediaPlayer::MediaStatus s) { Q_Q(QMediaPlayer); - switch (status) { - case QMediaPlayer::StalledMedia: - case QMediaPlayer::BufferingMedia: - q->addPropertyWatch("bufferStatus"); - emit q->mediaStatusChanged(status); - break; - default: - q->removePropertyWatch("bufferStatus"); - emit q->mediaStatusChanged(status); - break; + if (int(s) == ignoreNextStatusChange) { + ignoreNextStatusChange = -1; + return; } + if (s != status) { + status = s; + + switch (s) { + case QMediaPlayer::StalledMedia: + case QMediaPlayer::BufferingMedia: + q->addPropertyWatch("bufferStatus"); + break; + default: + q->removePropertyWatch("bufferStatus"); + break; + } + + emit q->mediaStatusChanged(s); + } } void QMediaPlayerPrivate::_q_error(int error, const QString &errorString) @@ -276,7 +297,7 @@ void QMediaPlayerPrivate::_q_updateMedia(const QMediaContent &media) const QMediaPlayer::State currentState = state; - control->setMedia(media, 0); + setMedia(media, 0); if (!media.isNull()) { switch (currentState) { @@ -297,11 +318,76 @@ void QMediaPlayerPrivate::_q_updateMedia(const QMediaContent &media) void QMediaPlayerPrivate::_q_playlistDestroyed() { playlist = 0; + setMedia(QMediaContent(), 0); +} + +void QMediaPlayerPrivate::setMedia(const QMediaContent &media, QIODevice *stream) +{ + Q_Q(QMediaPlayer); if (!control) return; - control->setMedia(QMediaContent(), 0); + QScopedPointer file; + + // Backends can't play qrc files directly. + // If the backend supports StreamPlayback, we pass a QFile for that resource. + // If it doesn't, we copy the data to a temporary file and pass its path. + if (!media.isNull() && !stream && media.canonicalUrl().scheme() == QLatin1String("qrc")) { + qrcMedia = media; + + file.reset(new QFile(QLatin1Char(':') + media.canonicalUrl().path())); + if (!file->open(QFile::ReadOnly)) { + QMetaObject::invokeMethod(q, "_q_error", Qt::QueuedConnection, + Q_ARG(int, QMediaPlayer::ResourceError), + Q_ARG(QString, QObject::tr("Attempting to play invalid Qt resource"))); + QMetaObject::invokeMethod(q, "_q_mediaStatusChanged", Qt::QueuedConnection, + Q_ARG(QMediaPlayer::MediaStatus, QMediaPlayer::InvalidMedia)); + file.reset(); + // Ignore the next NoMedia status change, we just want to clear the current media + // on the backend side since we can't load the new one and we want to be in the + // InvalidMedia status. + ignoreNextStatusChange = QMediaPlayer::NoMedia; + control->setMedia(QMediaContent(), 0); + + } else if (hasStreamPlaybackFeature) { + control->setMedia(media, file.data()); + } else { + QTemporaryFile *tempFile = new QTemporaryFile; + + // Preserve original file extension, some backends might not load the file if it doesn't + // have an extension. + const QString suffix = QFileInfo(*file).suffix(); + if (!suffix.isEmpty()) + tempFile->setFileTemplate(tempFile->fileTemplate() + QLatin1Char('.') + suffix); + + // Copy the qrc data into the temporary file + tempFile->open(); + char buffer[4096]; + while (true) { + qint64 len = file->read(buffer, sizeof(buffer)); + if (len < 1) + break; + tempFile->write(buffer, len); + } + tempFile->close(); + + file.reset(tempFile); + control->setMedia(QMediaContent(QUrl::fromLocalFile(file->fileName())), 0); + } + } else { + qrcMedia = QMediaContent(); + control->setMedia(media, stream); + } + + qrcFile.swap(file); // Cleans up any previous file +} + +void QMediaPlayerPrivate::_q_handleMediaChanged(const QMediaContent &media) +{ + Q_Q(QMediaPlayer); + + emit q->currentMediaChanged(qrcMedia.isNull() ? media : qrcMedia); } void QMediaPlayerPrivate::setPlaylist(QMediaPlaylist *pls) @@ -333,7 +419,7 @@ void QMediaPlayerPrivate::setPlaylistMedia() playlist->next(); } return; - } else if (control != 0) { + } else { // If we've just switched to a new playlist, // then last emitted currentMediaChanged was a playlist. // Make sure we emit currentMediaChanged if new playlist has @@ -344,14 +430,14 @@ void QMediaPlayerPrivate::setPlaylistMedia() // test.wav -- processed by backend, // media is not changed, // frontend needs to emit currentMediaChanged - bool isSameMedia = (control->media() == playlist->currentMedia()); - control->setMedia(playlist->currentMedia(), 0); + bool isSameMedia = (q->currentMedia() == playlist->currentMedia()); + setMedia(playlist->currentMedia(), 0); if (isSameMedia) { - emit q->currentMediaChanged(control->media()); + emit q->currentMediaChanged(q->currentMedia()); } } } else { - q->setMedia(QMediaContent(), 0); + setMedia(QMediaContent(), 0); } } @@ -441,7 +527,7 @@ void QMediaPlayerPrivate::_q_handlePlaylistLoadFailed() if (playlist) playlist->next(); else - control->setMedia(QMediaContent(), 0); + setMedia(QMediaContent(), 0); } static QMediaService *playerService(QMediaPlayer::Flags flags) @@ -484,7 +570,7 @@ QMediaPlayer::QMediaPlayer(QObject *parent, QMediaPlayer::Flags flags): d->control = qobject_cast(d->service->requestControl(QMediaPlayerControl_iid)); d->networkAccessControl = qobject_cast(d->service->requestControl(QMediaNetworkAccessControl_iid)); if (d->control != 0) { - connect(d->control, SIGNAL(mediaChanged(QMediaContent)), SIGNAL(currentMediaChanged(QMediaContent))); + connect(d->control, SIGNAL(mediaChanged(QMediaContent)), SLOT(_q_handleMediaChanged(QMediaContent))); connect(d->control, SIGNAL(stateChanged(QMediaPlayer::State)), SLOT(_q_stateChanged(QMediaPlayer::State))); connect(d->control, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), SLOT(_q_mediaStatusChanged(QMediaPlayer::MediaStatus))); @@ -500,11 +586,16 @@ QMediaPlayer::QMediaPlayer(QObject *parent, QMediaPlayer::Flags flags): connect(d->control, SIGNAL(playbackRateChanged(qreal)), SIGNAL(playbackRateChanged(qreal))); connect(d->control, SIGNAL(bufferStatusChanged(int)), SIGNAL(bufferStatusChanged(int))); - if (d->control->state() == PlayingState) + d->state = d->control->state(); + d->status = d->control->mediaStatus(); + + if (d->state == PlayingState) addPropertyWatch("position"); - if (d->control->mediaStatus() == StalledMedia || d->control->mediaStatus() == BufferingMedia) + if (d->status == StalledMedia || d->status == BufferingMedia) addPropertyWatch("bufferStatus"); + + d->hasStreamPlaybackFeature = d->provider->supportedFeatures(d->service).testFlag(QMediaServiceProviderHint::StreamPlayback); } if (d->networkAccessControl != 0) { connect(d->networkAccessControl, SIGNAL(configurationChanged(QNetworkConfiguration)), @@ -549,7 +640,9 @@ const QIODevice *QMediaPlayer::mediaStream() const { Q_D(const QMediaPlayer); - if (d->control != 0) + // When playing a resource file, we might have passed a QFile to the backend. Hide it from + // the user. + if (d->control && d->qrcMedia.isNull()) return d->control->mediaStream(); return 0; @@ -566,7 +659,12 @@ QMediaContent QMediaPlayer::currentMedia() const { Q_D(const QMediaPlayer); - if (d->control != 0) + // When playing a resource file, don't return the backend's current media, which + // can be a temporary file. + if (!d->qrcMedia.isNull()) + return d->qrcMedia; + + if (d->control) return d->control->media(); return QMediaContent(); @@ -600,12 +698,7 @@ QMediaPlayer::State QMediaPlayer::state() const QMediaPlayer::MediaStatus QMediaPlayer::mediaStatus() const { - Q_D(const QMediaPlayer); - - if (d->control != 0) - return d->control->mediaStatus(); - - return QMediaPlayer::UnknownMediaStatus; + return d_func()->status; } qint64 QMediaPlayer::duration() const @@ -877,8 +970,8 @@ void QMediaPlayer::setMedia(const QMediaContent &media, QIODevice *stream) // reset playlist to the 1st item media.playlist()->setCurrentIndex(0); d->setPlaylist(media.playlist()); - } else if (d->control != 0) { - d->control->setMedia(media, stream); + } else { + d->setMedia(media, stream); } } diff --git a/src/multimedia/playback/qmediaplayer.h b/src/multimedia/playback/qmediaplayer.h index b1215eee..735f1113 100644 --- a/src/multimedia/playback/qmediaplayer.h +++ b/src/multimedia/playback/qmediaplayer.h @@ -202,6 +202,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_error(int, const QString &)) Q_PRIVATE_SLOT(d_func(), void _q_updateMedia(const QMediaContent&)) Q_PRIVATE_SLOT(d_func(), void _q_playlistDestroyed()) + Q_PRIVATE_SLOT(d_func(), void _q_handleMediaChanged(const QMediaContent&)) Q_PRIVATE_SLOT(d_func(), void _q_handlePlaylistLoaded()) Q_PRIVATE_SLOT(d_func(), void _q_handlePlaylistLoadFailed()) }; diff --git a/src/multimedia/qmediaserviceprovider.cpp b/src/multimedia/qmediaserviceprovider.cpp index 563af846..658679c5 100644 --- a/src/multimedia/qmediaserviceprovider.cpp +++ b/src/multimedia/qmediaserviceprovider.cpp @@ -299,7 +299,14 @@ Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, loader, class QPluginServiceProvider : public QMediaServiceProvider { - QMap pluginMap; + struct MediaServiceData { + QByteArray type; + QMediaServiceProviderPlugin *plugin; + + MediaServiceData() : plugin(0) { } + }; + + QMap mediaServiceData; public: QMediaService* requestService(const QByteArray &type, const QMediaServiceProviderHint &hint) @@ -416,8 +423,12 @@ public: if (plugin != 0) { QMediaService *service = plugin->create(key); - if (service != 0) - pluginMap.insert(service, plugin); + if (service != 0) { + MediaServiceData d; + d.type = type; + d.plugin = plugin; + mediaServiceData.insert(service, d); + } return service; } @@ -430,13 +441,30 @@ public: void releaseService(QMediaService *service) { if (service != 0) { - QMediaServiceProviderPlugin *plugin = pluginMap.take(service); + MediaServiceData d = mediaServiceData.take(service); - if (plugin != 0) - plugin->release(service); + if (d.plugin != 0) + d.plugin->release(service); } } + QMediaServiceProviderHint::Features supportedFeatures(const QMediaService *service) const + { + if (service) { + MediaServiceData d = mediaServiceData.value(service); + + if (d.plugin) { + QMediaServiceFeaturesInterface *iface = + qobject_cast(d.plugin); + + if (iface) + return iface->supportedFeatures(d.type); + } + } + + return QMediaServiceProviderHint::Features(); + } + QMultimedia::SupportEstimate hasSupport(const QByteArray &serviceType, const QString &mimeType, const QStringList& codecs, @@ -660,6 +688,18 @@ Q_GLOBAL_STATIC(QPluginServiceProvider, pluginProvider); Releases a media \a service requested with requestService(). */ +/*! + \fn QMediaServiceProvider::supportedFeatures(const QMediaService *service) const + + Returns the features supported by a given \a service. +*/ +QMediaServiceProviderHint::Features QMediaServiceProvider::supportedFeatures(const QMediaService *service) const +{ + Q_UNUSED(service); + + return QMediaServiceProviderHint::Features(0); +} + /*! \fn QMultimedia::SupportEstimate QMediaServiceProvider::hasSupport(const QByteArray &serviceType, const QString &mimeType, const QStringList& codecs, int flags) const diff --git a/src/multimedia/qmediaserviceprovider_p.h b/src/multimedia/qmediaserviceprovider_p.h index 62ee510c..4230c427 100644 --- a/src/multimedia/qmediaserviceprovider_p.h +++ b/src/multimedia/qmediaserviceprovider_p.h @@ -53,6 +53,8 @@ public: virtual QMediaService* requestService(const QByteArray &type, const QMediaServiceProviderHint &hint = QMediaServiceProviderHint()) = 0; virtual void releaseService(QMediaService *service) = 0; + virtual QMediaServiceProviderHint::Features supportedFeatures(const QMediaService *service) const; + virtual QMultimedia::SupportEstimate hasSupport(const QByteArray &serviceType, const QString &mimeType, const QStringList& codecs, diff --git a/src/multimedia/qmediatimerange.cpp b/src/multimedia/qmediatimerange.cpp index 13906b8a..b30ee043 100644 --- a/src/multimedia/qmediatimerange.cpp +++ b/src/multimedia/qmediatimerange.cpp @@ -705,11 +705,13 @@ QMediaTimeRange operator-(const QMediaTimeRange &r1, const QMediaTimeRange &r2) #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QMediaTimeRange &range) { - dbg.nospace() << "QMediaTimeRange( "; - foreach (const QMediaTimeInterval &interval, range.intervals()) { - dbg.nospace() << "(" << interval.start() << ", " << interval.end() << ") "; - } - dbg.space() << ")"; + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "QMediaTimeRange( "; + foreach (const QMediaTimeInterval &interval, range.intervals()) + dbg << '(' << interval.start() << ", " << interval.end() << ") "; + dbg.space(); + dbg << ')'; return dbg; } #endif diff --git a/src/multimedia/video/qabstractvideobuffer.cpp b/src/multimedia/video/qabstractvideobuffer.cpp index 657e4fc8..ff29bd0b 100644 --- a/src/multimedia/video/qabstractvideobuffer.cpp +++ b/src/multimedia/video/qabstractvideobuffer.cpp @@ -350,33 +350,37 @@ uchar *QAbstractPlanarVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPe #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, QAbstractVideoBuffer::HandleType type) { + QDebugStateSaver saver(dbg); + dbg.nospace(); switch (type) { case QAbstractVideoBuffer::NoHandle: - return dbg.nospace() << "NoHandle"; + return dbg << "NoHandle"; case QAbstractVideoBuffer::GLTextureHandle: - return dbg.nospace() << "GLTextureHandle"; + return dbg << "GLTextureHandle"; case QAbstractVideoBuffer::XvShmImageHandle: - return dbg.nospace() << "XvShmImageHandle"; + return dbg << "XvShmImageHandle"; case QAbstractVideoBuffer::CoreImageHandle: - return dbg.nospace() << "CoreImageHandle"; + return dbg << "CoreImageHandle"; case QAbstractVideoBuffer::QPixmapHandle: - return dbg.nospace() << "QPixmapHandle"; + return dbg << "QPixmapHandle"; default: - return dbg.nospace() << QString(QLatin1String("UserHandle(%1)")).arg(int(type)).toLatin1().constData(); + return dbg << "UserHandle(" << int(type) << ')'; } } QDebug operator<<(QDebug dbg, QAbstractVideoBuffer::MapMode mode) { + QDebugStateSaver saver(dbg); + dbg.nospace(); switch (mode) { case QAbstractVideoBuffer::ReadOnly: - return dbg.nospace() << "ReadOnly"; + return dbg << "ReadOnly"; case QAbstractVideoBuffer::ReadWrite: - return dbg.nospace() << "ReadWrite"; + return dbg << "ReadWrite"; case QAbstractVideoBuffer::WriteOnly: - return dbg.nospace() << "WriteOnly"; + return dbg << "WriteOnly"; default: - return dbg.nospace() << "NotMapped"; + return dbg << "NotMapped"; } } #endif diff --git a/src/multimedia/video/qabstractvideosurface.cpp b/src/multimedia/video/qabstractvideosurface.cpp index d09c4e4c..c86d52dd 100644 --- a/src/multimedia/video/qabstractvideosurface.cpp +++ b/src/multimedia/video/qabstractvideosurface.cpp @@ -353,18 +353,26 @@ void QAbstractVideoSurface::setNativeResolution(const QSize &resolution) #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QAbstractVideoSurface::Error& error) { + QDebugStateSaver saver(dbg); + dbg.nospace(); switch (error) { case QAbstractVideoSurface::UnsupportedFormatError: - return dbg.nospace() << "UnsupportedFormatError"; + dbg << "UnsupportedFormatError"; + break; case QAbstractVideoSurface::IncorrectFormatError: - return dbg.nospace() << "IncorrectFormatError"; + dbg << "IncorrectFormatError"; + break; case QAbstractVideoSurface::StoppedError: - return dbg.nospace() << "StoppedError"; + dbg << "StoppedError"; + break; case QAbstractVideoSurface::ResourceError: - return dbg.nospace() << "ResourceError"; + dbg << "ResourceError"; + break; default: - return dbg.nospace() << "NoError"; + dbg << "NoError"; + break; } + return dbg; } #endif diff --git a/src/multimedia/video/qvideoframe.cpp b/src/multimedia/video/qvideoframe.cpp index 95f6acb3..4e9e28a4 100644 --- a/src/multimedia/video/qvideoframe.cpp +++ b/src/multimedia/video/qvideoframe.cpp @@ -1002,90 +1002,94 @@ QImage::Format QVideoFrame::imageFormatFromPixelFormat(PixelFormat format) #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, QVideoFrame::PixelFormat pf) { + QDebugStateSaver saver(dbg); + dbg.nospace(); switch (pf) { case QVideoFrame::Format_Invalid: - return dbg.nospace() << "Format_Invalid"; + return dbg << "Format_Invalid"; case QVideoFrame::Format_ARGB32: - return dbg.nospace() << "Format_ARGB32"; + return dbg << "Format_ARGB32"; case QVideoFrame::Format_ARGB32_Premultiplied: - return dbg.nospace() << "Format_ARGB32_Premultiplied"; + return dbg << "Format_ARGB32_Premultiplied"; case QVideoFrame::Format_RGB32: - return dbg.nospace() << "Format_RGB32"; + return dbg << "Format_RGB32"; case QVideoFrame::Format_RGB24: - return dbg.nospace() << "Format_RGB24"; + return dbg << "Format_RGB24"; case QVideoFrame::Format_RGB565: - return dbg.nospace() << "Format_RGB565"; + return dbg << "Format_RGB565"; case QVideoFrame::Format_RGB555: - return dbg.nospace() << "Format_RGB555"; + return dbg << "Format_RGB555"; case QVideoFrame::Format_ARGB8565_Premultiplied: - return dbg.nospace() << "Format_ARGB8565_Premultiplied"; + return dbg << "Format_ARGB8565_Premultiplied"; case QVideoFrame::Format_BGRA32: - return dbg.nospace() << "Format_BGRA32"; + return dbg << "Format_BGRA32"; case QVideoFrame::Format_BGRA32_Premultiplied: - return dbg.nospace() << "Format_BGRA32_Premultiplied"; + return dbg << "Format_BGRA32_Premultiplied"; case QVideoFrame::Format_BGR32: - return dbg.nospace() << "Format_BGR32"; + return dbg << "Format_BGR32"; case QVideoFrame::Format_BGR24: - return dbg.nospace() << "Format_BGR24"; + return dbg << "Format_BGR24"; case QVideoFrame::Format_BGR565: - return dbg.nospace() << "Format_BGR565"; + return dbg << "Format_BGR565"; case QVideoFrame::Format_BGR555: - return dbg.nospace() << "Format_BGR555"; + return dbg << "Format_BGR555"; case QVideoFrame::Format_BGRA5658_Premultiplied: - return dbg.nospace() << "Format_BGRA5658_Premultiplied"; + return dbg << "Format_BGRA5658_Premultiplied"; case QVideoFrame::Format_AYUV444: - return dbg.nospace() << "Format_AYUV444"; + return dbg << "Format_AYUV444"; case QVideoFrame::Format_AYUV444_Premultiplied: - return dbg.nospace() << "Format_AYUV444_Premultiplied"; + return dbg << "Format_AYUV444_Premultiplied"; case QVideoFrame::Format_YUV444: - return dbg.nospace() << "Format_YUV444"; + return dbg << "Format_YUV444"; case QVideoFrame::Format_YUV420P: - return dbg.nospace() << "Format_YUV420P"; + return dbg << "Format_YUV420P"; case QVideoFrame::Format_YV12: - return dbg.nospace() << "Format_YV12"; + return dbg << "Format_YV12"; case QVideoFrame::Format_UYVY: - return dbg.nospace() << "Format_UYVY"; + return dbg << "Format_UYVY"; case QVideoFrame::Format_YUYV: - return dbg.nospace() << "Format_YUYV"; + return dbg << "Format_YUYV"; case QVideoFrame::Format_NV12: - return dbg.nospace() << "Format_NV12"; + return dbg << "Format_NV12"; case QVideoFrame::Format_NV21: - return dbg.nospace() << "Format_NV21"; + return dbg << "Format_NV21"; case QVideoFrame::Format_IMC1: - return dbg.nospace() << "Format_IMC1"; + return dbg << "Format_IMC1"; case QVideoFrame::Format_IMC2: - return dbg.nospace() << "Format_IMC2"; + return dbg << "Format_IMC2"; case QVideoFrame::Format_IMC3: - return dbg.nospace() << "Format_IMC3"; + return dbg << "Format_IMC3"; case QVideoFrame::Format_IMC4: - return dbg.nospace() << "Format_IMC4"; + return dbg << "Format_IMC4"; case QVideoFrame::Format_Y8: - return dbg.nospace() << "Format_Y8"; + return dbg << "Format_Y8"; case QVideoFrame::Format_Y16: - return dbg.nospace() << "Format_Y16"; + return dbg << "Format_Y16"; case QVideoFrame::Format_Jpeg: - return dbg.nospace() << "Format_Jpeg"; + return dbg << "Format_Jpeg"; case QVideoFrame::Format_AdobeDng: - return dbg.nospace() << "Format_AdobeDng"; + return dbg << "Format_AdobeDng"; case QVideoFrame::Format_CameraRaw: - return dbg.nospace() << "Format_CameraRaw"; + return dbg << "Format_CameraRaw"; default: - return dbg.nospace() << QString(QLatin1String("UserType(%1)" )).arg(int(pf)).toLatin1().constData(); + return dbg << QString(QLatin1String("UserType(%1)" )).arg(int(pf)).toLatin1().constData(); } } QDebug operator<<(QDebug dbg, QVideoFrame::FieldType f) { + QDebugStateSaver saver(dbg); + dbg.nospace(); switch (f) { case QVideoFrame::TopField: - return dbg.nospace() << "TopField"; + return dbg << "TopField"; case QVideoFrame::BottomField: - return dbg.nospace() << "BottomField"; + return dbg << "BottomField"; case QVideoFrame::InterlacedFrame: - return dbg.nospace() << "InterlacedFrame"; + return dbg << "InterlacedFrame"; default: - return dbg.nospace() << "ProgressiveFrame"; + return dbg << "ProgressiveFrame"; } } @@ -1161,16 +1165,17 @@ static QString qFormatTimeStamps(qint64 start, qint64 end) QDebug operator<<(QDebug dbg, const QVideoFrame& f) { - dbg.nospace() << "QVideoFrame(" << f.size() << ", " + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "QVideoFrame(" << f.size() << ", " << f.pixelFormat() << ", " << f.handleType() << ", " << f.mapMode() << ", " << qFormatTimeStamps(f.startTime(), f.endTime()).toLatin1().constData(); - if (f.availableMetaData().count()) { - dbg.nospace() << ", metaData: "; - dbg.nospace() << f.availableMetaData(); - } - return dbg.nospace() << ")"; + if (f.availableMetaData().count()) + dbg << ", metaData: " << f.availableMetaData(); + dbg << ')'; + return dbg; } #endif diff --git a/src/multimedia/video/qvideosurfaceformat.cpp b/src/multimedia/video/qvideosurfaceformat.cpp index 1361dbc7..4c616b89 100644 --- a/src/multimedia/video/qvideosurfaceformat.cpp +++ b/src/multimedia/video/qvideosurfaceformat.cpp @@ -569,61 +569,62 @@ void QVideoSurfaceFormat::setProperty(const char *name, const QVariant &value) #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, QVideoSurfaceFormat::YCbCrColorSpace cs) { - QDebug nospace = dbg.nospace(); + QDebugStateSaver saver(dbg); + dbg.nospace(); switch (cs) { case QVideoSurfaceFormat::YCbCr_BT601: - nospace << "YCbCr_BT601"; + dbg << "YCbCr_BT601"; break; case QVideoSurfaceFormat::YCbCr_BT709: - nospace << "YCbCr_BT709"; + dbg << "YCbCr_BT709"; break; case QVideoSurfaceFormat::YCbCr_JPEG: - nospace << "YCbCr_JPEG"; + dbg << "YCbCr_JPEG"; break; case QVideoSurfaceFormat::YCbCr_xvYCC601: - nospace << "YCbCr_xvYCC601"; + dbg << "YCbCr_xvYCC601"; break; case QVideoSurfaceFormat::YCbCr_xvYCC709: - nospace << "YCbCr_xvYCC709"; + dbg << "YCbCr_xvYCC709"; break; case QVideoSurfaceFormat::YCbCr_CustomMatrix: - nospace << "YCbCr_CustomMatrix"; + dbg << "YCbCr_CustomMatrix"; break; default: - nospace << "YCbCr_Undefined"; + dbg << "YCbCr_Undefined"; break; } - return nospace; + return dbg; } QDebug operator<<(QDebug dbg, QVideoSurfaceFormat::Direction dir) { - QDebug nospace = dbg.nospace(); + QDebugStateSaver saver(dbg); + dbg.nospace(); switch (dir) { case QVideoSurfaceFormat::BottomToTop: - nospace << "BottomToTop"; + dbg << "BottomToTop"; break; case QVideoSurfaceFormat::TopToBottom: - nospace << "TopToBottom"; + dbg << "TopToBottom"; break; } - return nospace; + return dbg; } QDebug operator<<(QDebug dbg, const QVideoSurfaceFormat &f) { - dbg.nospace() << "QVideoSurfaceFormat(" << f.pixelFormat(); - dbg.nospace() << ", " << f.frameSize(); - dbg.nospace() << ", viewport=" << f.viewport(); - dbg.nospace() << ", pixelAspectRatio=" << f.pixelAspectRatio(); - dbg.nospace() << ", handleType=" << f.handleType(); - dbg.nospace() << ", yCbCrColorSpace=" << f.yCbCrColorSpace(); - dbg.nospace() << ")"; + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "QVideoSurfaceFormat(" << f.pixelFormat() << ", " << f.frameSize() + << ", viewport=" << f.viewport() << ", pixelAspectRatio=" << f.pixelAspectRatio() + << ", handleType=" << f.handleType() << ", yCbCrColorSpace=" << f.yCbCrColorSpace() + << ')'; foreach(const QByteArray& propertyName, f.propertyNames()) dbg << "\n " << propertyName.data() << " = " << f.property(propertyName.data()); - return dbg.space(); + return dbg; } #endif diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp index c65dec44..9a050e7a 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp @@ -318,8 +318,6 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent, if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized)) == 0) mMediaPlayer->release(); - QString mediaPath; - if (mediaContent.isNull()) { setMediaStatus(QMediaPlayer::NoMedia); } else { @@ -330,29 +328,17 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent, return; } - const QUrl url = mediaContent.canonicalUrl(); - if (url.scheme() == QLatin1String("qrc")) { - const QString path = url.toString().mid(3); - mTempFile.reset(QTemporaryFile::createNativeFile(path)); - if (!mTempFile.isNull()) - mediaPath = QStringLiteral("file://") + mTempFile->fileName(); - } else { - mediaPath = url.toString(QUrl::FullyEncoded); - } - if (mVideoSize.isValid() && mVideoOutput) mVideoOutput->setVideoSize(mVideoSize); if ((mMediaPlayer->display() == 0) && mVideoOutput) mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture()); - mMediaPlayer->setDataSource(mediaPath); + mMediaPlayer->setDataSource(mediaContent.canonicalUrl().toString(QUrl::FullyEncoded)); mMediaPlayer->prepareAsync(); } - if (!mReloadingMedia) { + if (!mReloadingMedia) Q_EMIT mediaChanged(mMediaContent); - Q_EMIT actualMediaLocationChanged(mediaPath); - } resetBufferingProgress(); diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h index dfc3853a..3f92d809 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h @@ -37,7 +37,6 @@ #include #include #include -#include QT_BEGIN_NAMESPACE @@ -72,7 +71,6 @@ public: Q_SIGNALS: void metaDataUpdated(); - void actualMediaLocationChanged(const QString &url); public Q_SLOTS: void setPosition(qint64 position) Q_DECL_OVERRIDE; @@ -112,7 +110,6 @@ private: int mPendingVolume; int mPendingMute; bool mReloadingMedia; - QScopedPointer mTempFile; int mActiveStateChangeNotifiers; void setState(QMediaPlayer::State state); diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp index 94df8d3c..74943ca6 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp @@ -45,8 +45,8 @@ QAndroidMediaService::QAndroidMediaService(QObject *parent) { mMediaControl = new QAndroidMediaPlayerControl; mMetadataControl = new QAndroidMetaDataReaderControl; - connect(mMediaControl, SIGNAL(actualMediaLocationChanged(QString)), - mMetadataControl, SLOT(onMediaChanged(QString))); + connect(mMediaControl, SIGNAL(mediaChanged(QMediaContent)), + mMetadataControl, SLOT(onMediaChanged(QMediaContent))); connect(mMediaControl, SIGNAL(metaDataUpdated()), mMetadataControl, SLOT(onUpdateMetaData())); } diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp index 81d7cf1a..d09a7734 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp @@ -93,18 +93,18 @@ QStringList QAndroidMetaDataReaderControl::availableMetaData() const return m_metadata.keys(); } -void QAndroidMetaDataReaderControl::onMediaChanged(const QString &url) +void QAndroidMetaDataReaderControl::onMediaChanged(const QMediaContent &media) { if (!m_retriever) return; - m_mediaLocation = url; + m_mediaContent = media; updateData(); } void QAndroidMetaDataReaderControl::onUpdateMetaData() { - if (!m_retriever || m_mediaLocation.isEmpty()) + if (!m_retriever || m_mediaContent.isNull()) return; updateData(); @@ -114,8 +114,8 @@ void QAndroidMetaDataReaderControl::updateData() { m_metadata.clear(); - if (!m_mediaLocation.isEmpty()) { - if (m_retriever->setDataSource(m_mediaLocation)) { + if (!m_mediaContent.isNull()) { + if (m_retriever->setDataSource(m_mediaContent.canonicalUrl())) { QString mimeType = m_retriever->extractMetadata(AndroidMediaMetadataRetriever::MimeType); if (!mimeType.isNull()) m_metadata.insert(QMediaMetaData::MediaType, mimeType); diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h index 26847730..14fb01ea 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h +++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.h @@ -54,13 +54,13 @@ public: QStringList availableMetaData() const Q_DECL_OVERRIDE; public Q_SLOTS: - void onMediaChanged(const QString &url); + void onMediaChanged(const QMediaContent &media); void onUpdateMetaData(); private: void updateData(); - QString m_mediaLocation; + QMediaContent m_mediaContent; bool m_available; QVariantMap m_metadata; diff --git a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp index 9714654a..56ac0e0a 100644 --- a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp +++ b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.cpp @@ -83,15 +83,14 @@ void AndroidMediaMetadataRetriever::release() m_metadataRetriever.callMethod("release"); } -bool AndroidMediaMetadataRetriever::setDataSource(const QString &urlString) +bool AndroidMediaMetadataRetriever::setDataSource(const QUrl &url) { if (!m_metadataRetriever.isValid()) return false; QJNIEnvironmentPrivate env; - QUrl url(urlString); - if (url.isLocalFile()) { // also includes qrc files (copied to a temp file) + if (url.isLocalFile()) { // also includes qrc files (copied to a temp file by QMediaPlayer) QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.path()); QJNIObjectPrivate fileInputStream("java/io/FileInputStream", "(Ljava/lang/String;)V", @@ -153,7 +152,7 @@ bool AndroidMediaMetadataRetriever::setDataSource(const QString &urlString) return false; } else if (QtAndroidPrivate::androidSdkVersion() >= 14) { // On API levels >= 14, only setDataSource(String, Map) accepts remote media - QJNIObjectPrivate string = QJNIObjectPrivate::fromString(urlString); + QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.toString(QUrl::FullyEncoded)); QJNIObjectPrivate hash("java/util/HashMap"); m_metadataRetriever.callMethod("setDataSource", @@ -165,7 +164,7 @@ bool AndroidMediaMetadataRetriever::setDataSource(const QString &urlString) } else { // While on API levels < 14, only setDataSource(Context, Uri) is available and works for // remote media... - QJNIObjectPrivate string = QJNIObjectPrivate::fromString(urlString); + QJNIObjectPrivate string = QJNIObjectPrivate::fromString(url.toString(QUrl::FullyEncoded)); QJNIObjectPrivate uri = m_metadataRetriever.callStaticObjectMethod("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", diff --git a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h index 13cab774..01a98490 100644 --- a/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h +++ b/src/plugins/android/src/wrappers/jni/androidmediametadataretriever.h @@ -72,7 +72,7 @@ public: QString extractMetadata(MetadataKey key); void release(); - bool setDataSource(const QString &url); + bool setDataSource(const QUrl &url); private: QJNIObjectPrivate m_metadataRetriever; diff --git a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm index 87bfeb82..cf13635f 100644 --- a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm +++ b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm @@ -32,6 +32,7 @@ ****************************************************************************/ #include "avfcameraviewfindersettingscontrol.h" +#include "private/qabstractvideobuffer_p.h" #include "avfcamerarenderercontrol.h" #include "avfcamerasession.h" #include "avfcameraservice.h" @@ -39,15 +40,17 @@ #include #include + #include QT_USE_NAMESPACE -class CVPixelBufferVideoBuffer : public QAbstractVideoBuffer +class CVPixelBufferVideoBuffer : public QAbstractPlanarVideoBuffer { + friend class CVPixelBufferVideoBufferPrivate; public: CVPixelBufferVideoBuffer(CVPixelBufferRef buffer) - : QAbstractVideoBuffer(NoHandle) + : QAbstractPlanarVideoBuffer(NoHandle) , m_buffer(buffer) , m_mode(NotMapped) { @@ -61,6 +64,42 @@ public: MapMode mapMode() const { return m_mode; } + int map(QAbstractVideoBuffer::MapMode mode, int *numBytes, int bytesPerLine[4], uchar *data[4]) + { + // We only support RGBA or NV12 (or Apple's version of NV12), + // they are either 0 planes or 2. + const size_t nPlanes = CVPixelBufferGetPlaneCount(m_buffer); + Q_ASSERT(nPlanes <= 2); + + if (!nPlanes) { + data[0] = map(mode, numBytes, bytesPerLine); + return data[0] ? 1 : 0; + } + + // For a bi-planar format we have to set the parameters correctly: + if (mode != QAbstractVideoBuffer::NotMapped && m_mode == QAbstractVideoBuffer::NotMapped) { + CVPixelBufferLockBaseAddress(m_buffer, 0); + + if (numBytes) + *numBytes = CVPixelBufferGetDataSize(m_buffer); + + if (bytesPerLine) { + // At the moment we handle only bi-planar format. + bytesPerLine[0] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, 0); + bytesPerLine[1] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, 1); + } + + if (data) { + data[0] = (uchar *)CVPixelBufferGetBaseAddressOfPlane(m_buffer, 0); + data[1] = (uchar *)CVPixelBufferGetBaseAddressOfPlane(m_buffer, 1); + } + + m_mode = mode; + } + + return nPlanes; + } + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) { if (mode != NotMapped && m_mode == NotMapped) { @@ -73,7 +112,6 @@ public: *bytesPerLine = CVPixelBufferGetBytesPerRow(m_buffer); m_mode = mode; - return (uchar*)CVPixelBufferGetBaseAddress(m_buffer); } else { return 0; @@ -93,6 +131,7 @@ private: MapMode m_mode; }; + @interface AVFCaptureFramesDelegate : NSObject { @private diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h index fccc938a..cf2f512a 100644 --- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h +++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.h @@ -74,6 +74,7 @@ private: void setFramerate(qreal minFPS, qreal maxFPS, bool useActive); void setPixelFormat(QVideoFrame::PixelFormat newFormat); AVCaptureDeviceFormat *findBestFormatMatch(const QCameraViewfinderSettings &settings) const; + QVector viewfinderPixelFormats() const; bool convertPixelFormatIfSupported(QVideoFrame::PixelFormat format, unsigned &avfFormat) const; void applySettings(); QCameraViewfinderSettings requestedSettings() const; diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm index 250aae9c..60df1e2e 100644 --- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm +++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm @@ -38,6 +38,8 @@ #include "avfcameraservice.h" #include "avfcameradebug.h" +#include + #include #include #include @@ -52,28 +54,6 @@ 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(); @@ -269,7 +249,8 @@ QList AVFCameraViewfinderSettingsControl2::supportedV QVector framerates; - QVector pixelFormats(qt_viewfinder_pixel_formats(m_videoOutput)); + QVector pixelFormats(viewfinderPixelFormats()); + if (!pixelFormats.size()) pixelFormats << QVideoFrame::Format_Invalid; // The default value. #if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_7_0) @@ -397,6 +378,9 @@ QVideoFrame::PixelFormat AVFCameraViewfinderSettingsControl2::QtPixelFormatFromC return QVideoFrame::Format_RGB24; case kCVPixelFormatType_24BGR: return QVideoFrame::Format_BGR24; + case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: + case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange: + return QVideoFrame::Format_NV12; default: return QVideoFrame::Format_Invalid; } @@ -414,6 +398,9 @@ bool AVFCameraViewfinderSettingsControl2::CVPixelFormatFromQtFormat(QVideoFrame: case QVideoFrame::Format_BGRA32: conv = kCVPixelFormatType_32ARGB; break; + case QVideoFrame::Format_NV12: + conv = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange; + break; // These two formats below are not supported // by QSGVideoNodeFactory_RGB, so for now I have to // disable them. @@ -467,7 +454,37 @@ AVCaptureDeviceFormat *AVFCameraViewfinderSettingsControl2::findBestFormatMatch( return nil; } -bool AVFCameraViewfinderSettingsControl2::convertPixelFormatIfSupported(QVideoFrame::PixelFormat qtFormat, unsigned &avfFormat)const +QVector AVFCameraViewfinderSettingsControl2::viewfinderPixelFormats() const +{ + Q_ASSERT(m_videoOutput); + + QVector qtFormats; + QList filter; + + NSArray *pixelFormats = [m_videoOutput availableVideoCVPixelFormatTypes]; + const QAbstractVideoSurface *surface = m_service->videoOutput() ? m_service->videoOutput()->surface() : 0; + + if (surface) + filter = surface->supportedPixelFormats(); + + 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(QtPixelFormatFromCVFormat([formatAsNSNumber unsignedIntValue])); + if (qtFormat != QVideoFrame::Format_Invalid && (!surface || filter.contains(qtFormat)) + && !qtFormats.contains(qtFormat)) { // Can happen, for example, with 8BiPlanar existing in video/full range. + qtFormats << qtFormat; + } + } + + return qtFormats; +} + +bool AVFCameraViewfinderSettingsControl2::convertPixelFormatIfSupported(QVideoFrame::PixelFormat qtFormat, + unsigned &avfFormat)const { Q_ASSERT(m_videoOutput); @@ -479,17 +496,25 @@ bool AVFCameraViewfinderSettingsControl2::convertPixelFormatIfSupported(QVideoFr if (!formats || !formats.count) return false; + if (m_service->videoOutput() && m_service->videoOutput()->surface()) { + const QAbstractVideoSurface *surface = m_service->videoOutput()->surface(); + if (!surface->supportedPixelFormats().contains(qtFormat)) + return false; + } + + bool found = 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; + found = true; } } - return false; + return found; } void AVFCameraViewfinderSettingsControl2::applySettings() diff --git a/src/plugins/directshow/player/directshowiosource.cpp b/src/plugins/directshow/player/directshowiosource.cpp index 03b53c44..3a4e1075 100644 --- a/src/plugins/directshow/player/directshowiosource.cpp +++ b/src/plugins/directshow/player/directshowiosource.cpp @@ -605,21 +605,3 @@ HRESULT DirectShowIOSource::QueryDirection(PIN_DIRECTION *pPinDir) return S_OK; } } - -DirectShowRcSource::DirectShowRcSource(DirectShowEventLoop *loop) - : DirectShowIOSource(loop) -{ -} - -bool DirectShowRcSource::open(const QUrl &url) -{ - m_file.moveToThread(QCoreApplication::instance()->thread()); - m_file.setFileName(QLatin1Char(':') + url.path()); - - if (m_file.open(QIODevice::ReadOnly)) { - setDevice(&m_file); - return true; - } else { - return false; - } -} diff --git a/src/plugins/directshow/player/directshowiosource.h b/src/plugins/directshow/player/directshowiosource.h index 241c86fb..fb3774af 100644 --- a/src/plugins/directshow/player/directshowiosource.h +++ b/src/plugins/directshow/player/directshowiosource.h @@ -127,15 +127,4 @@ private: QMutex m_mutex; }; -class DirectShowRcSource : public DirectShowIOSource -{ -public: - DirectShowRcSource(DirectShowEventLoop *loop); - - bool open(const QUrl &url); - -private: - QFile m_file; -}; - #endif diff --git a/src/plugins/directshow/player/directshowplayerservice.cpp b/src/plugins/directshow/player/directshowplayerservice.cpp index 809839c6..67aea6e9 100644 --- a/src/plugins/directshow/player/directshowplayerservice.cpp +++ b/src/plugins/directshow/player/directshowplayerservice.cpp @@ -289,15 +289,6 @@ void DirectShowPlayerService::doSetUrlSource(QMutexLocker *locker) fileSource->Release(); locker->relock(); } - } else if (m_url.scheme() == QLatin1String("qrc")) { - DirectShowRcSource *rcSource = new DirectShowRcSource(m_loop); - - locker->unlock(); - if (rcSource->open(m_url) && SUCCEEDED(hr = m_graph->AddFilter(rcSource, L"Source"))) - source = rcSource; - else - rcSource->Release(); - locker->relock(); } if (!SUCCEEDED(hr)) { diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp index 8fc301a3..4846353a 100644 --- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp @@ -54,7 +54,6 @@ QT_BEGIN_NAMESPACE QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *session, QObject *parent) : QMediaPlayerControl(parent) - , m_ownStream(false) , m_session(session) , m_userRequestedState(QMediaPlayer::StoppedState) , m_currentState(QMediaPlayer::StoppedState) @@ -370,31 +369,6 @@ void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice * emit bufferStatusChanged(0); } - if (m_stream && m_stream != stream) { - if (m_ownStream) - delete m_stream; - m_stream = 0; - m_ownStream = false; - } - - // If the canonical URL refers to a Qt resource, open with QFile and use - // the stream playback capability to play. - if (stream == 0 && content.canonicalUrl().scheme() == QLatin1String("qrc")) { - stream = new QFile(QLatin1Char(':') + content.canonicalUrl().path(), this); - if (!stream->open(QIODevice::ReadOnly)) { - delete stream; - m_mediaStatus = QMediaPlayer::InvalidMedia; - m_currentResource = content; - emit mediaChanged(m_currentResource); - emit error(QMediaPlayer::FormatError, tr("Attempting to play invalid Qt resource")); - if (m_currentState != QMediaPlayer::PlayingState) - m_resources->release(); - popAndNotifyState(); - return; - } - m_ownStream = true; - } - m_currentResource = content; m_stream = stream; diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h index 21688506..c9621b79 100644 --- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h @@ -116,7 +116,6 @@ private: void pushState(); void popAndNotifyState(); - bool m_ownStream; QGstreamerPlayerSession *m_session; QMediaPlayer::State m_userRequestedState; QMediaPlayer::State m_currentState; diff --git a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp b/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp index 30524106..3ba640cd 100644 --- a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp +++ b/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp @@ -162,22 +162,6 @@ QByteArray MmRendererMediaPlayerControl::resourcePathForUrl(const QUrl &url) const QFileInfo fileInfo(relativeFilePath); return QFile::encodeName(QStringLiteral("file://") + fileInfo.absoluteFilePath()); - // QRC, copy to temporary file, as mmrenderer does not support resource files - } else if (url.scheme() == QStringLiteral("qrc")) { - const QString qrcPath = ':' + url.path(); - const QFileInfo resourceFileInfo(qrcPath); - m_tempMediaFileName = QDir::tempPath() + QStringLiteral("/qtmedia_") + - QUuid::createUuid().toString() + QStringLiteral(".") + - resourceFileInfo.suffix(); - if (!QFile::copy(qrcPath, m_tempMediaFileName)) { - const QString errorMsg = QString("Failed to copy resource file to temporary file " - "%1 for playback").arg(m_tempMediaFileName); - qDebug() << errorMsg; - emit error(0, errorMsg); - return QByteArray(); - } - return QFile::encodeName(m_tempMediaFileName); - // HTTP or similar URL } else { return url.toEncoded(); @@ -187,7 +171,7 @@ QByteArray MmRendererMediaPlayerControl::resourcePathForUrl(const QUrl &url) void MmRendererMediaPlayerControl::attach() { // Should only be called in detached state - Q_ASSERT(m_audioId == -1 && !m_inputAttached && m_tempMediaFileName.isEmpty()); + Q_ASSERT(m_audioId == -1 && !m_inputAttached); if (m_media.isNull() || !m_context) { setMediaStatus(QMediaPlayer::NoMedia); @@ -251,10 +235,6 @@ void MmRendererMediaPlayerControl::detach() } } - if (!m_tempMediaFileName.isEmpty()) { - QFile::remove(m_tempMediaFileName); - m_tempMediaFileName.clear(); - } m_loadingTimer.stop(); } diff --git a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.h b/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.h index d4ddf363..79fc9be0 100644 --- a/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.h +++ b/src/plugins/qnx/mediaplayer/mmrenderermediaplayercontrol.h @@ -156,7 +156,6 @@ private: bool m_inputAttached; int m_stopEventsToIgnore; int m_bufferLevel; - QString m_tempMediaFileName; QTimer m_loadingTimer; }; diff --git a/src/plugins/winrt/qwinrtmediaplayercontrol.cpp b/src/plugins/winrt/qwinrtmediaplayercontrol.cpp index 50f76fd8..333a6d1e 100644 --- a/src/plugins/winrt/qwinrtmediaplayercontrol.cpp +++ b/src/plugins/winrt/qwinrtmediaplayercontrol.cpp @@ -752,7 +752,7 @@ void QWinRTMediaPlayerControl::setMedia(const QMediaContent &media, QIODevice *s } emit mediaChanged(media); - QString urlString; + QString urlString = media.canonicalUrl().toString(); if (!d->stream) { // If we can read the file via Qt, use the byte stream approach foreach (const QMediaResource &resource, media.resources()) { diff --git a/src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp b/src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp index 7970ae14..f4efe47e 100644 --- a/src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp +++ b/src/qtmultimediaquicktools/qdeclarativevideooutput_render.cpp @@ -444,10 +444,6 @@ void QSGVideoItemSurface::stop() bool QSGVideoItemSurface::present(const QVideoFrame &frame) { - if (!frame.isValid()) { - qWarning() << Q_FUNC_INFO << "I'm getting bad frames here..."; - return false; - } m_backend->present(frame); return true; } diff --git a/src/qtmultimediaquicktools/qdeclarativevideooutput_render_p.h b/src/qtmultimediaquicktools/qdeclarativevideooutput_render_p.h index d37df394..cb1168ee 100644 --- a/src/qtmultimediaquicktools/qdeclarativevideooutput_render_p.h +++ b/src/qtmultimediaquicktools/qdeclarativevideooutput_render_p.h @@ -36,7 +36,7 @@ #define QDECLARATIVEVIDEOOUTPUT_RENDER_P_H #include "qdeclarativevideooutput_backend_p.h" -#include "qsgvideonode_i420.h" +#include "qsgvideonode_yuv.h" #include "qsgvideonode_rgb.h" #include "qsgvideonode_texture.h" @@ -57,14 +57,14 @@ public: QDeclarativeVideoRendererBackend(QDeclarativeVideoOutput *parent); ~QDeclarativeVideoRendererBackend(); - bool init(QMediaService *service); - void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &changeData); - void releaseSource(); - void releaseControl(); - QSize nativeSize() const; - void updateGeometry(); - QSGNode *updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data); - QAbstractVideoSurface *videoSurface() const; + bool init(QMediaService *service) Q_DECL_OVERRIDE; + void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &changeData) Q_DECL_OVERRIDE; + void releaseSource() Q_DECL_OVERRIDE; + void releaseControl() Q_DECL_OVERRIDE; + QSize nativeSize() const Q_DECL_OVERRIDE; + void updateGeometry() Q_DECL_OVERRIDE; + QSGNode *updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) Q_DECL_OVERRIDE; + QAbstractVideoSurface *videoSurface() const Q_DECL_OVERRIDE; QRectF adjustedViewport() const Q_DECL_OVERRIDE; QOpenGLContext *glContext() const; @@ -86,7 +86,7 @@ private: QOpenGLContext *m_glContext; QVideoFrame m_frame; bool m_frameChanged; - QSGVideoNodeFactory_I420 m_i420Factory; + QSGVideoNodeFactory_YUV m_i420Factory; QSGVideoNodeFactory_RGB m_rgbFactory; QSGVideoNodeFactory_Texture m_textureFactory; QMutex m_frameMutex; diff --git a/src/qtmultimediaquicktools/qdeclarativevideooutput_window_p.h b/src/qtmultimediaquicktools/qdeclarativevideooutput_window_p.h index 0cec2e20..446ce52c 100644 --- a/src/qtmultimediaquicktools/qdeclarativevideooutput_window_p.h +++ b/src/qtmultimediaquicktools/qdeclarativevideooutput_window_p.h @@ -46,14 +46,14 @@ public: QDeclarativeVideoWindowBackend(QDeclarativeVideoOutput *parent); ~QDeclarativeVideoWindowBackend(); - bool init(QMediaService *service); - void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &changeData); - void releaseSource(); - void releaseControl(); - QSize nativeSize() const; - void updateGeometry(); - QSGNode *updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data); - QAbstractVideoSurface *videoSurface() const; + bool init(QMediaService *service) Q_DECL_OVERRIDE; + void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &changeData) Q_DECL_OVERRIDE; + void releaseSource() Q_DECL_OVERRIDE; + void releaseControl() Q_DECL_OVERRIDE; + QSize nativeSize() const Q_DECL_OVERRIDE; + void updateGeometry() Q_DECL_OVERRIDE; + QSGNode *updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) Q_DECL_OVERRIDE; + QAbstractVideoSurface *videoSurface() const Q_DECL_OVERRIDE; QRectF adjustedViewport() const Q_DECL_OVERRIDE; private: diff --git a/src/qtmultimediaquicktools/qsgvideonode_i420.cpp b/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp similarity index 54% rename from src/qtmultimediaquicktools/qsgvideonode_i420.cpp rename to src/qtmultimediaquicktools/qsgvideonode_yuv.cpp index 7c9acd30..fc40d659 100644 --- a/src/qtmultimediaquicktools/qsgvideonode_i420.cpp +++ b/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp @@ -30,7 +30,7 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include "qsgvideonode_i420.h" +#include "qsgvideonode_yuv.h" #include #include #include @@ -40,21 +40,23 @@ QT_BEGIN_NAMESPACE -QList QSGVideoNodeFactory_I420::supportedPixelFormats( +QList QSGVideoNodeFactory_YUV::supportedPixelFormats( QAbstractVideoBuffer::HandleType handleType) const { QList formats; - if (handleType == QAbstractVideoBuffer::NoHandle) - formats << QVideoFrame::Format_YUV420P << QVideoFrame::Format_YV12; + if (handleType == QAbstractVideoBuffer::NoHandle) { + formats << QVideoFrame::Format_YUV420P << QVideoFrame::Format_YV12 + << QVideoFrame::Format_NV12 << QVideoFrame::Format_NV21; + } return formats; } -QSGVideoNode *QSGVideoNodeFactory_I420::createNode(const QVideoSurfaceFormat &format) +QSGVideoNode *QSGVideoNodeFactory_YUV::createNode(const QVideoSurfaceFormat &format) { if (supportedPixelFormats(format.handleType()).contains(format.pixelFormat())) - return new QSGVideoNode_I420(format); + return new QSGVideoNode_YUV(format); return 0; } @@ -136,12 +138,85 @@ protected: int m_id_opacity; }; - -class QSGVideoMaterial_YUV420 : public QSGMaterial +class QSGVideoMaterialShader_NV_12_21 : public QSGVideoMaterialShader_YUV420 { public: - QSGVideoMaterial_YUV420(const QVideoSurfaceFormat &format); - ~QSGVideoMaterial_YUV420(); + QSGVideoMaterialShader_NV_12_21(bool isNV21) : m_isNV21(isNV21) { + } + + virtual void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial); + +protected: + + virtual const char *vertexShader() const { + const char *shader = + "uniform highp mat4 qt_Matrix; \n" + "uniform highp float yWidth; \n" + "attribute highp vec4 qt_VertexPosition; \n" + "attribute highp vec2 qt_VertexTexCoord; \n" + "varying highp vec2 yTexCoord; \n" + "void main() { \n" + " yTexCoord = qt_VertexTexCoord * vec2(yWidth, 1);\n" + " gl_Position = qt_Matrix * qt_VertexPosition; \n" + "}"; + return shader; + } + + virtual const char *fragmentShader() const { + static const char *shaderNV12 = + "uniform sampler2D yTexture; \n" + "uniform sampler2D uvTexture; \n" + "uniform mediump mat4 colorMatrix; \n" + "uniform lowp float opacity; \n" + "varying highp vec2 yTexCoord; \n" + "void main() \n" + "{ \n" + " mediump float Y = texture2D(yTexture, yTexCoord).r; \n" + " mediump vec2 UV = texture2D(uvTexture, yTexCoord).ra; \n" + " mediump vec4 color = vec4(Y, UV.x, UV.y, 1.); \n" + " gl_FragColor = colorMatrix * color * opacity; \n" + "}"; + + static const char *shaderNV21 = + "uniform sampler2D yTexture; \n" + "uniform sampler2D uvTexture; \n" + "uniform mediump mat4 colorMatrix; \n" + "uniform lowp float opacity; \n" + "varying highp vec2 yTexCoord; \n" + "void main() \n" + "{ \n" + " mediump float Y = texture2D(yTexture, yTexCoord).r; \n" + " mediump vec2 UV = texture2D(uvTexture, yTexCoord).ar; \n" + " mediump vec4 color = vec4(Y, UV.x, UV.y, 1.); \n" + " gl_FragColor = colorMatrix * color * opacity; \n" + "}"; + return m_isNV21 ? shaderNV21 : shaderNV12; + } + + virtual void initialize() { + m_id_yTexture = program()->uniformLocation("yTexture"); + m_id_uTexture = program()->uniformLocation("uvTexture"); + m_id_matrix = program()->uniformLocation("qt_Matrix"); + m_id_yWidth = program()->uniformLocation("yWidth"); + m_id_colorMatrix = program()->uniformLocation("colorMatrix"); + m_id_opacity = program()->uniformLocation("opacity"); + } + +private: + bool m_isNV21; +}; + + +class QSGVideoMaterial_YUV : public QSGMaterial +{ +public: + QSGVideoMaterial_YUV(const QVideoSurfaceFormat &format); + ~QSGVideoMaterial_YUV(); + + bool isNV12_21() const { + const QVideoFrame::PixelFormat pf = m_format.pixelFormat(); + return pf == QVideoFrame::Format_NV12 || pf == QVideoFrame::Format_NV21; + } virtual QSGMaterialType *type() const { static QSGMaterialType theType; @@ -149,18 +224,25 @@ public: } virtual QSGMaterialShader *createShader() const { + const QVideoFrame::PixelFormat pf = m_format.pixelFormat(); + if (isNV12_21()) + return new QSGVideoMaterialShader_NV_12_21(pf == QVideoFrame::Format_NV21); + return new QSGVideoMaterialShader_YUV420; } virtual int compare(const QSGMaterial *other) const { - const QSGVideoMaterial_YUV420 *m = static_cast(other); + const QSGVideoMaterial_YUV *m = static_cast(other); int d = m_textureIds[0] - m->m_textureIds[0]; if (d) return d; - else if ((d = m_textureIds[1] - m->m_textureIds[1]) != 0) + + d = m_textureIds[1] - m->m_textureIds[1]; + + if (m_textureIds.size() == 2 || d != 0) return d; - else - return m_textureIds[2] - m->m_textureIds[2]; + + return m_textureIds[2] - m->m_textureIds[2]; } void updateBlending() { @@ -173,13 +255,12 @@ public: } void bind(); - void bindTexture(int id, int w, int h, const uchar *bits); + void bindTexture(int id, int w, int h, const uchar *bits, GLenum format); QVideoSurfaceFormat m_format; QSize m_textureSize; - static const uint Num_Texture_IDs = 3; - GLuint m_textureIds[Num_Texture_IDs]; + QVector m_textureIds; qreal m_opacity; GLfloat m_yWidth; @@ -190,13 +271,13 @@ public: QMutex m_frameMutex; }; -QSGVideoMaterial_YUV420::QSGVideoMaterial_YUV420(const QVideoSurfaceFormat &format) : +QSGVideoMaterial_YUV::QSGVideoMaterial_YUV(const QVideoSurfaceFormat &format) : m_format(format), m_opacity(1.0), m_yWidth(1.0), m_uvWidth(1.0) { - memset(m_textureIds, 0, sizeof(m_textureIds)); + m_textureIds.resize(isNV12_21() ? 2 : 3); switch (format.yCbCrColorSpace()) { case QVideoSurfaceFormat::YCbCr_JPEG: @@ -225,20 +306,19 @@ QSGVideoMaterial_YUV420::QSGVideoMaterial_YUV420(const QVideoSurfaceFormat &form setFlag(Blending, false); } -QSGVideoMaterial_YUV420::~QSGVideoMaterial_YUV420() +QSGVideoMaterial_YUV::~QSGVideoMaterial_YUV() { if (!m_textureSize.isEmpty()) { if (QOpenGLContext *current = QOpenGLContext::currentContext()) - current->functions()->glDeleteTextures(Num_Texture_IDs, m_textureIds); + current->functions()->glDeleteTextures(m_textureIds.size(), &m_textureIds[0]); else - qWarning() << "QSGVideoMaterial_YUV420: Cannot obtain GL context, unable to delete textures"; + qWarning() << "QSGVideoMaterial_YUV: Cannot obtain GL context, unable to delete textures"; } } -void QSGVideoMaterial_YUV420::bind() +void QSGVideoMaterial_YUV::bind() { QOpenGLFunctions *functions = QOpenGLContext::currentContext()->functions(); - QMutexLocker lock(&m_frameMutex); if (m_frame.isValid()) { if (m_frame.map(QAbstractVideoBuffer::ReadOnly)) { @@ -248,31 +328,43 @@ void QSGVideoMaterial_YUV420::bind() // Frame has changed size, recreate textures... if (m_textureSize != m_frame.size()) { if (!m_textureSize.isEmpty()) - functions->glDeleteTextures(Num_Texture_IDs, m_textureIds); - functions->glGenTextures(Num_Texture_IDs, m_textureIds); + functions->glDeleteTextures(m_textureIds.size(), &m_textureIds[0]); + functions->glGenTextures(m_textureIds.size(), &m_textureIds[0]); m_textureSize = m_frame.size(); } - const int y = 0; - const int u = m_frame.pixelFormat() == QVideoFrame::Format_YUV420P ? 1 : 2; - const int v = m_frame.pixelFormat() == QVideoFrame::Format_YUV420P ? 2 : 1; - - m_yWidth = qreal(fw) / m_frame.bytesPerLine(y); - m_uvWidth = qreal(fw) / (2 * m_frame.bytesPerLine(u)); - GLint previousAlignment; functions->glGetIntegerv(GL_UNPACK_ALIGNMENT, &previousAlignment); functions->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - functions->glActiveTexture(GL_TEXTURE1); - bindTexture(m_textureIds[1], m_frame.bytesPerLine(u), fh / 2, m_frame.bits(u)); - functions->glActiveTexture(GL_TEXTURE2); - bindTexture(m_textureIds[2], m_frame.bytesPerLine(v), fh / 2, m_frame.bits(v)); - functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit - bindTexture(m_textureIds[0], m_frame.bytesPerLine(y), fh, m_frame.bits(y)); + if (isNV12_21()) { + const int y = 0; + const int uv = 1; + + m_yWidth = qreal(fw) / m_frame.bytesPerLine(y); + m_uvWidth = m_yWidth; + + functions->glActiveTexture(GL_TEXTURE1); + bindTexture(m_textureIds[1], m_frame.bytesPerLine(uv) / 2, fh / 2, m_frame.bits(uv), GL_LUMINANCE_ALPHA); + functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit + bindTexture(m_textureIds[0], m_frame.bytesPerLine(y), fh, m_frame.bits(y), GL_LUMINANCE); + } else { + const int y = 0; + const int u = m_frame.pixelFormat() == QVideoFrame::Format_YUV420P ? 1 : 2; + const int v = m_frame.pixelFormat() == QVideoFrame::Format_YUV420P ? 2 : 1; + + m_yWidth = qreal(fw) / m_frame.bytesPerLine(y); + m_uvWidth = qreal(fw) / (2 * m_frame.bytesPerLine(u)); + + functions->glActiveTexture(GL_TEXTURE1); + bindTexture(m_textureIds[1], m_frame.bytesPerLine(u), fh / 2, m_frame.bits(u), GL_LUMINANCE); + functions->glActiveTexture(GL_TEXTURE2); + bindTexture(m_textureIds[2], m_frame.bytesPerLine(v), fh / 2, m_frame.bits(v), GL_LUMINANCE); + functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit + bindTexture(m_textureIds[0], m_frame.bytesPerLine(y), fh, m_frame.bits(y), GL_LUMINANCE); + } functions->glPixelStorei(GL_UNPACK_ALIGNMENT, previousAlignment); - m_frame.unmap(); } @@ -280,51 +372,52 @@ void QSGVideoMaterial_YUV420::bind() } else { functions->glActiveTexture(GL_TEXTURE1); functions->glBindTexture(GL_TEXTURE_2D, m_textureIds[1]); - functions->glActiveTexture(GL_TEXTURE2); - functions->glBindTexture(GL_TEXTURE_2D, m_textureIds[2]); + if (!isNV12_21()) { + functions->glActiveTexture(GL_TEXTURE2); + functions->glBindTexture(GL_TEXTURE_2D, m_textureIds[2]); + } functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit functions->glBindTexture(GL_TEXTURE_2D, m_textureIds[0]); } } -void QSGVideoMaterial_YUV420::bindTexture(int id, int w, int h, const uchar *bits) +void QSGVideoMaterial_YUV::bindTexture(int id, int w, int h, const uchar *bits, GLenum format) { QOpenGLFunctions *functions = QOpenGLContext::currentContext()->functions(); functions->glBindTexture(GL_TEXTURE_2D, id); - functions->glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, bits); + functions->glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, format, GL_UNSIGNED_BYTE, bits); functions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); functions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); functions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); functions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } -QSGVideoNode_I420::QSGVideoNode_I420(const QVideoSurfaceFormat &format) : +QSGVideoNode_YUV::QSGVideoNode_YUV(const QVideoSurfaceFormat &format) : m_format(format) { setFlag(QSGNode::OwnsMaterial); - m_material = new QSGVideoMaterial_YUV420(format); + m_material = new QSGVideoMaterial_YUV(format); setMaterial(m_material); } -QSGVideoNode_I420::~QSGVideoNode_I420() +QSGVideoNode_YUV::~QSGVideoNode_YUV() { } -void QSGVideoNode_I420::setCurrentFrame(const QVideoFrame &frame, FrameFlags) +void QSGVideoNode_YUV::setCurrentFrame(const QVideoFrame &frame, FrameFlags) { m_material->setCurrentFrame(frame); markDirty(DirtyMaterial); } - void QSGVideoMaterialShader_YUV420::updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) { Q_UNUSED(oldMaterial); - QSGVideoMaterial_YUV420 *mat = static_cast(newMaterial); + QSGVideoMaterial_YUV *mat = static_cast(newMaterial); program()->setUniformValue(m_id_yTexture, 0); program()->setUniformValue(m_id_uTexture, 1); program()->setUniformValue(m_id_vTexture, 2); @@ -342,4 +435,26 @@ void QSGVideoMaterialShader_YUV420::updateState(const RenderState &state, program()->setUniformValue(m_id_matrix, state.combinedMatrix()); } +void QSGVideoMaterialShader_NV_12_21::updateState(const RenderState &state, + QSGMaterial *newMaterial, + QSGMaterial *oldMaterial) +{ + Q_UNUSED(oldMaterial); + + QSGVideoMaterial_YUV *mat = static_cast(newMaterial); + program()->setUniformValue(m_id_yTexture, 0); + program()->setUniformValue(m_id_uTexture, 1); + + mat->bind(); + + program()->setUniformValue(m_id_colorMatrix, mat->m_colorMatrix); + program()->setUniformValue(m_id_yWidth, mat->m_yWidth); + if (state.isOpacityDirty()) { + mat->m_opacity = state.opacity(); + program()->setUniformValue(m_id_opacity, GLfloat(mat->m_opacity)); + } + if (state.isMatrixDirty()) + program()->setUniformValue(m_id_matrix, state.combinedMatrix()); +} + QT_END_NAMESPACE diff --git a/src/qtmultimediaquicktools/qsgvideonode_i420.h b/src/qtmultimediaquicktools/qsgvideonode_yuv.h similarity index 86% rename from src/qtmultimediaquicktools/qsgvideonode_i420.h rename to src/qtmultimediaquicktools/qsgvideonode_yuv.h index dd97a9ab..776f0a5a 100644 --- a/src/qtmultimediaquicktools/qsgvideonode_i420.h +++ b/src/qtmultimediaquicktools/qsgvideonode_yuv.h @@ -31,20 +31,20 @@ ** ****************************************************************************/ -#ifndef QSGVIDEONODE_I420_H -#define QSGVIDEONODE_I420_H +#ifndef QSGVIDEONODE_YUV_H +#define QSGVIDEONODE_YUV_H #include #include QT_BEGIN_NAMESPACE -class QSGVideoMaterial_YUV420; -class QSGVideoNode_I420 : public QSGVideoNode +class QSGVideoMaterial_YUV; +class QSGVideoNode_YUV : public QSGVideoNode { public: - QSGVideoNode_I420(const QVideoSurfaceFormat &format); - ~QSGVideoNode_I420(); + QSGVideoNode_YUV(const QVideoSurfaceFormat &format); + ~QSGVideoNode_YUV(); virtual QVideoFrame::PixelFormat pixelFormat() const { return m_format.pixelFormat(); @@ -58,10 +58,10 @@ private: void bindTexture(int id, int unit, int w, int h, const uchar *bits); QVideoSurfaceFormat m_format; - QSGVideoMaterial_YUV420 *m_material; + QSGVideoMaterial_YUV *m_material; }; -class QSGVideoNodeFactory_I420 : public QSGVideoNodeFactoryInterface { +class QSGVideoNodeFactory_YUV : public QSGVideoNodeFactoryInterface { public: QList supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const; QSGVideoNode *createNode(const QVideoSurfaceFormat &format); @@ -69,4 +69,4 @@ public: QT_END_NAMESPACE -#endif // QSGVIDEONODE_I420_H +#endif // QSGVIDEONODE_YUV_H diff --git a/src/qtmultimediaquicktools/qtmultimediaquicktools.pro b/src/qtmultimediaquicktools/qtmultimediaquicktools.pro index 6fd38be8..917a5ac2 100644 --- a/src/qtmultimediaquicktools/qtmultimediaquicktools.pro +++ b/src/qtmultimediaquicktools/qtmultimediaquicktools.pro @@ -21,7 +21,7 @@ SOURCES += \ qdeclarativevideooutput.cpp \ qdeclarativevideooutput_render.cpp \ qdeclarativevideooutput_window.cpp \ - qsgvideonode_i420.cpp \ + qsgvideonode_yuv.cpp \ qsgvideonode_rgb.cpp \ qsgvideonode_texture.cpp @@ -29,6 +29,6 @@ HEADERS += \ $$PRIVATE_HEADERS \ qdeclarativevideooutput_render_p.h \ qdeclarativevideooutput_window_p.h \ - qsgvideonode_i420.h \ + qsgvideonode_yuv.h \ qsgvideonode_rgb.h \ qsgvideonode_texture.h diff --git a/tests/auto/unit/qmediaplayer/qmediaplayer.pro b/tests/auto/unit/qmediaplayer/qmediaplayer.pro index 52568c07..cbdbf71f 100644 --- a/tests/auto/unit/qmediaplayer/qmediaplayer.pro +++ b/tests/auto/unit/qmediaplayer/qmediaplayer.pro @@ -2,6 +2,7 @@ CONFIG += testcase no_private_qt_headers_warning TARGET = tst_qmediaplayer QT += network multimedia-private testlib SOURCES += tst_qmediaplayer.cpp +RESOURCES += testdata.qrc include (../qmultimedia_common/mock.pri) include (../qmultimedia_common/mockplayer.pri) diff --git a/tests/auto/unit/qmediaplayer/testdata.qrc b/tests/auto/unit/qmediaplayer/testdata.qrc new file mode 100644 index 00000000..1afc630d --- /dev/null +++ b/tests/auto/unit/qmediaplayer/testdata.qrc @@ -0,0 +1,5 @@ + + + testdata/nokia-tune.mp3 + + diff --git a/tests/auto/unit/qmediaplayer/testdata/nokia-tune.mp3 b/tests/auto/unit/qmediaplayer/testdata/nokia-tune.mp3 new file mode 100644 index 00000000..2435f65b Binary files /dev/null and b/tests/auto/unit/qmediaplayer/testdata/nokia-tune.mp3 differ diff --git a/tests/auto/unit/qmediaplayer/tst_qmediaplayer.cpp b/tests/auto/unit/qmediaplayer/tst_qmediaplayer.cpp index 2082fe14..0271f1a8 100644 --- a/tests/auto/unit/qmediaplayer/tst_qmediaplayer.cpp +++ b/tests/auto/unit/qmediaplayer/tst_qmediaplayer.cpp @@ -133,6 +133,8 @@ private slots: void testPlayerFlags(); void testDestructor(); void testSupportedMimeTypes(); + void testQrc_data(); + void testQrc(); private: void setupCommonTestData(); @@ -976,12 +978,17 @@ void tst_QMediaPlayer::testPlaylist() player->setPlaylist(playlist); player->play(); QCOMPARE(ss.count(), 1); + QCOMPARE(ms.count(), 1); + QCOMPARE(qvariant_cast(ms.last().value(0)), QMediaPlayer::LoadingMedia); + ms.clear(); mockService->setState(QMediaPlayer::StoppedState, QMediaPlayer::InvalidMedia); QCOMPARE(player->state(), QMediaPlayer::PlayingState); - QCOMPARE(player->mediaStatus(), QMediaPlayer::InvalidMedia); + QCOMPARE(player->mediaStatus(), QMediaPlayer::LoadingMedia); QCOMPARE(ss.count(), 1); - QCOMPARE(ms.count(), 1); + QCOMPARE(ms.count(), 2); + QCOMPARE(qvariant_cast(ms.at(0).value(0)), QMediaPlayer::InvalidMedia); + QCOMPARE(qvariant_cast(ms.at(1).value(0)), QMediaPlayer::LoadingMedia); // NOTE: status should begin transitioning through to BufferedMedia. QCOMPARE(player->currentMedia(), content1); @@ -1210,5 +1217,84 @@ void tst_QMediaPlayer::testSupportedMimeTypes() // This is empty on some platforms, and not on others, so can't test something here at the moment. } +void tst_QMediaPlayer::testQrc_data() +{ + QTest::addColumn("mediaContent"); + QTest::addColumn("status"); + QTest::addColumn("error"); + QTest::addColumn("errorCount"); + QTest::addColumn("hasStreamFeature"); + QTest::addColumn("backendMediaContentScheme"); + QTest::addColumn("backendHasStream"); + + QTest::newRow("invalid") << QMediaContent(QUrl(QLatin1String("qrc:/invalid.mp3"))) + << QMediaPlayer::InvalidMedia + << QMediaPlayer::ResourceError + << 1 // error count + << false // No StreamPlayback support + << QString() // backend should not have got any media (empty URL scheme) + << false; // backend should not have got any stream + + QTest::newRow("valid+nostream") << QMediaContent(QUrl(QLatin1String("qrc:/testdata/nokia-tune.mp3"))) + << QMediaPlayer::LoadingMedia + << QMediaPlayer::NoError + << 0 // error count + << false // No StreamPlayback support + << QStringLiteral("file") // backend should have a got a temporary file + << false; // backend should not have got any stream + + QTest::newRow("valid+stream") << QMediaContent(QUrl(QLatin1String("qrc:/testdata/nokia-tune.mp3"))) + << QMediaPlayer::LoadingMedia + << QMediaPlayer::NoError + << 0 // error count + << true // StreamPlayback support + << QStringLiteral("qrc") + << true; // backend should have got a stream (QFile opened from the resource) +} + +void tst_QMediaPlayer::testQrc() +{ + QFETCH(QMediaContent, mediaContent); + QFETCH(QMediaPlayer::MediaStatus, status); + QFETCH(QMediaPlayer::Error, error); + QFETCH(int, errorCount); + QFETCH(bool, hasStreamFeature); + QFETCH(QString, backendMediaContentScheme); + QFETCH(bool, backendHasStream); + + if (hasStreamFeature) + mockProvider->setSupportedFeatures(QMediaServiceProviderHint::StreamPlayback); + + QMediaPlayer player; + + mockService->setState(QMediaPlayer::PlayingState, QMediaPlayer::NoMedia); + + QSignalSpy mediaSpy(&player, SIGNAL(currentMediaChanged(QMediaContent))); + QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); + QSignalSpy errorSpy(&player, SIGNAL(error(QMediaPlayer::Error))); + + player.setMedia(mediaContent); + + QTRY_COMPARE(player.mediaStatus(), status); + QCOMPARE(statusSpy.count(), 1); + QCOMPARE(qvariant_cast(statusSpy.last().value(0)), status); + + QCOMPARE(player.media(), mediaContent); + QCOMPARE(player.currentMedia(), mediaContent); + QCOMPARE(mediaSpy.count(), 1); + QCOMPARE(qvariant_cast(mediaSpy.last().value(0)), mediaContent); + + QCOMPARE(player.error(), error); + QCOMPARE(errorSpy.count(), errorCount); + if (errorCount > 0) { + QCOMPARE(qvariant_cast(errorSpy.last().value(0)), error); + QVERIFY(!player.errorString().isEmpty()); + } + + // Check the media actually passed to the backend + QCOMPARE(mockService->mockControl->media().canonicalUrl().scheme(), backendMediaContentScheme); + QCOMPARE(bool(mockService->mockControl->mediaStream()), backendHasStream); +} + QTEST_GUILESS_MAIN(tst_QMediaPlayer) #include "tst_qmediaplayer.moc" diff --git a/tests/auto/unit/qmultimedia_common/mockmediaplayercontrol.h b/tests/auto/unit/qmultimedia_common/mockmediaplayercontrol.h index 5b313fb9..5127498b 100644 --- a/tests/auto/unit/qmultimedia_common/mockmediaplayercontrol.h +++ b/tests/auto/unit/qmultimedia_common/mockmediaplayercontrol.h @@ -91,11 +91,10 @@ public: { _stream = stream; _media = content; - if (_state != QMediaPlayer::StoppedState) { - _mediaStatus = _media.isNull() ? QMediaPlayer::NoMedia : QMediaPlayer::LoadingMedia; + _mediaStatus = _media.isNull() ? QMediaPlayer::NoMedia : QMediaPlayer::LoadingMedia; + if (_state != QMediaPlayer::StoppedState) emit stateChanged(_state = QMediaPlayer::StoppedState); - emit mediaStatusChanged(_mediaStatus); - } + emit mediaStatusChanged(_mediaStatus); emit mediaChanged(_media = content); } QIODevice *mediaStream() const { return _stream; } diff --git a/tests/auto/unit/qmultimedia_common/mockmediaserviceprovider.h b/tests/auto/unit/qmultimedia_common/mockmediaserviceprovider.h index 60ea672c..89820c55 100644 --- a/tests/auto/unit/qmultimedia_common/mockmediaserviceprovider.h +++ b/tests/auto/unit/qmultimedia_common/mockmediaserviceprovider.h @@ -61,6 +61,16 @@ public: } } + QMediaServiceProviderHint::Features supportedFeatures(const QMediaService *) const + { + return features; + } + + void setSupportedFeatures(QMediaServiceProviderHint::Features f) + { + features = f; + } + QByteArray defaultDevice(const QByteArray &serviceType) const { if (serviceType == Q_MEDIASERVICE_CAMERA) @@ -97,6 +107,7 @@ public: QMediaService *service; bool deleteServiceOnRelease; + QMediaServiceProviderHint::Features features; }; #endif // MOCKMEDIASERVICEPROVIDER_H