From 11432c0e77727a88a9456b421c867928898b3663 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Tue, 13 Aug 2013 15:16:54 +0200 Subject: [PATCH 1/8] Enable QSoundEffect with loopCount of Infinite to play Previously if the loopCount property of a QSoundEffect was set to QSoundEffect::Infinite then no sound would be played at all. This is because QSoundEffect::Infinite == -2 and playback was only continued on values above 0. Task-number: QTBUG-32882 Change-Id: I739919a3e538128fc16f26ede5eb6cc4f2eb29fb Reviewed-by: Christian Stromme --- src/multimedia/audio/qsoundeffect_qaudio_p.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/multimedia/audio/qsoundeffect_qaudio_p.cpp b/src/multimedia/audio/qsoundeffect_qaudio_p.cpp index 835f60b4..524c856a 100644 --- a/src/multimedia/audio/qsoundeffect_qaudio_p.cpp +++ b/src/multimedia/audio/qsoundeffect_qaudio_p.cpp @@ -369,7 +369,7 @@ void PrivateSoundSource::stateChanged(QAudio::State state) qint64 PrivateSoundSource::readData( char* data, qint64 len) { - if (m_runningCount > 0 && m_playing) { + if ((m_runningCount > 0 || m_runningCount == QSoundEffect::Infinite) && m_playing) { if (m_sample->state() != QSample::Ready) return 0; From bdf0cc7a168e88c5692831ada7f209ce3b41a501 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Wed, 31 Jul 2013 12:52:17 +0200 Subject: [PATCH 2/8] WMF: use qFabs instead of fabsf. Task-number: QTBUG-32360 Change-Id: Ibec3d044ac38f54abd895d56f1851011bf6b5272 Reviewed-by: Christian Stromme --- src/plugins/wmf/evrcustompresenter.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/wmf/evrcustompresenter.cpp b/src/plugins/wmf/evrcustompresenter.cpp index eb73e672..70acbddb 100644 --- a/src/plugins/wmf/evrcustompresenter.cpp +++ b/src/plugins/wmf/evrcustompresenter.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -325,7 +326,7 @@ HRESULT Scheduler::processSample(IMFSample *sample, LONG *pNextSleep) // Adjust the sleep time for the clock rate. (The presentation clock runs // at m_fRate, but sleeping uses the system clock.) if (m_playbackRate != 0) - nextSleep = (LONG)(nextSleep / fabsf(m_playbackRate)); + nextSleep = (LONG)(nextSleep / qFabs(m_playbackRate)); // Don't present yet. presentNow = false; @@ -987,7 +988,7 @@ HRESULT EVRCustomPresenter::IsRateSupported(BOOL thin, float rate, float *neares // Note: We have no minimum rate (that is, we support anything down to 0). maxRate = getMaxRate(thin); - if (fabsf(rate) > maxRate) { + if (qFabs(rate) > maxRate) { // The (absolute) requested rate exceeds the maximum rate. hr = MF_E_UNSUPPORTED_RATE; From 291f1229feff087ac9a850ec7a92152f1b69af58 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Tue, 30 Jul 2013 14:24:48 +0200 Subject: [PATCH 3/8] WMF: fixed QMediaPlayer changing to EndOfMedia status too early. It was changing to EndOfMedia status and explicitly stopping playback when receiving the MEEndOfPresentation event from the WMF session. However, this event means that all data has bean read from the source but not necessarily played yet. According to the documentation, playback is done when the MESessionEnded event is sent. It now reports the EndOfMedia status at that moment instead. stop() is not explicitly called anymore since MESessionEnded also implies the session has stopped. Task-number: QTBUG-30825 Change-Id: I6c6c09e736fe33f7cf17c75038ea7be1b5701a1c Reviewed-by: Christian Stromme --- src/plugins/wmf/player/mfplayersession.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/plugins/wmf/player/mfplayersession.cpp b/src/plugins/wmf/player/mfplayersession.cpp index fb150c3e..4e4b5658 100644 --- a/src/plugins/wmf/player/mfplayersession.cpp +++ b/src/plugins/wmf/player/mfplayersession.cpp @@ -1918,19 +1918,17 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent) changeStatus(QMediaPlayer::BufferedMedia); emit bufferStatusChanged(bufferStatus()); break; - case MEEndOfPresentation: - stop(); - changeStatus(QMediaPlayer::EndOfMedia); - m_varStart.vt = VT_I8; - //keep reporting the final position after end of media - m_varStart.hVal.QuadPart = m_duration; - break; case MESessionEnded: m_pendingState = NoPending; m_state.command = CmdStop; m_state.prevCmd = CmdNone; m_request.command = CmdNone; m_request.prevCmd = CmdNone; + + changeStatus(QMediaPlayer::EndOfMedia); + m_varStart.vt = VT_I8; + //keep reporting the final position after end of media + m_varStart.hVal.QuadPart = m_duration; break; case MEEndOfPresentationSegment: break; @@ -1993,6 +1991,8 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent) } } break; + default: + break; } sessionEvent->Release(); From df4336a84426c84e692df86172cc0bf381261830 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Fri, 16 Aug 2013 18:13:56 +0200 Subject: [PATCH 4/8] Add changes-5.1.1 file. Task-number: QTBUG-32808 Change-Id: I998548df399ec6d2dd6b061b5ba2c1ca451276bc Reviewed-by: Sergio Ahumada --- dist/changes-5.1.1 | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 dist/changes-5.1.1 diff --git a/dist/changes-5.1.1 b/dist/changes-5.1.1 new file mode 100644 index 00000000..45fc4ded --- /dev/null +++ b/dist/changes-5.1.1 @@ -0,0 +1,45 @@ +Qt 5.1.1 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.1.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + + http://qt-project.org/doc/qt-5.1/ + +The Qt version 5.1 series is binary compatible with the 5.0.x series. +Applications compiled for 5.0 will continue to run with 5.1. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + http://bugreports.qt-project.org/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Platform Specific Changes * +**************************************************************************** + +Qt for Windows +-------------- + + - Use correct default audio output and input devices on Windows. + - [QTBUG-29206] DirectShow: avoid unnecessary RGB32 -> BGR32 conversion. + - [QTBUG-32282] DirectShow: Don't create the widget and renderer controls + until requested. + - [QTBUG-23822] Fix resource leak in directshow plugin. + - WMF: fixed MediaPlayer buffering logic. + +Qt for Android +-------------- + + - Fixed potential memory leak when destroying a media player. + - Fixed media player showing black frames on some hardware. + - [QTBUG-31422] Make it possible for the media player to play assets. + - Fixed Java exception being thrown at app startup when using multimedia. + +Qt for BlackBerry +----------------- + + - [QTBUG-31534] Fix frame size of video playback. From c86d14a380118bc530547c69ba982d2c5e04a992 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 31 Jul 2013 11:15:25 +0200 Subject: [PATCH 5/8] Fix file browser in qmlvideofx example. Similar to de9092389f2e43370c2cfcd6759d08cc11da9a68 for qmlvideo. Use QUrl::fromLocalFile() to get Windows drive handling right. Emulate its behavior in QML code. Fix up() to terminate correctly. Task-number: QTBUG-32139 Change-Id: I36bafaa608ff054190dc76694f6254a74f3b513e Reviewed-by: Yoann Lopes --- examples/multimedia/video/qmlvideofx/main.cpp | 14 +++----------- .../qmlvideofx/qml/qmlvideofx/FileBrowser.qml | 9 +++++++-- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/examples/multimedia/video/qmlvideofx/main.cpp b/examples/multimedia/video/qmlvideofx/main.cpp index b0698e23..7465deab 100644 --- a/examples/multimedia/video/qmlvideofx/main.cpp +++ b/examples/multimedia/video/qmlvideofx/main.cpp @@ -116,21 +116,13 @@ int main(int argc, char *argv[]) FileReader fileReader; viewer.rootContext()->setContextProperty("fileReader", &fileReader); - QUrl appPath(QString("file://%1").arg(app.applicationDirPath())); - QUrl imagePath; + const QUrl appPath(QUrl::fromLocalFile(app.applicationDirPath())); const QStringList picturesLocation = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); - if (picturesLocation.isEmpty()) - imagePath = appPath.resolved(QUrl("images")); - else - imagePath = QString("file://%1").arg(picturesLocation.first()); + const QUrl imagePath = picturesLocation.isEmpty() ? appPath : QUrl::fromLocalFile(picturesLocation.first()); viewer.rootContext()->setContextProperty("imagePath", imagePath); - QUrl videoPath; const QStringList moviesLocation = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation); - if (moviesLocation.isEmpty()) - videoPath = appPath.resolved(QUrl("./")); - else - videoPath = QString("file://%1").arg(moviesLocation.first()); + const QUrl videoPath = moviesLocation.isEmpty() ? appPath : QUrl::fromLocalFile(moviesLocation.first()); viewer.rootContext()->setContextProperty("videoPath", videoPath); viewer.setTitle("qmlvideofx"); diff --git a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/FileBrowser.qml b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/FileBrowser.qml index 3d4343c2..7c861036 100644 --- a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/FileBrowser.qml +++ b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/FileBrowser.qml @@ -102,7 +102,10 @@ Rectangle { Rectangle { id: wrapper function launch() { - var path = "file://" + filePath + var path = "file://"; + if (filePath.length > 2 && filePath[1] === ':') // Windows drive logic, see QUrl::fromLocalFile() + path += '/'; + path += filePath; if (folders.isFolder(index)) down(path); else @@ -307,7 +310,7 @@ Rectangle { MouseArea { id: upRegion; anchors.centerIn: parent width: 56 height: 56 - onClicked: if (folders.parentFolder != "") up() + onClicked: up() } states: [ State { @@ -353,6 +356,8 @@ Rectangle { function up() { var path = folders.parentFolder; + if (path.toString().length === 0 || path.toString() === 'file:') + return; if (folders == folders1) { view = view2 folders = folders2; From 4585518d523ec5fcc2c0e35f472926d060ca7871 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Mon, 29 Jul 2013 16:28:15 +0200 Subject: [PATCH 6/8] Android: fixed media player buffering logic. When the media is ready, the status should always transition to LoadedMedia and then immediately to BufferingMedia or BufferedMedia. Also, when the duration is queried before the media is ready but already buffering, it should always return 0 to avoid errors from the Android media player. Task-number: QTBUG-32635 Change-Id: Ibcb9c23b4f64c4f9a1a8e0ef81989ae78cfb19ef Reviewed-by: Christian Stromme --- .../android/multimedia/QtAndroidMediaPlayer.java | 2 +- .../mediaplayer/qandroidmediaplayercontrol.cpp | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer.java b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer.java index d1abf658..2ca07a63 100644 --- a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer.java +++ b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlayer.java @@ -191,8 +191,8 @@ public class QtAndroidMediaPlayer extends MediaPlayer @Override public void onPrepared(final MediaPlayer mp) { - onMediaPlayerInfoNative(MEDIA_PLAYER_DURATION, getDuration(), mID); onMediaPlayerInfoNative(MEDIA_PLAYER_READY, 0, mID); + onMediaPlayerInfoNative(MEDIA_PLAYER_DURATION, getDuration(), mID); mPreparing = false; } diff --git a/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.cpp index a70f4e13..4dc56ebd 100644 --- a/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.cpp +++ b/src/plugins/android/mediaplayer/qandroidmediaplayercontrol.cpp @@ -98,8 +98,9 @@ QMediaPlayer::MediaStatus QAndroidMediaPlayerControl::mediaStatus() const qint64 QAndroidMediaPlayerControl::duration() const { return (mCurrentMediaStatus == QMediaPlayer::InvalidMedia - || mCurrentMediaStatus == QMediaPlayer::NoMedia) ? 0 - : mMediaPlayer->getDuration(); + || mCurrentMediaStatus == QMediaPlayer::NoMedia + || !mMediaPlayerReady) ? 0 + : mMediaPlayer->getDuration(); } qint64 QAndroidMediaPlayerControl::position() const @@ -330,14 +331,12 @@ void QAndroidMediaPlayerControl::onMediaPlayerInfo(qint32 what, qint32 extra) setState(QMediaPlayer::StoppedState); break; case JMediaPlayer::MEDIA_PLAYER_READY: + setMediaStatus(QMediaPlayer::LoadedMedia); if (mBuffering) { setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia : QMediaPlayer::BufferingMedia); } else { - setMediaStatus(QMediaPlayer::LoadedMedia); - mBufferPercent = 100; - Q_EMIT bufferStatusChanged(mBufferPercent); - updateAvailablePlaybackRanges(); + onBufferChanged(100); } setAudioAvailable(true); mMediaPlayerReady = true; @@ -402,7 +401,7 @@ void QAndroidMediaPlayerControl::onError(qint32 what, qint32 extra) void QAndroidMediaPlayerControl::onBufferChanged(qint32 percent) { - mBuffering = true; + mBuffering = percent != 100; mBufferPercent = percent; Q_EMIT bufferStatusChanged(mBufferPercent); From ca769ba264f868ea2f41f2656f7126218b428ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Str=C3=B8mme?= Date: Fri, 16 Aug 2013 17:50:10 +0200 Subject: [PATCH 7/8] Android: Use isValid() to check if the jobject is valid. Change-Id: I5ec67b9b2abfae2e2c2a44f0bcc7c72cb54beb49 Reviewed-by: Yoann Lopes --- src/plugins/android/wrappers/jsurfacetexture.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/android/wrappers/jsurfacetexture.cpp b/src/plugins/android/wrappers/jsurfacetexture.cpp index 107f7be3..34edf1b6 100644 --- a/src/plugins/android/wrappers/jsurfacetexture.cpp +++ b/src/plugins/android/wrappers/jsurfacetexture.cpp @@ -60,13 +60,13 @@ JSurfaceTexture::JSurfaceTexture(unsigned int texName) , QJNIObject(g_qtSurfaceTextureClass, "(I)V", jint(texName)) , m_texID(int(texName)) { - if (m_jobject) + if (isValid()) g_objectMap.insert(int(texName), this); } JSurfaceTexture::~JSurfaceTexture() { - if (m_jobject) + if (isValid()) g_objectMap.remove(m_texID); } From a2f078f1088827ec2bc066aaee7ca3199c6cb4eb Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Fri, 16 Aug 2013 17:51:53 +0200 Subject: [PATCH 8/8] WMF and GStreamer: fixed incorrect frame startTime and endTime. The QVideoFrame documentation explicitly says that the time is in microseconds, however the GStreamer backend was setting the time in milliseconds and the WMF backend in 100-nanosecond units. With WMF, the time was missing from the QVideoFrame when presenting it to the video surface. Task-number: QTBUG-31731 Change-Id: I0638d2abf8eed25b3a531db67c19a18703e5b630 Reviewed-by: Andy Nichols --- src/gsttools/qvideosurfacegstsink.cpp | 5 ++-- src/plugins/wmf/evrd3dpresentengine.cpp | 18 +++++++++++--- src/plugins/wmf/mftvideo.cpp | 5 ++-- .../wmf/player/mfvideorenderercontrol.cpp | 24 +++++++++++++++---- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/gsttools/qvideosurfacegstsink.cpp b/src/gsttools/qvideosurfacegstsink.cpp index 94aa1262..f91c1192 100644 --- a/src/gsttools/qvideosurfacegstsink.cpp +++ b/src/gsttools/qvideosurfacegstsink.cpp @@ -713,13 +713,14 @@ QVideoSurfaceFormat QVideoSurfaceGstSink::formatForCaps(GstCaps *caps, int *byte void QVideoSurfaceGstSink::setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer) { + // GStreamer uses nanoseconds, Qt uses microseconds qint64 startTime = GST_BUFFER_TIMESTAMP(buffer); if (startTime >= 0) { - frame->setStartTime(startTime/G_GINT64_CONSTANT (1000000)); + frame->setStartTime(startTime/G_GINT64_CONSTANT (1000)); qint64 duration = GST_BUFFER_DURATION(buffer); if (duration >= 0) - frame->setEndTime((startTime + duration)/G_GINT64_CONSTANT (1000000)); + frame->setEndTime((startTime + duration)/G_GINT64_CONSTANT (1000)); } } diff --git a/src/plugins/wmf/evrd3dpresentengine.cpp b/src/plugins/wmf/evrd3dpresentengine.cpp index c67b5d44..01a5c334 100644 --- a/src/plugins/wmf/evrd3dpresentengine.cpp +++ b/src/plugins/wmf/evrd3dpresentengine.cpp @@ -288,9 +288,21 @@ void D3DPresentEngine::presentSample(void *opaque, qint64) } if (surface && updateTexture(surface)) { - m_surface->present(QVideoFrame(new TextureVideoBuffer(m_glTexture), - m_surfaceFormat.frameSize(), - m_surfaceFormat.pixelFormat())); + QVideoFrame frame = QVideoFrame(new TextureVideoBuffer(m_glTexture), + m_surfaceFormat.frameSize(), + m_surfaceFormat.pixelFormat()); + + // WMF uses 100-nanosecond units, Qt uses microseconds + LONGLONG startTime = -1; + if (SUCCEEDED(sample->GetSampleTime(&startTime))) { + frame.setStartTime(startTime * 0.1); + + LONGLONG duration = -1; + if (SUCCEEDED(sample->GetSampleDuration(&duration))) + frame.setEndTime((startTime + duration) * 0.1); + } + + m_surface->present(frame); } done: diff --git a/src/plugins/wmf/mftvideo.cpp b/src/plugins/wmf/mftvideo.cpp index acec88d6..8e7ce069 100644 --- a/src/plugins/wmf/mftvideo.cpp +++ b/src/plugins/wmf/mftvideo.cpp @@ -632,13 +632,14 @@ QVideoFrame MFTransform::makeVideoFrame() // That is why we copy data from IMFMediaBuffer here. frame = QVideoFrame(new QMemoryVideoBuffer(array, m_bytesPerLine), m_format.frameSize(), m_format.pixelFormat()); + // WMF uses 100-nanosecond units, Qt uses microseconds LONGLONG startTime = -1; if (SUCCEEDED(m_sample->GetSampleTime(&startTime))) { - frame.setStartTime(startTime); + frame.setStartTime(startTime * 0.1); LONGLONG duration = -1; if (SUCCEEDED(m_sample->GetSampleDuration(&duration))) - frame.setEndTime(startTime + duration); + frame.setEndTime((startTime + duration) * 0.1); } } while (false); diff --git a/src/plugins/wmf/player/mfvideorenderercontrol.cpp b/src/plugins/wmf/player/mfvideorenderercontrol.cpp index 8f73244c..83768c8e 100644 --- a/src/plugins/wmf/player/mfvideorenderercontrol.cpp +++ b/src/plugins/wmf/player/mfvideorenderercontrol.cpp @@ -254,6 +254,8 @@ namespace , m_workQueueCB(this, &MediaStream::onDispatchWorkItem) , m_finalizeResult(0) , m_scheduledBuffer(0) + , m_bufferStartTime(-1) + , m_bufferDuration(-1) , m_presentationClock(0) , m_currentMediaType(0) , m_prerolling(false) @@ -839,10 +841,13 @@ namespace QMutexLocker locker(&m_mutex); if (!m_scheduledBuffer) return; - m_surface->present(QVideoFrame( - new MediaSampleVideoBuffer(m_scheduledBuffer, m_bytesPerLine), - m_surfaceFormat.frameSize(), - m_surfaceFormat.pixelFormat())); + QVideoFrame frame = QVideoFrame( + new MediaSampleVideoBuffer(m_scheduledBuffer, m_bytesPerLine), + m_surfaceFormat.frameSize(), + m_surfaceFormat.pixelFormat()); + frame.setStartTime(m_bufferStartTime * 0.1); + frame.setEndTime((m_bufferStartTime + m_bufferDuration) * 0.1); + m_surface->present(frame); m_scheduledBuffer->Release(); m_scheduledBuffer = NULL; if (m_rate != 0) @@ -1309,8 +1314,10 @@ namespace HRESULT processSampleData(IMFSample *pSample) { - LONGLONG time; + LONGLONG time, duration = -1; HRESULT hr = pSample->GetSampleTime(&time); + if (SUCCEEDED(hr)) + pSample->GetSampleDuration(&duration); if (m_prerolling) { if (SUCCEEDED(hr) && time >= m_prerollTargetTime) { @@ -1320,6 +1327,7 @@ namespace SampleBuffer sb; sb.m_buffer = pBuffer; sb.m_time = time; + sb.m_duration = duration; m_bufferCache.push_back(sb); endPreroll(S_OK); } @@ -1336,6 +1344,7 @@ namespace SampleBuffer sb; sb.m_buffer = pBuffer; sb.m_time = time; + sb.m_duration = duration; m_bufferCache.push_back(sb); } if (m_rate == 0) @@ -1351,6 +1360,7 @@ namespace public: IMFMediaBuffer *m_buffer; LONGLONG m_time; + LONGLONG m_duration; }; QList m_bufferCache; static const int BUFFER_CACHE_SIZE = 2; @@ -1383,6 +1393,8 @@ namespace continue; } m_scheduledBuffer = sb.m_buffer; + m_bufferStartTime = sb.m_time; + m_bufferDuration = sb.m_duration; QCoreApplication::postEvent(m_rendererControl, new PresentEvent(sb.m_time)); if (m_rate == 0) queueEvent(MEStreamSinkScrubSampleComplete, GUID_NULL, S_OK, NULL); @@ -1393,6 +1405,8 @@ namespace queueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL); } IMFMediaBuffer *m_scheduledBuffer; + MFTIME m_bufferStartTime; + MFTIME m_bufferDuration; IMFPresentationClock *m_presentationClock; float m_rate; };