diff --git a/dist/changes-5.1.1 b/dist/changes-5.1.1 new file mode 100644 index 0000000000000000000000000000000000000000..45fc4ded5507b51e1f76d0e256a595bd98053da6 --- /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. diff --git a/examples/multimedia/video/qmlvideofx/main.cpp b/examples/multimedia/video/qmlvideofx/main.cpp index b0698e2368652d1639725a3eedd5f9b50dff87d9..7465deab857d938a83f61d40d3b10f445e523fe0 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 3d4343c2578c5e9286eb6d38ced61cedf6862b65..7c86103614adafb050f799ff91f1fe8c470d894c 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; diff --git a/src/gsttools/qvideosurfacegstsink.cpp b/src/gsttools/qvideosurfacegstsink.cpp index 94aa1262e83b8d7eb203894b5daf6ef50a3c03ff..f91c1192d4c3f2a6c4ad982c7c9a52611e6c70a7 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/multimedia/audio/qsoundeffect_qaudio_p.cpp b/src/multimedia/audio/qsoundeffect_qaudio_p.cpp index 835f60b45fcbb4c56642ec7d8533d3b11e2c69c0..524c856a2fd78850b06eaa2b832a61e6ce798577 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; 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 d1abf658e488bbb8f597ba6a1ba939055908e333..2ca07a63e9fc006b50a61f60f09c9e1392d32f87 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 a70f4e1302ee65e6d628e90aee89d83e6d3f082d..4dc56ebd98fe07e1fe471e6ef520d320a6bebd4c 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); diff --git a/src/plugins/android/wrappers/jsurfacetexture.cpp b/src/plugins/android/wrappers/jsurfacetexture.cpp index 107f7be39f790b0b488f3cfaf0e4b91721b5ec32..34edf1b66cf4f91d4554147a1f41f66bbc4769c6 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); } diff --git a/src/plugins/wmf/evrcustompresenter.cpp b/src/plugins/wmf/evrcustompresenter.cpp index eb73e672058cf998e7340667f2118d60c644cdb8..70acbddbaf567a048ef2d33e1d0b01c4dc9d50d2 100644 --- a/src/plugins/wmf/evrcustompresenter.cpp +++ b/src/plugins/wmf/evrcustompresenter.cpp @@ -50,6 +50,7 @@ #include <qabstractvideosurface.h> #include <qthread.h> #include <qcoreapplication.h> +#include <qmath.h> #include <QtCore/qdebug.h> #include <d3d9.h> #include <dshow.h> @@ -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; diff --git a/src/plugins/wmf/evrd3dpresentengine.cpp b/src/plugins/wmf/evrd3dpresentengine.cpp index c67b5d4480b7b96045da3674928070a30d0f910a..01a5c3341747f5b2defbd1180b3b100cef708edb 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 acec88d6be6fab0a8a5428f08646fbb0d8ef09e5..8e7ce069333549057c2135bfeba81031e325bc02 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/mfplayersession.cpp b/src/plugins/wmf/player/mfplayersession.cpp index fb150c3e9e934a4d23306e75c5a7bc3cad84d4ab..4e4b56589a327742d3cfd18b7312c1c95fa9272b 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(); diff --git a/src/plugins/wmf/player/mfvideorenderercontrol.cpp b/src/plugins/wmf/player/mfvideorenderercontrol.cpp index 8f73244c0afe0ec70ef47af920b2e85a60f4315f..83768c8e227fdbe1c21650a91415e675b49618cd 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<SampleBuffer> 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; };