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:
committed by
The Qt Project
parent
b770fefa81
commit
5ff2f4c52d
@@ -772,9 +772,12 @@ namespace
|
|||||||
qDebug() << "MediaStream::setRate" << rate;
|
qDebug() << "MediaStream::setRate" << rate;
|
||||||
#endif
|
#endif
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
|
HRESULT hr = validateOperation(OpSetRate);
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
m_rate = rate;
|
m_rate = rate;
|
||||||
queueEvent(MEStreamSinkRateChanged, GUID_NULL, S_OK, NULL);
|
hr = queueAsyncOperation(OpSetRate);
|
||||||
return S_OK;
|
}
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user