WMF: support playback rate in our custom video sink.

Change-Id: Ic8fde3398813df68d2f77b2542f1fa507c8ea674
Reviewed-by: Christian Stromme <christian.stromme@digia.com>
This commit is contained in:
Yoann Lopes
2014-04-04 15:06:07 +02:00
committed by The Qt Project
parent b770fefa81
commit 5ff2f4c52d

View File

@@ -772,9 +772,12 @@ namespace
qDebug() << "MediaStream::setRate" << rate; qDebug() << "MediaStream::setRate" << rate;
#endif #endif
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
m_rate = rate; HRESULT hr = validateOperation(OpSetRate);
queueEvent(MEStreamSinkRateChanged, GUID_NULL, S_OK, NULL); if (SUCCEEDED(hr)) {
return S_OK; m_rate = rate;
hr = queueAsyncOperation(OpSetRate);
}
return hr;
} }
void supportedFormatsChanged() void supportedFormatsChanged()
@@ -1158,7 +1161,7 @@ namespace
hr = queueEvent(MEStreamSinkPaused, GUID_NULL, hr, NULL); hr = queueEvent(MEStreamSinkPaused, GUID_NULL, hr, NULL);
break; break;
case OpSetRate: case OpSetRate:
//TODO: hr = queueEvent(MEStreamSinkRateChanged, GUID_NULL, S_OK, NULL);
break; break;
case OpProcessSample: case OpProcessSample:
case OpPlaceMarker: case OpPlaceMarker:
@@ -1335,7 +1338,7 @@ namespace
pSample->GetSampleDuration(&duration); pSample->GetSampleDuration(&duration);
if (m_prerolling) { if (m_prerolling) {
if (SUCCEEDED(hr) && time >= m_prerollTargetTime) { if (SUCCEEDED(hr) && ((time - m_prerollTargetTime) * m_rate) >= 0) {
IMFMediaBuffer *pBuffer = NULL; IMFMediaBuffer *pBuffer = NULL;
hr = pSample->ConvertToContiguousBuffer(&pBuffer); hr = pSample->ConvertToContiguousBuffer(&pBuffer);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
@@ -1352,7 +1355,7 @@ namespace
} else { } else {
bool requestSample = true; bool requestSample = true;
// If the time stamp is too early, just discard this sample. // 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; IMFMediaBuffer *pBuffer = NULL;
hr = pSample->ConvertToContiguousBuffer(&pBuffer); hr = pSample->ConvertToContiguousBuffer(&pBuffer);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
@@ -1400,9 +1403,8 @@ namespace
timeOK = false; timeOK = false;
} }
while (!m_bufferCache.isEmpty()) { while (!m_bufferCache.isEmpty()) {
SampleBuffer sb = m_bufferCache.first(); SampleBuffer sb = m_bufferCache.takeFirst();
m_bufferCache.pop_front(); if (timeOK && ((sb.m_time - currentTime) * m_rate) < 0) {
if (timeOK && currentTime > sb.m_time) {
sb.m_buffer->Release(); sb.m_buffer->Release();
#ifdef DEBUG_MEDIAFOUNDATION #ifdef DEBUG_MEDIAFOUNDATION
qDebug() << "currentPresentTime =" << float(currentTime / 10000) * 0.001f << " and sampleTime is" << float(sb.m_time / 10000) * 0.001f; 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. // 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: public:
MediaSink(MFVideoRendererControl *rendererControl) MediaSink(MFVideoRendererControl *rendererControl)
@@ -1526,10 +1532,14 @@ namespace
return E_POINTER; return E_POINTER;
if (riid == IID_IMFMediaSink) { if (riid == IID_IMFMediaSink) {
*ppvObject = static_cast<IMFMediaSink*>(this); *ppvObject = static_cast<IMFMediaSink*>(this);
} else if (riid == IID_IMFGetService) {
*ppvObject = static_cast<IMFGetService*>(this);
} else if (riid == IID_IMFMediaSinkPreroll) { } else if (riid == IID_IMFMediaSinkPreroll) {
*ppvObject = static_cast<IMFMediaSinkPreroll*>(this); *ppvObject = static_cast<IMFMediaSinkPreroll*>(this);
} else if (riid == IID_IMFClockStateSink) { } else if (riid == IID_IMFClockStateSink) {
*ppvObject = static_cast<IMFClockStateSink*>(this); *ppvObject = static_cast<IMFClockStateSink*>(this);
} else if (riid == IID_IMFRateSupport) {
*ppvObject = static_cast<IMFRateSupport*>(this);
} else if (riid == IID_IUnknown) { } else if (riid == IID_IUnknown) {
*ppvObject = static_cast<IUnknown*>(static_cast<IMFFinalizableMediaSink*>(this)); *ppvObject = static_cast<IUnknown*>(static_cast<IMFFinalizableMediaSink*>(this));
} else { } else {
@@ -1554,7 +1564,19 @@ namespace
return cRef; 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 //IMFMediaSinkPreroll
STDMETHODIMP NotifyPreroll(MFTIME hnsUpcomingStartTime) STDMETHODIMP NotifyPreroll(MFTIME hnsUpcomingStartTime)
@@ -1749,6 +1771,68 @@ namespace
return m_stream->setRate(flRate); 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: private:
long m_cRef; long m_cRef;
QMutex m_mutex; QMutex m_mutex;
@@ -2201,13 +2285,13 @@ void MFVideoRendererControl::customEvent(QEvent *event)
MFTIME targetTime = static_cast<MediaStream::PresentEvent*>(event)->targetTime(); MFTIME targetTime = static_cast<MediaStream::PresentEvent*>(event)->targetTime();
MFTIME currentTime = static_cast<VideoRendererActivate*>(m_currentActivate)->getTime(); MFTIME currentTime = static_cast<VideoRendererActivate*>(m_currentActivate)->getTime();
float playRate = static_cast<VideoRendererActivate*>(m_currentActivate)->getPlayRate(); float playRate = static_cast<VideoRendererActivate*>(m_currentActivate)->getPlayRate();
if (!qFuzzyIsNull(playRate)) { if (!qFuzzyIsNull(playRate) && targetTime != currentTime) {
// If the scheduled frame is too late or too much in advance, skip it // If the scheduled frame is too late, skip it
const int diff = (targetTime - currentTime) / 10000; const int interval = ((targetTime - currentTime) / 10000) / playRate;
if (diff < 0 || diff > 500) if (interval < 0)
static_cast<VideoRendererActivate*>(m_currentActivate)->clearScheduledFrame(); static_cast<VideoRendererActivate*>(m_currentActivate)->clearScheduledFrame();
else else
QTimer::singleShot(diff / playRate, this, SLOT(present())); QTimer::singleShot(interval, this, SLOT(present()));
} else { } else {
present(); present();
} }