Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
mfvideorenderercontrol.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include "mfactivate_p.h"
6
8
9#include <private/qplatformvideosink_p.h>
10#include <private/qabstractvideobuffer_p.h>
11#include <private/qwindowsmfdefs_p.h>
12#include <qvideosink.h>
13#include <qvideoframeformat.h>
14#include <qtimer.h>
15#include <qmutex.h>
16#include <qcoreevent.h>
17#include <qcoreapplication.h>
18#include <qthread.h>
19#include "guiddef.h"
20#include <qdebug.h>
21
22//#define DEBUG_MEDIAFOUNDATION
23#define PAD_TO_DWORD(x) (((x) + 3) & ~3)
24
26
27namespace
28{
30 {
31 public:
32 MediaSampleVideoBuffer(IMFMediaBuffer *buffer, int bytesPerLine)
34 , m_buffer(buffer)
35 , m_bytesPerLine(bytesPerLine)
36 , m_mapMode(QVideoFrame::NotMapped)
37 {
38 buffer->AddRef();
39 }
40
42 {
43 m_buffer->Release();
44 }
45
47 {
49 if (m_mapMode == QVideoFrame::NotMapped && mode != QVideoFrame::NotMapped) {
50 BYTE *bytes;
51 DWORD length;
52 HRESULT hr = m_buffer->Lock(&bytes, NULL, &length);
53 if (SUCCEEDED(hr)) {
54 mapData.nPlanes = 1;
55 mapData.bytesPerLine[0] = m_bytesPerLine;
56 mapData.data[0] = reinterpret_cast<uchar *>(bytes);
58 m_mapMode = mode;
59 } else {
60 qWarning("Faild to lock mf buffer!");
61 }
62 }
63 return mapData;
64 }
65
66 void unmap() override
67 {
68 if (m_mapMode == QVideoFrame::NotMapped)
69 return;
70 m_mapMode = QVideoFrame::NotMapped;
71 m_buffer->Unlock();
72 }
73
75 {
76 return m_mapMode;
77 }
78
79 private:
80 IMFMediaBuffer *m_buffer;
81 int m_bytesPerLine;
82 QVideoFrame::MapMode m_mapMode;
83 };
84
85 // Custom interface for handling IMFStreamSink::PlaceMarker calls asynchronously.
86 static const GUID IID_IMarker = {0xa3ff32de, 0x1031, 0x438a, {0x8b, 0x47, 0x82, 0xf8, 0xac, 0xda, 0x59, 0xb7}};
87 MIDL_INTERFACE("a3ff32de-1031-438a-8b47-82f8acda59b7")
88 IMarker : public IUnknown
89 {
90 virtual STDMETHODIMP GetMarkerType(MFSTREAMSINK_MARKER_TYPE *pType) = 0;
91 virtual STDMETHODIMP GetMarkerValue(PROPVARIANT *pvar) = 0;
92 virtual STDMETHODIMP GetContext(PROPVARIANT *pvar) = 0;
93 };
94
95 class Marker : public IMarker
96 {
97 public:
99 MFSTREAMSINK_MARKER_TYPE eMarkerType,
100 const PROPVARIANT* pvarMarkerValue, // Can be NULL.
101 const PROPVARIANT* pvarContextValue, // Can be NULL.
102 IMarker **ppMarker)
103 {
104 if (ppMarker == NULL)
105 return E_POINTER;
106
107 HRESULT hr = S_OK;
108 Marker *pMarker = new Marker(eMarkerType);
109 if (pMarker == NULL)
110 hr = E_OUTOFMEMORY;
111
112 // Copy the marker data.
113 if (SUCCEEDED(hr) && pvarMarkerValue)
114 hr = PropVariantCopy(&pMarker->m_varMarkerValue, pvarMarkerValue);
115
116 if (SUCCEEDED(hr) && pvarContextValue)
117 hr = PropVariantCopy(&pMarker->m_varContextValue, pvarContextValue);
118
119 if (SUCCEEDED(hr)) {
120 *ppMarker = pMarker;
121 (*ppMarker)->AddRef();
122 }
123
124 if (pMarker)
125 pMarker->Release();
126
127 return hr;
128 }
129
130 // IUnknown methods.
131 STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override
132 {
133 if (!ppv)
134 return E_POINTER;
135 if (iid == IID_IUnknown) {
136 *ppv = static_cast<IUnknown*>(this);
137 } else if (iid == IID_IMarker) {
138 *ppv = static_cast<IMarker*>(this);
139 } else {
140 *ppv = NULL;
141 return E_NOINTERFACE;
142 }
143 AddRef();
144 return S_OK;
145 }
146
147 STDMETHODIMP_(ULONG) AddRef() override
148 {
149 return InterlockedIncrement(&m_cRef);
150 }
151
152 STDMETHODIMP_(ULONG) Release() override
153 {
154 LONG cRef = InterlockedDecrement(&m_cRef);
155 if (cRef == 0)
156 delete this;
157 // For thread safety, return a temporary variable.
158 return cRef;
159 }
160
161 STDMETHODIMP GetMarkerType(MFSTREAMSINK_MARKER_TYPE *pType) override
162 {
163 if (pType == NULL)
164 return E_POINTER;
165 *pType = m_eMarkerType;
166 return S_OK;
167 }
168
169 STDMETHODIMP GetMarkerValue(PROPVARIANT *pvar) override
170 {
171 if (pvar == NULL)
172 return E_POINTER;
173 return PropVariantCopy(pvar, &m_varMarkerValue);
174 }
175
176 STDMETHODIMP GetContext(PROPVARIANT *pvar) override
177 {
178 if (pvar == NULL)
179 return E_POINTER;
180 return PropVariantCopy(pvar, &m_varContextValue);
181 }
182
183 protected:
184 MFSTREAMSINK_MARKER_TYPE m_eMarkerType;
185 PROPVARIANT m_varMarkerValue;
186 PROPVARIANT m_varContextValue;
187
188 private:
189 long m_cRef = 1;
190
191 Marker(MFSTREAMSINK_MARKER_TYPE eMarkerType) : m_eMarkerType(eMarkerType)
192 {
193 PropVariantInit(&m_varMarkerValue);
194 PropVariantInit(&m_varContextValue);
195 }
196
197 virtual ~Marker()
198 {
199 PropVariantClear(&m_varMarkerValue);
200 PropVariantClear(&m_varContextValue);
201 }
202 };
203
204 class MediaStream : public QObject, public IMFStreamSink, public IMFMediaTypeHandler
205 {
208 public:
209 static const DWORD DEFAULT_MEDIA_STREAM_ID = 0x0;
210
211 MediaStream(IMFMediaSink *parent, MFVideoRendererControl *rendererControl)
212 : m_workQueueCB(this, &MediaStream::onDispatchWorkItem)
213 , m_rendererControl(rendererControl)
214 {
215 m_sink = parent;
216
217 if (FAILED(MFCreateEventQueue(&m_eventQueue)))
218 qWarning("Failed to create mf event queue!");
219 if (FAILED(MFAllocateWorkQueue(&m_workQueueId)))
220 qWarning("Failed to allocated mf work queue!");
221 }
222
224 {
225 Q_ASSERT(m_shutdown);
226 }
227
228 //from IUnknown
229 STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) override
230 {
231 if (!ppvObject)
232 return E_POINTER;
233 if (riid == IID_IMFStreamSink) {
234 *ppvObject = static_cast<IMFStreamSink*>(this);
235 } else if (riid == IID_IMFMediaEventGenerator) {
236 *ppvObject = static_cast<IMFMediaEventGenerator*>(this);
237 } else if (riid == IID_IMFMediaTypeHandler) {
238 *ppvObject = static_cast<IMFMediaTypeHandler*>(this);
239 } else if (riid == IID_IUnknown) {
240 *ppvObject = static_cast<IUnknown*>(static_cast<IMFStreamSink*>(this));
241 } else {
242 *ppvObject = NULL;
243 return E_NOINTERFACE;
244 }
245 AddRef();
246 return S_OK;
247 }
248
249 STDMETHODIMP_(ULONG) AddRef(void) override
250 {
251 return InterlockedIncrement(&m_cRef);
252 }
253
254 STDMETHODIMP_(ULONG) Release(void) override
255 {
256 LONG cRef = InterlockedDecrement(&m_cRef);
257 if (cRef == 0)
258 delete this;
259 // For thread safety, return a temporary variable.
260 return cRef;
261 }
262
263 //from IMFMediaEventGenerator
264 STDMETHODIMP GetEvent(
265 DWORD dwFlags,
266 IMFMediaEvent **ppEvent) override
267 {
268 // GetEvent can block indefinitely, so we don't hold the lock.
269 // This requires some juggling with the event queue pointer.
270 HRESULT hr = S_OK;
271 IMFMediaEventQueue *queue = NULL;
272
273 m_mutex.lock();
274 if (m_shutdown)
275 hr = MF_E_SHUTDOWN;
276 if (SUCCEEDED(hr)) {
277 queue = m_eventQueue;
278 queue->AddRef();
279 }
280 m_mutex.unlock();
281
282 // Now get the event.
283 if (SUCCEEDED(hr)) {
284 hr = queue->GetEvent(dwFlags, ppEvent);
285 queue->Release();
286 }
287
288 return hr;
289 }
290
291 STDMETHODIMP BeginGetEvent(
292 IMFAsyncCallback *pCallback,
293 IUnknown *punkState) override
294 {
295 QMutexLocker locker(&m_mutex);
296 if (m_shutdown)
297 return MF_E_SHUTDOWN;
298 return m_eventQueue->BeginGetEvent(pCallback, punkState);
299 }
300
301 STDMETHODIMP EndGetEvent(
302 IMFAsyncResult *pResult,
303 IMFMediaEvent **ppEvent) override
304 {
305 QMutexLocker locker(&m_mutex);
306 if (m_shutdown)
307 return MF_E_SHUTDOWN;
308 return m_eventQueue->EndGetEvent(pResult, ppEvent);
309 }
310
311 STDMETHODIMP QueueEvent(
312 MediaEventType met,
313 REFGUID guidExtendedType,
314 HRESULT hrStatus,
315 const PROPVARIANT *pvValue) override
316 {
317#ifdef DEBUG_MEDIAFOUNDATION
318 qDebug() << "MediaStream::QueueEvent" << met;
319#endif
320 QMutexLocker locker(&m_mutex);
321 if (m_shutdown)
322 return MF_E_SHUTDOWN;
323 return m_eventQueue->QueueEventParamVar(met, guidExtendedType, hrStatus, pvValue);
324 }
325
326 //from IMFStreamSink
327 STDMETHODIMP GetMediaSink(
328 IMFMediaSink **ppMediaSink) override
329 {
330 QMutexLocker locker(&m_mutex);
331 if (m_shutdown)
332 return MF_E_SHUTDOWN;
333 else if (!ppMediaSink)
334 return E_INVALIDARG;
335
336 m_sink->AddRef();
337 *ppMediaSink = m_sink;
338 return S_OK;
339 }
340
341 STDMETHODIMP GetIdentifier(
342 DWORD *pdwIdentifier) override
343 {
344 *pdwIdentifier = MediaStream::DEFAULT_MEDIA_STREAM_ID;
345 return S_OK;
346 }
347
349 IMFMediaTypeHandler **ppHandler) override
350 {
351 LPVOID handler = NULL;
352 HRESULT hr = QueryInterface(IID_IMFMediaTypeHandler, &handler);
353 *ppHandler = (IMFMediaTypeHandler*)(handler);
354 return hr;
355 }
356
357 STDMETHODIMP ProcessSample(
358 IMFSample *pSample) override
359 {
360 if (pSample == NULL)
361 return E_INVALIDARG;
362 HRESULT hr = S_OK;
363 QMutexLocker locker(&m_mutex);
364 if (m_shutdown)
365 return MF_E_SHUTDOWN;
366
367 if (!m_prerolling) {
368 hr = validateOperation(OpProcessSample);
369 if (FAILED(hr))
370 return hr;
371 }
372
373 pSample->AddRef();
374 m_sampleQueue.push_back(pSample);
375
376 // Unless we are paused, start an async operation to dispatch the next sample.
377 if (m_state != State_Paused)
378 hr = queueAsyncOperation(OpProcessSample);
379
380 return hr;
381 }
382
383 STDMETHODIMP PlaceMarker(
384 MFSTREAMSINK_MARKER_TYPE eMarkerType,
385 const PROPVARIANT *pvarMarkerValue,
386 const PROPVARIANT *pvarContextValue) override
387 {
388 HRESULT hr = S_OK;
389 QMutexLocker locker(&m_mutex);
390 IMarker *pMarker = NULL;
391 if (m_shutdown)
392 return MF_E_SHUTDOWN;
393
394 hr = validateOperation(OpPlaceMarker);
395 if (FAILED(hr))
396 return hr;
397
398 // Create a marker object and put it on the sample queue.
399 hr = Marker::Create(eMarkerType, pvarMarkerValue, pvarContextValue, &pMarker);
400 if (FAILED(hr))
401 return hr;
402
403 m_sampleQueue.push_back(pMarker);
404
405 // Unless we are paused, start an async operation to dispatch the next sample/marker.
406 if (m_state != State_Paused)
407 hr = queueAsyncOperation(OpPlaceMarker); // Increments ref count on pOp.
408 return hr;
409 }
410
411 STDMETHODIMP Flush(void) override
412 {
413#ifdef DEBUG_MEDIAFOUNDATION
414 qDebug() << "MediaStream::Flush";
415#endif
416 QMutexLocker locker(&m_mutex);
417 if (m_shutdown)
418 return MF_E_SHUTDOWN;
419 // Note: Even though we are flushing data, we still need to send
420 // any marker events that were queued.
421 clearBufferCache();
422 return processSamplesFromQueue(DropSamples);
423 }
424
425 //from IMFMediaTypeHandler
427 IMFMediaType *pMediaType,
428 IMFMediaType **ppMediaType) override
429 {
430 if (ppMediaType)
431 *ppMediaType = NULL;
432 QMutexLocker locker(&m_mutex);
433 if (m_shutdown)
434 return MF_E_SHUTDOWN;
435
436 int index = getMediaTypeIndex(pMediaType);
437 if (index < 0) {
438 if (ppMediaType && m_mediaTypes.size() > 0) {
439 *ppMediaType = m_mediaTypes[0];
440 (*ppMediaType)->AddRef();
441 }
442 return MF_E_INVALIDMEDIATYPE;
443 }
444
445 BOOL compressed = TRUE;
446 pMediaType->IsCompressedFormat(&compressed);
447 if (compressed) {
448 if (ppMediaType && (SUCCEEDED(MFCreateMediaType(ppMediaType)))) {
449 (*ppMediaType)->CopyAllItems(pMediaType);
450 (*ppMediaType)->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, TRUE);
451 (*ppMediaType)->SetUINT32(MF_MT_COMPRESSED, FALSE);
452 (*ppMediaType)->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
453 }
454 return MF_E_INVALIDMEDIATYPE;
455 }
456
457 return S_OK;
458 }
459
460 STDMETHODIMP GetMediaTypeCount(
461 DWORD *pdwTypeCount) override
462 {
463 if (pdwTypeCount == NULL)
464 return E_INVALIDARG;
465 QMutexLocker locker(&m_mutex);
466 *pdwTypeCount = DWORD(m_mediaTypes.size());
467 return S_OK;
468 }
469
471 DWORD dwIndex,
472 IMFMediaType **ppType) override
473 {
474 if (ppType == NULL)
475 return E_INVALIDARG;
476 HRESULT hr = S_OK;
477 QMutexLocker locker(&m_mutex);
478 if (m_shutdown)
479 hr = MF_E_SHUTDOWN;
480
481 if (SUCCEEDED(hr)) {
482 if (dwIndex >= DWORD(m_mediaTypes.size()))
483 hr = MF_E_NO_MORE_TYPES;
484 }
485
486 if (SUCCEEDED(hr)) {
487 *ppType = m_mediaTypes[dwIndex];
488 (*ppType)->AddRef();
489 }
490 return hr;
491 }
492
494 IMFMediaType *pMediaType) override
495 {
496 HRESULT hr = S_OK;
497 QMutexLocker locker(&m_mutex);
498 if (m_shutdown)
499 return MF_E_SHUTDOWN;
500
501 DWORD flag = MF_MEDIATYPE_EQUAL_MAJOR_TYPES |
502 MF_MEDIATYPE_EQUAL_FORMAT_TYPES |
503 MF_MEDIATYPE_EQUAL_FORMAT_DATA;
504
505 if (m_currentMediaType && (m_currentMediaType->IsEqual(pMediaType, &flag) == S_OK))
506 return S_OK;
507
508 hr = validateOperation(OpSetMediaType);
509
510 if (SUCCEEDED(hr)) {
511 int index = getMediaTypeIndex(pMediaType);
512 if (index >= 0) {
513 UINT64 size;
514 hr = pMediaType->GetUINT64(MF_MT_FRAME_SIZE, &size);
515 if (SUCCEEDED(hr)) {
516 m_currentFormatIndex = index;
517 int width = int(HI32(size));
518 int height = int(LO32(size));
519 QVideoFrameFormat format(QSize(width, height), m_pixelFormats[index]);
520 m_surfaceFormat = format;
521
522 MFVideoArea viewport;
523 if (SUCCEEDED(pMediaType->GetBlob(MF_MT_GEOMETRIC_APERTURE,
524 reinterpret_cast<UINT8*>(&viewport),
525 sizeof(MFVideoArea),
526 NULL))) {
527
528 m_surfaceFormat.setViewport(QRect(viewport.OffsetX.value,
529 viewport.OffsetY.value,
530 viewport.Area.cx,
531 viewport.Area.cy));
532 }
533
534 if (FAILED(pMediaType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&m_bytesPerLine))) {
535 m_bytesPerLine = getBytesPerLine(format);
536 }
537
538 m_state = State_Ready;
539 if (m_currentMediaType)
540 m_currentMediaType->Release();
541 m_currentMediaType = pMediaType;
542 pMediaType->AddRef();
543 }
544 } else {
545 hr = MF_E_INVALIDREQUEST;
546 }
547 }
548 return hr;
549 }
550
552 IMFMediaType **ppMediaType) override
553 {
554 if (ppMediaType == NULL)
555 return E_INVALIDARG;
556 QMutexLocker locker(&m_mutex);
557 if (m_shutdown)
558 return MF_E_SHUTDOWN;
559 if (m_currentFormatIndex < 0)
560 return MF_E_NOT_INITIALIZED;
561 *ppMediaType = m_currentMediaType;
562 (*ppMediaType)->AddRef();
563 return S_OK;
564 }
565
566 STDMETHODIMP GetMajorType(
567 GUID *pguidMajorType) override
568 {
569 if (pguidMajorType == NULL)
570 return E_INVALIDARG;
571 *pguidMajorType = MFMediaType_Video;
572 return S_OK;
573 }
574
575 //
577 {
578 m_mutex.lock();
579 m_videoSink = sink;
580 m_mutex.unlock();
581 supportedFormatsChanged();
582 }
583
584 void setClock(IMFPresentationClock *presentationClock)
585 {
586 QMutexLocker locker(&m_mutex);
587 if (!m_shutdown) {
588 if (m_presentationClock)
589 m_presentationClock->Release();
590 m_presentationClock = presentationClock;
591 if (m_presentationClock)
592 m_presentationClock->AddRef();
593 }
594 }
595
596 void shutdown()
597 {
598 QMutexLocker locker(&m_mutex);
599 Q_ASSERT(!m_shutdown);
600
601 if (m_currentMediaType) {
602 m_currentMediaType->Release();
603 m_currentMediaType = NULL;
604 m_currentFormatIndex = -1;
605 }
606
607 if (m_eventQueue)
608 m_eventQueue->Shutdown();
609
610 MFUnlockWorkQueue(m_workQueueId);
611
612 if (m_presentationClock) {
613 m_presentationClock->Release();
614 m_presentationClock = NULL;
615 }
616
617 clearMediaTypes();
618 clearSampleQueue();
619 clearBufferCache();
620
621 if (m_eventQueue) {
622 m_eventQueue->Release();
623 m_eventQueue = NULL;
624 }
625
626 m_shutdown = true;
627 }
628
629 HRESULT startPreroll(MFTIME hnsUpcomingStartTime)
630 {
631 QMutexLocker locker(&m_mutex);
632 HRESULT hr = validateOperation(OpPreroll);
633 if (SUCCEEDED(hr)) {
634 m_state = State_Prerolling;
635 m_prerollTargetTime = hnsUpcomingStartTime;
636 hr = queueAsyncOperation(OpPreroll);
637 }
638 return hr;
639 }
640
641 HRESULT finalize(IMFAsyncCallback *pCallback, IUnknown *punkState)
642 {
643 QMutexLocker locker(&m_mutex);
644 HRESULT hr = S_OK;
645 hr = validateOperation(OpFinalize);
646 if (SUCCEEDED(hr) && m_finalizeResult != NULL)
647 hr = MF_E_INVALIDREQUEST; // The operation is already pending.
648
649 // Create and store the async result object.
650 if (SUCCEEDED(hr))
651 hr = MFCreateAsyncResult(NULL, pCallback, punkState, &m_finalizeResult);
652
653 if (SUCCEEDED(hr)) {
654 m_state = State_Finalized;
655 hr = queueAsyncOperation(OpFinalize);
656 }
657 return hr;
658 }
659
661 {
662#ifdef DEBUG_MEDIAFOUNDATION
663 qDebug() << "MediaStream::start" << start;
664#endif
665 HRESULT hr = S_OK;
666 QMutexLocker locker(&m_mutex);
667 if (m_rate != 0)
668 hr = validateOperation(OpStart);
669
670 if (SUCCEEDED(hr)) {
671 MFTIME sysTime;
673 m_startTime = start; // Cache the start time.
674 else
675 m_presentationClock->GetCorrelatedTime(0, &m_startTime, &sysTime);
676 m_state = State_Started;
677 hr = queueAsyncOperation(OpStart);
678 }
679 return hr;
680 }
681
683 {
684#ifdef DEBUG_MEDIAFOUNDATION
685 qDebug() << "MediaStream::restart";
686#endif
687 QMutexLocker locker(&m_mutex);
688 HRESULT hr = validateOperation(OpRestart);
689 if (SUCCEEDED(hr)) {
690 m_state = State_Started;
691 hr = queueAsyncOperation(OpRestart);
692 }
693 return hr;
694 }
695
697 {
698#ifdef DEBUG_MEDIAFOUNDATION
699 qDebug() << "MediaStream::stop";
700#endif
701 QMutexLocker locker(&m_mutex);
702 HRESULT hr = validateOperation(OpStop);
703 if (SUCCEEDED(hr)) {
704 m_state = State_Stopped;
705 hr = queueAsyncOperation(OpStop);
706 }
707 return hr;
708 }
709
711 {
712#ifdef DEBUG_MEDIAFOUNDATION
713 qDebug() << "MediaStream::pause";
714#endif
715 QMutexLocker locker(&m_mutex);
716 HRESULT hr = validateOperation(OpPause);
717 if (SUCCEEDED(hr)) {
718 m_state = State_Paused;
719 hr = queueAsyncOperation(OpPause);
720 }
721 return hr;
722 }
723
725 {
726#ifdef DEBUG_MEDIAFOUNDATION
727 qDebug() << "MediaStream::setRate" << rate;
728#endif
729 QMutexLocker locker(&m_mutex);
730 HRESULT hr = validateOperation(OpSetRate);
731 if (SUCCEEDED(hr)) {
732 m_rate = rate;
733 hr = queueAsyncOperation(OpSetRate);
734 }
735 return hr;
736 }
737
739 {
740 QMutexLocker locker(&m_mutex);
741 m_pixelFormats.clear();
742 clearMediaTypes();
743 if (!m_videoSink)
744 return;
745 for (int f = 0; f < QVideoFrameFormat::NPixelFormats; ++f) {
747 IMFMediaType *mediaType;
748 if (FAILED(MFCreateMediaType(&mediaType))) {
749 qWarning("Failed to create mf media type!");
750 continue;
751 }
752 mediaType->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, TRUE);
753 mediaType->SetUINT32(MF_MT_COMPRESSED, FALSE);
754 mediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
755 mediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
756 switch (format) {
759 mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_ARGB32);
760 break;
762 mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
763 break;
766 mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_AYUV);
767 break;
769 mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_I420);
770 break;
772 mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_UYVY);
773 break;
775 mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YV12);
776 break;
778 mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12);
779 break;
780 default:
781 mediaType->Release();
782 continue;
783 }
784 // #### QAbstractVideoSurface::supportedPixelFormats() returns formats in descending
785 // order of preference, while IMFMediaTypeHandler is supposed to return supported
786 // formats in ascending order of preference. We need to reverse the list.
787 m_pixelFormats.prepend(format);
788 m_mediaTypes.prepend(mediaType);
789 }
790 }
791
792 void present()
793 {
794 QMutexLocker locker(&m_mutex);
795 if (!m_scheduledBuffer)
796 return;
798 new MediaSampleVideoBuffer(m_scheduledBuffer, m_bytesPerLine), m_surfaceFormat);
799 frame.setStartTime(m_bufferStartTime * 0.1);
800 frame.setEndTime((m_bufferStartTime + m_bufferDuration) * 0.1);
801 m_videoSink->platformVideoSink()->setVideoFrame(frame);
802 m_scheduledBuffer->Release();
803 m_scheduledBuffer = NULL;
804 if (m_rate != 0)
805 schedulePresentation(true);
806 }
807
809 {
810 QMutexLocker locker(&m_mutex);
811 if (m_scheduledBuffer) {
812 m_scheduledBuffer->Release();
813 m_scheduledBuffer = NULL;
814 schedulePresentation(true);
815 }
816 }
817
818 enum
819 {
820 PresentSurface
821 };
822
823 class PresentEvent : public QEvent
824 {
825 public:
826 PresentEvent(MFTIME targetTime)
827 : QEvent(QEvent::Type(PresentSurface))
828 , m_time(targetTime)
829 {
830 }
831
832 MFTIME targetTime()
833 {
834 return m_time;
835 }
836
837 private:
838 MFTIME m_time;
839 };
840
841 protected:
843
844 private:
845 enum FlushState
846 {
847 DropSamples = 0,
848 WriteSamples
849 };
850
851 // State enum: Defines the current state of the stream.
852 enum State
853 {
854 State_TypeNotSet = 0, // No media type is set
855 State_Ready, // Media type is set, Start has never been called.
856 State_Prerolling,
857 State_Started,
858 State_Paused,
859 State_Stopped,
860 State_WaitForSurfaceStart,
861 State_Finalized,
862 State_Count = State_Finalized + 1 // Number of states
863 };
864
865 // StreamOperation: Defines various operations that can be performed on the stream.
866 enum StreamOperation
867 {
868 OpSetMediaType = 0,
869 OpStart,
870 OpPreroll,
871 OpRestart,
872 OpPause,
873 OpStop,
874 OpSetRate,
875 OpProcessSample,
876 OpPlaceMarker,
877 OpFinalize,
878
879 Op_Count = OpFinalize + 1 // Number of operations
880 };
881
882 // AsyncOperation:
883 // Used to queue asynchronous operations. When we call MFPutWorkItem, we use this
884 // object for the callback state (pState). Then, when the callback is invoked,
885 // we can use the object to determine which asynchronous operation to perform.
886 class AsyncOperation : public IUnknown
887 {
888 public:
889 AsyncOperation(StreamOperation op)
890 :m_op(op)
891 {
892 }
893
894 StreamOperation m_op; // The operation to perform.
895
896 //from IUnknown
897 STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override
898 {
899 if (!ppv)
900 return E_POINTER;
901 if (iid == IID_IUnknown) {
902 *ppv = static_cast<IUnknown*>(this);
903 } else {
904 *ppv = NULL;
905 return E_NOINTERFACE;
906 }
907 AddRef();
908 return S_OK;
909 }
910 STDMETHODIMP_(ULONG) AddRef() override
911 {
912 return InterlockedIncrement(&m_cRef);
913 }
914 STDMETHODIMP_(ULONG) Release() override
915 {
916 ULONG uCount = InterlockedDecrement(&m_cRef);
917 if (uCount == 0)
918 delete this;
919 // For thread safety, return a temporary variable.
920 return uCount;
921 }
922
923 private:
924 long m_cRef = 1;
925 virtual ~AsyncOperation()
926 {
927 Q_ASSERT(m_cRef == 0);
928 }
929 };
930
931 // ValidStateMatrix: Defines a look-up table that says which operations
932 // are valid from which states.
933 static BOOL ValidStateMatrix[State_Count][Op_Count];
934
935 long m_cRef = 1;
936 QMutex m_mutex;
937
938 IMFMediaType *m_currentMediaType = nullptr;
939 State m_state = State_TypeNotSet;
940 IMFMediaSink *m_sink = nullptr;
941 IMFMediaEventQueue *m_eventQueue = nullptr;
942 DWORD m_workQueueId = 0;
943 AsyncCallback<MediaStream> m_workQueueCB;
944 QList<IUnknown*> m_sampleQueue;
945 IMFAsyncResult *m_finalizeResult = nullptr; // Result object for Finalize operation.
946 MFTIME m_startTime = 0; // Presentation time when the clock started.
947
948 bool m_shutdown = false;
949 QList<IMFMediaType*> m_mediaTypes;
951 int m_currentFormatIndex = -1;
952 int m_bytesPerLine = 0;
953 QVideoFrameFormat m_surfaceFormat;
954 QVideoSink *m_videoSink = nullptr;
955 MFVideoRendererControl *m_rendererControl = nullptr;
956
957 void clearMediaTypes()
958 {
959 for (IMFMediaType* mediaType : std::as_const(m_mediaTypes))
960 mediaType->Release();
961 m_mediaTypes.clear();
962 }
963
964 int getMediaTypeIndex(IMFMediaType *mt)
965 {
966 GUID majorType;
967 if (FAILED(mt->GetMajorType(&majorType)))
968 return -1;
969 if (majorType != MFMediaType_Video)
970 return -1;
971
972 GUID subType;
973 if (FAILED(mt->GetGUID(MF_MT_SUBTYPE, &subType)))
974 return -1;
975
976 for (int index = 0; index < m_mediaTypes.size(); ++index) {
977 GUID st;
978 m_mediaTypes[index]->GetGUID(MF_MT_SUBTYPE, &st);
979 if (st == subType)
980 return index;
981 }
982 return -1;
983 }
984
985 int getBytesPerLine(const QVideoFrameFormat &format)
986 {
987 switch (format.pixelFormat()) {
988 // 32 bpp packed formats.
994 return format.frameWidth() * 4;
995 // 16 bpp packed formats.
998 return PAD_TO_DWORD(format.frameWidth() * 2);
999 // Planar formats.
1007 return PAD_TO_DWORD(format.frameWidth());
1008 default:
1009 return 0;
1010 }
1011 }
1012
1013 // Callback for MFPutWorkItem.
1014 HRESULT onDispatchWorkItem(IMFAsyncResult* pAsyncResult)
1015 {
1016 QMutexLocker locker(&m_mutex);
1017 if (m_shutdown)
1018 return MF_E_SHUTDOWN;
1019
1020 HRESULT hr = S_OK;
1021 IUnknown *pState = NULL;
1022 hr = pAsyncResult->GetState(&pState);
1023 if (SUCCEEDED(hr)) {
1024 // The state object is an AsncOperation object.
1025 AsyncOperation *pOp = (AsyncOperation*)pState;
1026 StreamOperation op = pOp->m_op;
1027 switch (op) {
1028 case OpStart:
1029 endPreroll(S_FALSE);
1030 schedulePresentation(true);
1031 // fallthrough
1032 case OpRestart:
1033 endPreroll(S_FALSE);
1034 if (SUCCEEDED(hr)) {
1035 // Send MEStreamSinkStarted.
1036 hr = queueEvent(MEStreamSinkStarted, GUID_NULL, hr, NULL);
1037 // Kick things off by requesting samples...
1038 schedulePresentation(true);
1039 // There might be samples queue from earlier (ie, while paused).
1040 if (SUCCEEDED(hr))
1041 hr = processSamplesFromQueue(WriteSamples);
1042 }
1043 break;
1044 case OpPreroll:
1045 beginPreroll();
1046 break;
1047 case OpStop:
1048 // Drop samples from queue.
1049 hr = processSamplesFromQueue(DropSamples);
1050 clearBufferCache();
1051 // Send the event even if the previous call failed.
1052 hr = queueEvent(MEStreamSinkStopped, GUID_NULL, hr, NULL);
1053 break;
1054 case OpPause:
1055 hr = queueEvent(MEStreamSinkPaused, GUID_NULL, hr, NULL);
1056 break;
1057 case OpSetRate:
1058 hr = queueEvent(MEStreamSinkRateChanged, GUID_NULL, S_OK, NULL);
1059 break;
1060 case OpProcessSample:
1061 case OpPlaceMarker:
1062 hr = dispatchProcessSample(pOp);
1063 break;
1064 case OpFinalize:
1065 endPreroll(S_FALSE);
1066 hr = dispatchFinalize(pOp);
1067 break;
1068 default:
1069 break;
1070 }
1071 }
1072
1073 if (pState)
1074 pState->Release();
1075 return hr;
1076 }
1077
1078
1079 HRESULT queueEvent(MediaEventType met, REFGUID guidExtendedType, HRESULT hrStatus, const PROPVARIANT* pvValue)
1080 {
1081 HRESULT hr = S_OK;
1082 if (m_shutdown)
1083 hr = MF_E_SHUTDOWN;
1084 if (SUCCEEDED(hr))
1085 hr = m_eventQueue->QueueEventParamVar(met, guidExtendedType, hrStatus, pvValue);
1086 return hr;
1087 }
1088
1089 HRESULT validateOperation(StreamOperation op)
1090 {
1091 Q_ASSERT(!m_shutdown);
1092 if (ValidStateMatrix[m_state][op])
1093 return S_OK;
1094 else
1095 return MF_E_INVALIDREQUEST;
1096 }
1097
1098 HRESULT queueAsyncOperation(StreamOperation op)
1099 {
1100 HRESULT hr = S_OK;
1101 AsyncOperation *asyncOp = new AsyncOperation(op);
1102 if (asyncOp == NULL)
1103 hr = E_OUTOFMEMORY;
1104
1105 if (SUCCEEDED(hr))
1106 hr = MFPutWorkItem(m_workQueueId, &m_workQueueCB, asyncOp);
1107
1108 if (asyncOp)
1109 asyncOp->Release();
1110
1111 return hr;
1112 }
1113
1114 HRESULT processSamplesFromQueue(FlushState bFlushData)
1115 {
1116 HRESULT hr = S_OK;
1117 QList<IUnknown*>::Iterator pos = m_sampleQueue.begin();
1118 // Enumerate all of the samples/markers in the queue.
1119 while (pos != m_sampleQueue.end()) {
1120 IUnknown *pUnk = NULL;
1121 IMarker *pMarker = NULL;
1122 IMFSample *pSample = NULL;
1123 pUnk = *pos;
1124 // Figure out if this is a marker or a sample.
1125 if (SUCCEEDED(hr)) {
1126 hr = pUnk->QueryInterface(IID_IMarker, (void**)&pMarker);
1127 if (hr == E_NOINTERFACE)
1128 hr = pUnk->QueryInterface(IID_IMFSample, (void**)&pSample);
1129 }
1130
1131 // Now handle the sample/marker appropriately.
1132 if (SUCCEEDED(hr)) {
1133 if (pMarker) {
1134 hr = sendMarkerEvent(pMarker, bFlushData);
1135 } else {
1136 Q_ASSERT(pSample != NULL); // Not a marker, must be a sample
1137 if (bFlushData == WriteSamples)
1138 hr = processSampleData(pSample);
1139 }
1140 }
1141 if (pMarker)
1142 pMarker->Release();
1143 if (pSample)
1144 pSample->Release();
1145
1146 if (FAILED(hr))
1147 break;
1148
1149 pos++;
1150 }
1151
1152 clearSampleQueue();
1153 return hr;
1154 }
1155
1156 void beginPreroll()
1157 {
1158 if (m_prerolling)
1159 return;
1160 m_prerolling = true;
1161 clearSampleQueue();
1162 clearBufferCache();
1163 queueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL);
1164 }
1165
1166 void endPreroll(HRESULT hrStatus)
1167 {
1168 if (!m_prerolling)
1169 return;
1170 m_prerolling = false;
1171 queueEvent(MEStreamSinkPrerolled, GUID_NULL, hrStatus, NULL);
1172 }
1173 MFTIME m_prerollTargetTime = 0;
1174 bool m_prerolling = false;
1175
1176 void clearSampleQueue() {
1177 for (IUnknown* sample : std::as_const(m_sampleQueue))
1178 sample->Release();
1179 m_sampleQueue.clear();
1180 }
1181
1182 HRESULT sendMarkerEvent(IMarker *pMarker, FlushState FlushState)
1183 {
1184 HRESULT hr = S_OK;
1185 HRESULT hrStatus = S_OK; // Status code for marker event.
1186 if (FlushState == DropSamples)
1187 hrStatus = E_ABORT;
1188
1189 PROPVARIANT var;
1190 PropVariantInit(&var);
1191
1192 // Get the context data.
1193 hr = pMarker->GetContext(&var);
1194
1195 if (SUCCEEDED(hr))
1196 hr = queueEvent(MEStreamSinkMarker, GUID_NULL, hrStatus, &var);
1197
1198 PropVariantClear(&var);
1199 return hr;
1200 }
1201
1202 HRESULT dispatchProcessSample(AsyncOperation* pOp)
1203 {
1204 HRESULT hr = S_OK;
1205 Q_ASSERT(pOp != NULL);
1206 Q_UNUSED(pOp);
1207 hr = processSamplesFromQueue(WriteSamples);
1208 // We are in the middle of an asynchronous operation, so if something failed, send an error.
1209 if (FAILED(hr))
1210 hr = queueEvent(MEError, GUID_NULL, hr, NULL);
1211
1212 return hr;
1213 }
1214
1215 HRESULT dispatchFinalize(AsyncOperation*)
1216 {
1217 HRESULT hr = S_OK;
1218 // Write any samples left in the queue...
1219 hr = processSamplesFromQueue(WriteSamples);
1220
1221 // Set the async status and invoke the callback.
1222 m_finalizeResult->SetStatus(hr);
1223 hr = MFInvokeCallback(m_finalizeResult);
1224 return hr;
1225 }
1226
1227 HRESULT processSampleData(IMFSample *pSample)
1228 {
1229 m_sampleRequested = false;
1230
1231 LONGLONG time, duration = -1;
1232 HRESULT hr = pSample->GetSampleTime(&time);
1233 if (SUCCEEDED(hr))
1234 pSample->GetSampleDuration(&duration);
1235
1236 if (m_prerolling) {
1237 if (SUCCEEDED(hr) && ((time - m_prerollTargetTime) * m_rate) >= 0) {
1238 IMFMediaBuffer *pBuffer = NULL;
1239 hr = pSample->ConvertToContiguousBuffer(&pBuffer);
1240 if (SUCCEEDED(hr)) {
1241 SampleBuffer sb;
1242 sb.m_buffer = pBuffer;
1243 sb.m_time = time;
1244 sb.m_duration = duration;
1245 m_bufferCache.push_back(sb);
1246 endPreroll(S_OK);
1247 }
1248 } else {
1249 queueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL);
1250 }
1251 } else {
1252 bool requestSample = true;
1253 // If the time stamp is too early, just discard this sample.
1254 if (SUCCEEDED(hr) && ((time - m_startTime) * m_rate) >= 0) {
1255 IMFMediaBuffer *pBuffer = NULL;
1256 hr = pSample->ConvertToContiguousBuffer(&pBuffer);
1257 if (SUCCEEDED(hr)) {
1258 SampleBuffer sb;
1259 sb.m_buffer = pBuffer;
1260 sb.m_time = time;
1261 sb.m_duration = duration;
1262 m_bufferCache.push_back(sb);
1263 }
1264 if (m_rate == 0)
1265 requestSample = false;
1266 }
1267 schedulePresentation(requestSample);
1268 }
1269 return hr;
1270 }
1271
1272 class SampleBuffer
1273 {
1274 public:
1275 IMFMediaBuffer *m_buffer;
1276 LONGLONG m_time;
1277 LONGLONG m_duration;
1278 };
1279 QList<SampleBuffer> m_bufferCache;
1280 static const int BUFFER_CACHE_SIZE = 2;
1281
1282 void clearBufferCache()
1283 {
1284 for (SampleBuffer sb : std::as_const(m_bufferCache))
1285 sb.m_buffer->Release();
1286 m_bufferCache.clear();
1287
1288 if (m_scheduledBuffer) {
1289 m_scheduledBuffer->Release();
1290 m_scheduledBuffer = NULL;
1291 }
1292 }
1293
1294 void schedulePresentation(bool requestSample)
1295 {
1296 if (m_state == State_Paused || m_state == State_Prerolling)
1297 return;
1298 if (!m_scheduledBuffer) {
1299 //get time from presentation time
1300 MFTIME currentTime = m_startTime, sysTime;
1301 bool timeOK = true;
1302 if (m_rate != 0) {
1303 if (FAILED(m_presentationClock->GetCorrelatedTime(0, &currentTime, &sysTime)))
1304 timeOK = false;
1305 }
1306 while (!m_bufferCache.isEmpty()) {
1307 SampleBuffer sb = m_bufferCache.takeFirst();
1308 if (timeOK && ((sb.m_time - currentTime) * m_rate) < 0) {
1309 sb.m_buffer->Release();
1310#ifdef DEBUG_MEDIAFOUNDATION
1311 qDebug() << "currentPresentTime =" << float(currentTime / 10000) * 0.001f << " and sampleTime is" << float(sb.m_time / 10000) * 0.001f;
1312#endif
1313 continue;
1314 }
1315 m_scheduledBuffer = sb.m_buffer;
1316 m_bufferStartTime = sb.m_time;
1317 m_bufferDuration = sb.m_duration;
1318 QCoreApplication::postEvent(m_rendererControl, new PresentEvent(sb.m_time));
1319 if (m_rate == 0)
1320 queueEvent(MEStreamSinkScrubSampleComplete, GUID_NULL, S_OK, NULL);
1321 break;
1322 }
1323 }
1324 if (requestSample && !m_sampleRequested && m_bufferCache.size() < BUFFER_CACHE_SIZE) {
1325 m_sampleRequested = true;
1326 queueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL);
1327 }
1328 }
1329 IMFMediaBuffer *m_scheduledBuffer = nullptr;
1330 MFTIME m_bufferStartTime = -1;
1331 MFTIME m_bufferDuration = -1;
1332 IMFPresentationClock *m_presentationClock = nullptr;
1333 bool m_sampleRequested = false;
1334 float m_rate = 1.f;
1335 };
1336
1337 BOOL MediaStream::ValidStateMatrix[MediaStream::State_Count][MediaStream::Op_Count] =
1338 {
1339 // States: Operations:
1340 // SetType Start Preroll, Restart Pause Stop SetRate Sample Marker Finalize
1341 /* NotSet */ TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
1342
1343 /* Ready */ TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE,
1344
1345 /* Prerolling */ TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
1346
1347 /* Start */ FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
1348
1349 /* Pause */ FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
1350
1351 /* Stop */ FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE,
1352
1353 /*WaitForSurfaceStart*/ FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE,
1354
1355 /* Final */ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE
1356
1357 // Note about states:
1358 // 1. OnClockRestart should only be called from paused state.
1359 // 2. While paused, the sink accepts samples but does not process them.
1360 };
1361
1362 class MediaSink : public IMFFinalizableMediaSink,
1363 public IMFClockStateSink,
1364 public IMFMediaSinkPreroll,
1365 public IMFGetService,
1366 public IMFRateSupport
1367 {
1368 public:
1370 {
1371 m_stream = new MediaStream(this, rendererControl);
1372 }
1373
1374 virtual ~MediaSink()
1375 {
1376 Q_ASSERT(m_shutdown);
1377 }
1378
1379 void setSurface(QVideoSink *surface)
1380 {
1381 QMutexLocker locker(&m_mutex);
1382 if (m_shutdown)
1383 return;
1384 m_stream->setSink(surface);
1385 }
1386
1387 void present()
1388 {
1389 QMutexLocker locker(&m_mutex);
1390 if (m_shutdown)
1391 return;
1392 m_stream->present();
1393 }
1394
1396 {
1397 QMutexLocker locker(&m_mutex);
1398 if (m_shutdown)
1399 return;
1400 m_stream->clearScheduledFrame();
1401 }
1402
1403 MFTIME getTime()
1404 {
1405 QMutexLocker locker(&m_mutex);
1406 if (!m_presentationClock)
1407 return 0;
1408 MFTIME time, sysTime;
1409 m_presentationClock->GetCorrelatedTime(0, &time, &sysTime);
1410 return time;
1411 }
1412
1414 {
1415 QMutexLocker locker(&m_mutex);
1416 return m_playRate;
1417 }
1418
1419 //from IUnknown
1420 STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) override
1421 {
1422 if (!ppvObject)
1423 return E_POINTER;
1424 if (riid == IID_IMFMediaSink) {
1425 *ppvObject = static_cast<IMFMediaSink*>(this);
1426 } else if (riid == IID_IMFGetService) {
1427 *ppvObject = static_cast<IMFGetService*>(this);
1428 } else if (riid == IID_IMFMediaSinkPreroll) {
1429 *ppvObject = static_cast<IMFMediaSinkPreroll*>(this);
1430 } else if (riid == IID_IMFClockStateSink) {
1431 *ppvObject = static_cast<IMFClockStateSink*>(this);
1432 } else if (riid == IID_IMFRateSupport) {
1433 *ppvObject = static_cast<IMFRateSupport*>(this);
1434 } else if (riid == IID_IUnknown) {
1435 *ppvObject = static_cast<IUnknown*>(static_cast<IMFFinalizableMediaSink*>(this));
1436 } else {
1437 *ppvObject = NULL;
1438 return E_NOINTERFACE;
1439 }
1440 AddRef();
1441 return S_OK;
1442 }
1443
1444 STDMETHODIMP_(ULONG) AddRef(void) override
1445 {
1446 return InterlockedIncrement(&m_cRef);
1447 }
1448
1449 STDMETHODIMP_(ULONG) Release(void) override
1450 {
1451 LONG cRef = InterlockedDecrement(&m_cRef);
1452 if (cRef == 0)
1453 delete this;
1454 // For thread safety, return a temporary variable.
1455 return cRef;
1456 }
1457
1458 // IMFGetService methods
1459 STDMETHODIMP GetService(const GUID &guidService,
1460 const IID &riid,
1461 LPVOID *ppvObject) override
1462 {
1463 if (!ppvObject)
1464 return E_POINTER;
1465
1466 if (guidService != MF_RATE_CONTROL_SERVICE)
1467 return MF_E_UNSUPPORTED_SERVICE;
1468
1469 return QueryInterface(riid, ppvObject);
1470 }
1471
1472 //IMFMediaSinkPreroll
1473 STDMETHODIMP NotifyPreroll(MFTIME hnsUpcomingStartTime) override
1474 {
1475 QMutexLocker locker(&m_mutex);
1476 if (m_shutdown)
1477 return MF_E_SHUTDOWN;
1478 return m_stream->startPreroll(hnsUpcomingStartTime);
1479 }
1480
1481 //from IMFFinalizableMediaSink
1482 STDMETHODIMP BeginFinalize(IMFAsyncCallback *pCallback, IUnknown *punkState) override
1483 {
1484 QMutexLocker locker(&m_mutex);
1485 if (m_shutdown)
1486 return MF_E_SHUTDOWN;
1487 return m_stream->finalize(pCallback, punkState);
1488 }
1489
1490 STDMETHODIMP EndFinalize(IMFAsyncResult *pResult) override
1491 {
1492 HRESULT hr = S_OK;
1493 // Return the status code from the async result.
1494 if (pResult == NULL)
1495 hr = E_INVALIDARG;
1496 else
1497 hr = pResult->GetStatus();
1498 return hr;
1499 }
1500
1501 //from IMFMediaSink
1503 DWORD *pdwCharacteristics) override
1504 {
1505 QMutexLocker locker(&m_mutex);
1506 if (m_shutdown)
1507 return MF_E_SHUTDOWN;
1508 *pdwCharacteristics = MEDIASINK_FIXED_STREAMS | MEDIASINK_CAN_PREROLL;
1509 return S_OK;
1510 }
1511
1512 STDMETHODIMP AddStreamSink(
1513 DWORD,
1514 IMFMediaType *,
1515 IMFStreamSink **) override
1516 {
1517 QMutexLocker locker(&m_mutex);
1518 return m_shutdown ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED;
1519 }
1520
1521 STDMETHODIMP RemoveStreamSink(
1522 DWORD) override
1523 {
1524 QMutexLocker locker(&m_mutex);
1525 return m_shutdown ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED;
1526 }
1527
1529 DWORD *pcStreamSinkCount) override
1530 {
1531 QMutexLocker locker(&m_mutex);
1532 if (m_shutdown)
1533 return MF_E_SHUTDOWN;
1534 *pcStreamSinkCount = 1;
1535 return S_OK;
1536 }
1537
1539 DWORD dwIndex,
1540 IMFStreamSink **ppStreamSink) override
1541 {
1542 QMutexLocker locker(&m_mutex);
1543 if (m_shutdown)
1544 return MF_E_SHUTDOWN;
1545
1546 if (dwIndex != 0)
1547 return MF_E_INVALIDINDEX;
1548
1549 *ppStreamSink = m_stream;
1550 m_stream->AddRef();
1551 return S_OK;
1552 }
1553
1554 STDMETHODIMP GetStreamSinkById(
1555 DWORD dwStreamSinkIdentifier,
1556 IMFStreamSink **ppStreamSink) override
1557 {
1558 if (ppStreamSink == NULL)
1559 return E_INVALIDARG;
1560 if (dwStreamSinkIdentifier != MediaStream::DEFAULT_MEDIA_STREAM_ID)
1561 return MF_E_INVALIDSTREAMNUMBER;
1562
1563 QMutexLocker locker(&m_mutex);
1564 if (m_shutdown)
1565 return MF_E_SHUTDOWN;
1566
1567 *ppStreamSink = m_stream;
1568 m_stream->AddRef();
1569 return S_OK;
1570 }
1571
1573 IMFPresentationClock *pPresentationClock) override
1574 {
1575 QMutexLocker locker(&m_mutex);
1576 if (m_shutdown)
1577 return MF_E_SHUTDOWN;
1578
1579 if (m_presentationClock) {
1580 m_presentationClock->RemoveClockStateSink(this);
1581 m_presentationClock->Release();
1582 }
1583 m_presentationClock = pPresentationClock;
1584 if (m_presentationClock) {
1585 m_presentationClock->AddRef();
1586 m_presentationClock->AddClockStateSink(this);
1587 }
1588 m_stream->setClock(m_presentationClock);
1589 return S_OK;
1590 }
1591
1593 IMFPresentationClock **ppPresentationClock) override
1594 {
1595 QMutexLocker locker(&m_mutex);
1596 if (m_shutdown)
1597 return MF_E_SHUTDOWN;
1598 *ppPresentationClock = m_presentationClock;
1599 if (m_presentationClock) {
1600 m_presentationClock->AddRef();
1601 return S_OK;
1602 }
1603 return MF_E_NO_CLOCK;
1604 }
1605
1606 STDMETHODIMP Shutdown(void) override
1607 {
1608 QMutexLocker locker(&m_mutex);
1609 if (m_shutdown)
1610 return MF_E_SHUTDOWN;
1611
1612 m_stream->shutdown();
1613 if (m_presentationClock) {
1614 m_presentationClock->Release();
1615 m_presentationClock = NULL;
1616 }
1617 m_stream->Release();
1618 m_stream = NULL;
1619 m_shutdown = true;
1620 return S_OK;
1621 }
1622
1623 // IMFClockStateSink methods
1624 STDMETHODIMP OnClockStart(MFTIME, LONGLONG llClockStartOffset) override
1625 {
1626 QMutexLocker locker(&m_mutex);
1627 if (m_shutdown)
1628 return MF_E_SHUTDOWN;
1629 return m_stream->start(llClockStartOffset);
1630 }
1631
1632 STDMETHODIMP OnClockStop(MFTIME) override
1633 {
1634 QMutexLocker locker(&m_mutex);
1635 if (m_shutdown)
1636 return MF_E_SHUTDOWN;
1637 return m_stream->stop();
1638 }
1639
1640 STDMETHODIMP OnClockPause(MFTIME) override
1641 {
1642 QMutexLocker locker(&m_mutex);
1643 if (m_shutdown)
1644 return MF_E_SHUTDOWN;
1645 return m_stream->pause();
1646 }
1647
1648 STDMETHODIMP OnClockRestart(MFTIME) override
1649 {
1650 QMutexLocker locker(&m_mutex);
1651 if (m_shutdown)
1652 return MF_E_SHUTDOWN;
1653 return m_stream->restart();
1654 }
1655
1656 STDMETHODIMP OnClockSetRate(MFTIME, float flRate) override
1657 {
1658 QMutexLocker locker(&m_mutex);
1659 if (m_shutdown)
1660 return MF_E_SHUTDOWN;
1661 m_playRate = flRate;
1662 return m_stream->setRate(flRate);
1663 }
1664
1665 // IMFRateSupport methods
1666 STDMETHODIMP GetFastestRate(MFRATE_DIRECTION eDirection,
1667 BOOL fThin,
1668 float *pflRate) override
1669 {
1670 if (!pflRate)
1671 return E_POINTER;
1672
1673 *pflRate = (fThin ? 8.f : 2.0f) * (eDirection == MFRATE_FORWARD ? 1 : -1) ;
1674
1675 return S_OK;
1676 }
1677
1678 STDMETHODIMP GetSlowestRate(MFRATE_DIRECTION eDirection,
1679 BOOL fThin,
1680 float *pflRate) override
1681 {
1682 Q_UNUSED(eDirection);
1683 Q_UNUSED(fThin);
1684
1685 if (!pflRate)
1686 return E_POINTER;
1687
1688 // we support any rate
1689 *pflRate = 0.f;
1690
1691 return S_OK;
1692 }
1693
1694 STDMETHODIMP IsRateSupported(BOOL fThin,
1695 float flRate,
1696 float *pflNearestSupportedRate) override
1697 {
1698 HRESULT hr = S_OK;
1699
1700 if (!qFuzzyIsNull(flRate)) {
1701 MFRATE_DIRECTION direction = flRate > 0.f ? MFRATE_FORWARD
1702 : MFRATE_REVERSE;
1703
1704 float fastestRate = 0.f;
1705 float slowestRate = 0.f;
1706 GetFastestRate(direction, fThin, &fastestRate);
1707 GetSlowestRate(direction, fThin, &slowestRate);
1708
1709 if (direction == MFRATE_REVERSE)
1710 qSwap(fastestRate, slowestRate);
1711
1712 if (flRate < slowestRate || flRate > fastestRate) {
1713 hr = MF_E_UNSUPPORTED_RATE;
1714 if (pflNearestSupportedRate) {
1715 *pflNearestSupportedRate = qBound(slowestRate,
1716 flRate,
1717 fastestRate);
1718 }
1719 }
1720 } else if (pflNearestSupportedRate) {
1721 *pflNearestSupportedRate = flRate;
1722 }
1723
1724 return hr;
1725 }
1726
1727 private:
1728 long m_cRef = 1;
1729 QMutex m_mutex;
1730 bool m_shutdown = false;
1731 IMFPresentationClock *m_presentationClock = nullptr;
1732 MediaStream *m_stream = nullptr;
1733 float m_playRate = 1;
1734 };
1735
1736 class VideoRendererActivate : public IMFActivate
1737 {
1738 public:
1740 : m_cRef(1)
1741 , m_sink(0)
1742 , m_rendererControl(rendererControl)
1743 , m_attributes(0)
1744 , m_videoSink(0)
1745 {
1746 MFCreateAttributes(&m_attributes, 0);
1747 m_sink = new MediaSink(rendererControl);
1748 }
1749
1751 {
1752 m_attributes->Release();
1753 }
1754
1755 //from IUnknown
1756 STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) override
1757 {
1758 if (!ppvObject)
1759 return E_POINTER;
1760 if (riid == IID_IMFActivate) {
1761 *ppvObject = static_cast<IMFActivate*>(this);
1762 } else if (riid == IID_IMFAttributes) {
1763 *ppvObject = static_cast<IMFAttributes*>(this);
1764 } else if (riid == IID_IUnknown) {
1765 *ppvObject = static_cast<IUnknown*>(static_cast<IMFActivate*>(this));
1766 } else {
1767 *ppvObject = NULL;
1768 return E_NOINTERFACE;
1769 }
1770 AddRef();
1771 return S_OK;
1772 }
1773
1774 STDMETHODIMP_(ULONG) AddRef(void) override
1775 {
1776 return InterlockedIncrement(&m_cRef);
1777 }
1778
1779 STDMETHODIMP_(ULONG) Release(void) override
1780 {
1781 LONG cRef = InterlockedDecrement(&m_cRef);
1782 if (cRef == 0)
1783 delete this;
1784 // For thread safety, return a temporary variable.
1785 return cRef;
1786 }
1787
1788 //from IMFActivate
1789 STDMETHODIMP ActivateObject(REFIID riid, void **ppv) override
1790 {
1791 if (!ppv)
1792 return E_INVALIDARG;
1793 QMutexLocker locker(&m_mutex);
1794 if (!m_sink) {
1795 m_sink = new MediaSink(m_rendererControl);
1796 if (m_videoSink)
1797 m_sink->setSurface(m_videoSink);
1798 }
1799 return m_sink->QueryInterface(riid, ppv);
1800 }
1801
1802 STDMETHODIMP ShutdownObject(void) override
1803 {
1804 QMutexLocker locker(&m_mutex);
1805 HRESULT hr = S_OK;
1806 if (m_sink) {
1807 hr = m_sink->Shutdown();
1808 m_sink->Release();
1809 m_sink = NULL;
1810 }
1811 return hr;
1812 }
1813
1814 STDMETHODIMP DetachObject(void) override
1815 {
1816 QMutexLocker locker(&m_mutex);
1817 if (m_sink) {
1818 m_sink->Release();
1819 m_sink = NULL;
1820 }
1821 return S_OK;
1822 }
1823
1824 //from IMFAttributes
1825 STDMETHODIMP GetItem(
1826 REFGUID guidKey,
1827 PROPVARIANT *pValue) override
1828 {
1829 return m_attributes->GetItem(guidKey, pValue);
1830 }
1831
1832 STDMETHODIMP GetItemType(
1833 REFGUID guidKey,
1834 MF_ATTRIBUTE_TYPE *pType) override
1835 {
1836 return m_attributes->GetItemType(guidKey, pType);
1837 }
1838
1839 STDMETHODIMP CompareItem(
1840 REFGUID guidKey,
1841 REFPROPVARIANT Value,
1842 BOOL *pbResult) override
1843 {
1844 return m_attributes->CompareItem(guidKey, Value, pbResult);
1845 }
1846
1847 STDMETHODIMP Compare(
1848 IMFAttributes *pTheirs,
1849 MF_ATTRIBUTES_MATCH_TYPE MatchType,
1850 BOOL *pbResult) override
1851 {
1852 return m_attributes->Compare(pTheirs, MatchType, pbResult);
1853 }
1854
1855 STDMETHODIMP GetUINT32(
1856 REFGUID guidKey,
1857 UINT32 *punValue) override
1858 {
1859 return m_attributes->GetUINT32(guidKey, punValue);
1860 }
1861
1862 STDMETHODIMP GetUINT64(
1863 REFGUID guidKey,
1864 UINT64 *punValue) override
1865 {
1866 return m_attributes->GetUINT64(guidKey, punValue);
1867 }
1868
1869 STDMETHODIMP GetDouble(
1870 REFGUID guidKey,
1871 double *pfValue) override
1872 {
1873 return m_attributes->GetDouble(guidKey, pfValue);
1874 }
1875
1876 STDMETHODIMP GetGUID(
1877 REFGUID guidKey,
1878 GUID *pguidValue) override
1879 {
1880 return m_attributes->GetGUID(guidKey, pguidValue);
1881 }
1882
1883 STDMETHODIMP GetStringLength(
1884 REFGUID guidKey,
1885 UINT32 *pcchLength) override
1886 {
1887 return m_attributes->GetStringLength(guidKey, pcchLength);
1888 }
1889
1890 STDMETHODIMP GetString(
1891 REFGUID guidKey,
1892 LPWSTR pwszValue,
1893 UINT32 cchBufSize,
1894 UINT32 *pcchLength) override
1895 {
1896 return m_attributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength);
1897 }
1898
1900 REFGUID guidKey,
1901 LPWSTR *ppwszValue,
1902 UINT32 *pcchLength) override
1903 {
1904 return m_attributes->GetAllocatedString(guidKey, ppwszValue, pcchLength);
1905 }
1906
1907 STDMETHODIMP GetBlobSize(
1908 REFGUID guidKey,
1909 UINT32 *pcbBlobSize) override
1910 {
1911 return m_attributes->GetBlobSize(guidKey, pcbBlobSize);
1912 }
1913
1914 STDMETHODIMP GetBlob(
1915 REFGUID guidKey,
1916 UINT8 *pBuf,
1917 UINT32 cbBufSize,
1918 UINT32 *pcbBlobSize) override
1919 {
1920 return m_attributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize);
1921 }
1922
1923 STDMETHODIMP GetAllocatedBlob(
1924 REFGUID guidKey,
1925 UINT8 **ppBuf,
1926 UINT32 *pcbSize) override
1927 {
1928 return m_attributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize);
1929 }
1930
1931 STDMETHODIMP GetUnknown(
1932 REFGUID guidKey,
1933 REFIID riid,
1934 LPVOID *ppv) override
1935 {
1936 return m_attributes->GetUnknown(guidKey, riid, ppv);
1937 }
1938
1939 STDMETHODIMP SetItem(
1940 REFGUID guidKey,
1941 REFPROPVARIANT Value) override
1942 {
1943 return m_attributes->SetItem(guidKey, Value);
1944 }
1945
1946 STDMETHODIMP DeleteItem(
1947 REFGUID guidKey) override
1948 {
1949 return m_attributes->DeleteItem(guidKey);
1950 }
1951
1952 STDMETHODIMP DeleteAllItems(void) override
1953 {
1954 return m_attributes->DeleteAllItems();
1955 }
1956
1957 STDMETHODIMP SetUINT32(
1958 REFGUID guidKey,
1959 UINT32 unValue) override
1960 {
1961 return m_attributes->SetUINT32(guidKey, unValue);
1962 }
1963
1964 STDMETHODIMP SetUINT64(
1965 REFGUID guidKey,
1966 UINT64 unValue) override
1967 {
1968 return m_attributes->SetUINT64(guidKey, unValue);
1969 }
1970
1971 STDMETHODIMP SetDouble(
1972 REFGUID guidKey,
1973 double fValue) override
1974 {
1975 return m_attributes->SetDouble(guidKey, fValue);
1976 }
1977
1978 STDMETHODIMP SetGUID(
1979 REFGUID guidKey,
1980 REFGUID guidValue) override
1981 {
1982 return m_attributes->SetGUID(guidKey, guidValue);
1983 }
1984
1985 STDMETHODIMP SetString(
1986 REFGUID guidKey,
1987 LPCWSTR wszValue) override
1988 {
1989 return m_attributes->SetString(guidKey, wszValue);
1990 }
1991
1992 STDMETHODIMP SetBlob(
1993 REFGUID guidKey,
1994 const UINT8 *pBuf,
1995 UINT32 cbBufSize) override
1996 {
1997 return m_attributes->SetBlob(guidKey, pBuf, cbBufSize);
1998 }
1999
2000 STDMETHODIMP SetUnknown(
2001 REFGUID guidKey,
2002 IUnknown *pUnknown) override
2003 {
2004 return m_attributes->SetUnknown(guidKey, pUnknown);
2005 }
2006
2007 STDMETHODIMP LockStore(void) override
2008 {
2009 return m_attributes->LockStore();
2010 }
2011
2012 STDMETHODIMP UnlockStore(void) override
2013 {
2014 return m_attributes->UnlockStore();
2015 }
2016
2017 STDMETHODIMP GetCount(
2018 UINT32 *pcItems) override
2019 {
2020 return m_attributes->GetCount(pcItems);
2021 }
2022
2023 STDMETHODIMP GetItemByIndex(
2024 UINT32 unIndex,
2025 GUID *pguidKey,
2026 PROPVARIANT *pValue) override
2027 {
2028 return m_attributes->GetItemByIndex(unIndex, pguidKey, pValue);
2029 }
2030
2031 STDMETHODIMP CopyAllItems(
2032 IMFAttributes *pDest) override
2033 {
2034 return m_attributes->CopyAllItems(pDest);
2035 }
2036
2039 {
2040 QMutexLocker locker(&m_mutex);
2041 if (m_videoSink == sink)
2042 return;
2043
2044 m_videoSink = sink;
2045
2046 if (!m_sink)
2047 return;
2048 m_sink->setSurface(m_videoSink);
2049 }
2050
2051 void present()
2052 {
2053 QMutexLocker locker(&m_mutex);
2054 if (!m_sink)
2055 return;
2056 m_sink->present();
2057 }
2058
2060 {
2061 QMutexLocker locker(&m_mutex);
2062 if (!m_sink)
2063 return;
2064 m_sink->clearScheduledFrame();
2065 }
2066
2067 MFTIME getTime()
2068 {
2069 if (m_sink)
2070 return m_sink->getTime();
2071 return 0;
2072 }
2073
2075 {
2076 if (m_sink)
2077 return m_sink->getPlayRate();
2078 return 1;
2079 }
2080
2081 private:
2082 long m_cRef;
2083 MediaSink *m_sink;
2084 MFVideoRendererControl *m_rendererControl;
2085 IMFAttributes *m_attributes;
2086 QVideoSink *m_videoSink;
2087 QMutex m_mutex;
2088 };
2089}
2090
2091
2093{
2094public:
2097 { }
2098
2099 STDMETHODIMP ActivateObject(REFIID riid, void **ppv) override;
2100 STDMETHODIMP ShutdownObject() override;
2101 STDMETHODIMP DetachObject() override;
2102
2103 void setSink(QVideoSink *sink);
2104 void setCropRect(QRect cropRect);
2105
2106private:
2107 EVRCustomPresenter *m_presenter;
2108 QVideoSink *m_videoSink;
2109 QRect m_cropRect;
2110 QMutex m_mutex;
2111};
2112
2113
2115 : QObject(parent)
2116{
2117}
2118
2120{
2121 clear();
2122}
2123
2124void MFVideoRendererControl::clear()
2125{
2126 if (m_sink)
2127 m_sink->platformVideoSink()->setVideoFrame(QVideoFrame());
2128
2129 if (m_presenterActivate) {
2130 m_presenterActivate->ShutdownObject();
2131 m_presenterActivate->Release();
2132 m_presenterActivate = NULL;
2133 }
2134
2135 if (m_currentActivate) {
2136 m_currentActivate->ShutdownObject();
2137 m_currentActivate->Release();
2138 }
2139 m_currentActivate = NULL;
2140}
2141
2143{
2144 clear();
2145}
2146
2148{
2149 return m_sink;
2150}
2151
2153{
2154 m_sink = sink;
2155
2156 if (m_presenterActivate)
2157 m_presenterActivate->setSink(m_sink);
2158 else if (m_currentActivate)
2159 static_cast<VideoRendererActivate*>(m_currentActivate)->setSink(m_sink);
2160}
2161
2163{
2164 if (m_presenterActivate)
2165 m_presenterActivate->setCropRect(cropRect);
2166}
2167
2169{
2170 if (m_presenterActivate)
2171 return;
2172
2173 if (!m_currentActivate)
2174 return;
2175
2176 if (event->type() == QEvent::Type(MediaStream::PresentSurface)) {
2177 MFTIME targetTime = static_cast<MediaStream::PresentEvent*>(event)->targetTime();
2178 MFTIME currentTime = static_cast<VideoRendererActivate*>(m_currentActivate)->getTime();
2179 float playRate = static_cast<VideoRendererActivate*>(m_currentActivate)->getPlayRate();
2180 if (!qFuzzyIsNull(playRate) && targetTime != currentTime) {
2181 // If the scheduled frame is too late, skip it
2182 const int interval = ((targetTime - currentTime) / 10000) / playRate;
2183 if (interval < 0)
2184 static_cast<VideoRendererActivate*>(m_currentActivate)->clearScheduledFrame();
2185 else
2186 QTimer::singleShot(interval, this, SLOT(present()));
2187 } else {
2188 present();
2189 }
2190 return;
2191 }
2193}
2194
2195void MFVideoRendererControl::present()
2196{
2197 if (m_presenterActivate)
2198 return;
2199
2200 if (m_currentActivate)
2201 static_cast<VideoRendererActivate*>(m_currentActivate)->present();
2202}
2203
2205{
2206 clear();
2207
2208 if (m_sink) {
2209 // Create the EVR media sink, but replace the presenter with our own
2210 if (SUCCEEDED(MFCreateVideoRendererActivate(::GetShellWindow(), &m_currentActivate))) {
2211 m_presenterActivate = new EVRCustomPresenterActivate(m_sink);
2212 m_currentActivate->SetUnknown(MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE, m_presenterActivate);
2213 } else {
2214 m_currentActivate = new VideoRendererActivate(this);
2215 }
2216 }
2217
2218 setSink(m_sink);
2219
2220 return m_currentActivate;
2221}
2222
2223
2226 , m_presenter(0)
2227 , m_videoSink(sink)
2228{ }
2229
2231{
2232 if (!ppv)
2233 return E_INVALIDARG;
2234 QMutexLocker locker(&m_mutex);
2235 if (!m_presenter) {
2236 m_presenter = new EVRCustomPresenter(m_videoSink);
2237 m_presenter->setCropRect(m_cropRect);
2238 }
2239 return m_presenter->QueryInterface(riid, ppv);
2240}
2241
2243{
2244 // The presenter does not implement IMFShutdown so
2245 // this function is the same as DetachObject()
2246 return DetachObject();
2247}
2248
2250{
2251 QMutexLocker locker(&m_mutex);
2252 if (m_presenter) {
2253 m_presenter->Release();
2254 m_presenter = 0;
2255 }
2256 return S_OK;
2257}
2258
2260{
2261 QMutexLocker locker(&m_mutex);
2262 if (m_videoSink == sink)
2263 return;
2264
2265 m_videoSink = sink;
2266
2267 if (m_presenter)
2268 m_presenter->setSink(sink);
2269}
2270
2272{
2273 QMutexLocker locker(&m_mutex);
2274 if (m_cropRect == cropRect)
2275 return;
2276
2277 m_cropRect = cropRect;
2278
2279 if (m_presenter)
2280 m_presenter->setCropRect(cropRect);
2281}
2282
2284
2285#include "moc_mfvideorenderercontrol_p.cpp"
2286#include "mfvideorenderercontrol.moc"
STDMETHODIMP ActivateObject(REFIID riid, void **ppv) override
STDMETHODIMP ShutdownObject() override
STDMETHODIMP QueryInterface(REFIID riid, void **ppv) override
void setCropRect(QRect cropRect)
void setSink(QVideoSink *sink)
void setSink(QVideoSink *surface)
MFVideoRendererControl(QObject *parent=0)
void customEvent(QEvent *event) override
This event handler can be reimplemented in a subclass to receive custom events.
The QAbstractVideoBuffer class is an abstraction for video data. \inmodule QtMultimedia.
static void postEvent(QObject *receiver, QEvent *event, int priority=Qt::NormalEventPriority)
\inmodule QtCore
Definition qcoreevent.h:45
Type
This enum type defines the valid event types in Qt.
Definition qcoreevent.h:51
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
iterator end()
Definition qlist.h:609
value_type takeFirst()
Definition qlist.h:549
iterator begin()
Definition qlist.h:608
void clear()
Definition qlist.h:417
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
Definition qmutex.h:285
\inmodule QtCore
Definition qobject.h:90
virtual void customEvent(QEvent *event)
This event handler can be reimplemented in a subclass to receive custom events.
Definition qobject.cpp:1485
\inmodule QtCore\reentrant
Definition qrect.h:30
\inmodule QtCore
Definition qsize.h:25
STDMETHODIMP_(ULONG) Release() override
STDMETHODIMP QueryInterface(REFIID iid, void **ppv) override
STDMETHODIMP_(ULONG) AddRef() override
MFSTREAMSINK_MARKER_TYPE m_eMarkerType
STDMETHODIMP GetMarkerType(MFSTREAMSINK_MARKER_TYPE *pType) override
STDMETHODIMP GetMarkerValue(PROPVARIANT *pvar) override
static HRESULT Create(MFSTREAMSINK_MARKER_TYPE eMarkerType, const PROPVARIANT *pvarMarkerValue, const PROPVARIANT *pvarContextValue, IMarker **ppMarker)
STDMETHODIMP GetContext(PROPVARIANT *pvar) override
MapData map(QVideoFrame::MapMode mode) override
Independently maps the planes of a video buffer to memory.
MediaSampleVideoBuffer(IMFMediaBuffer *buffer, int bytesPerLine)
void unmap() override
Releases the memory mapped by the map() function.
QVideoFrame::MapMode mapMode() const override
STDMETHODIMP NotifyPreroll(MFTIME hnsUpcomingStartTime) override
STDMETHODIMP IsRateSupported(BOOL fThin, float flRate, float *pflNearestSupportedRate) override
STDMETHODIMP GetStreamSinkCount(DWORD *pcStreamSinkCount) override
STDMETHODIMP OnClockSetRate(MFTIME, float flRate) override
STDMETHODIMP AddStreamSink(DWORD, IMFMediaType *, IMFStreamSink **) override
MediaSink(MFVideoRendererControl *rendererControl)
STDMETHODIMP GetPresentationClock(IMFPresentationClock **ppPresentationClock) override
STDMETHODIMP SetPresentationClock(IMFPresentationClock *pPresentationClock) override
STDMETHODIMP GetService(const GUID &guidService, const IID &riid, LPVOID *ppvObject) override
STDMETHODIMP EndFinalize(IMFAsyncResult *pResult) override
STDMETHODIMP_(ULONG) Release(void) override
STDMETHODIMP OnClockStop(MFTIME) override
STDMETHODIMP GetStreamSinkByIndex(DWORD dwIndex, IMFStreamSink **ppStreamSink) override
STDMETHODIMP OnClockStart(MFTIME, LONGLONG llClockStartOffset) override
STDMETHODIMP RemoveStreamSink(DWORD) override
STDMETHODIMP BeginFinalize(IMFAsyncCallback *pCallback, IUnknown *punkState) override
STDMETHODIMP OnClockRestart(MFTIME) override
STDMETHODIMP GetCharacteristics(DWORD *pdwCharacteristics) override
STDMETHODIMP GetFastestRate(MFRATE_DIRECTION eDirection, BOOL fThin, float *pflRate) override
STDMETHODIMP OnClockPause(MFTIME) override
STDMETHODIMP Shutdown(void) override
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override
STDMETHODIMP GetStreamSinkById(DWORD dwStreamSinkIdentifier, IMFStreamSink **ppStreamSink) override
STDMETHODIMP GetSlowestRate(MFRATE_DIRECTION eDirection, BOOL fThin, float *pflRate) override
STDMETHODIMP_(ULONG) AddRef(void) override
STDMETHODIMP GetIdentifier(DWORD *pdwIdentifier) override
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override
HRESULT finalize(IMFAsyncCallback *pCallback, IUnknown *punkState)
void setClock(IMFPresentationClock *presentationClock)
STDMETHODIMP GetMediaSink(IMFMediaSink **ppMediaSink) override
STDMETHODIMP ProcessSample(IMFSample *pSample) override
STDMETHODIMP QueueEvent(MediaEventType met, REFGUID guidExtendedType, HRESULT hrStatus, const PROPVARIANT *pvValue) override
STDMETHODIMP IsMediaTypeSupported(IMFMediaType *pMediaType, IMFMediaType **ppMediaType) override
STDMETHODIMP GetMediaTypeCount(DWORD *pdwTypeCount) override
STDMETHODIMP GetCurrentMediaType(IMFMediaType **ppMediaType) override
STDMETHODIMP BeginGetEvent(IMFAsyncCallback *pCallback, IUnknown *punkState) override
STDMETHODIMP SetCurrentMediaType(IMFMediaType *pMediaType) override
MediaStream(IMFMediaSink *parent, MFVideoRendererControl *rendererControl)
STDMETHODIMP EndGetEvent(IMFAsyncResult *pResult, IMFMediaEvent **ppEvent) override
STDMETHODIMP GetMajorType(GUID *pguidMajorType) override
STDMETHODIMP PlaceMarker(MFSTREAMSINK_MARKER_TYPE eMarkerType, const PROPVARIANT *pvarMarkerValue, const PROPVARIANT *pvarContextValue) override
STDMETHODIMP_(ULONG) Release(void) override
STDMETHODIMP GetEvent(DWORD dwFlags, IMFMediaEvent **ppEvent) override
STDMETHODIMP GetMediaTypeByIndex(DWORD dwIndex, IMFMediaType **ppType) override
STDMETHODIMP_(ULONG) AddRef(void) override
HRESULT startPreroll(MFTIME hnsUpcomingStartTime)
STDMETHODIMP GetMediaTypeHandler(IMFMediaTypeHandler **ppHandler) override
STDMETHODIMP SetBlob(REFGUID guidKey, const UINT8 *pBuf, UINT32 cbBufSize) override
STDMETHODIMP GetString(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32 *pcchLength) override
STDMETHODIMP GetUINT32(REFGUID guidKey, UINT32 *punValue) override
STDMETHODIMP GetStringLength(REFGUID guidKey, UINT32 *pcchLength) override
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override
STDMETHODIMP SetUnknown(REFGUID guidKey, IUnknown *pUnknown) override
STDMETHODIMP GetUnknown(REFGUID guidKey, REFIID riid, LPVOID *ppv) override
VideoRendererActivate(MFVideoRendererControl *rendererControl)
STDMETHODIMP GetDouble(REFGUID guidKey, double *pfValue) override
STDMETHODIMP SetUINT32(REFGUID guidKey, UINT32 unValue) override
STDMETHODIMP GetBlob(REFGUID guidKey, UINT8 *pBuf, UINT32 cbBufSize, UINT32 *pcbBlobSize) override
STDMETHODIMP GetItem(REFGUID guidKey, PROPVARIANT *pValue) override
STDMETHODIMP CompareItem(REFGUID guidKey, REFPROPVARIANT Value, BOOL *pbResult) override
STDMETHODIMP SetDouble(REFGUID guidKey, double fValue) override
STDMETHODIMP SetItem(REFGUID guidKey, REFPROPVARIANT Value) override
STDMETHODIMP GetItemType(REFGUID guidKey, MF_ATTRIBUTE_TYPE *pType) override
STDMETHODIMP GetUINT64(REFGUID guidKey, UINT64 *punValue) override
STDMETHODIMP SetString(REFGUID guidKey, LPCWSTR wszValue) override
STDMETHODIMP GetItemByIndex(UINT32 unIndex, GUID *pguidKey, PROPVARIANT *pValue) override
STDMETHODIMP GetAllocatedBlob(REFGUID guidKey, UINT8 **ppBuf, UINT32 *pcbSize) override
STDMETHODIMP CopyAllItems(IMFAttributes *pDest) override
STDMETHODIMP DeleteItem(REFGUID guidKey) override
STDMETHODIMP GetGUID(REFGUID guidKey, GUID *pguidValue) override
STDMETHODIMP Compare(IMFAttributes *pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, BOOL *pbResult) override
STDMETHODIMP SetUINT64(REFGUID guidKey, UINT64 unValue) override
STDMETHODIMP GetAllocatedString(REFGUID guidKey, LPWSTR *ppwszValue, UINT32 *pcchLength) override
STDMETHODIMP SetGUID(REFGUID guidKey, REFGUID guidValue) override
STDMETHODIMP ActivateObject(REFIID riid, void **ppv) override
STDMETHODIMP GetBlobSize(REFGUID guidKey, UINT32 *pcbBlobSize) override
STDMETHODIMP GetCount(UINT32 *pcItems) override
bool singleShot
whether the timer is a single-shot timer
Definition qtimer.h:22
The QVideoFrameFormat class specifies the stream format of a video presentation surface.
PixelFormat
Enumerates video data types.
static constexpr int NPixelFormats
The QVideoFrame class represents a frame of video data.
Definition qvideoframe.h:26
MapMode
Enumerates how a video buffer's data is mapped to system memory.
Definition qvideoframe.h:36
The QVideoSink class represents a generic sink for video data.
Definition qvideosink.h:22
#define this
Definition dialogs.cpp:9
qSwap(pi, e)
direction
STDMETHODIMP_(ULONG) MFDecoderSourceReader
#define PAD_TO_DWORD(x)
Combined button and popup list for selecting options.
static const GUID IID_IMarker
virtual STDMETHODIMP GetMarkerValue(PROPVARIANT *pvar)=0
virtual STDMETHODIMP GetContext(PROPVARIANT *pvar)=0
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:303
#define qDebug
[1]
Definition qlogging.h:160
#define qWarning
Definition qlogging.h:162
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
#define SLOT(a)
Definition qobjectdefs.h:51
GLenum mode
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLenum GLuint GLenum GLsizei length
GLfloat GLfloat f
GLenum GLuint buffer
GLint GLsizei width
GLuint start
GLint GLsizei GLsizei GLenum format
struct _cl_event * event
GLuint GLenum * rate
GLsizei GLenum GLboolean sink
static QAbstractVideoBuffer::MapData mapData(const camera_frame_nv12_t &frame, unsigned char *baseAddress)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_OBJECT
#define Q_UNUSED(x)
unsigned char uchar
Definition qtypes.h:27
ptrdiff_t qsizetype
Definition qtypes.h:70
static double currentTime()
IUIViewSettingsInterop __RPC__in REFIID riid
IUIViewSettingsInterop __RPC__in REFIID __RPC__deref_out_opt void ** ppv
long HRESULT
#define QMM_PRESENTATION_CURRENT_POSITION
QByteArray compressed
QQueue< int > queue
[0]
view viewport() -> scroll(dx, dy, deviceRect)
QFrame frame
[0]
Definition moc.h:24
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent