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"
20#include <private/qwindowsmfdefs_p.h>
21#include <private/qwindowsaudioutils_p.h>
28#include <wmcodecdsp.h>
30#include <mmdeviceapi.h>
31#include <propvarutil.h>
32#include <functiondiscoverykeys_devpkey.h>
40 m_playerControl(playerControl),
53 m_signalPositionChangeTimer.
callOnTimeout(
this, &MFPlayerSession::timeout);
55 m_pendingState = NoPending;
56 ZeroMemory(&m_state,
sizeof(m_state));
57 m_state.command = CmdStop;
58 m_state.prevCmd = CmdNone;
60 ZeroMemory(&m_request,
sizeof(m_request));
61 m_request.command = CmdNone;
62 m_request.prevCmd = CmdNone;
63 m_request.rate = 1.0f;
68void MFPlayerSession::timeout()
72 if (
pos != m_lastPosition) {
73 const bool updatePos = m_timeCounter++ % 10 == 0;
74 if (
pos >=
qint64(m_duration / 10000 - 20)) {
75 if (m_playerControl->
doLoop()) {
93#ifdef DEBUG_MEDIAFOUNDATION
97 m_signalPositionChangeTimer.
stop();
105 hr = m_session->Close();
107 DWORD dwWaitResult = WaitForSingleObject(m_hCloseEvent.
get(), 2000);
108 if (dwWaitResult == WAIT_TIMEOUT) {
109 qWarning() <<
"session close time out!";
117 m_session->Shutdown();
118 if (m_sourceResolver)
119 m_sourceResolver->shutdown();
121 m_sourceResolver.Reset();
136#ifdef DEBUG_MEDIAFOUNDATION
142 m_sourceResolver->cancel();
151 }
else if (createSession()) {
155 m_updateRoutingOnStart =
true;
160void MFPlayerSession::handleSourceError(
long hr)
167 errorString =
tr(
"Attempting to play invalid Qt resource.");
169 case NS_E_FILE_NOT_FOUND:
170 errorString =
tr(
"The system cannot find the file specified.");
172 case NS_E_SERVER_NOT_FOUND:
173 errorString =
tr(
"The specified server could not be found.");
175 case MF_E_UNSUPPORTED_BYTESTREAM_TYPE:
177 errorString =
tr(
"Unsupported media type.");
179 case MF_E_UNSUPPORTED_SCHEME:
181 errorString =
tr(
"Unsupported URL scheme.");
185 errorString =
tr(
"Connection to server could not be established.");
189 << Qt::showbase << Qt::hex << Qt::uppercasedigits << static_cast<quint32>(hr);
190 errorString =
tr(
"Failed to load source.");
194 error(errorCode, errorString,
true);
197void MFPlayerSession::handleMediaSourceReady()
200 || m_sourceResolver.Get() !=
sender())
202#ifdef DEBUG_MEDIAFOUNDATION
203 qDebug() <<
"handleMediaSourceReady";
206 IMFMediaSource* mediaSource = m_sourceResolver->mediaSource();
208 DWORD dwCharacteristics = 0;
209 mediaSource->GetCharacteristics(&dwCharacteristics);
212 ComPtr<IMFPresentationDescriptor> sourcePD;
213 hr = mediaSource->CreatePresentationDescriptor(&sourcePD);
218 sourcePD->GetUINT64(MF_PD_DURATION, &m_duration);
221 setupPlaybackTopology(mediaSource, sourcePD.Get());
229bool MFPlayerSession::getStreamInfo(IMFStreamDescriptor *
stream,
230 MFPlayerSession::MediaType *
type,
242 ComPtr<IMFMediaTypeHandler> typeHandler;
244 if (SUCCEEDED(
stream->GetMediaTypeHandler(&typeHandler))) {
248 WCHAR *wstr =
new WCHAR[
len+1];
255 WCHAR *wstr =
new WCHAR[
len+1];
263 if (SUCCEEDED(typeHandler->GetMajorType(&guidMajorType))) {
264 if (guidMajorType == MFMediaType_Audio)
266 else if (guidMajorType == MFMediaType_Video)
270 ComPtr<IMFMediaType> mediaType;
271 if (SUCCEEDED(typeHandler->GetCurrentMediaType(&mediaType))) {
272 mediaType->GetGUID(MF_MT_SUBTYPE,
format);
276 return *
type != Unknown;
279void MFPlayerSession::setupPlaybackTopology(IMFMediaSource *
source, IMFPresentationDescriptor *sourcePD)
283 DWORD cSourceStreams = 0;
284 hr = sourcePD->GetStreamDescriptorCount(&cSourceStreams);
291 ComPtr<IMFTopology> topology;
292 hr = MFCreateTopology(&topology);
300 DWORD succeededCount = 0;
301 for (DWORD
i = 0;
i < cSourceStreams;
i++) {
302 BOOL selected = FALSE;
303 bool streamAdded =
false;
304 ComPtr<IMFStreamDescriptor> streamDesc;
306 HRESULT hr = sourcePD->GetStreamDescriptorByIndex(
i, &selected, &streamDesc);
310 MediaType mediaType = Unknown;
315 if (getStreamInfo(streamDesc.Get(), &mediaType, &streamName, &streamLanguage,
329 m_trackInfo[trackType].nativeIndexes.
append(
i);
330 m_trackInfo[trackType].format =
format;
332 if (((m_mediaTypes & mediaType) == 0) && selected) {
333 ComPtr<IMFTopologyNode> sourceNode =
334 addSourceNode(topology.Get(),
source, sourcePD, streamDesc.Get());
336 ComPtr<IMFTopologyNode> outputNode =
337 addOutputNode(mediaType, topology.Get(), 0);
339 sourceNode->GetTopoNodeID(&m_trackInfo[trackType].sourceNodeId);
340 outputNode->GetTopoNodeID(&m_trackInfo[trackType].outputNodeId);
342 hr = sourceNode->ConnectOutput(0, outputNode.Get(), 0);
347 m_trackInfo[trackType].currentIndex = m_trackInfo[trackType].nativeIndexes.
count() - 1;
350 m_mediaTypes |= mediaType;
364 topology->RemoveNode(sourceNode.Get());
370 if (selected && !streamAdded)
371 sourcePD->DeselectStream(
i);
375 if (succeededCount == 0) {
382 hr = m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology.Get());
384 m_updatingTopology =
true;
392ComPtr<IMFTopologyNode> MFPlayerSession::addSourceNode(IMFTopology *topology,
394 IMFPresentationDescriptor *presentationDesc,
395 IMFStreamDescriptor *streamDesc)
397 ComPtr<IMFTopologyNode> node;
398 HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node);
400 hr = node->SetUnknown(MF_TOPONODE_SOURCE,
source);
402 hr = node->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, presentationDesc);
404 hr = node->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, streamDesc);
406 hr = topology->AddNode(node.Get());
416ComPtr<IMFTopologyNode> MFPlayerSession::addOutputNode(MediaType mediaType, IMFTopology *topology,
419 ComPtr<IMFTopologyNode> node;
420 if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node)))
423 ComPtr<IMFActivate> activate;
424 if (mediaType == Audio) {
426 auto id = m_audioOutput->
device.
id();
428 qInfo() <<
"No audio output";
432 HRESULT hr = MFCreateAudioRendererActivate(&activate);
434 qWarning() <<
"Failed to create audio renderer activate";
439 hr = activate->SetString(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID, (LPCWSTR)
s.utf16());
441 qWarning() <<
"Failed to set attribute for audio device"
446 }
else if (mediaType == Video) {
459 if (!activate || FAILED(node->SetObject(activate.Get()))
460 || FAILED(node->SetUINT32(MF_TOPONODE_STREAMID, sinkID))
461 || FAILED(node->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE))
462 || FAILED(topology->AddNode(node.Get()))) {
466 if (activate && mediaType == Audio)
478 ComPtr<IUnknown> nodeObject;
479 ComPtr<IMFActivate> activate;
480 ComPtr<IMFStreamSink>
stream;
481 ComPtr<IMFMediaSink>
sink;
483 HRESULT hr = pNode->GetObject(&nodeObject);
487 hr = nodeObject->QueryInterface(IID_PPV_ARGS(&activate));
489 DWORD dwStreamID = 0;
492 hr = activate->ActivateObject(IID_PPV_ARGS(&
sink));
494 dwStreamID = MFGetAttributeUINT32(pNode, MF_TOPONODE_STREAMID, 0);
498 hr =
sink->GetStreamSinkById(dwStreamID, &
stream);
501 hr =
sink->AddStreamSink(dwStreamID, NULL, &
stream);
507 hr = pNode->SetObject(
stream.Get());
510 hr = nodeObject->QueryInterface(IID_PPV_ARGS(&
stream));
520 ComPtr<IMFCollection> collection;
523 HRESULT hr = pTopology->GetOutputNodeCollection(&collection);
528 hr = collection->GetElementCount(&cNodes);
531 for (DWORD
i = 0;
i < cNodes;
i++) {
532 ComPtr<IUnknown> element;
533 hr = collection->GetElement(
i, &element);
537 ComPtr<IMFTopologyNode> node;
538 hr = element->QueryInterface(IID_IMFTopologyNode, &node);
556ComPtr<IMFTopology> MFPlayerSession::insertMFT(
const ComPtr<IMFTopology> &topology,
559 bool isNewTopology =
false;
561 ComPtr<IMFTopoLoader> topoLoader;
562 ComPtr<IMFTopology> resolvedTopology;
563 ComPtr<IMFCollection> outputNodes;
569 if (FAILED(MFCreateTopoLoader(&topoLoader)))
572 if (FAILED(topoLoader->Load(topology.Get(), &resolvedTopology, NULL))) {
575 insertColorConverter(topology.Get(), outputNodeId);
576 if (FAILED(topoLoader->Load(topology.Get(), &resolvedTopology, NULL)))
580 if (insertResizer(resolvedTopology.Get()))
581 isNewTopology =
true;
585 return resolvedTopology;
595bool MFPlayerSession::insertResizer(IMFTopology *topology)
597 bool inserted =
false;
598 WORD elementCount = 0;
599 ComPtr<IMFTopologyNode> node;
601 ComPtr<IWMColorConvProps> colorConv;
602 ComPtr<IMFTransform> resizer;
603 ComPtr<IMFTopologyNode> resizerNode;
604 ComPtr<IMFTopologyNode> inputNode;
606 HRESULT hr = topology->GetNodeCount(&elementCount);
610 for (WORD
i = 0;
i < elementCount; ++
i) {
614 if (FAILED(topology->GetNode(
i, &node)))
617 MF_TOPOLOGY_TYPE nodeType;
618 if (FAILED(node->GetNodeType(&nodeType)))
621 if (nodeType != MF_TOPOLOGY_TRANSFORM_NODE)
624 if (FAILED(node->GetObject(&
object)))
627 if (FAILED(
object->QueryInterface(IID_PPV_ARGS(&colorConv))))
630 if (FAILED(CoCreateInstance(CLSID_CResizerDMO, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform,
634 if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &resizerNode)))
637 if (FAILED(resizerNode->SetObject(resizer.Get())))
640 if (FAILED(topology->AddNode(resizerNode.Get())))
643 DWORD outputIndex = 0;
644 if (FAILED(node->GetInput(0, &inputNode, &outputIndex))) {
645 topology->RemoveNode(resizerNode.Get());
649 if (FAILED(inputNode->ConnectOutput(0, resizerNode.Get(), 0))) {
650 topology->RemoveNode(resizerNode.Get());
654 if (FAILED(resizerNode->ConnectOutput(0, node.Get(), 0))) {
655 inputNode->ConnectOutput(0, node.Get(), 0);
656 topology->RemoveNode(resizerNode.Get());
671void MFPlayerSession::insertColorConverter(IMFTopology *topology, TOPOID outputNodeId)
673 ComPtr<IMFCollection> outputNodes;
675 if (FAILED(topology->GetOutputNodeCollection(&outputNodes)))
678 DWORD elementCount = 0;
679 if (FAILED(outputNodes->GetElementCount(&elementCount)))
682 for (DWORD
n = 0;
n < elementCount;
n++) {
683 ComPtr<IUnknown> element;
684 ComPtr<IMFTopologyNode> node;
685 ComPtr<IMFTopologyNode> inputNode;
686 ComPtr<IMFTopologyNode> mftNode;
687 ComPtr<IMFTransform> converter;
690 if (FAILED(outputNodes->GetElement(
n, &element)))
693 if (FAILED(element->QueryInterface(IID_IMFTopologyNode, &node)))
697 if (FAILED(node->GetTopoNodeID(&
id)))
700 if (
id != outputNodeId)
703 DWORD outputIndex = 0;
704 if (FAILED(node->GetInput(0, &inputNode, &outputIndex)))
707 if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mftNode)))
710 if (FAILED(CoCreateInstance(CLSID_CColorConvertDMO, NULL, CLSCTX_INPROC_SERVER,
711 IID_IMFTransform, &converter)))
714 if (FAILED(mftNode->SetObject(converter.Get())))
717 if (FAILED(topology->AddNode(mftNode.Get())))
720 if (FAILED(inputNode->ConnectOutput(0, mftNode.Get(), 0)))
723 if (FAILED(mftNode->ConnectOutput(0, node.Get(), 0)))
732#ifdef DEBUG_MEDIAFOUNDATION
735 if (!immediate && m_pendingState != NoPending) {
736 m_request.setCommand(CmdStop);
738 if (m_state.command == CmdStop)
744 if (SUCCEEDED(m_session->Stop())) {
746 m_state.setCommand(CmdStop);
747 m_pendingState = CmdPending;
761 m_updateRoutingOnStart =
false;
770#ifdef DEBUG_MEDIAFOUNDATION
774 if (m_pendingState != NoPending) {
775 m_request.setCommand(CmdStart);
777 if (m_state.command == CmdStart)
785 if (m_restorePosition >= 0) {
786 m_position = m_restorePosition;
787 if (!m_updatingTopology)
788 m_restorePosition = -1;
791 PROPVARIANT varStart;
792 InitPropVariantFromInt64(m_position, &varStart);
794 if (SUCCEEDED(m_session->Start(&GUID_NULL, &varStart))) {
795 m_state.setCommand(CmdStart);
796 m_pendingState = CmdPending;
800 PropVariantClear(&varStart);
806#ifdef DEBUG_MEDIAFOUNDATION
809 if (m_pendingState != NoPending) {
810 m_request.setCommand(CmdPause);
812 if (m_state.command == CmdPause)
815 if (SUCCEEDED(m_session->Pause())) {
816 m_state.setCommand(CmdPause);
817 m_pendingState = CmdPending;
830 if (m_status == newStatus)
832#ifdef DEBUG_MEDIAFOUNDATION
833 qDebug() <<
"MFPlayerSession::changeStatus" << newStatus;
835 m_status = newStatus;
844bool MFPlayerSession::createSession()
850 HRESULT hr = MFCreateMediaSession(NULL, &m_session);
857 m_hCloseEvent =
EventHandle{ CreateEvent(NULL, FALSE, FALSE, NULL) };
859 hr = m_session->BeginGetEvent(
this, m_session.Get());
867 m_sourceResolver = makeComObject<SourceResolver>();
869 &MFPlayerSession::handleMediaSourceReady);
871 &MFPlayerSession::handleSourceError);
879 if (m_request.command == CmdSeek || m_request.command == CmdSeekResume)
880 return m_request.start;
882 if (m_pendingState == SeekPending)
883 return m_state.start;
885 if (m_state.command == CmdStop)
886 return m_position / 10000;
888 if (m_presentationClock) {
889 MFTIME
time, sysTime;
890 if (FAILED(m_presentationClock->GetCorrelatedTime(0, &
time, &sysTime)))
891 return m_position / 10000;
894 return m_position / 10000;
899#ifdef DEBUG_MEDIAFOUNDATION
900 qDebug() <<
"setPosition";
902 if (m_pendingState != NoPending) {
903 m_request.setCommand(CmdSeek);
906 setPositionInternal(
position, CmdNone);
910void MFPlayerSession::setPositionInternal(
qint64 position, Command requestCmd)
914 if (m_state.command == CmdStop && requestCmd != CmdSeekResume) {
922 if (m_state.command == CmdPause)
925#ifdef DEBUG_MEDIAFOUNDATION
926 qDebug() <<
"setPositionInternal";
929 PROPVARIANT varStart;
931 varStart.hVal.QuadPart = LONGLONG(
position * 10000);
932 if (SUCCEEDED(m_session->Start(NULL, &varStart))) {
933 PropVariantClear(&varStart);
935 m_state.setCommand(CmdStart);
937 m_pendingState = SeekPending;
946 return m_restoreRate;
953 m_restoreRate =
rate;
957 setPlaybackRateInternal(
rate);
960void MFPlayerSession::setPlaybackRateInternal(
qreal rate)
962 if (
rate == m_request.rate)
965 m_pendingRate =
rate;
969#ifdef DEBUG_MEDIAFOUNDATION
970 qDebug() <<
"setPlaybackRate";
979 if (FAILED(m_rateSupport->IsRateSupported(FALSE,
rate, NULL))) {
981 if (FAILED(m_rateSupport->IsRateSupported(isThin,
rate, NULL))) {
983 m_pendingRate = m_request.rate = m_state.rate;
987 if (m_pendingState != NoPending) {
988 m_request.rate =
rate;
989 m_request.isThin = isThin;
993 if (m_request.command == CmdNone)
994 m_request.setCommand(m_state.command);
997 commitRateChange(
rate, isThin);
1001void MFPlayerSession::commitRateChange(
qreal rate, BOOL isThin)
1003#ifdef DEBUG_MEDIAFOUNDATION
1004 qDebug() <<
"commitRateChange";
1006 Q_ASSERT(m_pendingState == NoPending);
1007 MFTIME hnsSystemTime = 0;
1008 MFTIME hnsClockTime = 0;
1009 Command cmdNow = m_state.command;
1010 bool resetPosition =
false;
1015 if ((
rate > 0 && m_state.rate <= 0) || (rate < 0 && m_state.rate >= 0)) {
1016 if (cmdNow == CmdStart) {
1018 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1021 if (
rate < 0 || m_state.rate < 0)
1022 m_request.setCommand(CmdSeekResume);
1023 else if (isThin || m_state.isThin)
1024 m_request.setCommand(CmdStartAndSeek);
1026 m_request.setCommand(CmdStart);
1029 if (
rate >= 0 && m_state.rate >= 0)
1036 m_request.start = hnsClockTime / 10000;
1037 }
else if (cmdNow == CmdPause) {
1038 if (
rate < 0 || m_state.rate < 0) {
1043 qWarning() <<
"Unable to change rate from positive to negative or vice versa in paused state";
1044 rate = m_state.rate;
1045 isThin = m_state.isThin;
1053 if (
rate > 0 && m_state.rate == 0) {
1054 m_state.setCommand(CmdNone);
1058 }
else if (
rate == 0 && m_state.rate > 0) {
1059 if (cmdNow != CmdPause) {
1066 m_request.setCommand(cmdNow);
1068 }
else if (
rate == 0 && m_state.rate < 0) {
1070 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1072 m_request.setCommand(CmdSeekResume);
1077 m_request.start = hnsClockTime / 10000;
1078 }
else if (!isThin && m_state.isThin) {
1079 if (cmdNow == CmdStart) {
1084 resetPosition =
true;
1085 }
else if (cmdNow == CmdPause) {
1088 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1089 m_request.setCommand(CmdSeekResume);
1090 m_request.start = hnsClockTime / 10000;
1096 if (FAILED(m_rateControl->SetRate(isThin,
rate))) {
1098 rate = m_state.rate;
1099 isThin = m_state.isThin;
1103 if (resetPosition) {
1104 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1110 m_pendingRate = m_request.rate = m_state.rate =
rate;
1112 m_state.isThin = isThin;
1116void MFPlayerSession::scrub(
bool enableScrub)
1118 if (m_scrubbing == enableScrub)
1121 m_scrubbing = enableScrub;
1125 m_pendingRate = m_restoreRate;
1131 m_restoreRate = m_request.rate;
1132 setPlaybackRateInternal(0.0f);
1135 setPlaybackRateInternal(m_restoreRate);
1141 if (m_volume == volume)
1146 setVolumeInternal(volume);
1151 if (m_muted == muted)
1155 setVolumeInternal(muted ? 0 : m_volume);
1158void MFPlayerSession::setVolumeInternal(
float volume)
1160 if (m_volumeControl) {
1162 if (!SUCCEEDED(m_volumeControl->GetChannelCount(&channelCount))
1163 || channelCount == 0)
1167 m_volumeControl->SetChannelVolume(
i, volume);
1173 if (!m_netsourceStatistics)
1176 PropVariantInit(&
var);
1178 key.fmtid = MFNETSOURCE_STATISTICS;
1179 key.pid = MFNETSOURCE_BUFFERPROGRESS_ID;
1183 if (m_netsourceStatistics->GetValue(
key, &
var) == S_OK) {
1184 progress =
var.lVal;
1185 PropVariantClear(&
var);
1188#ifdef DEBUG_MEDIAFOUNDATION
1189 qDebug() <<
"bufferProgress: progress = " << progress;
1192 return progress/100.;
1201 if (m_netsourceStatistics) {
1203 PropVariantInit(&
var);
1205 key.fmtid = MFNETSOURCE_STATISTICS;
1206 key.pid = MFNETSOURCE_SEEKRANGESTART_ID;
1209 if (m_netsourceStatistics->GetValue(
key, &
var) == S_OK) {
1211 PropVariantClear(&
var);
1212 PropVariantInit(&
var);
1213 key.pid = MFNETSOURCE_SEEKRANGEEND_ID;
1214 if (m_netsourceStatistics->GetValue(
key, &
var) == S_OK) {
1216 PropVariantClear(&
var);
1228 if (
riid == IID_IMFAsyncCallback) {
1229 *ppvObject =
static_cast<IMFAsyncCallback*
>(
this);
1230 }
else if (
riid == IID_IUnknown) {
1231 *ppvObject =
static_cast<IUnknown*
>(
this);
1234 return E_NOINTERFACE;
1239ULONG MFPlayerSession::AddRef(
void)
1241 return InterlockedIncrement(&m_cRef);
1244ULONG MFPlayerSession::Release(
void)
1246 LONG cRef = InterlockedDecrement(&m_cRef);
1252 m_playerControl =
nullptr;
1259 if (pResult->GetStateNoAddRef() != m_session.Get())
1262 ComPtr<IMFMediaEvent> pEvent;
1264 HRESULT hr = m_session->EndGetEvent(pResult, &pEvent);
1269 MediaEventType meType = MEUnknown;
1270 hr = pEvent->GetType(&meType);
1275 if (meType == MESessionClosed) {
1276 SetEvent(m_hCloseEvent.
get());
1279 hr = m_session->BeginGetEvent(
this, m_session.Get());
1291void MFPlayerSession::handleSessionEvent(
const ComPtr<IMFMediaEvent> &sessionEvent)
1295 if (FAILED(hr) || !m_session) {
1299 MediaEventType meType = MEUnknown;
1301#ifdef DEBUG_MEDIAFOUNDATION
1302 if (FAILED(hrStatus))
1303 qDebug() <<
"handleSessionEvent: MediaEventType = " << meType <<
"Failed";
1305 qDebug() <<
"handleSessionEvent: MediaEventType = " << meType;
1309 case MENonFatalError: {
1311 PropVariantInit(&
var);
1313 qWarning() <<
"handleSessionEvent: non fatal error = " <<
var.ulVal;
1314 PropVariantClear(&
var);
1318 case MESourceUnknown:
1322 if (hrStatus == MF_E_ALREADY_INITIALIZED) {
1325#ifdef DEBUG_MEDIAFOUNDATION
1326 qDebug() <<
"handleSessionEvent: ignoring MF_E_ALREADY_INITIALIZED";
1331 qWarning() <<
"handleSessionEvent: serious error = "
1332 << Qt::showbase << Qt::hex << Qt::uppercasedigits << static_cast<quint32>(hrStatus);
1337 case MF_E_NET_WRITE:
1343 case MF_E_MEDIAPROC_WRONGSTATE:
1346 case MF_E_INVALID_STREAM_DATA:
1354 case MESessionRateChanged:
1357 if (FAILED(hrStatus)) {
1359 PropVariantInit(&
var);
1361 m_state.rate =
var.fltVal;
1366 case MESessionScrubSampleComplete :
1368 updatePendingCommands(CmdStart);
1370 case MESessionStarted:
1377 updatePendingCommands(CmdStart);
1383 m_signalPositionChangeTimer.
start();
1385 case MESessionStopped:
1394 updatePendingCommands(CmdStop);
1395 m_signalPositionChangeTimer.
stop();
1397 case MESessionPaused:
1399 updatePendingCommands(CmdPause);
1400 m_signalPositionChangeTimer.
stop();
1404 case MEReconnectStart:
1405#ifdef DEBUG_MEDIAFOUNDATION
1406 qDebug() <<
"MEReconnectStart" << ((hrStatus == S_OK) ?
"OK" :
"Failed");
1409 case MEReconnectEnd:
1410#ifdef DEBUG_MEDIAFOUNDATION
1411 qDebug() <<
"MEReconnectEnd" << ((hrStatus == S_OK) ?
"OK" :
"Failed");
1414 case MESessionTopologySet:
1415 if (FAILED(hrStatus)) {
1421 m_lastPosition = -1;
1429 if (FAILED(hrStatus)) {
1434 case MEBufferingStarted:
1438 case MEBufferingStopped:
1442 case MESessionEnded:
1443 m_pendingState = NoPending;
1444 m_state.command = CmdStop;
1445 m_state.prevCmd = CmdNone;
1446 m_request.command = CmdNone;
1447 m_request.prevCmd = CmdNone;
1450 m_position =
qint64(m_duration);
1455 case MEEndOfPresentationSegment:
1457 case MESessionTopologyStatus: {
1460 if (
status == MF_TOPOSTATUS_READY) {
1461 ComPtr<IMFClock> clock;
1462 if (SUCCEEDED(m_session->GetClock(&clock))) {
1463 clock->QueryInterface(IID_IMFPresentationClock, &m_presentationClock);
1466 if (SUCCEEDED(MFGetService(m_session.Get(), MF_RATE_CONTROL_SERVICE,
1467 IID_PPV_ARGS(&m_rateControl)))) {
1468 if (SUCCEEDED(MFGetService(m_session.Get(), MF_RATE_CONTROL_SERVICE,
1469 IID_PPV_ARGS(&m_rateSupport)))) {
1470 if (SUCCEEDED(m_rateSupport->IsRateSupported(TRUE, 0, NULL)))
1473 BOOL isThin = FALSE;
1475 if (SUCCEEDED(m_rateControl->GetRate(&isThin, &
rate))) {
1476 if (m_pendingRate !=
rate) {
1477 m_state.rate = m_request.rate =
rate;
1482 MFGetService(m_session.Get(), MFNETSOURCE_STATISTICS_SERVICE,
1483 IID_PPV_ARGS(&m_netsourceStatistics));
1485 if (SUCCEEDED(MFGetService(m_session.Get(), MR_STREAM_VOLUME_SERVICE,
1486 IID_PPV_ARGS(&m_volumeControl))))
1487 setVolumeInternal(m_muted ? 0 : m_volume);
1489 m_updatingTopology =
false;
1500void MFPlayerSession::updatePendingCommands(Command command)
1503 if (m_state.command != command || m_pendingState == NoPending)
1507 if (m_pendingState == SeekPending && m_state.prevCmd == CmdPause) {
1508 m_pendingState = NoPending;
1516 m_state.setCommand(CmdPause);
1519 m_pendingState = NoPending;
1522 if (m_request.rate != m_state.rate) {
1523 commitRateChange(m_request.rate, m_request.isThin);
1527 if (m_pendingState == NoPending) {
1528 switch (m_request.command) {
1540 setPositionInternal(m_request.start, m_request.command);
1542 case CmdStartAndSeek:
1544 setPositionInternal(m_request.start, m_request.command);
1549 m_request.setCommand(CmdNone);
1554bool MFPlayerSession::canScrub()
const
1556 return m_canScrub && m_rateSupport && m_rateControl;
1559void MFPlayerSession::clear()
1561#ifdef DEBUG_MEDIAFOUNDATION
1562 qDebug() <<
"MFPlayerSession::clear";
1567 m_pendingState = NoPending;
1568 m_state.command = CmdStop;
1569 m_state.prevCmd = CmdNone;
1570 m_request.command = CmdNone;
1571 m_request.prevCmd = CmdNone;
1574 m_trackInfo[
i].metaData.
clear();
1575 m_trackInfo[
i].nativeIndexes.
clear();
1576 m_trackInfo[
i].currentIndex = -1;
1577 m_trackInfo[
i].sourceNodeId = TOPOID(-1);
1578 m_trackInfo[
i].outputNodeId = TOPOID(-1);
1579 m_trackInfo[
i].format = GUID_NULL;
1587 m_presentationClock.Reset();
1588 m_rateControl.Reset();
1589 m_rateSupport.Reset();
1590 m_volumeControl.Reset();
1591 m_netsourceStatistics.Reset();
1596 if (m_audioOutput ==
device)
1603 if (m_audioOutput) {
1616 if (currentAudioTrack > -1)
1634 const auto &nativeIndexes = m_trackInfo[
type].nativeIndexes;
1636 if (index < -1 || index >= nativeIndexes.count())
1644 ComPtr<IMFTopology> topology;
1648 m_restorePosition =
position() * 10000;
1650 if (m_state.command == CmdStart)
1653 if (m_trackInfo[
type].outputNodeId != TOPOID(-1)) {
1654 ComPtr<IMFTopologyNode> node;
1655 if (SUCCEEDED(topology->GetNodeByID(m_trackInfo[
type].outputNodeId, &node))) {
1656 topology->RemoveNode(node.Get());
1657 m_trackInfo[
type].outputNodeId = TOPOID(-1);
1660 if (m_trackInfo[
type].sourceNodeId != TOPOID(-1)) {
1661 ComPtr<IMFTopologyNode> node;
1662 if (SUCCEEDED(topology->GetNodeByID(m_trackInfo[
type].sourceNodeId, &node))) {
1663 topology->RemoveNode(node.Get());
1664 m_trackInfo[
type].sourceNodeId = TOPOID(-1);
1668 IMFMediaSource *mediaSource = m_sourceResolver->mediaSource();
1670 ComPtr<IMFPresentationDescriptor> sourcePD;
1671 if (SUCCEEDED(mediaSource->CreatePresentationDescriptor(&sourcePD))) {
1673 if (m_trackInfo[
type].currentIndex >= 0 && m_trackInfo[
type].currentIndex < nativeIndexes.count())
1674 sourcePD->DeselectStream(nativeIndexes.at(m_trackInfo[
type].currentIndex));
1679 m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology.Get());
1681 int nativeIndex = nativeIndexes.at(
index);
1682 sourcePD->SelectStream(nativeIndex);
1684 ComPtr<IMFStreamDescriptor> streamDesc;
1685 BOOL selected = FALSE;
1687 if (SUCCEEDED(sourcePD->GetStreamDescriptorByIndex(nativeIndex, &selected, &streamDesc))) {
1688 ComPtr<IMFTopologyNode> sourceNode = addSourceNode(
1689 topology.Get(), mediaSource, sourcePD.Get(), streamDesc.Get());
1691 ComPtr<IMFTopologyNode> outputNode =
1692 addOutputNode(MFPlayerSession::Audio, topology.Get(), 0);
1694 if (SUCCEEDED(sourceNode->ConnectOutput(0, outputNode.Get(), 0))) {
1695 sourceNode->GetTopoNodeID(&m_trackInfo[
type].sourceNodeId);
1696 outputNode->GetTopoNodeID(&m_trackInfo[
type].outputNodeId);
1697 m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE,
1704 m_updatingTopology =
true;
1713 return m_trackInfo[
type].currentIndex;
1720 return m_trackInfo[
type].metaData.
count();
1728 if (trackNumber < 0 || trackNumber >= m_trackInfo[
type].
metaData.count())
1731 return m_trackInfo[
type].metaData.
at(trackNumber);
1736#include "moc_mfplayersession_p.cpp"
IOBluetoothDevice * device
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 sessionEvent(const ComPtr< IMFMediaEvent > &sessionEvent)
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 updateOutputRouting()
int trackCount(QPlatformMediaPlayer::TrackType)
void setCropRect(const 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
const_reference at(qsizetype i) const noexcept
qsizetype count() const noexcept
void append(parameter_type t)
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...
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)
Type get() const noexcept
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.
HRESULT BindOutputNode(IMFTopologyNode *pNode)
HRESULT BindOutputNodes(IMFTopology *pTopology)
Combined button and popup list for selecting options.
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]