summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYoann Lopes <yoann.lopes@digia.com>2014-04-04 15:06:07 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-04-08 00:03:30 +0200
commit5ff2f4c52d77798063afbd2117ee1a06942f3612 (patch)
tree3fdaa58fefdee4de04031648997cf9c06c61e6d8
parentb770fefa819dabb7f53f4ed5fe544f3b68b6279c (diff)
downloadqtmultimedia-5ff2f4c52d77798063afbd2117ee1a06942f3612.tar.gz
WMF: support playback rate in our custom video sink.
Change-Id: Ic8fde3398813df68d2f77b2542f1fa507c8ea674 Reviewed-by: Christian Stromme <christian.stromme@digia.com>
-rw-r--r--src/plugins/wmf/player/mfvideorenderercontrol.cpp114
1 files changed, 99 insertions, 15 deletions
diff --git a/src/plugins/wmf/player/mfvideorenderercontrol.cpp b/src/plugins/wmf/player/mfvideorenderercontrol.cpp
index 1705b2a48..d4d341a45 100644
--- a/src/plugins/wmf/player/mfvideorenderercontrol.cpp
+++ b/src/plugins/wmf/player/mfvideorenderercontrol.cpp
@@ -772,9 +772,12 @@ namespace
qDebug() << "MediaStream::setRate" << rate;
#endif
QMutexLocker locker(&m_mutex);
- m_rate = rate;
- queueEvent(MEStreamSinkRateChanged, GUID_NULL, S_OK, NULL);
- return S_OK;
+ HRESULT hr = validateOperation(OpSetRate);
+ if (SUCCEEDED(hr)) {
+ m_rate = rate;
+ hr = queueAsyncOperation(OpSetRate);
+ }
+ return hr;
}
void supportedFormatsChanged()
@@ -1158,7 +1161,7 @@ namespace
hr = queueEvent(MEStreamSinkPaused, GUID_NULL, hr, NULL);
break;
case OpSetRate:
- //TODO:
+ hr = queueEvent(MEStreamSinkRateChanged, GUID_NULL, S_OK, NULL);
break;
case OpProcessSample:
case OpPlaceMarker:
@@ -1335,7 +1338,7 @@ namespace
pSample->GetSampleDuration(&duration);
if (m_prerolling) {
- if (SUCCEEDED(hr) && time >= m_prerollTargetTime) {
+ if (SUCCEEDED(hr) && ((time - m_prerollTargetTime) * m_rate) >= 0) {
IMFMediaBuffer *pBuffer = NULL;
hr = pSample->ConvertToContiguousBuffer(&pBuffer);
if (SUCCEEDED(hr)) {
@@ -1352,7 +1355,7 @@ namespace
} else {
bool requestSample = true;
// If the time stamp is too early, just discard this sample.
- if (SUCCEEDED(hr) && time >= m_startTime) {
+ if (SUCCEEDED(hr) && ((time - m_startTime) * m_rate) >= 0) {
IMFMediaBuffer *pBuffer = NULL;
hr = pSample->ConvertToContiguousBuffer(&pBuffer);
if (SUCCEEDED(hr)) {
@@ -1400,9 +1403,8 @@ namespace
timeOK = false;
}
while (!m_bufferCache.isEmpty()) {
- SampleBuffer sb = m_bufferCache.first();
- m_bufferCache.pop_front();
- if (timeOK && currentTime > sb.m_time) {
+ SampleBuffer sb = m_bufferCache.takeFirst();
+ if (timeOK && ((sb.m_time - currentTime) * m_rate) < 0) {
sb.m_buffer->Release();
#ifdef DEBUG_MEDIAFOUNDATION
qDebug() << "currentPresentTime =" << float(currentTime / 10000) * 0.001f << " and sampleTime is" << float(sb.m_time / 10000) * 0.001f;
@@ -1454,7 +1456,11 @@ namespace
// 2. While paused, the sink accepts samples but does not process them.
};
- class MediaSink : public IMFFinalizableMediaSink, public IMFClockStateSink, public IMFMediaSinkPreroll
+ class MediaSink : public IMFFinalizableMediaSink,
+ public IMFClockStateSink,
+ public IMFMediaSinkPreroll,
+ public IMFGetService,
+ public IMFRateSupport
{
public:
MediaSink(MFVideoRendererControl *rendererControl)
@@ -1526,10 +1532,14 @@ namespace
return E_POINTER;
if (riid == IID_IMFMediaSink) {
*ppvObject = static_cast<IMFMediaSink*>(this);
+ } else if (riid == IID_IMFGetService) {
+ *ppvObject = static_cast<IMFGetService*>(this);
} else if (riid == IID_IMFMediaSinkPreroll) {
*ppvObject = static_cast<IMFMediaSinkPreroll*>(this);
} else if (riid == IID_IMFClockStateSink) {
*ppvObject = static_cast<IMFClockStateSink*>(this);
+ } else if (riid == IID_IMFRateSupport) {
+ *ppvObject = static_cast<IMFRateSupport*>(this);
} else if (riid == IID_IUnknown) {
*ppvObject = static_cast<IUnknown*>(static_cast<IMFFinalizableMediaSink*>(this));
} else {
@@ -1554,7 +1564,19 @@ namespace
return cRef;
}
+ // IMFGetService methods
+ STDMETHODIMP GetService(const GUID &guidService,
+ const IID &riid,
+ LPVOID *ppvObject)
+ {
+ if (!ppvObject)
+ return E_POINTER;
+
+ if (guidService != MF_RATE_CONTROL_SERVICE)
+ return MF_E_UNSUPPORTED_SERVICE;
+ return QueryInterface(riid, ppvObject);
+ }
//IMFMediaSinkPreroll
STDMETHODIMP NotifyPreroll(MFTIME hnsUpcomingStartTime)
@@ -1749,6 +1771,68 @@ namespace
return m_stream->setRate(flRate);
}
+ // IMFRateSupport methods
+ STDMETHODIMP GetFastestRate(MFRATE_DIRECTION eDirection,
+ BOOL fThin,
+ float *pflRate)
+ {
+ if (!pflRate)
+ return E_POINTER;
+
+ *pflRate = (fThin ? 8.f : 2.0f) * (eDirection == MFRATE_FORWARD ? 1 : -1) ;
+
+ return S_OK;
+ }
+
+ STDMETHODIMP GetSlowestRate(MFRATE_DIRECTION eDirection,
+ BOOL fThin,
+ float *pflRate)
+ {
+ Q_UNUSED(eDirection);
+ Q_UNUSED(fThin);
+
+ if (!pflRate)
+ return E_POINTER;
+
+ // we support any rate
+ *pflRate = 0.f;
+
+ return S_OK;
+ }
+
+ STDMETHODIMP IsRateSupported(BOOL fThin,
+ float flRate,
+ float *pflNearestSupportedRate)
+ {
+ HRESULT hr = S_OK;
+
+ if (!qFuzzyIsNull(flRate)) {
+ MFRATE_DIRECTION direction = flRate > 0.f ? MFRATE_FORWARD
+ : MFRATE_REVERSE;
+
+ float fastestRate = 0.f;
+ float slowestRate = 0.f;
+ GetFastestRate(direction, fThin, &fastestRate);
+ GetSlowestRate(direction, fThin, &slowestRate);
+
+ if (direction == MFRATE_REVERSE)
+ qSwap(fastestRate, slowestRate);
+
+ if (flRate < slowestRate || flRate > fastestRate) {
+ hr = MF_E_UNSUPPORTED_RATE;
+ if (pflNearestSupportedRate) {
+ *pflNearestSupportedRate = qBound(slowestRate,
+ flRate,
+ fastestRate);
+ }
+ }
+ } else if (pflNearestSupportedRate) {
+ *pflNearestSupportedRate = flRate;
+ }
+
+ return hr;
+ }
+
private:
long m_cRef;
QMutex m_mutex;
@@ -2201,13 +2285,13 @@ void MFVideoRendererControl::customEvent(QEvent *event)
MFTIME targetTime = static_cast<MediaStream::PresentEvent*>(event)->targetTime();
MFTIME currentTime = static_cast<VideoRendererActivate*>(m_currentActivate)->getTime();
float playRate = static_cast<VideoRendererActivate*>(m_currentActivate)->getPlayRate();
- if (!qFuzzyIsNull(playRate)) {
- // If the scheduled frame is too late or too much in advance, skip it
- const int diff = (targetTime - currentTime) / 10000;
- if (diff < 0 || diff > 500)
+ if (!qFuzzyIsNull(playRate) && targetTime != currentTime) {
+ // If the scheduled frame is too late, skip it
+ const int interval = ((targetTime - currentTime) / 10000) / playRate;
+ if (interval < 0)
static_cast<VideoRendererActivate*>(m_currentActivate)->clearScheduledFrame();
else
- QTimer::singleShot(diff / playRate, this, SLOT(present()));
+ QTimer::singleShot(interval, this, SLOT(present()));
} else {
present();
}