6#include "private/qwindowsmultimediautils_p.h"
8#include <qmediadevices.h>
9#include <qaudiodevice.h>
10#include <private/qmemoryvideobuffer_p.h>
11#include <private/qvideoframe_p.h>
12#include <QtCore/private/qcomptr_p.h>
13#include <QtCore/qdebug.h>
15#include <mmdeviceapi.h>
26 m_durationTimer.setInterval(100);
27 connect(&m_durationTimer, &QTimer::timeout,
this, &QWindowsMediaDeviceReader::updateDuration);
43 IMFAttributes *sourceAttributes =
nullptr;
45 HRESULT hr = MFCreateAttributes(&sourceAttributes, 2);
48 hr = sourceAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
49 video ? MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
50 : MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID);
53 hr = sourceAttributes->SetString(video ? MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK
54 : MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID,
55 reinterpret_cast<LPCWSTR>(deviceId.utf16()));
58 hr = MFCreateDeviceSource(sourceAttributes, source);
61 sourceAttributes->Release();
70 IMFMediaSource *secondSource,
71 IMFMediaSource **aggregateSource,
72 IMFSourceReader **sourceReader)
74 if ((!firstSource && !secondSource) || !aggregateSource || !sourceReader)
77 *aggregateSource =
nullptr;
78 *sourceReader =
nullptr;
80 IMFCollection *sourceCollection =
nullptr;
82 HRESULT hr = MFCreateCollection(&sourceCollection);
86 sourceCollection->AddElement(firstSource);
89 sourceCollection->AddElement(secondSource);
91 hr = MFCreateAggregateSource(sourceCollection, aggregateSource);
94 IMFAttributes *readerAttributes =
nullptr;
96 hr = MFCreateAttributes(&readerAttributes, 1);
100 hr = readerAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK,
101 static_cast<IMFSourceReaderCallback*>(
this));
104 hr = MFCreateSourceReaderFromMediaSource(*aggregateSource, readerAttributes, sourceReader);
106 readerAttributes->Release();
109 sourceCollection->Release();
120 if (m_sourceReader && m_videoSource) {
123 IMFMediaType *mediaType =
nullptr;
126 float currFrameRate = 0.0f;
128 while (SUCCEEDED(m_sourceReader->GetNativeMediaType(DWORD(MF_SOURCE_READER_FIRST_VIDEO_STREAM),
129 index, &mediaType))) {
131 GUID subtype = GUID_NULL;
132 if (SUCCEEDED(mediaType->GetGUID(MF_MT_SUBTYPE, &subtype))) {
134 auto pixelFormat = QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype);
135 if (pixelFormat != QVideoFrameFormat::Format_Invalid) {
137 UINT32 width, height;
138 if (SUCCEEDED(MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &width, &height))) {
141 if (SUCCEEDED(MFGetAttributeRatio(mediaType, MF_MT_FRAME_RATE, &num, &den))) {
143 UINT32 area = width * height;
144 float frameRate =
float(num) / den;
146 if (!reqFormat.isNull()
147 && UINT32(reqFormat.resolution().width()) == width
148 && UINT32(reqFormat.resolution().height()) == height
149 && QtPrivate::fuzzyCompare(reqFormat.maxFrameRate(), frameRate)
150 && reqFormat.pixelFormat() == pixelFormat) {
151 mediaType->Release();
155 if ((currFrameRate < 29.9 && currFrameRate < frameRate) ||
156 (currFrameRate == frameRate && currArea < area)) {
158 currFrameRate = frameRate;
165 mediaType->Release();
186 hr = m_sourceReader->GetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_VIDEO_STREAM),
189 hr = m_sourceReader->GetNativeMediaType(DWORD(MF_SOURCE_READER_FIRST_VIDEO_STREAM),
190 mediaTypeIndex, &m_videoMediaType);
192 hr = m_sourceReader->SetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_VIDEO_STREAM),
193 nullptr, m_videoMediaType);
198 GUID subtype = GUID_NULL;
199 hr = m_videoMediaType->GetGUID(MF_MT_SUBTYPE, &subtype);
202 m_pixelFormat = QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype);
204 if (m_pixelFormat == QVideoFrameFormat::Format_Invalid) {
209 hr = MFGetAttributeSize(m_videoMediaType, MF_MT_FRAME_SIZE, &m_frameWidth, &m_frameHeight);
213 hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, m_frameWidth, &m_stride);
215 m_stride = qAbs(m_stride);
216 UINT32 frameRateNum, frameRateDen;
217 hr = MFGetAttributeRatio(m_videoMediaType, MF_MT_FRAME_RATE, &frameRateNum, &frameRateDen);
220 m_frameRate = qreal(frameRateNum) / frameRateDen;
222 hr = m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_VIDEO_STREAM), TRUE);
224 UINT32 nominalRange = 0;
226 if (SUCCEEDED(m_videoMediaType->GetUINT32(MF_MT_VIDEO_NOMINAL_RANGE, &nominalRange)))
227 m_colorRange = QWindowsMultimediaUtils::colorRangeFromNominalRange(nominalRange);
229 UINT32 yuvMatrix = 0;
231 if (SUCCEEDED(m_videoMediaType->GetUINT32(MF_MT_YUV_MATRIX, &yuvMatrix))) {
232 m_colorSpace = QWindowsMultimediaUtils::colorSpaceFromMatrix(yuvMatrix);
244HRESULT
QWindowsMediaDeviceReader::initAudioType(IMFMediaType *mediaType, UINT32 channels, UINT32 samplesPerSec,
bool flt)
249 HRESULT hr = mediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
251 hr = mediaType->SetGUID(MF_MT_SUBTYPE, flt ? MFAudioFormat_Float : MFAudioFormat_PCM);
253 hr = mediaType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, channels);
255 hr = mediaType->SetUINT32(MF_MT_AUDIO_CHANNEL_MASK,
256 (channels == 1) ? SPEAKER_FRONT_CENTER : (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT ));
258 hr = mediaType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, samplesPerSec);
260 UINT32 bitsPerSample = flt ? 32 : 16;
261 UINT32 bytesPerFrame = channels * bitsPerSample/8;
262 hr = mediaType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample);
264 hr = mediaType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, bytesPerFrame);
266 hr = mediaType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, bytesPerFrame * samplesPerSec);
287 HRESULT hr = m_sourceReader->GetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM),
290 hr = initAudioType(m_audioMediaType, 2, 48000,
true);
292 hr = m_sourceReader->SetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM),
293 nullptr, m_audioMediaType);
295 hr = m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE);
308 m_sourceVideoStreamIndex = MF_SOURCE_READER_INVALID_STREAM_INDEX;
309 m_sourceAudioStreamIndex = MF_SOURCE_READER_INVALID_STREAM_INDEX;
312 BOOL selected = FALSE;
314 while (m_sourceReader->GetStreamSelection(index, &selected) == S_OK) {
316 IMFMediaType *mediaType =
nullptr;
317 if (SUCCEEDED(m_sourceReader->GetCurrentMediaType(index, &mediaType))) {
318 GUID majorType = GUID_NULL;
319 if (SUCCEEDED(mediaType->GetGUID(MF_MT_MAJOR_TYPE, &majorType))) {
320 if (majorType == MFMediaType_Video)
321 m_sourceVideoStreamIndex = index;
322 else if (majorType == MFMediaType_Audio)
323 m_sourceAudioStreamIndex = index;
325 mediaType->Release();
330 if ((m_videoSource && m_sourceVideoStreamIndex == MF_SOURCE_READER_INVALID_STREAM_INDEX) ||
331 (m_audioSource && m_sourceAudioStreamIndex == MF_SOURCE_READER_INVALID_STREAM_INDEX))
338 QMutexLocker locker(&m_mutex);
342 m_audioOutputId = audioOutputId;
344 if (!m_active || m_audioOutputId.isEmpty())
347 HRESULT hr = startMonitoring();
349 return SUCCEEDED(hr);
354 if (m_audioOutputId.isEmpty())
357 IMFAttributes *sinkAttributes =
nullptr;
359 HRESULT hr = MFCreateAttributes(&sinkAttributes, 1);
362 hr = sinkAttributes->SetString(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID,
363 reinterpret_cast<LPCWSTR>(m_audioOutputId.utf16()));
366 IMFMediaSink *mediaSink =
nullptr;
367 hr = MFCreateAudioRenderer(sinkAttributes, &mediaSink);
370 IMFStreamSink *streamSink =
nullptr;
371 hr = mediaSink->GetStreamSinkByIndex(0, &streamSink);
374 IMFMediaTypeHandler *typeHandler =
nullptr;
375 hr = streamSink->GetMediaTypeHandler(&typeHandler);
378 hr = typeHandler->IsMediaTypeSupported(m_audioMediaType,
nullptr);
381 hr = typeHandler->SetCurrentMediaType(m_audioMediaType);
384 IMFAttributes *writerAttributes =
nullptr;
386 HRESULT hr = MFCreateAttributes(&writerAttributes, 1);
389 hr = writerAttributes->SetUINT32(MF_SINK_WRITER_DISABLE_THROTTLING, TRUE);
392 IMFSinkWriter *sinkWriter =
nullptr;
393 hr = MFCreateSinkWriterFromMediaSink(mediaSink, writerAttributes, &sinkWriter);
396 hr = sinkWriter->SetInputMediaType(0, m_audioMediaType,
nullptr);
399 IMFSimpleAudioVolume *audioVolume =
nullptr;
401 if (SUCCEEDED(MFGetService(
402 mediaSink, MR_POLICY_VOLUME_SERVICE,
403 IID_PPV_ARGS(&audioVolume)))) {
404 audioVolume->SetMasterVolume(
float(m_outputVolume));
405 audioVolume->SetMute(m_outputMuted);
406 audioVolume->Release();
409 hr = sinkWriter->BeginWriting();
411 m_monitorSink = mediaSink;
412 m_monitorSink->AddRef();
413 m_monitorWriter = sinkWriter;
414 m_monitorWriter->AddRef();
417 sinkWriter->Release();
420 writerAttributes->Release();
424 typeHandler->Release();
426 streamSink->Release();
428 mediaSink->Release();
431 sinkAttributes->Release();
439 if (m_monitorWriter) {
440 m_monitorWriter->Release();
441 m_monitorWriter =
nullptr;
444 m_monitorSink->Shutdown();
445 m_monitorSink->Release();
446 m_monitorSink =
nullptr;
453 const QCameraFormat &cameraFormat,
454 const QString µphoneId)
456 QMutexLocker locker(&m_mutex);
458 if (cameraId.isEmpty() && microphoneId.isEmpty())
467 if (!cameraId.isEmpty()) {
468 if (!SUCCEEDED(createSource(cameraId,
true, &m_videoSource))) {
474 if (!microphoneId.isEmpty()) {
475 if (!SUCCEEDED(createSource(microphoneId,
false, &m_audioSource))) {
481 if (!SUCCEEDED(createAggregateReader(m_videoSource, m_audioSource, &m_aggregateSource, &m_sourceReader))) {
486 DWORD mediaTypeIndex = findMediaTypeIndex(cameraFormat);
488 if (!SUCCEEDED(prepareVideoStream(mediaTypeIndex))) {
493 if (!SUCCEEDED(prepareAudioStream())) {
498 if (!SUCCEEDED(initSourceIndexes())) {
503 updateSinkInputMediaTypes();
507 if (!SUCCEEDED(m_sourceReader->ReadSample(MF_SOURCE_READER_ANY_STREAM, 0,
nullptr,
nullptr,
nullptr,
nullptr))) {
526 QMutexLocker locker(&m_mutex);
533 if (m_videoMediaType) {
534 m_videoMediaType->Release();
535 m_videoMediaType =
nullptr;
537 if (m_audioMediaType) {
538 m_audioMediaType->Release();
539 m_audioMediaType =
nullptr;
541 if (m_sourceReader) {
542 m_sourceReader->Release();
543 m_sourceReader =
nullptr;
545 if (m_aggregateSource) {
546 m_aggregateSource->Release();
547 m_aggregateSource =
nullptr;
550 m_videoSource->Release();
551 m_videoSource =
nullptr;
554 m_audioSource->Release();
555 m_audioSource =
nullptr;
560 UINT32 height, qreal frameRate, IMFMediaType **mediaType)
565 *mediaType =
nullptr;
566 IMFMediaType *targetMediaType =
nullptr;
568 if (SUCCEEDED(MFCreateMediaType(&targetMediaType))) {
570 if (SUCCEEDED(targetMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video))) {
572 if (SUCCEEDED(targetMediaType->SetGUID(MF_MT_SUBTYPE, format))) {
574 if (SUCCEEDED(targetMediaType->SetUINT32(MF_MT_AVG_BITRATE, bitRate))) {
576 if (SUCCEEDED(MFSetAttributeSize(targetMediaType, MF_MT_FRAME_SIZE, width, height))) {
578 if (SUCCEEDED(MFSetAttributeRatio(targetMediaType, MF_MT_FRAME_RATE,
579 UINT32(frameRate * 1000), 1000))) {
581 if (SUCCEEDED(MFGetAttributeRatio(m_videoMediaType, MF_MT_PIXEL_ASPECT_RATIO, &t1, &t2))) {
583 if (SUCCEEDED(MFSetAttributeRatio(targetMediaType, MF_MT_PIXEL_ASPECT_RATIO, t1, t2))) {
585 if (SUCCEEDED(m_videoMediaType->GetUINT32(MF_MT_INTERLACE_MODE, &t1))) {
587 if (SUCCEEDED(targetMediaType->SetUINT32(MF_MT_INTERLACE_MODE, t1))) {
589 *mediaType = targetMediaType;
600 targetMediaType->Release();
610 *mediaType =
nullptr;
611 IMFMediaType *targetMediaType =
nullptr;
613 if (SUCCEEDED(MFCreateMediaType(&targetMediaType))) {
615 if (SUCCEEDED(targetMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio))) {
617 if (SUCCEEDED(targetMediaType->SetGUID(MF_MT_SUBTYPE, format))) {
619 if (bitRate == 0 || SUCCEEDED(targetMediaType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, bitRate / 8))) {
621 *mediaType = targetMediaType;
626 targetMediaType->Release();
635 if (m_videoSource && m_videoMediaType && m_sinkVideoStreamIndex != MF_SINK_WRITER_INVALID_STREAM_INDEX) {
636 hr = m_sinkWriter->SetInputMediaType(m_sinkVideoStreamIndex, m_videoMediaType,
nullptr);
639 if (m_audioSource && m_audioMediaType && m_sinkAudioStreamIndex != MF_SINK_WRITER_INVALID_STREAM_INDEX) {
640 hr = m_sinkWriter->SetInputMediaType(m_sinkAudioStreamIndex, m_audioMediaType,
nullptr);
648 const QString &fileName,
const GUID &container,
const GUID &videoFormat, UINT32 videoBitRate,
649 UINT32 width, UINT32 height, qreal frameRate,
const GUID &audioFormat, UINT32 audioBitRate)
651 QMutexLocker locker(&m_mutex);
653 if (!m_active || m_recording || (videoFormat == GUID_NULL && audioFormat == GUID_NULL))
654 return QMediaRecorder::ResourceError;
656 ComPtr<IMFAttributes> writerAttributes;
658 HRESULT hr = MFCreateAttributes(writerAttributes.GetAddressOf(), 2);
660 return QMediaRecorder::ResourceError;
663 hr = writerAttributes->SetUnknown(MF_SINK_WRITER_ASYNC_CALLBACK,
664 static_cast<IMFSinkWriterCallback*>(
this));
666 return QMediaRecorder::ResourceError;
668 hr = writerAttributes->SetGUID(MF_TRANSCODE_CONTAINERTYPE, container);
670 return QMediaRecorder::ResourceError;
672 ComPtr<IMFSinkWriter> sinkWriter;
673 hr = MFCreateSinkWriterFromURL(
reinterpret_cast<LPCWSTR>(fileName.utf16()),
674 nullptr, writerAttributes.Get(), sinkWriter.GetAddressOf());
676 return QMediaRecorder::LocationNotWritable;
678 m_sinkVideoStreamIndex = MF_SINK_WRITER_INVALID_STREAM_INDEX;
679 m_sinkAudioStreamIndex = MF_SINK_WRITER_INVALID_STREAM_INDEX;
681 if (m_videoSource && videoFormat != GUID_NULL) {
682 IMFMediaType *targetMediaType =
nullptr;
684 hr = createVideoMediaType(videoFormat, videoBitRate, width, height, frameRate, &targetMediaType);
687 hr = sinkWriter->AddStream(targetMediaType, &m_sinkVideoStreamIndex);
690 hr = sinkWriter->SetInputMediaType(m_sinkVideoStreamIndex, m_videoMediaType,
nullptr);
692 targetMediaType->Release();
697 if (m_audioSource && audioFormat != GUID_NULL) {
698 IMFMediaType *targetMediaType =
nullptr;
700 hr = createAudioMediaType(audioFormat, audioBitRate, &targetMediaType);
703 hr = sinkWriter->AddStream(targetMediaType, &m_sinkAudioStreamIndex);
706 hr = sinkWriter->SetInputMediaType(m_sinkAudioStreamIndex, m_audioMediaType,
nullptr);
708 targetMediaType->Release();
714 return QMediaRecorder::FormatError;
716 hr = sinkWriter->BeginWriting();
718 return QMediaRecorder::ResourceError;
720 m_sinkWriter = sinkWriter.Detach();
722 m_currentDuration = 0;
724 m_durationTimer.start();
728 m_pauseChanging =
false;
730 return QMediaRecorder::NoError;
735 QMutexLocker locker(&m_mutex);
737 if (m_sinkWriter && m_recording) {
739 HRESULT hr = m_sinkWriter->Finalize();
742 m_hasFinalized.wait(&m_mutex);
744 m_sinkWriter->Release();
745 m_sinkWriter =
nullptr;
747 QMetaObject::invokeMethod(
this,
"recordingError",
748 Qt::QueuedConnection, Q_ARG(
int, hr));
754 m_pauseChanging =
false;
756 m_durationTimer.stop();
758 m_currentDuration = -1;
763 if (!m_recording || m_paused)
765 m_pauseTime = m_lastTimestamp;
767 m_pauseChanging =
true;
773 if (!m_recording || !m_paused)
776 m_pauseChanging =
true;
785 if (riid == IID_IMFSourceReaderCallback) {
786 *ppvObject =
static_cast<IMFSourceReaderCallback*>(
this);
787 }
else if (riid == IID_IMFSinkWriterCallback) {
788 *ppvObject =
static_cast<IMFSinkWriterCallback*>(
this);
789 }
else if (riid == IID_IUnknown) {
790 *ppvObject =
static_cast<IUnknown*>(
static_cast<IMFSourceReaderCallback*>(
this));
792 *ppvObject =
nullptr;
793 return E_NOINTERFACE;
801 return InterlockedIncrement(&m_cRef);
806 LONG cRef = InterlockedDecrement(&m_cRef);
820 return m_frameHeight;
830 m_inputMuted = muted;
835 m_inputVolume = qBound(0.0, volume, 1.0);
840 QMutexLocker locker(&m_mutex);
842 m_outputMuted = muted;
844 if (m_active && m_monitorSink) {
845 IMFSimpleAudioVolume *audioVolume =
nullptr;
846 if (SUCCEEDED(MFGetService(m_monitorSink, MR_POLICY_VOLUME_SERVICE,
847 IID_PPV_ARGS(&audioVolume)))) {
848 audioVolume->SetMute(m_outputMuted);
849 audioVolume->Release();
856 QMutexLocker locker(&m_mutex);
858 m_outputVolume = qBound(0.0, volume, 1.0);
860 if (m_active && m_monitorSink) {
861 IMFSimpleAudioVolume *audioVolume =
nullptr;
862 if (SUCCEEDED(MFGetService(m_monitorSink, MR_POLICY_VOLUME_SERVICE,
863 IID_PPV_ARGS(&audioVolume)))) {
864 audioVolume->SetMasterVolume(
float(m_outputVolume));
865 audioVolume->Release();
872 if (m_currentDuration >= 0 && m_lastDuration != m_currentDuration) {
873 m_lastDuration = m_currentDuration;
874 emit durationChanged(m_currentDuration);
880 DWORD dwStreamFlags, LONGLONG llTimestamp,
883 QMutexLocker locker(&m_mutex);
885 if (FAILED(hrStatus)) {
886 emit streamingError(
int(hrStatus));
890 m_lastTimestamp = llTimestamp;
892 if ((dwStreamFlags & MF_SOURCE_READERF_ENDOFSTREAM) == MF_SOURCE_READERF_ENDOFSTREAM) {
894 emit streamingStopped();
899 emit streamingStarted();
903 if (m_monitorWriter && dwStreamIndex == m_sourceAudioStreamIndex)
904 m_monitorWriter->WriteSample(0, pSample);
909 m_timeOffset = llTimestamp;
910 m_firstFrame =
false;
911 emit recordingStarted();
914 if (m_pauseChanging) {
917 m_pauseTime = llTimestamp;
919 m_timeOffset += llTimestamp - m_pauseTime;
920 m_pauseChanging =
false;
924 if (m_sinkWriter && !m_paused) {
926 pSample->SetSampleTime(llTimestamp - m_timeOffset);
928 if (dwStreamIndex == m_sourceVideoStreamIndex) {
930 m_sinkWriter->WriteSample(m_sinkVideoStreamIndex, pSample);
932 }
else if (dwStreamIndex == m_sourceAudioStreamIndex) {
934 float volume = m_inputMuted ? 0.0f :
float(m_inputVolume);
937 if (volume != 1.0f) {
938 IMFMediaBuffer *mediaBuffer =
nullptr;
939 if (SUCCEEDED(pSample->ConvertToContiguousBuffer(&mediaBuffer))) {
942 BYTE *buffer =
nullptr;
944 if (SUCCEEDED(mediaBuffer->Lock(&buffer,
nullptr, &bufLen))) {
946 float *floatBuffer =
reinterpret_cast<
float*>(buffer);
948 for (DWORD i = 0; i < bufLen/4; ++i)
949 floatBuffer[i] *= volume;
951 mediaBuffer->Unlock();
953 mediaBuffer->Release();
957 m_sinkWriter->WriteSample(m_sinkAudioStreamIndex, pSample);
959 m_currentDuration = (llTimestamp - m_timeOffset) / 10000;
964 if (dwStreamIndex == m_sourceVideoStreamIndex) {
965 IMFMediaBuffer *mediaBuffer =
nullptr;
966 if (SUCCEEDED(pSample->ConvertToContiguousBuffer(&mediaBuffer))) {
969 BYTE *buffer =
nullptr;
971 if (SUCCEEDED(mediaBuffer->Lock(&buffer,
nullptr, &bufLen))) {
972 auto bytes = QByteArray(
reinterpret_cast<
char*>(buffer), bufLen);
973 QVideoFrameFormat format(QSize(m_frameWidth, m_frameHeight), m_pixelFormat);
974 format.setColorRange(m_colorRange);
975 format.setColorSpace(m_colorSpace);
977 QVideoFrame frame = QVideoFramePrivate::createFrame(
978 std::make_unique<QMemoryVideoBuffer>(std::move(bytes), m_stride),
982 frame.setStartTime(llTimestamp * 0.1);
984 LONGLONG duration = -1;
985 if (SUCCEEDED(pSample->GetSampleDuration(&duration)))
986 frame.setEndTime((llTimestamp + duration) * 0.1);
988 emit videoFrameChanged(frame);
990 mediaBuffer->Unlock();
992 mediaBuffer->Release();
998 m_sourceReader->ReadSample(MF_SOURCE_READER_ANY_STREAM,
999 0,
nullptr,
nullptr,
nullptr,
nullptr);
1018 QMutexLocker locker(&m_mutex);
1020 m_sinkWriter->Release();
1021 m_sinkWriter =
nullptr;
1023 emit recordingStopped();
1024 m_hasFinalized.notify_one();
1035#include "moc_qwindowsmediadevicereader_p.cpp"
Combined button and popup list for selecting options.