summaryrefslogtreecommitdiffstats
path: root/src/plugins/common/evr/evrcustompresenter.cpp
diff options
context:
space:
mode:
authorYoann Lopes <yoann.lopes@theqtcompany.com>2015-11-27 11:56:50 +0100
committerYoann Lopes <yoann.lopes@theqtcompany.com>2016-01-12 15:51:32 +0000
commitc7397523e77578cf8f09ba3258791f68c34e2e5f (patch)
tree9c293c768038f74564d411cd27cff5ce22ed46ac /src/plugins/common/evr/evrcustompresenter.cpp
parent36549dbe148055d6ecf98952b05b4ff2310fe491 (diff)
Windows: Improve EVR presenter.
Removed hard dependency to Angle; it's now required only when rendering to a texture. If Angle is not used, the buffers are now mappable to system memory. This enables HW-accelerated video decoding in QML for non-ANGLE builds. Note that the frame data have to do a round-trip through system memory, which is not ideal but still faster than SW decoding. Task-number: QTBUG-45593 Change-Id: Icc3503142df4e8dbb53b4e11f409b161fd2f9bde Reviewed-by: Christian Stromme <christian.stromme@theqtcompany.com>
Diffstat (limited to 'src/plugins/common/evr/evrcustompresenter.cpp')
-rw-r--r--src/plugins/common/evr/evrcustompresenter.cpp310
1 files changed, 170 insertions, 140 deletions
diff --git a/src/plugins/common/evr/evrcustompresenter.cpp b/src/plugins/common/evr/evrcustompresenter.cpp
index 969dd1415..73d032aa6 100644
--- a/src/plugins/common/evr/evrcustompresenter.cpp
+++ b/src/plugins/common/evr/evrcustompresenter.cpp
@@ -56,7 +56,6 @@ static const LONG ONE_MSEC = 1000;
static HRESULT setDesiredSampleTime(IMFSample *sample, const LONGLONG& hnsSampleTime, const LONGLONG& hnsDuration);
static HRESULT clearDesiredSampleTime(IMFSample *sample);
static HRESULT setMixerSourceRect(IMFTransform *mixer, const MFVideoNormalizedRect& nrcSource);
-static DWORD getFourCCFromPixelFormat(QVideoFrame::PixelFormat pixelFormat);
static QVideoFrame::PixelFormat pixelFormatFromMediaType(IMFMediaType *type);
static inline LONG MFTimeToMsec(const LONGLONG& time)
@@ -80,9 +79,32 @@ bool qt_evr_setCustomPresenter(IUnknown *evr, EVRCustomPresenter *presenter)
return result == S_OK;
}
-Scheduler::Scheduler()
- : m_clock(NULL)
- , m_CB(NULL)
+class PresentSampleEvent : public QEvent
+{
+public:
+ PresentSampleEvent(IMFSample *sample)
+ : QEvent(QEvent::Type(EVRCustomPresenter::PresentSample))
+ , m_sample(sample)
+ {
+ if (m_sample)
+ m_sample->AddRef();
+ }
+
+ ~PresentSampleEvent()
+ {
+ if (m_sample)
+ m_sample->Release();
+ }
+
+ IMFSample *sample() const { return m_sample; }
+
+private:
+ IMFSample *m_sample;
+};
+
+Scheduler::Scheduler(EVRCustomPresenter *presenter)
+ : m_presenter(presenter)
+ , m_clock(NULL)
, m_threadID(0)
, m_schedulerThread(0)
, m_threadReadyEvent(0)
@@ -228,9 +250,6 @@ HRESULT Scheduler::flush()
HRESULT Scheduler::scheduleSample(IMFSample *sample, bool presentNow)
{
- if (!m_CB)
- return MF_E_NOT_INITIALIZED;
-
if (!m_schedulerThread)
return MF_E_NOT_INITIALIZED;
@@ -242,13 +261,7 @@ HRESULT Scheduler::scheduleSample(IMFSample *sample, bool presentNow)
return E_FAIL;
if (presentNow || !m_clock) {
- // Present the sample immediately.
- sample->AddRef();
- QMetaObject::invokeMethod(m_CB,
- "presentSample",
- Qt::QueuedConnection,
- Q_ARG(void*, sample),
- Q_ARG(qint64, 0));
+ m_presenter->presentSample(sample);
} else {
// Queue the sample and ask the scheduler thread to wake up.
m_mutex.lock();
@@ -344,12 +357,7 @@ HRESULT Scheduler::processSample(IMFSample *sample, LONG *pNextSleep)
}
if (presentNow) {
- sample->AddRef();
- QMetaObject::invokeMethod(m_CB,
- "presentSample",
- Qt::QueuedConnection,
- Q_ARG(void*, sample),
- Q_ARG(qint64, hnsPresentationTime));
+ m_presenter->presentSample(sample);
} else {
// The sample is not ready yet. Return it to the queue.
m_mutex.lock();
@@ -538,24 +546,27 @@ HRESULT SamplePool::clear()
}
-EVRCustomPresenter::EVRCustomPresenter()
+EVRCustomPresenter::EVRCustomPresenter(QAbstractVideoSurface *surface)
: QObject()
, m_sampleFreeCB(this, &EVRCustomPresenter::onSampleFree)
, m_refCount(1)
, m_renderState(RenderShutdown)
, m_mutex(QMutex::Recursive)
+ , m_scheduler(this)
, m_tokenCounter(0)
, m_sampleNotify(false)
, m_repaint(false)
, m_prerolled(false)
, m_endStreaming(false)
, m_playbackRate(1.0f)
- , m_D3DPresentEngine(0)
+ , m_presentEngine(new D3DPresentEngine)
, m_clock(0)
, m_mixer(0)
, m_mediaEventSink(0)
, m_mediaType(0)
, m_surface(0)
+ , m_canRenderToSurface(false)
+ , m_sampleToPresent(0)
{
// Initial source rectangle = (0,0,1,1)
m_sourceRect.top = 0;
@@ -563,18 +574,21 @@ EVRCustomPresenter::EVRCustomPresenter()
m_sourceRect.bottom = 1;
m_sourceRect.right = 1;
- m_D3DPresentEngine = new D3DPresentEngine;
- m_scheduler.setCallback(m_D3DPresentEngine);
+ setSurface(surface);
}
EVRCustomPresenter::~EVRCustomPresenter()
{
+ m_scheduler.flush();
+ m_scheduler.stopScheduler();
+ m_samplePool.clear();
+
qt_evr_safe_release(&m_clock);
qt_evr_safe_release(&m_mixer);
qt_evr_safe_release(&m_mediaEventSink);
qt_evr_safe_release(&m_mediaType);
- m_D3DPresentEngine->deleteLater();
+ delete m_presentEngine;
}
HRESULT EVRCustomPresenter::QueryInterface(REFIID riid, void ** ppvObject)
@@ -628,7 +642,7 @@ HRESULT EVRCustomPresenter::GetService(REFGUID guidService, REFIID riid, LPVOID
return MF_E_UNSUPPORTED_SERVICE;
// First try to get the service interface from the D3DPresentEngine object.
- hr = m_D3DPresentEngine->getService(guidService, riid, ppvObject);
+ hr = m_presentEngine->getService(guidService, riid, ppvObject);
if (FAILED(hr))
// Next, check if this object supports the interface.
hr = QueryInterface(riid, ppvObject);
@@ -725,6 +739,11 @@ HRESULT EVRCustomPresenter::ReleaseServicePointers()
return S_OK;
}
+bool EVRCustomPresenter::isValid() const
+{
+ return m_presentEngine->isValid() && m_canRenderToSurface;
+}
+
HRESULT EVRCustomPresenter::ProcessMessage(MFVP_MESSAGE_TYPE message, ULONG_PTR param)
{
HRESULT hr = S_OK;
@@ -837,11 +856,7 @@ HRESULT EVRCustomPresenter::OnClockStart(MFTIME, LONGLONG clockStartOffset)
return hr;
}
- // Start the video surface in the main thread
- if (thread() == QThread::currentThread())
- startSurface();
- else
- QMetaObject::invokeMethod(this, "startSurface", Qt::QueuedConnection);
+ startSurface();
// Now try to get new output samples from the mixer.
processOutputLoop();
@@ -890,11 +905,7 @@ HRESULT EVRCustomPresenter::OnClockStop(MFTIME)
cancelFrameStep();
}
- // Stop the video surface in the main thread
- if (thread() == QThread::currentThread())
- stopSurface();
- else
- QMetaObject::invokeMethod(this, "stopSurface", Qt::QueuedConnection);
+ stopSurface();
return S_OK;
}
@@ -1021,16 +1032,27 @@ void EVRCustomPresenter::supportedFormatsChanged()
{
QMutexLocker locker(&m_mutex);
- m_supportedGLFormats.clear();
- if (!m_surface)
- return;
+ m_canRenderToSurface = false;
+ m_presentEngine->setHint(D3DPresentEngine::RenderToTexture, false);
- QList<QVideoFrame::PixelFormat> formats = m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle);
- for (int i = 0; i < formats.size(); ++i) {
- DWORD fourCC = getFourCCFromPixelFormat(formats.at(i));
- if (fourCC)
- m_supportedGLFormats.append(fourCC);
+ // check if we can render to the surface (compatible formats)
+ if (m_surface) {
+ QList<QVideoFrame::PixelFormat> formats = m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle);
+ if (m_presentEngine->supportsTextureRendering() && formats.contains(QVideoFrame::Format_RGB32)) {
+ m_presentEngine->setHint(D3DPresentEngine::RenderToTexture, true);
+ m_canRenderToSurface = true;
+ } else {
+ formats = m_surface->supportedPixelFormats(QAbstractVideoBuffer::NoHandle);
+ Q_FOREACH (QVideoFrame::PixelFormat format, formats) {
+ if (SUCCEEDED(m_presentEngine->checkFormat(qt_evr_D3DFormatFromPixelFormat(format)))) {
+ m_canRenderToSurface = true;
+ break;
+ }
+ }
+ }
}
+
+ // TODO: if media type already set, renegotiate?
}
void EVRCustomPresenter::setSurface(QAbstractVideoSurface *surface)
@@ -1044,9 +1066,6 @@ void EVRCustomPresenter::setSurface(QAbstractVideoSurface *surface)
m_surface = surface;
- if (m_D3DPresentEngine)
- m_D3DPresentEngine->setSurface(surface);
-
if (m_surface) {
connect(m_surface, &QAbstractVideoSurface::supportedFormatsChanged,
this, &EVRCustomPresenter::supportedFormatsChanged);
@@ -1140,11 +1159,7 @@ HRESULT EVRCustomPresenter::flush()
if (m_renderState == RenderStopped) {
// Repaint with black.
- QMetaObject::invokeMethod(m_D3DPresentEngine,
- "presentSample",
- Qt::QueuedConnection,
- Q_ARG(void*, 0),
- Q_ARG(qint64, 0));
+ presentSample(NULL);
}
return S_OK;
@@ -1401,7 +1416,6 @@ HRESULT EVRCustomPresenter::setMediaType(IMFMediaType *mediaType)
if (!mediaType) {
qt_evr_safe_release(&m_mediaType);
releaseResources();
- m_D3DPresentEngine->setSurfaceFormat(QVideoSurfaceFormat());
return S_OK;
}
@@ -1410,11 +1424,6 @@ HRESULT EVRCustomPresenter::setMediaType(IMFMediaType *mediaType)
IMFSample *sample = NULL;
- QVideoSurfaceFormat surfaceFormat;
- UINT64 size;
- int width;
- int height;
-
// Cannot set the media type after shutdown.
HRESULT hr = checkShutdown();
if (FAILED(hr))
@@ -1432,7 +1441,7 @@ HRESULT EVRCustomPresenter::setMediaType(IMFMediaType *mediaType)
// Initialize the presenter engine with the new media type.
// The presenter engine allocates the samples.
- hr = m_D3DPresentEngine->createVideoSamples(mediaType, sampleQueue);
+ hr = m_presentEngine->createVideoSamples(mediaType, sampleQueue);
if (FAILED(hr))
goto done;
@@ -1465,15 +1474,6 @@ HRESULT EVRCustomPresenter::setMediaType(IMFMediaType *mediaType)
m_mediaType = mediaType;
m_mediaType->AddRef();
- // Create the surface format
- hr = m_mediaType->GetUINT64(MF_MT_FRAME_SIZE, &size);
- width = int(HI32(size));
- height = int(LO32(size));
- surfaceFormat = QVideoSurfaceFormat(QSize(width, height),
- pixelFormatFromMediaType(m_mediaType),
- QAbstractVideoBuffer::GLTextureHandle);
- m_D3DPresentEngine->setSurfaceFormat(surfaceFormat);
-
done:
if (FAILED(hr))
releaseResources();
@@ -1493,9 +1493,16 @@ HRESULT EVRCustomPresenter::isMediaTypeSupported(IMFMediaType *proposed)
if (FAILED(hr))
return hr;
- // Only accept pixel formats supported by the video surface
- if (!m_supportedGLFormats.contains((DWORD)d3dFormat))
+ QVideoFrame::PixelFormat pixelFormat = pixelFormatFromMediaType(proposed);
+ if (pixelFormat == QVideoFrame::Format_Invalid)
+ return MF_E_INVALIDMEDIATYPE;
+
+ // When not rendering to texture, only accept pixel formats supported by the video surface
+ if (!m_presentEngine->isTextureRenderingEnabled()
+ && m_surface
+ && !m_surface->supportedPixelFormats().contains(pixelFormat)) {
return MF_E_INVALIDMEDIATYPE;
+ }
// Reject compressed media types.
hr = proposed->IsCompressedFormat(&compressed);
@@ -1505,9 +1512,8 @@ HRESULT EVRCustomPresenter::isMediaTypeSupported(IMFMediaType *proposed)
if (compressed)
return MF_E_INVALIDMEDIATYPE;
- // The D3DPresentEngine checks whether the format can be used as
- // the back-buffer format for the swap chains.
- hr = m_D3DPresentEngine->checkFormat(d3dFormat);
+ // The D3DPresentEngine checks whether surfaces can be created using this format
+ hr = m_presentEngine->checkFormat(d3dFormat);
if (FAILED(hr))
return hr;
@@ -1788,7 +1794,7 @@ void EVRCustomPresenter::releaseResources()
m_samplePool.clear();
- m_D3DPresentEngine->releaseResources();
+ m_presentEngine->releaseResources();
}
HRESULT EVRCustomPresenter::onSampleFree(IMFAsyncResult *result)
@@ -1853,18 +1859,6 @@ done:
return hr;
}
-void EVRCustomPresenter::startSurface()
-{
- if (m_D3DPresentEngine)
- m_D3DPresentEngine->start();
-}
-
-void EVRCustomPresenter::stopSurface()
-{
- if (m_D3DPresentEngine)
- m_D3DPresentEngine->stop();
-}
-
float EVRCustomPresenter::getMaxRate(bool thin)
{
// Non-thinned:
@@ -1880,7 +1874,7 @@ float EVRCustomPresenter::getMaxRate(bool thin)
if (!thin && m_mediaType) {
qt_evr_getFrameRate(m_mediaType, &fps);
- monitorRateHz = m_D3DPresentEngine->refreshRate();
+ monitorRateHz = m_presentEngine->refreshRate();
if (fps.Denominator && fps.Numerator && monitorRateHz) {
// Max Rate = Refresh Rate / Frame Rate
@@ -1891,6 +1885,74 @@ float EVRCustomPresenter::getMaxRate(bool thin)
return maxRate;
}
+bool EVRCustomPresenter::event(QEvent *e)
+{
+ if (e->type() == StartSurface) {
+ startSurface();
+ return true;
+ } else if (e->type() == StopSurface) {
+ stopSurface();
+ return true;
+ } else if (e->type() == PresentSample) {
+ PresentSampleEvent *ev = static_cast<PresentSampleEvent *>(e);
+ presentSample(ev->sample());
+ return true;
+ }
+
+ return QObject::event(e);
+}
+
+void EVRCustomPresenter::startSurface()
+{
+ if (thread() != QThread::currentThread()) {
+ QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StartSurface)));
+ return;
+ }
+
+ if (!m_surface || m_surface->isActive())
+ return;
+
+ QVideoSurfaceFormat format = m_presentEngine->videoSurfaceFormat();
+ if (!format.isValid())
+ return;
+
+ m_surface->start(format);
+}
+
+void EVRCustomPresenter::stopSurface()
+{
+ if (thread() != QThread::currentThread()) {
+ QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StopSurface)));
+ return;
+ }
+
+ if (!m_surface || !m_surface->isActive())
+ return;
+
+ m_surface->stop();
+}
+
+void EVRCustomPresenter::presentSample(IMFSample *sample)
+{
+ if (thread() != QThread::currentThread()) {
+ QCoreApplication::postEvent(this, new PresentSampleEvent(sample));
+ return;
+ }
+
+ if (!m_surface || !m_surface->isActive() || !m_presentEngine->videoSurfaceFormat().isValid())
+ return;
+
+ QVideoFrame frame = m_presentEngine->makeVideoFrame(sample);
+
+ if (m_surface->isActive() && m_surface->surfaceFormat() != m_presentEngine->videoSurfaceFormat()) {
+ m_surface->stop();
+ if (!m_surface->start(m_presentEngine->videoSurfaceFormat()))
+ return;
+ }
+
+ m_surface->present(frame);
+}
+
HRESULT setDesiredSampleTime(IMFSample *sample, const LONGLONG &sampleTime, const LONGLONG &duration)
{
if (!sample)
@@ -1925,8 +1987,6 @@ HRESULT clearDesiredSampleTime(IMFSample *sample)
UINT32 counter = MFGetAttributeUINT32(sample, MFSamplePresenter_SampleCounter, (UINT32)-1);
- sample->GetUnknown(MFSamplePresenter_SampleSwapChain, IID_IUnknown, (void**)&unkSwapChain);
-
hr = sample->QueryInterface(IID_PPV_ARGS(&desired));
if (SUCCEEDED(hr)) {
desired->Clear();
@@ -1934,12 +1994,6 @@ HRESULT clearDesiredSampleTime(IMFSample *sample)
hr = sample->SetUINT32(MFSamplePresenter_SampleCounter, counter);
if (FAILED(hr))
goto done;
-
- if (unkSwapChain) {
- hr = sample->SetUnknown(MFSamplePresenter_SampleSwapChain, unkSwapChain);
- if (FAILED(hr))
- goto done;
- }
}
done:
@@ -1963,48 +2017,6 @@ HRESULT setMixerSourceRect(IMFTransform *mixer, const MFVideoNormalizedRect &sou
return hr;
}
-DWORD getFourCCFromPixelFormat(QVideoFrame::PixelFormat pixelFormat)
-{
- DWORD fourCC = 0;
- switch (pixelFormat) {
- case QVideoFrame::Format_ARGB32:
- case QVideoFrame::Format_ARGB32_Premultiplied:
- fourCC = MFVideoFormat_ARGB32.Data1;
- break;
- case QVideoFrame::Format_RGB32:
- fourCC = MFVideoFormat_RGB32.Data1;
- break;
- case QVideoFrame::Format_RGB24:
- fourCC = MFVideoFormat_RGB24.Data1;
- break;
- case QVideoFrame::Format_RGB565:
- fourCC = MFVideoFormat_RGB565.Data1;
- break;
- case QVideoFrame::Format_RGB555:
- fourCC = MFVideoFormat_RGB555.Data1;
- break;
- case QVideoFrame::Format_AYUV444:
- case QVideoFrame::Format_AYUV444_Premultiplied:
- fourCC = MFVideoFormat_AYUV.Data1;
- break;
- case QVideoFrame::Format_YUV420P:
- fourCC = MFVideoFormat_I420.Data1;
- break;
- case QVideoFrame::Format_UYVY:
- fourCC = MFVideoFormat_UYVY.Data1;
- break;
- case QVideoFrame::Format_YV12:
- fourCC = MFVideoFormat_YV12.Data1;
- break;
- case QVideoFrame::Format_NV12:
- fourCC = MFVideoFormat_NV12.Data1;
- break;
- default:
- break;
- }
- return fourCC;
-}
-
static QVideoFrame::PixelFormat pixelFormatFromMediaType(IMFMediaType *type)
{
GUID majorType;
@@ -2013,12 +2025,30 @@ static QVideoFrame::PixelFormat pixelFormatFromMediaType(IMFMediaType *type)
if (majorType != MFMediaType_Video)
return QVideoFrame::Format_Invalid;
- GUID subType;
- if (FAILED(type->GetGUID(MF_MT_SUBTYPE, &subType)))
+ GUID subtype;
+ if (FAILED(type->GetGUID(MF_MT_SUBTYPE, &subtype)))
return QVideoFrame::Format_Invalid;
- if (subType == MFVideoFormat_RGB32)
+ if (subtype == MFVideoFormat_RGB32)
return QVideoFrame::Format_RGB32;
+ else if (subtype == MFVideoFormat_ARGB32)
+ return QVideoFrame::Format_ARGB32;
+ else if (subtype == MFVideoFormat_RGB24)
+ return QVideoFrame::Format_RGB24;
+ else if (subtype == MFVideoFormat_RGB565)
+ return QVideoFrame::Format_RGB565;
+ else if (subtype == MFVideoFormat_RGB555)
+ return QVideoFrame::Format_RGB555;
+ else if (subtype == MFVideoFormat_AYUV)
+ return QVideoFrame::Format_AYUV444;
+ else if (subtype == MFVideoFormat_I420)
+ return QVideoFrame::Format_YUV420P;
+ else if (subtype == MFVideoFormat_UYVY)
+ return QVideoFrame::Format_UYVY;
+ else if (subtype == MFVideoFormat_YV12)
+ return QVideoFrame::Format_YV12;
+ else if (subtype == MFVideoFormat_NV12)
+ return QVideoFrame::Format_NV12;
return QVideoFrame::Format_Invalid;
}