4#include "private/qplatformmediaplayer_p.h"
6#include <QtCore/qcoreapplication.h>
7#include <QtCore/qdatetime.h>
8#include <QtCore/qthread.h>
9#include <QtCore/qvarlengtharray.h>
10#include <QtCore/qdebug.h>
11#include <QtCore/qfile.h>
12#include <QtCore/qbuffer.h>
14#include "private/qplatformaudiooutput_p.h"
21#include <private/qwindowsmfdefs_p.h>
22#include <private/qwindowsaudioutils_p.h>
31#include <wmcodecdsp.h>
33#include <mmdeviceapi.h>
34#include <propvarutil.h>
35#include <functiondiscoverykeys_devpkey.h>
43 , m_playerControl(playerControl)
45 , m_presentationClock(0)
49 , m_netsourceStatistics(0)
58 , m_audioSampleGrabber(0)
59 , m_audioSampleGrabberNode(0)
67 m_signalPositionChangeTimer.
callOnTimeout(
this, &MFPlayerSession::timeout);
69 m_pendingState = NoPending;
70 ZeroMemory(&m_state,
sizeof(m_state));
71 m_state.command = CmdStop;
72 m_state.prevCmd = CmdNone;
74 ZeroMemory(&m_request,
sizeof(m_request));
75 m_request.command = CmdNone;
76 m_request.prevCmd = CmdNone;
77 m_request.rate = 1.0f;
83void MFPlayerSession::timeout()
87 if (
pos != m_lastPosition) {
88 const bool updatePos = m_timeCounter++ % 10 == 0;
89 if (
pos >=
qint64(m_duration / 10000 - 20)) {
90 if (m_playerControl->
doLoop()) {
102 m_lastPosition =
pos;
108#ifdef DEBUG_MEDIAFOUNDATION
112 m_signalPositionChangeTimer.
stop();
120 hr = m_session->Close();
122 DWORD dwWaitResult = WaitForSingleObject(m_hCloseEvent, 2000);
123 if (dwWaitResult == WAIT_TIMEOUT) {
124 qWarning() <<
"session close time out!";
132 m_session->Shutdown();
133 if (m_sourceResolver)
136 if (m_sourceResolver) {
137 m_sourceResolver->Release();
138 m_sourceResolver = 0;
140 if (m_videoProbeMFT) {
141 m_videoProbeMFT->Release();
151 m_session->Release();
154 CloseHandle(m_hCloseEvent);
162 m_audioSampleGrabber->Release();
168#ifdef DEBUG_MEDIAFOUNDATION
174 m_sourceResolver->
cancel();
183 }
else if (createSession()) {
187 m_updateRoutingOnStart =
true;
192void MFPlayerSession::handleSourceError(
long hr)
199 errorString =
tr(
"Attempting to play invalid Qt resource.");
201 case NS_E_FILE_NOT_FOUND:
202 errorString =
tr(
"The system cannot find the file specified.");
204 case NS_E_SERVER_NOT_FOUND:
205 errorString =
tr(
"The specified server could not be found.");
207 case MF_E_UNSUPPORTED_BYTESTREAM_TYPE:
209 errorString =
tr(
"Unsupported media type.");
211 case MF_E_UNSUPPORTED_SCHEME:
213 errorString =
tr(
"Unsupported URL scheme.");
217 errorString =
tr(
"Connection to server could not be established.");
221 << Qt::showbase << Qt::hex << Qt::uppercasedigits << static_cast<quint32>(hr);
222 errorString =
tr(
"Failed to load source.");
226 error(errorCode, errorString,
true);
229void MFPlayerSession::handleMediaSourceReady()
233#ifdef DEBUG_MEDIAFOUNDATION
234 qDebug() <<
"handleMediaSourceReady";
237 IMFMediaSource* mediaSource = m_sourceResolver->
mediaSource();
239 DWORD dwCharacteristics = 0;
240 mediaSource->GetCharacteristics(&dwCharacteristics);
243 IMFPresentationDescriptor* sourcePD;
244 hr = mediaSource->CreatePresentationDescriptor(&sourcePD);
249 sourcePD->GetUINT64(MF_PD_DURATION, &m_duration);
252 setupPlaybackTopology(mediaSource, sourcePD);
261bool MFPlayerSession::getStreamInfo(IMFStreamDescriptor *
stream,
262 MFPlayerSession::MediaType *
type,
274 IMFMediaTypeHandler *typeHandler =
nullptr;
276 if (SUCCEEDED(
stream->GetMediaTypeHandler(&typeHandler))) {
280 WCHAR *wstr =
new WCHAR[
len+1];
287 WCHAR *wstr =
new WCHAR[
len+1];
295 if (SUCCEEDED(typeHandler->GetMajorType(&guidMajorType))) {
296 if (guidMajorType == MFMediaType_Audio)
298 else if (guidMajorType == MFMediaType_Video)
302 IMFMediaType *mediaType =
nullptr;
303 if (SUCCEEDED(typeHandler->GetCurrentMediaType(&mediaType))) {
304 mediaType->GetGUID(MF_MT_SUBTYPE,
format);
305 mediaType->Release();
308 typeHandler->Release();
311 return *
type != Unknown;
314void MFPlayerSession::setupPlaybackTopology(IMFMediaSource *
source, IMFPresentationDescriptor *sourcePD)
318 DWORD cSourceStreams = 0;
319 hr = sourcePD->GetStreamDescriptorCount(&cSourceStreams);
326 IMFTopology *topology;
327 hr = MFCreateTopology(&topology);
335 DWORD succeededCount = 0;
336 for (DWORD
i = 0;
i < cSourceStreams;
i++) {
337 BOOL selected = FALSE;
338 bool streamAdded =
false;
339 IMFStreamDescriptor *streamDesc = NULL;
341 HRESULT hr = sourcePD->GetStreamDescriptorByIndex(
i, &selected, &streamDesc);
345 MediaType mediaType = Unknown;
350 if (getStreamInfo(streamDesc, &mediaType, &streamName, &streamLanguage, &
format)) {
362 m_trackInfo[trackType].metaData.append(
metaData);
363 m_trackInfo[trackType].nativeIndexes.append(
i);
364 m_trackInfo[trackType].format =
format;
366 if (((m_mediaTypes & mediaType) == 0) && selected) {
367 IMFTopologyNode *sourceNode = addSourceNode(topology,
source, sourcePD, streamDesc);
369 IMFTopologyNode *outputNode = addOutputNode(mediaType, topology, 0);
372 if (mediaType == Audio) {
373 if (!m_audioSampleGrabberNode)
374 connected = setupAudioSampleGrabber(topology, sourceNode, outputNode);
376 sourceNode->GetTopoNodeID(&m_trackInfo[trackType].sourceNodeId);
377 outputNode->GetTopoNodeID(&m_trackInfo[trackType].outputNodeId);
380 hr = sourceNode->ConnectOutput(0, outputNode, 0);
385 m_trackInfo[trackType].currentIndex = m_trackInfo[trackType].nativeIndexes.count() - 1;
388 m_mediaTypes |= mediaType;
400 outputNode->Release();
402 sourceNode->Release();
407 if (selected && !streamAdded)
408 sourcePD->DeselectStream(
i);
410 streamDesc->Release();
414 if (succeededCount == 0) {
421 hr = m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology);
423 m_updatingTopology =
true;
432IMFTopologyNode* MFPlayerSession::addSourceNode(IMFTopology* topology, IMFMediaSource*
source,
433 IMFPresentationDescriptor* presentationDesc, IMFStreamDescriptor *streamDesc)
435 IMFTopologyNode *node = NULL;
436 HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node);
438 hr = node->SetUnknown(MF_TOPONODE_SOURCE,
source);
440 hr = node->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, presentationDesc);
442 hr = node->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, streamDesc);
444 hr = topology->AddNode(node);
455IMFTopologyNode* MFPlayerSession::addOutputNode(MediaType mediaType, IMFTopology* topology, DWORD sinkID)
457 IMFTopologyNode *node = NULL;
458 if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node)))
461 IMFActivate *activate = NULL;
462 if (mediaType == Audio) {
464 HRESULT hr = MFCreateAudioRendererActivate(&activate);
466 qWarning() <<
"Failed to create audio renderer activate";
471 auto id = m_audioOutput->
device.
id();
480 hr = activate->SetString(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID, (LPCWSTR)
s.utf16());
488 }
else if (mediaType == Video) {
502 || FAILED(node->SetObject(activate))
503 || FAILED(node->SetUINT32(MF_TOPONODE_STREAMID, sinkID))
504 || FAILED(node->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE))
505 || FAILED(topology->AddNode(node))) {
510 if (activate && mediaType == Audio)
516bool MFPlayerSession::addAudioSampleGrabberNode(IMFTopology *topology)
519 IMFMediaType *pType = 0;
520 IMFActivate *sinkActivate = 0;
522 hr = MFCreateMediaType(&pType);
526 hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
530 hr = pType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
534 hr = MFCreateSampleGrabberSinkActivate(pType, m_audioSampleGrabber, &sinkActivate);
539 hr = sinkActivate->SetUINT32(MF_SAMPLEGRABBERSINK_IGNORE_CLOCK, FALSE);
543 hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &m_audioSampleGrabberNode);
547 hr = m_audioSampleGrabberNode->SetObject(sinkActivate);
551 hr = m_audioSampleGrabberNode->SetUINT32(MF_TOPONODE_STREAMID, 0);
555 hr = m_audioSampleGrabberNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);
559 hr = topology->AddNode(m_audioSampleGrabberNode);
564 sinkActivate->Release();
571 sinkActivate->Release();
572 if (m_audioSampleGrabberNode) {
573 m_audioSampleGrabberNode->Release();
574 m_audioSampleGrabberNode = NULL;
579bool MFPlayerSession::setupAudioSampleGrabber(IMFTopology *topology, IMFTopologyNode *sourceNode, IMFTopologyNode *outputNode)
581 if (!addAudioSampleGrabberNode(topology))
585 IMFTopologyNode *pTeeNode = NULL;
587 IMFMediaTypeHandler *typeHandler = NULL;
588 IMFMediaType *mediaType = NULL;
590 hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &pTeeNode);
593 hr = sourceNode->ConnectOutput(0, pTeeNode, 0);
596 hr = pTeeNode->ConnectOutput(0, outputNode, 0);
599 hr = pTeeNode->ConnectOutput(1, m_audioSampleGrabberNode, 0);
607 mediaType->Release();
609 typeHandler->Release();
619 IUnknown *nodeObject = NULL;
620 IMFActivate *activate = NULL;
621 IMFStreamSink *
stream = NULL;
622 IMFMediaSink *
sink = NULL;
624 HRESULT hr = pNode->GetObject(&nodeObject);
628 hr = nodeObject->QueryInterface(IID_PPV_ARGS(&activate));
630 DWORD dwStreamID = 0;
633 hr = activate->ActivateObject(IID_PPV_ARGS(&
sink));
635 dwStreamID = MFGetAttributeUINT32(pNode, MF_TOPONODE_STREAMID, 0);
639 hr =
sink->GetStreamSinkById(dwStreamID, &
stream);
642 hr =
sink->AddStreamSink(dwStreamID, NULL, &
stream);
648 hr = pNode->SetObject(
stream);
651 hr = nodeObject->QueryInterface(IID_PPV_ARGS(&
stream));
655 nodeObject->Release();
669 IMFCollection *collection;
672 HRESULT hr = pTopology->GetOutputNodeCollection(&collection);
677 hr = collection->GetElementCount(&cNodes);
680 for (DWORD
i = 0;
i < cNodes;
i++) {
682 hr = collection->GetElement(
i, &element);
686 IMFTopologyNode *node;
687 hr = element->QueryInterface(IID_IMFTopologyNode, (
void**)&node);
699 collection->Release();
708IMFTopology *MFPlayerSession::insertMFT(IMFTopology *topology, TOPOID outputNodeId)
710 bool isNewTopology =
false;
712 IMFTopoLoader *topoLoader = 0;
713 IMFTopology *resolvedTopology = 0;
714 IMFCollection *outputNodes = 0;
720 if (FAILED(MFCreateTopoLoader(&topoLoader)))
723 if (FAILED(topoLoader->Load(topology, &resolvedTopology, NULL))) {
726 insertColorConverter(topology, outputNodeId);
727 if (FAILED(topoLoader->Load(topology, &resolvedTopology, NULL)))
731 if (insertResizer(resolvedTopology))
732 isNewTopology =
true;
735 if (FAILED(resolvedTopology->GetOutputNodeCollection(&outputNodes)))
738 DWORD elementCount = 0;
739 if (FAILED(outputNodes->GetElementCount(&elementCount)))
742 for (DWORD
n = 0;
n < elementCount;
n++) {
743 IUnknown *element = 0;
744 IMFTopologyNode *node = 0;
745 IUnknown *outputObject = 0;
746 IMFTopologyNode *inputNode = 0;
747 IMFTopologyNode *mftNode = 0;
748 bool mftAdded =
false;
751 if (FAILED(outputNodes->GetElement(
n, &element)))
754 if (FAILED(element->QueryInterface(IID_IMFTopologyNode, (
void**)&node)))
758 if (FAILED(node->GetTopoNodeID(&
id)))
761 if (
id != outputNodeId)
764 if (FAILED(node->GetObject(&outputObject)))
770 DWORD outputIndex = 0;
771 if (FAILED(node->GetInput(0, &inputNode, &outputIndex)))
774 if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mftNode)))
777 if (FAILED(mftNode->SetObject(m_videoProbeMFT)))
780 if (FAILED(resolvedTopology->AddNode(mftNode)))
783 if (FAILED(inputNode->ConnectOutput(0, mftNode, 0)))
786 if (FAILED(mftNode->ConnectOutput(0, node, 0)))
790 isNewTopology =
true;
796 inputNode->Release();
802 outputObject->Release();
812 outputNodes->Release();
815 topoLoader->Release();
819 return resolvedTopology;
822 if (resolvedTopology)
823 resolvedTopology->Release();
832bool MFPlayerSession::insertResizer(IMFTopology *topology)
834 bool inserted =
false;
835 WORD elementCount = 0;
836 IMFTopologyNode *node = 0;
837 IUnknown *
object = 0;
838 IWMColorConvProps *colorConv = 0;
839 IMFTransform *resizer = 0;
840 IMFTopologyNode *resizerNode = 0;
841 IMFTopologyNode *inputNode = 0;
843 HRESULT hr = topology->GetNodeCount(&elementCount);
847 for (WORD
i = 0;
i < elementCount; ++
i) {
857 if (FAILED(topology->GetNode(
i, &node)))
860 MF_TOPOLOGY_TYPE nodeType;
861 if (FAILED(node->GetNodeType(&nodeType)))
864 if (nodeType != MF_TOPOLOGY_TRANSFORM_NODE)
867 if (FAILED(node->GetObject(&
object)))
870 if (FAILED(
object->QueryInterface(&colorConv)))
873 if (FAILED(CoCreateInstance(CLSID_CResizerDMO, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform, (
void**)&resizer)))
876 if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &resizerNode)))
879 if (FAILED(resizerNode->SetObject(resizer)))
882 if (FAILED(topology->AddNode(resizerNode)))
885 DWORD outputIndex = 0;
886 if (FAILED(node->GetInput(0, &inputNode, &outputIndex))) {
887 topology->RemoveNode(resizerNode);
891 if (FAILED(inputNode->ConnectOutput(0, resizerNode, 0))) {
892 topology->RemoveNode(resizerNode);
896 if (FAILED(resizerNode->ConnectOutput(0, node, 0))) {
897 inputNode->ConnectOutput(0, node, 0);
898 topology->RemoveNode(resizerNode);
911 colorConv->Release();
915 resizerNode->Release();
917 inputNode->Release();
926void MFPlayerSession::insertColorConverter(IMFTopology *topology, TOPOID outputNodeId)
928 IMFCollection *outputNodes = 0;
930 if (FAILED(topology->GetOutputNodeCollection(&outputNodes)))
933 DWORD elementCount = 0;
934 if (FAILED(outputNodes->GetElementCount(&elementCount)))
937 for (DWORD
n = 0;
n < elementCount;
n++) {
938 IUnknown *element = 0;
939 IMFTopologyNode *node = 0;
940 IMFTopologyNode *inputNode = 0;
941 IMFTopologyNode *mftNode = 0;
942 IMFTransform *converter = 0;
945 if (FAILED(outputNodes->GetElement(
n, &element)))
948 if (FAILED(element->QueryInterface(IID_IMFTopologyNode, (
void**)&node)))
952 if (FAILED(node->GetTopoNodeID(&
id)))
955 if (
id != outputNodeId)
958 DWORD outputIndex = 0;
959 if (FAILED(node->GetInput(0, &inputNode, &outputIndex)))
962 if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mftNode)))
965 if (FAILED(CoCreateInstance(CLSID_CColorConvertDMO, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform, (
void**)&converter)))
968 if (FAILED(mftNode->SetObject(converter)))
971 if (FAILED(topology->AddNode(mftNode)))
974 if (FAILED(inputNode->ConnectOutput(0, mftNode, 0)))
977 if (FAILED(mftNode->ConnectOutput(0, node, 0)))
985 inputNode->Release();
991 converter->Release();
996 outputNodes->Release();
1001#ifdef DEBUG_MEDIAFOUNDATION
1004 if (!immediate && m_pendingState != NoPending) {
1005 m_request.setCommand(CmdStop);
1007 if (m_state.command == CmdStop)
1013 if (SUCCEEDED(m_session->Stop())) {
1015 m_state.setCommand(CmdStop);
1016 m_pendingState = CmdPending;
1030 m_updateRoutingOnStart =
false;
1039#ifdef DEBUG_MEDIAFOUNDATION
1043 if (m_pendingState != NoPending) {
1044 m_request.setCommand(CmdStart);
1046 if (m_state.command == CmdStart)
1054 if (m_restorePosition >= 0) {
1055 m_position = m_restorePosition;
1056 if (!m_updatingTopology)
1057 m_restorePosition = -1;
1060 PROPVARIANT varStart;
1061 InitPropVariantFromInt64(m_position, &varStart);
1063 if (SUCCEEDED(m_session->Start(&GUID_NULL, &varStart))) {
1064 m_state.setCommand(CmdStart);
1065 m_pendingState = CmdPending;
1069 PropVariantClear(&varStart);
1075#ifdef DEBUG_MEDIAFOUNDATION
1078 if (m_pendingState != NoPending) {
1079 m_request.setCommand(CmdPause);
1081 if (m_state.command == CmdPause)
1084 if (SUCCEEDED(m_session->Pause())) {
1085 m_state.setCommand(CmdPause);
1086 m_pendingState = CmdPending;
1099 if (m_status == newStatus)
1101#ifdef DEBUG_MEDIAFOUNDATION
1102 qDebug() <<
"MFPlayerSession::changeStatus" << newStatus;
1104 m_status = newStatus;
1113bool MFPlayerSession::createSession()
1119 HRESULT hr = MFCreateMediaSession(NULL, &m_session);
1126 m_hCloseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
1128 hr = m_session->BeginGetEvent(
this, m_session);
1150 if (m_request.command == CmdSeek || m_request.command == CmdSeekResume)
1151 return m_request.start;
1153 if (m_pendingState == SeekPending)
1154 return m_state.start;
1156 if (m_state.command == CmdStop)
1157 return m_position / 10000;
1159 if (m_presentationClock) {
1160 MFTIME
time, sysTime;
1161 if (FAILED(m_presentationClock->GetCorrelatedTime(0, &
time, &sysTime)))
1162 return m_position / 10000;
1165 return m_position / 10000;
1170#ifdef DEBUG_MEDIAFOUNDATION
1171 qDebug() <<
"setPosition";
1173 if (m_pendingState != NoPending) {
1174 m_request.setCommand(CmdSeek);
1177 setPositionInternal(
position, CmdNone);
1181void MFPlayerSession::setPositionInternal(
qint64 position, Command requestCmd)
1185 if (m_state.command == CmdStop && requestCmd != CmdSeekResume) {
1193 if (m_state.command == CmdPause)
1196#ifdef DEBUG_MEDIAFOUNDATION
1197 qDebug() <<
"setPositionInternal";
1200 PROPVARIANT varStart;
1201 varStart.vt = VT_I8;
1202 varStart.hVal.QuadPart = LONGLONG(
position * 10000);
1203 if (SUCCEEDED(m_session->Start(NULL, &varStart))) {
1204 PropVariantClear(&varStart);
1206 m_state.setCommand(CmdStart);
1208 m_pendingState = SeekPending;
1217 return m_restoreRate;
1218 return m_state.rate;
1224 m_restoreRate =
rate;
1228 setPlaybackRateInternal(
rate);
1231void MFPlayerSession::setPlaybackRateInternal(
qreal rate)
1233 if (
rate == m_request.rate)
1236 m_pendingRate =
rate;
1240#ifdef DEBUG_MEDIAFOUNDATION
1241 qDebug() <<
"setPlaybackRate";
1243 BOOL isThin = FALSE;
1250 if (FAILED(m_rateSupport->IsRateSupported(FALSE,
rate, NULL))) {
1252 if (FAILED(m_rateSupport->IsRateSupported(isThin,
rate, NULL))) {
1254 m_pendingRate = m_request.rate = m_state.rate;
1258 if (m_pendingState != NoPending) {
1259 m_request.rate =
rate;
1260 m_request.isThin = isThin;
1264 if (m_request.command == CmdNone)
1265 m_request.setCommand(m_state.command);
1268 commitRateChange(
rate, isThin);
1272void MFPlayerSession::commitRateChange(
qreal rate, BOOL isThin)
1274#ifdef DEBUG_MEDIAFOUNDATION
1275 qDebug() <<
"commitRateChange";
1277 Q_ASSERT(m_pendingState == NoPending);
1278 MFTIME hnsSystemTime = 0;
1279 MFTIME hnsClockTime = 0;
1280 Command cmdNow = m_state.command;
1281 bool resetPosition =
false;
1286 if ((
rate > 0 && m_state.rate <= 0) || (rate < 0 && m_state.rate >= 0)) {
1287 if (cmdNow == CmdStart) {
1289 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1292 if (
rate < 0 || m_state.rate < 0)
1293 m_request.setCommand(CmdSeekResume);
1294 else if (isThin || m_state.isThin)
1295 m_request.setCommand(CmdStartAndSeek);
1297 m_request.setCommand(CmdStart);
1300 if (
rate >= 0 && m_state.rate >= 0)
1307 m_request.start = hnsClockTime / 10000;
1308 }
else if (cmdNow == CmdPause) {
1309 if (
rate < 0 || m_state.rate < 0) {
1314 qWarning() <<
"Unable to change rate from positive to negative or vice versa in paused state";
1315 rate = m_state.rate;
1316 isThin = m_state.isThin;
1324 if (
rate > 0 && m_state.rate == 0) {
1325 m_state.setCommand(CmdNone);
1329 }
else if (
rate == 0 && m_state.rate > 0) {
1330 if (cmdNow != CmdPause) {
1337 m_request.setCommand(cmdNow);
1339 }
else if (
rate == 0 && m_state.rate < 0) {
1341 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1343 m_request.setCommand(CmdSeekResume);
1348 m_request.start = hnsClockTime / 10000;
1349 }
else if (!isThin && m_state.isThin) {
1350 if (cmdNow == CmdStart) {
1355 resetPosition =
true;
1356 }
else if (cmdNow == CmdPause) {
1359 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1360 m_request.setCommand(CmdSeekResume);
1361 m_request.start = hnsClockTime / 10000;
1367 if (FAILED(m_rateControl->SetRate(isThin,
rate))) {
1369 rate = m_state.rate;
1370 isThin = m_state.isThin;
1374 if (resetPosition) {
1375 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1381 m_pendingRate = m_request.rate = m_state.rate =
rate;
1383 m_state.isThin = isThin;
1387void MFPlayerSession::scrub(
bool enableScrub)
1389 if (m_scrubbing == enableScrub)
1392 m_scrubbing = enableScrub;
1396 m_pendingRate = m_restoreRate;
1402 m_restoreRate = m_request.rate;
1403 setPlaybackRateInternal(0.0f);
1406 setPlaybackRateInternal(m_restoreRate);
1412 if (m_volume == volume)
1417 setVolumeInternal(volume);
1422 if (m_muted == muted)
1426 setVolumeInternal(muted ? 0 : m_volume);
1429void MFPlayerSession::setVolumeInternal(
float volume)
1431 if (m_volumeControl) {
1433 if (!SUCCEEDED(m_volumeControl->GetChannelCount(&channelCount))
1434 || channelCount == 0)
1438 m_volumeControl->SetChannelVolume(
i, volume);
1444 if (!m_netsourceStatistics)
1447 PropVariantInit(&
var);
1449 key.fmtid = MFNETSOURCE_STATISTICS;
1450 key.pid = MFNETSOURCE_BUFFERPROGRESS_ID;
1454 if (m_netsourceStatistics->GetValue(
key, &
var) == S_OK) {
1455 progress =
var.lVal;
1456 PropVariantClear(&
var);
1459#ifdef DEBUG_MEDIAFOUNDATION
1460 qDebug() <<
"bufferProgress: progress = " << progress;
1463 return progress/100.;
1472 if (m_netsourceStatistics) {
1474 PropVariantInit(&
var);
1476 key.fmtid = MFNETSOURCE_STATISTICS;
1477 key.pid = MFNETSOURCE_SEEKRANGESTART_ID;
1480 if (m_netsourceStatistics->GetValue(
key, &
var) == S_OK) {
1482 PropVariantClear(&
var);
1483 PropVariantInit(&
var);
1484 key.pid = MFNETSOURCE_SEEKRANGEEND_ID;
1485 if (m_netsourceStatistics->GetValue(
key, &
var) == S_OK) {
1487 PropVariantClear(&
var);
1499 if (
riid == IID_IMFAsyncCallback) {
1500 *ppvObject =
static_cast<IMFAsyncCallback*
>(
this);
1501 }
else if (
riid == IID_IUnknown) {
1502 *ppvObject =
static_cast<IUnknown*
>(
this);
1505 return E_NOINTERFACE;
1510ULONG MFPlayerSession::AddRef(
void)
1512 return InterlockedIncrement(&m_cRef);
1515ULONG MFPlayerSession::Release(
void)
1517 LONG cRef = InterlockedDecrement(&m_cRef);
1523 m_playerControl =
nullptr;
1530 if (pResult->GetStateNoAddRef() != m_session)
1533 IMFMediaEvent *pEvent = NULL;
1535 HRESULT hr = m_session->EndGetEvent(pResult, &pEvent);
1540 MediaEventType meType = MEUnknown;
1541 hr = pEvent->GetType(&meType);
1547 if (meType == MESessionClosed) {
1548 SetEvent(m_hCloseEvent);
1552 hr = m_session->BeginGetEvent(
this, m_session);
1567void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent)
1571 if (FAILED(hr) || !m_session) {
1576 MediaEventType meType = MEUnknown;
1578#ifdef DEBUG_MEDIAFOUNDATION
1579 if (FAILED(hrStatus))
1580 qDebug() <<
"handleSessionEvent: MediaEventType = " << meType <<
"Failed";
1582 qDebug() <<
"handleSessionEvent: MediaEventType = " << meType;
1586 case MENonFatalError: {
1588 PropVariantInit(&
var);
1590 qWarning() <<
"handleSessionEvent: non fatal error = " <<
var.ulVal;
1591 PropVariantClear(&
var);
1595 case MESourceUnknown:
1599 if (hrStatus == MF_E_ALREADY_INITIALIZED) {
1602#ifdef DEBUG_MEDIAFOUNDATION
1603 qDebug() <<
"handleSessionEvent: ignoring MF_E_ALREADY_INITIALIZED";
1608 qWarning() <<
"handleSessionEvent: serious error = "
1609 << Qt::showbase << Qt::hex << Qt::uppercasedigits << static_cast<quint32>(hrStatus);
1614 case MF_E_NET_WRITE:
1620 case MF_E_MEDIAPROC_WRONGSTATE:
1628 case MESessionRateChanged:
1631 if (FAILED(hrStatus)) {
1633 PropVariantInit(&
var);
1635 m_state.rate =
var.fltVal;
1640 case MESessionScrubSampleComplete :
1642 updatePendingCommands(CmdStart);
1644 case MESessionStarted:
1651 updatePendingCommands(CmdStart);
1657 m_signalPositionChangeTimer.
start();
1659 case MESessionStopped:
1668 updatePendingCommands(CmdStop);
1669 m_signalPositionChangeTimer.
stop();
1671 case MESessionPaused:
1673 updatePendingCommands(CmdPause);
1674 m_signalPositionChangeTimer.
stop();
1678 case MEReconnectStart:
1679#ifdef DEBUG_MEDIAFOUNDATION
1680 qDebug() <<
"MEReconnectStart" << ((hrStatus == S_OK) ?
"OK" :
"Failed");
1683 case MEReconnectEnd:
1684#ifdef DEBUG_MEDIAFOUNDATION
1685 qDebug() <<
"MEReconnectEnd" << ((hrStatus == S_OK) ?
"OK" :
"Failed");
1688 case MESessionTopologySet:
1689 if (FAILED(hrStatus)) {
1693 if (m_audioSampleGrabberNode) {
1695 if (SUCCEEDED(m_audioSampleGrabberNode->GetObject(&
obj))) {
1696 IMFStreamSink *streamSink = 0;
1697 if (SUCCEEDED(
obj->QueryInterface(IID_PPV_ARGS(&streamSink)))) {
1698 IMFMediaTypeHandler *typeHandler = 0;
1699 if (SUCCEEDED(streamSink->GetMediaTypeHandler((&typeHandler)))) {
1700 IMFMediaType *mediaType = 0;
1701 if (SUCCEEDED(typeHandler->GetCurrentMediaType(&mediaType))) {
1703 mediaType->Release();
1705 typeHandler->Release();
1707 streamSink->Release();
1715 m_lastPosition = -1;
1723 if (FAILED(hrStatus)) {
1729 case MEBufferingStarted:
1733 case MEBufferingStopped:
1737 case MESessionEnded:
1738 m_pendingState = NoPending;
1739 m_state.command = CmdStop;
1740 m_state.prevCmd = CmdNone;
1741 m_request.command = CmdNone;
1742 m_request.prevCmd = CmdNone;
1745 m_position =
qint64(m_duration);
1750 case MEEndOfPresentationSegment:
1752 case MESessionTopologyStatus: {
1755 if (
status == MF_TOPOSTATUS_READY) {
1757 if (SUCCEEDED(m_session->GetClock(&clock))) {
1758 clock->QueryInterface(IID_IMFPresentationClock, (
void**)(&m_presentationClock));
1762 if (SUCCEEDED(MFGetService(m_session, MF_RATE_CONTROL_SERVICE, IID_PPV_ARGS(&m_rateControl)))) {
1763 if (SUCCEEDED(MFGetService(m_session, MF_RATE_CONTROL_SERVICE, IID_PPV_ARGS(&m_rateSupport)))) {
1764 if (SUCCEEDED(m_rateSupport->IsRateSupported(TRUE, 0, NULL)))
1767 BOOL isThin = FALSE;
1769 if (SUCCEEDED(m_rateControl->GetRate(&isThin, &
rate))) {
1770 if (m_pendingRate !=
rate) {
1771 m_state.rate = m_request.rate =
rate;
1776 MFGetService(m_session, MFNETSOURCE_STATISTICS_SERVICE, IID_PPV_ARGS(&m_netsourceStatistics));
1778 if (SUCCEEDED(MFGetService(m_session, MR_STREAM_VOLUME_SERVICE, IID_PPV_ARGS(&m_volumeControl))))
1779 setVolumeInternal(m_muted ? 0 : m_volume);
1781 m_updatingTopology =
false;
1794void MFPlayerSession::updatePendingCommands(Command command)
1797 if (m_state.command != command || m_pendingState == NoPending)
1801 if (m_pendingState == SeekPending && m_state.prevCmd == CmdPause) {
1802 m_pendingState = NoPending;
1810 m_state.setCommand(CmdPause);
1813 m_pendingState = NoPending;
1816 if (m_request.rate != m_state.rate) {
1817 commitRateChange(m_request.rate, m_request.isThin);
1821 if (m_pendingState == NoPending) {
1822 switch (m_request.command) {
1834 setPositionInternal(m_request.start, m_request.command);
1836 case CmdStartAndSeek:
1838 setPositionInternal(m_request.start, m_request.command);
1843 m_request.setCommand(CmdNone);
1848bool MFPlayerSession::canScrub()
const
1850 return m_canScrub && m_rateSupport && m_rateControl;
1853void MFPlayerSession::clear()
1855#ifdef DEBUG_MEDIAFOUNDATION
1856 qDebug() <<
"MFPlayerSession::clear";
1861 m_pendingState = NoPending;
1862 m_state.command = CmdStop;
1863 m_state.prevCmd = CmdNone;
1864 m_request.command = CmdNone;
1865 m_request.prevCmd = CmdNone;
1868 m_trackInfo[
i].metaData.clear();
1869 m_trackInfo[
i].nativeIndexes.clear();
1870 m_trackInfo[
i].currentIndex = -1;
1871 m_trackInfo[
i].sourceNodeId = TOPOID(-1);
1872 m_trackInfo[
i].outputNodeId = TOPOID(-1);
1873 m_trackInfo[
i].format = GUID_NULL;
1881 if (m_presentationClock) {
1882 m_presentationClock->Release();
1883 m_presentationClock = NULL;
1885 if (m_rateControl) {
1886 m_rateControl->Release();
1887 m_rateControl = NULL;
1889 if (m_rateSupport) {
1890 m_rateSupport->Release();
1891 m_rateSupport = NULL;
1893 if (m_volumeControl) {
1894 m_volumeControl->Release();
1895 m_volumeControl = NULL;
1897 if (m_netsourceStatistics) {
1898 m_netsourceStatistics->Release();
1899 m_netsourceStatistics = NULL;
1901 if (m_audioSampleGrabberNode) {
1902 m_audioSampleGrabberNode->Release();
1903 m_audioSampleGrabberNode = NULL;
1909 if (m_audioOutput ==
device)
1916 if (m_audioOutput) {
1929 if (currentAudioTrack > -1)
1947 const auto &nativeIndexes = m_trackInfo[
type].nativeIndexes;
1949 if (index < -1 || index >= nativeIndexes.count())
1957 IMFTopology *topology =
nullptr;
1961 m_restorePosition =
position() * 10000;
1963 if (m_state.command == CmdStart)
1966 if (m_trackInfo[
type].outputNodeId != TOPOID(-1)) {
1967 IMFTopologyNode *node =
nullptr;
1968 if (SUCCEEDED(topology->GetNodeByID(m_trackInfo[
type].outputNodeId, &node))) {
1969 topology->RemoveNode(node);
1971 m_trackInfo[
type].outputNodeId = TOPOID(-1);
1974 if (m_trackInfo[
type].sourceNodeId != TOPOID(-1)) {
1975 IMFTopologyNode *node =
nullptr;
1976 if (SUCCEEDED(topology->GetNodeByID(m_trackInfo[
type].sourceNodeId, &node))) {
1977 topology->RemoveNode(node);
1979 m_trackInfo[
type].sourceNodeId = TOPOID(-1);
1983 IMFMediaSource *mediaSource = m_sourceResolver->
mediaSource();
1985 IMFPresentationDescriptor *sourcePD =
nullptr;
1986 if (SUCCEEDED(mediaSource->CreatePresentationDescriptor(&sourcePD))) {
1988 if (m_trackInfo[
type].currentIndex >= 0 && m_trackInfo[
type].currentIndex < nativeIndexes.count())
1989 sourcePD->DeselectStream(nativeIndexes.at(m_trackInfo[
type].currentIndex));
1994 m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology);
1996 int nativeIndex = nativeIndexes.at(
index);
1997 sourcePD->SelectStream(nativeIndex);
1999 IMFStreamDescriptor *streamDesc =
nullptr;
2000 BOOL selected = FALSE;
2002 if (SUCCEEDED(sourcePD->GetStreamDescriptorByIndex(nativeIndex, &selected, &streamDesc))) {
2003 IMFTopologyNode *sourceNode = addSourceNode(topology, mediaSource, sourcePD, streamDesc);
2005 IMFTopologyNode *outputNode = addOutputNode(MFPlayerSession::Audio, topology, 0);
2007 if (SUCCEEDED(sourceNode->ConnectOutput(0, outputNode, 0))) {
2008 sourceNode->GetTopoNodeID(&m_trackInfo[
type].sourceNodeId);
2009 outputNode->GetTopoNodeID(&m_trackInfo[
type].outputNodeId);
2010 m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology);
2012 outputNode->Release();
2014 sourceNode->Release();
2016 streamDesc->Release();
2019 m_updatingTopology =
true;
2020 sourcePD->Release();
2022 topology->Release();
2030 return m_trackInfo[
type].currentIndex;
2037 return m_trackInfo[
type].metaData.count();
2045 if (trackNumber < 0 || trackNumber >= m_trackInfo[
type].
metaData.count())
2048 return m_trackInfo[
type].metaData.at(trackNumber);
2053#include "moc_mfplayersession_p.cpp"
AVFCameraSession * m_session
IOBluetoothDevice * device
void setFormat(const QAudioFormat &format)
void seekableUpdate(bool seekable)
void setPlaybackRate(qreal rate)
void setPosition(qint64 position)
QMediaMetaData metaData() const
void setAudioOutput(QPlatformAudioOutput *device)
void setVideoSink(QVideoSink *sink)
void bufferProgressChanged(float percentFilled)
void setActiveTrack(QPlatformMediaPlayer::TrackType type, int index)
void playbackRateChanged(qreal rate)
STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject) override
void positionChanged(qint64 position)
MFPlayerSession(MFPlayerControl *playerControl=0)
void durationUpdate(qint64 duration)
qreal playbackRate() const
void stop(bool immediate=false)
void setVolume(float volume)
QMediaMetaData trackMetaData(QPlatformMediaPlayer::TrackType type, int trackNumber)
STDMETHODIMP Invoke(IMFAsyncResult *pResult) override
void load(const QUrl &media, QIODevice *stream)
int activeTrack(QPlatformMediaPlayer::TrackType type)
QMediaPlayer::MediaStatus status() const
void changeStatus(QMediaPlayer::MediaStatus newStatus)
QMediaTimeRange availablePlaybackRanges()
void setMuted(bool muted)
void sessionEvent(IMFMediaEvent *sessionEvent)
friend class SourceResolver
void updateOutputRouting()
int trackCount(QPlatformMediaPlayer::TrackType)
void setCropRect(QRect cropRect)
void setSink(QVideoSink *surface)
IMFActivate * createActivate()
QString description
\qmlproperty string QtMultimedia::audioDevice::description
QByteArray id
\qmlproperty string QtMultimedia::audioDevice::id
void mutedChanged(bool muted)
float volume
\qmlproperty real QtMultimedia::AudioOutput::volume
void volumeChanged(float volume)
\inmodule QtCore \reentrant
Language language() const
Returns the language of this locale.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
QObject * sender() const
Returns a pointer to the object that sent the signal, if called in a slot activated by a signal; othe...
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
void deleteLater()
\threadsafe
\inmodule QtCore\reentrant
\inmodule QtCore\reentrant
constexpr bool isValid() const noexcept
Returns true if both the width and height is equal to or greater than 0; otherwise returns false.
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QString fromUtf16(const char16_t *, qsizetype size=-1)
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
void setInterval(int msec)
void stop()
Stops the timer.
QMetaObject::Connection callOnTimeout(Args &&...args)
void setTimerType(Qt::TimerType atype)
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
QSize toSize() const
Returns the variant as a QSize if the variant has userType() \l QMetaType::QSize; otherwise returns a...
The QVideoSink class represents a generic sink for video data.
IMFMediaSource * mediaSource() const
void load(const QUrl &url, QIODevice *stream)
HRESULT BindOutputNode(IMFTopologyNode *pNode)
HRESULT BindOutputNodes(IMFTopology *pTopology)
Combined button and popup list for selecting options.
Q_MULTIMEDIA_EXPORT QAudioFormat mediaTypeToFormat(IMFMediaType *mediaType)
DBusConnection const char DBusError * error
n void setPosition(void) \n\
GLint GLsizei GLsizei GLenum format
GLsizei GLsizei GLchar * source
GLsizei GLenum GLboolean sink
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
IUIViewSettingsInterop __RPC__in REFIID riid
const GUID QMM_MF_SD_LANGUAGE
const GUID QMM_MF_SD_STREAM_NAME
#define QMM_MFSESSION_GETFULLTOPOLOGY_CURRENT
#define QMM_WININET_E_CANNOT_CONNECT
QUrl url("example.com")
[constructor-url-reference]