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"
19#include <mfmetadata_p.h>
20#include <private/qwindowsaudioutils_p.h>
27#include <wmcodecdsp.h>
30#include <mmdeviceapi.h>
31#include <propvarutil.h>
33#include <functiondiscoverykeys_devpkey.h>
41 m_playerControl(playerControl),
49 connect(
this, &MFPlayerSession::sessionEvent,
this, &MFPlayerSession::handleSessionEvent);
51 m_signalPositionChangeTimer.setInterval(10);
52 m_signalPositionChangeTimer.setTimerType(Qt::PreciseTimer);
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;
65 m_videoRendererControl =
new MFVideoRendererControl(
this);
70 const qint64 pos = position();
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
141 if (status() == QMediaPlayer::LoadingMedia && m_sourceResolver)
142 m_sourceResolver->cancel();
144 if (url.isEmpty() && !stream) {
146 changeStatus(QMediaPlayer::NoMedia);
147 }
else if (stream && (!stream->isReadable())) {
149 changeStatus(QMediaPlayer::InvalidMedia);
150 error(QMediaPlayer::ResourceError, tr(
"Invalid stream source."),
true);
151 }
else if (createSession()) {
152 changeStatus(QMediaPlayer::LoadingMedia);
153 m_sourceResolver->load(url, stream);
154 if (url.isLocalFile())
155 m_updateRoutingOnStart =
true;
157 positionChanged(position());
163 QMediaPlayer::Error errorCode = QMediaPlayer::ResourceError;
165 case QMediaPlayer::FormatError:
166 errorCode = QMediaPlayer::FormatError;
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:
176 errorCode = QMediaPlayer::FormatError;
177 errorString = tr(
"Unsupported media type.");
179 case MF_E_UNSUPPORTED_SCHEME:
180 errorCode = QMediaPlayer::ResourceError;
181 errorString = tr(
"Unsupported URL scheme.");
183 case INET_E_CANNOT_CONNECT:
184 errorCode = QMediaPlayer::NetworkError;
185 errorString = tr(
"Connection to server could not be established.");
188 qWarning() <<
"handleSourceError:"
189 << Qt::showbase << Qt::hex << Qt::uppercasedigits <<
static_cast<quint32>(hr);
190 errorString = tr(
"Failed to load source.");
193 changeStatus(QMediaPlayer::InvalidMedia);
194 error(errorCode, errorString,
true);
199 if (QMediaPlayer::LoadingMedia != status() || !m_sourceResolver
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);
216 m_metaData = MFMetaData::fromNative(mediaSource);
218 sourcePD->GetUINT64(MF_PD_DURATION, &m_duration);
220 durationUpdate(qint64(m_duration / 10000));
221 setupPlaybackTopology(mediaSource, sourcePD.Get());
224 changeStatus(QMediaPlayer::InvalidMedia);
225 error(QMediaPlayer::ResourceError, tr(
"Cannot create presentation descriptor."),
true);
235 if (!stream || !type || !name || !language || !format)
240 *language = QString();
242 ComPtr<IMFMediaTypeHandler> typeHandler;
244 if (SUCCEEDED(stream->GetMediaTypeHandler(&typeHandler))) {
247 if (SUCCEEDED(stream->GetStringLength(MF_SD_STREAM_NAME, &len)) && len > 0) {
248 WCHAR *wstr =
new WCHAR[len+1];
249 if (SUCCEEDED(stream->GetString(MF_SD_STREAM_NAME, wstr, len + 1, &len))) {
250 *name = QString::fromUtf16(
reinterpret_cast<
const char16_t *>(wstr));
254 if (SUCCEEDED(stream->GetStringLength(MF_SD_LANGUAGE, &len)) && len > 0) {
255 WCHAR *wstr =
new WCHAR[len+1];
256 if (SUCCEEDED(stream->GetString(MF_SD_LANGUAGE, wstr, len + 1, &len))) {
257 *language = QString::fromUtf16(
reinterpret_cast<
const char16_t *>(wstr));
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);
286 changeStatus(QMediaPlayer::InvalidMedia);
287 error(QMediaPlayer::ResourceError, tr(
"Failed to get stream count."),
true);
291 ComPtr<IMFTopology> topology;
292 hr = MFCreateTopology(&topology);
294 changeStatus(QMediaPlayer::InvalidMedia);
295 error(QMediaPlayer::ResourceError, tr(
"Failed to create topology."),
true);
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;
312 QString streamLanguage;
313 GUID format = GUID_NULL;
315 if (getStreamInfo(streamDesc.Get(), &mediaType, &streamName, &streamLanguage,
318 QPlatformMediaPlayer::TrackType trackType = (mediaType == Audio) ?
319 QPlatformMediaPlayer::AudioStream : QPlatformMediaPlayer::VideoStream;
321 QLocale::Language lang = streamLanguage.isEmpty() ?
322 QLocale::Language::AnyLanguage : QLocale(streamLanguage).language();
324 QMediaMetaData metaData;
325 metaData.insert(QMediaMetaData::Title, streamName);
326 metaData.insert(QMediaMetaData::Language, lang);
328 m_trackInfo[trackType].metaData.append(metaData);
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);
345 error(QMediaPlayer::FormatError, tr(
"Unable to play any stream."),
false);
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) {
376 changeStatus(QMediaPlayer::InvalidMedia);
377 error(QMediaPlayer::ResourceError, tr(
"Unable to play."),
true);
379 if (m_trackInfo[QPlatformMediaPlayer::VideoStream].outputNodeId != TOPOID(-1))
380 topology = insertMFT(topology, m_trackInfo[QPlatformMediaPlayer::VideoStream].outputNodeId);
382 hr = m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology.Get());
384 m_updatingTopology =
true;
386 changeStatus(QMediaPlayer::InvalidMedia);
387 error(QMediaPlayer::ResourceError, tr(
"Failed to set topology."),
true);
392ComPtr<IMFTopologyNode>
MFPlayerSession::addSourceNode(IMFTopology *topology,
393 IMFMediaSource *source,
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";
438 QString s = QString::fromUtf8(id);
439 hr = activate->SetString(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID, (LPCWSTR)s.utf16());
441 qWarning() <<
"Failed to set attribute for audio device"
442 << m_audioOutput->device.description();
446 }
else if (mediaType == Video) {
447 activate = m_videoRendererControl->createActivate();
449 QSize resolution = m_metaData.value(QMediaMetaData::Resolution).toSize();
451 if (resolution.isValid())
452 m_videoRendererControl->setCropRect(QRect(QPoint(), resolution));
456 error(QMediaPlayer::FormatError, tr(
"Unknown stream type."),
false);
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);
543 hr = BindOutputNode(node.Get());
556ComPtr<IMFTopology>
MFPlayerSession::insertMFT(
const ComPtr<IMFTopology> &topology,
559 bool isNewTopology =
false;
561 ComPtr<IMFTopoLoader> topoLoader;
562 ComPtr<IMFTopology> resolvedTopology;
563 ComPtr<IMFCollection> outputNodes;
566 if (FAILED(BindOutputNodes(topology.Get())))
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;
597 bool inserted =
false;
598 WORD elementCount = 0;
599 ComPtr<IMFTopologyNode> node;
600 ComPtr<IUnknown> object;
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;
748 if (status() != QMediaPlayer::EndOfMedia) {
753 error(QMediaPlayer::ResourceError, tr(
"Failed to stop."),
true);
760 if (status() == QMediaPlayer::LoadedMedia && m_updateRoutingOnStart) {
761 m_updateRoutingOnStart =
false;
765 if (status() == QMediaPlayer::EndOfMedia) {
770#ifdef DEBUG_MEDIAFOUNDATION
774 if (m_pendingState != NoPending) {
775 m_request.setCommand(CmdStart);
777 if (m_state.command == CmdStart)
782 m_position = position() * 10000;
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;
798 error(QMediaPlayer::ResourceError, tr(
"failed to start playback"),
true);
800 PropVariantClear(&varStart);
806#ifdef DEBUG_MEDIAFOUNDATION
814 if (status() == QMediaPlayer::LoadingMedia) {
815 m_deferredPause =
true;
819 if (m_pendingState != NoPending) {
820 m_request.setCommand(CmdPause);
822 if (m_state.command == CmdPause)
825 if (SUCCEEDED(m_session->Pause())) {
826 m_state.setCommand(CmdPause);
827 m_pendingState = CmdPending;
829 error(QMediaPlayer::ResourceError, tr(
"Failed to pause."),
false);
831 if (status() == QMediaPlayer::EndOfMedia) {
840 if (!m_playerControl)
842#ifdef DEBUG_MEDIAFOUNDATION
843 qDebug() <<
"MFPlayerSession::changeStatus" << newStatus;
846 statusChanged(newStatus);
851 if (!m_playerControl)
852 return QMediaPlayer::NoMedia;
853 return m_playerControl->mediaStatus();
860 Q_ASSERT(m_session == NULL);
862 HRESULT hr = MFCreateMediaSession(NULL, &m_session);
864 changeStatus(QMediaPlayer::InvalidMedia);
865 error(QMediaPlayer::ResourceError, tr(
"Unable to create mediasession."),
true);
869 m_hCloseEvent = EventHandle{ CreateEvent(NULL, FALSE, FALSE, NULL) };
871 hr = m_session->BeginGetEvent(
this, m_session.Get());
873 changeStatus(QMediaPlayer::InvalidMedia);
874 error(QMediaPlayer::ResourceError, tr(
"Unable to pull session events."),
false);
879 m_sourceResolver = makeComObject<SourceResolver>();
880 QObject::connect(m_sourceResolver.Get(), &SourceResolver::mediaSourceReady,
this,
881 &MFPlayerSession::handleMediaSourceReady);
882 QObject::connect(m_sourceResolver.Get(), &SourceResolver::error,
this,
883 &MFPlayerSession::handleSourceError);
891 if (m_request.command == CmdSeek || m_request.command == CmdSeekResume)
892 return m_request.start;
894 if (m_pendingState == SeekPending)
895 return m_state.start;
897 if (m_state.command == CmdStop)
898 return m_position / 10000;
900 if (m_presentationClock) {
901 MFTIME time, sysTime;
902 if (FAILED(m_presentationClock->GetCorrelatedTime(0, &time, &sysTime)))
903 return m_position / 10000;
904 return qint64(time / 10000);
906 return m_position / 10000;
911#ifdef DEBUG_MEDIAFOUNDATION
912 qDebug() <<
"setPosition";
914 if (m_pendingState != NoPending) {
915 m_request.setCommand(CmdSeek);
916 m_request.start = position;
918 setPositionInternal(position, CmdNone);
922void MFPlayerSession::setPositionInternal(qint64 position, Command requestCmd)
924 if (status() == QMediaPlayer::EndOfMedia)
925 changeStatus(QMediaPlayer::LoadedMedia);
926 if (m_state.command == CmdStop && requestCmd != CmdSeekResume) {
927 m_position = position * 10000;
930 positionChanged(
this->position());
934 if (m_state.command == CmdPause)
937#ifdef DEBUG_MEDIAFOUNDATION
938 qDebug() <<
"setPositionInternal";
941 PROPVARIANT varStart;
943 varStart.hVal.QuadPart = LONGLONG(position * 10000);
944 if (SUCCEEDED(m_session->Start(NULL, &varStart))) {
945 PropVariantClear(&varStart);
947 m_state.setCommand(CmdStart);
948 m_state.start = position;
949 m_pendingState = SeekPending;
951 error(QMediaPlayer::ResourceError, tr(
"Failed to seek."),
true);
958 return m_restoreRate;
965 m_restoreRate = rate;
966 playbackRateChanged(rate);
969 setPlaybackRateInternal(rate);
974 if (rate == m_request.rate)
977 m_pendingRate = rate;
981#ifdef DEBUG_MEDIAFOUNDATION
982 qDebug() <<
"setPlaybackRate";
991 if (FAILED(m_rateSupport->IsRateSupported(FALSE, rate, NULL))) {
993 if (FAILED(m_rateSupport->IsRateSupported(isThin, rate, NULL))) {
994 qWarning() <<
"unable to set playbackrate = " << rate;
995 m_pendingRate = m_request.rate = m_state.rate;
999 if (m_pendingState != NoPending) {
1000 m_request.rate = rate;
1001 m_request.isThin = isThin;
1005 if (m_request.command == CmdNone)
1006 m_request.setCommand(m_state.command);
1009 commitRateChange(rate, isThin);
1015#ifdef DEBUG_MEDIAFOUNDATION
1016 qDebug() <<
"commitRateChange";
1018 Q_ASSERT(m_pendingState == NoPending);
1019 MFTIME hnsSystemTime = 0;
1020 MFTIME hnsClockTime = 0;
1021 Command cmdNow = m_state.command;
1022 bool resetPosition =
false;
1027 if ((rate > 0 && m_state.rate <= 0) || (rate < 0 && m_state.rate >= 0)) {
1028 if (cmdNow == CmdStart) {
1030 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1031 Q_ASSERT(hnsSystemTime != 0);
1033 if (rate < 0 || m_state.rate < 0)
1034 m_request.setCommand(CmdSeekResume);
1035 else if (isThin || m_state.isThin)
1036 m_request.setCommand(CmdStartAndSeek);
1038 m_request.setCommand(CmdStart);
1041 if (rate >= 0 && m_state.rate >= 0)
1048 m_request.start = hnsClockTime / 10000;
1049 }
else if (cmdNow == CmdPause) {
1050 if (rate < 0 || m_state.rate < 0) {
1055 qWarning() <<
"Unable to change rate from positive to negative or vice versa in paused state";
1056 rate = m_state.rate;
1057 isThin = m_state.isThin;
1065 if (rate > 0 && m_state.rate == 0) {
1066 m_state.setCommand(CmdNone);
1070 }
else if (rate == 0 && m_state.rate > 0) {
1071 if (cmdNow != CmdPause) {
1078 m_request.setCommand(cmdNow);
1080 }
else if (rate == 0 && m_state.rate < 0) {
1082 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1084 m_request.setCommand(CmdSeekResume);
1089 m_request.start = hnsClockTime / 10000;
1090 }
else if (!isThin && m_state.isThin) {
1091 if (cmdNow == CmdStart) {
1096 resetPosition =
true;
1097 }
else if (cmdNow == CmdPause) {
1100 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1101 m_request.setCommand(CmdSeekResume);
1102 m_request.start = hnsClockTime / 10000;
1108 if (FAILED(m_rateControl->SetRate(isThin, rate))) {
1109 qWarning() <<
"failed to set playbackrate = " << rate;
1110 rate = m_state.rate;
1111 isThin = m_state.isThin;
1115 if (resetPosition) {
1116 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1117 setPosition(hnsClockTime / 10000);
1122 m_pendingRate = m_request.rate = m_state.rate = rate;
1124 m_state.isThin = isThin;
1125 playbackRateChanged(rate);
1130 if (m_scrubbing == enableScrub)
1133 m_scrubbing = enableScrub;
1137 m_pendingRate = m_restoreRate;
1143 m_restoreRate = m_request.rate;
1144 setPlaybackRateInternal(0.0f);
1147 setPlaybackRateInternal(m_restoreRate);
1153 if (m_volume == volume)
1158 setVolumeInternal(volume);
1163 if (m_muted == muted)
1167 setVolumeInternal(muted ? 0 : m_volume);
1172 if (m_volumeControl) {
1173 quint32 channelCount = 0;
1174 if (!SUCCEEDED(m_volumeControl->GetChannelCount(&channelCount))
1175 || channelCount == 0)
1178 for (quint32 i = 0; i < channelCount; ++i)
1179 m_volumeControl->SetChannelVolume(i, volume);
1185 if (!m_netsourceStatistics)
1188 PropVariantInit(&var);
1190 key.fmtid = MFNETSOURCE_STATISTICS;
1191 key.pid = MFNETSOURCE_BUFFERPROGRESS_ID;
1195 if (m_netsourceStatistics->GetValue(key, &var) == S_OK) {
1196 progress = var.lVal;
1197 PropVariantClear(&var);
1200#ifdef DEBUG_MEDIAFOUNDATION
1201 qDebug() <<
"bufferProgress: progress = " << progress;
1204 return progress/100.;
1211 qint64 end = qint64(m_duration / 10000);
1213 if (m_netsourceStatistics) {
1215 PropVariantInit(&var);
1217 key.fmtid = MFNETSOURCE_STATISTICS;
1218 key.pid = MFNETSOURCE_SEEKRANGESTART_ID;
1221 if (m_netsourceStatistics->GetValue(key, &var) == S_OK) {
1222 start = qint64(var.uhVal.QuadPart / 10000);
1223 PropVariantClear(&var);
1224 PropVariantInit(&var);
1225 key.pid = MFNETSOURCE_SEEKRANGEEND_ID;
1226 if (m_netsourceStatistics->GetValue(key, &var) == S_OK) {
1227 end = qint64(var.uhVal.QuadPart / 10000);
1228 PropVariantClear(&var);
1233 return QMediaTimeRange(start, end);
1240 if (riid == IID_IMFAsyncCallback) {
1241 *ppvObject =
static_cast<IMFAsyncCallback*>(
this);
1242 }
else if (riid == IID_IUnknown) {
1243 *ppvObject =
static_cast<IUnknown*>(
this);
1246 return E_NOINTERFACE;
1253 return InterlockedIncrement(&m_cRef);
1258 LONG cRef = InterlockedDecrement(&m_cRef);
1264 m_playerControl =
nullptr;
1271 if (pResult->GetStateNoAddRef() != m_session.Get())
1274 ComPtr<IMFMediaEvent> pEvent;
1276 HRESULT hr = m_session->EndGetEvent(pResult, &pEvent);
1281 MediaEventType meType = MEUnknown;
1282 hr = pEvent->GetType(&meType);
1287 if (meType == MESessionClosed) {
1288 SetEvent(m_hCloseEvent.get());
1291 hr = m_session->BeginGetEvent(
this, m_session.Get());
1298 emit sessionEvent(pEvent);
1303void MFPlayerSession::handleSessionEvent(
const ComPtr<IMFMediaEvent> &sessionEvent)
1305 HRESULT hrStatus = S_OK;
1306 HRESULT hr = sessionEvent->GetStatus(&hrStatus);
1307 if (FAILED(hr) || !m_session) {
1311 MediaEventType meType = MEUnknown;
1312 hr = sessionEvent->GetType(&meType);
1313#ifdef DEBUG_MEDIAFOUNDATION
1314 if (FAILED(hrStatus))
1315 qDebug() <<
"handleSessionEvent: MediaEventType = " << meType <<
"Failed";
1317 qDebug() <<
"handleSessionEvent: MediaEventType = " << meType;
1321 case MENonFatalError: {
1323 PropVariantInit(&var);
1324 sessionEvent->GetValue(&var);
1325 qWarning() <<
"handleSessionEvent: non fatal error = " << var.ulVal;
1326 PropVariantClear(&var);
1327 error(QMediaPlayer::ResourceError, tr(
"Media session non-fatal error."),
false);
1330 case MESourceUnknown:
1331 changeStatus(QMediaPlayer::InvalidMedia);
1334 if (hrStatus == MF_E_ALREADY_INITIALIZED) {
1337#ifdef DEBUG_MEDIAFOUNDATION
1338 qDebug() <<
"handleSessionEvent: ignoring MF_E_ALREADY_INITIALIZED";
1342 changeStatus(QMediaPlayer::InvalidMedia);
1343 qWarning() <<
"handleSessionEvent: serious error = "
1344 << Qt::showbase << Qt::hex << Qt::uppercasedigits <<
static_cast<quint32>(hrStatus);
1347 error(QMediaPlayer::NetworkError, tr(
"Error reading from the network."),
true);
1349 case MF_E_NET_WRITE:
1350 error(QMediaPlayer::NetworkError, tr(
"Error writing to the network."),
true);
1353 error(QMediaPlayer::NetworkError, tr(
"Network packets might be blocked by a firewall."),
true);
1355 case MF_E_MEDIAPROC_WRONGSTATE:
1356 error(QMediaPlayer::ResourceError, tr(
"Media session state error."),
true);
1358 case MF_E_INVALID_STREAM_DATA:
1359 error(QMediaPlayer::ResourceError, tr(
"Invalid stream data."),
true);
1362 error(QMediaPlayer::ResourceError, tr(
"Media session serious error."),
true);
1366 case MESessionRateChanged:
1369 if (FAILED(hrStatus)) {
1371 PropVariantInit(&var);
1372 if (SUCCEEDED(sessionEvent->GetValue(&var)) && (var.vt == VT_R4)) {
1373 m_state.rate = var.fltVal;
1375 playbackRateChanged(playbackRate());
1378 case MESessionScrubSampleComplete :
1380 updatePendingCommands(CmdStart);
1382 case MESessionStarted:
1383 if (status() == QMediaPlayer::EndOfMedia
1384 || status() == QMediaPlayer::LoadedMedia) {
1386 changeStatus(QMediaPlayer::BufferedMedia);
1389 updatePendingCommands(CmdStart);
1395 m_signalPositionChangeTimer.start();
1397 case MESessionStopped:
1398 if (status() != QMediaPlayer::EndOfMedia) {
1403 if (status() != QMediaPlayer::LoadingMedia && m_request.command != CmdSeekResume)
1404 changeStatus(QMediaPlayer::LoadedMedia);
1406 updatePendingCommands(CmdStop);
1407 m_signalPositionChangeTimer.stop();
1409 case MESessionPaused:
1410 m_position = position() * 10000;
1411 updatePendingCommands(CmdPause);
1412 m_signalPositionChangeTimer.stop();
1413 if (status() == QMediaPlayer::LoadedMedia)
1414 setPosition(position());
1416 case MEReconnectStart:
1417#ifdef DEBUG_MEDIAFOUNDATION
1418 qDebug() <<
"MEReconnectStart" << ((hrStatus == S_OK) ?
"OK" :
"Failed");
1421 case MEReconnectEnd:
1422#ifdef DEBUG_MEDIAFOUNDATION
1423 qDebug() <<
"MEReconnectEnd" << ((hrStatus == S_OK) ?
"OK" :
"Failed");
1426 case MESessionTopologySet:
1427 if (FAILED(hrStatus)) {
1428 changeStatus(QMediaPlayer::InvalidMedia);
1429 error(QMediaPlayer::FormatError, tr(
"Unsupported media, a codec is missing."),
true);
1433 m_lastPosition = -1;
1436 changeStatus(QMediaPlayer::LoadedMedia);
1441 if (FAILED(hrStatus)) {
1446 case MEBufferingStarted:
1447 changeStatus(QMediaPlayer::StalledMedia);
1450 case MEBufferingStopped:
1451 changeStatus(QMediaPlayer::BufferedMedia);
1454 case MESessionEnded:
1455 m_pendingState = NoPending;
1456 m_state.command = CmdStop;
1457 m_state.prevCmd = CmdNone;
1458 m_request.command = CmdNone;
1459 m_request.prevCmd = CmdNone;
1462 m_position = qint64(m_duration);
1463 positionChanged(position());
1465 changeStatus(QMediaPlayer::EndOfMedia);
1467 case MEEndOfPresentationSegment:
1469 case MESessionTopologyStatus: {
1471 if (SUCCEEDED(sessionEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &status))) {
1472 if (status == MF_TOPOSTATUS_READY) {
1473 ComPtr<IMFClock> clock;
1474 if (SUCCEEDED(m_session->GetClock(&clock))) {
1475 clock->QueryInterface(IID_IMFPresentationClock, &m_presentationClock);
1478 if (SUCCEEDED(MFGetService(m_session.Get(), MF_RATE_CONTROL_SERVICE,
1479 IID_PPV_ARGS(&m_rateControl)))) {
1480 if (SUCCEEDED(MFGetService(m_session.Get(), MF_RATE_CONTROL_SERVICE,
1481 IID_PPV_ARGS(&m_rateSupport)))) {
1482 if (SUCCEEDED(m_rateSupport->IsRateSupported(TRUE, 0, NULL)))
1485 BOOL isThin = FALSE;
1487 if (SUCCEEDED(m_rateControl->GetRate(&isThin, &rate))) {
1488 if (m_pendingRate != rate) {
1489 m_state.rate = m_request.rate = rate;
1490 setPlaybackRate(m_pendingRate);
1494 MFGetService(m_session.Get(), MFNETSOURCE_STATISTICS_SERVICE,
1495 IID_PPV_ARGS(&m_netsourceStatistics));
1497 if (SUCCEEDED(MFGetService(m_session.Get(), MR_STREAM_VOLUME_SERVICE,
1498 IID_PPV_ARGS(&m_volumeControl))))
1499 setVolumeInternal(m_muted ? 0 : m_volume);
1501 m_updatingTopology =
false;
1504 if (m_deferredPause) {
1505 m_deferredPause =
false;
1519 positionChanged(position());
1520 if (m_state.command != command || m_pendingState == NoPending)
1524 if (m_pendingState == SeekPending && m_state.prevCmd == CmdPause) {
1525 m_pendingState = NoPending;
1533 m_state.setCommand(CmdPause);
1536 m_pendingState = NoPending;
1539 if (m_request.rate != m_state.rate) {
1540 commitRateChange(m_request.rate, m_request.isThin);
1544 if (m_pendingState == NoPending) {
1545 switch (m_request.command) {
1557 setPositionInternal(m_request.start, m_request.command);
1559 case CmdStartAndSeek:
1561 setPositionInternal(m_request.start, m_request.command);
1566 m_request.setCommand(CmdNone);
1573 return m_canScrub && m_rateSupport && m_rateControl;
1578#ifdef DEBUG_MEDIAFOUNDATION
1579 qDebug() <<
"MFPlayerSession::clear";
1583 m_deferredPause =
false;
1585 m_pendingState = NoPending;
1586 m_state.command = CmdStop;
1587 m_state.prevCmd = CmdNone;
1588 m_request.command = CmdNone;
1589 m_request.prevCmd = CmdNone;
1591 for (
int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
1592 m_trackInfo[i].metaData.clear();
1593 m_trackInfo[i].nativeIndexes.clear();
1594 m_trackInfo[i].currentIndex = -1;
1595 m_trackInfo[i].sourceNodeId = TOPOID(-1);
1596 m_trackInfo[i].outputNodeId = TOPOID(-1);
1597 m_trackInfo[i].format = GUID_NULL;
1600 if (!m_metaData.isEmpty()) {
1605 m_presentationClock.Reset();
1606 m_rateControl.Reset();
1607 m_rateSupport.Reset();
1608 m_volumeControl.Reset();
1609 m_netsourceStatistics.Reset();
1614 if (m_audioOutput == device)
1618 m_audioOutput->q->disconnect(
this);
1620 m_audioOutput = device;
1621 if (m_audioOutput) {
1623 setVolume(m_audioOutput->q->volume());
1626 connect(m_audioOutput->q, &QAudioOutput::volumeChanged,
this, &MFPlayerSession::setVolume);
1633 int currentAudioTrack = m_trackInfo[QPlatformMediaPlayer::AudioStream].currentIndex;
1634 if (currentAudioTrack > -1)
1635 setActiveTrack(QPlatformMediaPlayer::AudioStream, currentAudioTrack);
1649 if (type != QPlatformMediaPlayer::AudioStream)
1652 const auto &nativeIndexes = m_trackInfo[type].nativeIndexes;
1654 if (index < -1 || index >= nativeIndexes.count())
1659 if (m_trackInfo[QPlatformMediaPlayer::VideoStream].format == MFVideoFormat_HEVC)
1662 ComPtr<IMFTopology> topology;
1664 if (SUCCEEDED(m_session->GetFullTopology(MFSESSION_GETFULLTOPOLOGY_CURRENT, 0, &topology))) {
1666 m_restorePosition = position() * 10000;
1668 if (m_state.command == CmdStart)
1671 if (m_trackInfo[type].outputNodeId != TOPOID(-1)) {
1672 ComPtr<IMFTopologyNode> node;
1673 if (SUCCEEDED(topology->GetNodeByID(m_trackInfo[type].outputNodeId, &node))) {
1674 topology->RemoveNode(node.Get());
1675 m_trackInfo[type].outputNodeId = TOPOID(-1);
1678 if (m_trackInfo[type].sourceNodeId != TOPOID(-1)) {
1679 ComPtr<IMFTopologyNode> node;
1680 if (SUCCEEDED(topology->GetNodeByID(m_trackInfo[type].sourceNodeId, &node))) {
1681 topology->RemoveNode(node.Get());
1682 m_trackInfo[type].sourceNodeId = TOPOID(-1);
1686 IMFMediaSource *mediaSource = m_sourceResolver->mediaSource();
1688 ComPtr<IMFPresentationDescriptor> sourcePD;
1689 if (SUCCEEDED(mediaSource->CreatePresentationDescriptor(&sourcePD))) {
1691 if (m_trackInfo[type].currentIndex >= 0 && m_trackInfo[type].currentIndex < nativeIndexes.count())
1692 sourcePD->DeselectStream(nativeIndexes.at(m_trackInfo[type].currentIndex));
1694 m_trackInfo[type].currentIndex = index;
1697 m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology.Get());
1699 int nativeIndex = nativeIndexes.at(index);
1700 sourcePD->SelectStream(nativeIndex);
1702 ComPtr<IMFStreamDescriptor> streamDesc;
1703 BOOL selected = FALSE;
1705 if (SUCCEEDED(sourcePD->GetStreamDescriptorByIndex(nativeIndex, &selected, &streamDesc))) {
1706 ComPtr<IMFTopologyNode> sourceNode = addSourceNode(
1707 topology.Get(), mediaSource, sourcePD.Get(), streamDesc.Get());
1709 ComPtr<IMFTopologyNode> outputNode =
1710 addOutputNode(MFPlayerSession::Audio, topology.Get(), 0);
1712 if (SUCCEEDED(sourceNode->ConnectOutput(0, outputNode.Get(), 0))) {
1713 sourceNode->GetTopoNodeID(&m_trackInfo[type].sourceNodeId);
1714 outputNode->GetTopoNodeID(&m_trackInfo[type].outputNodeId);
1715 m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE,
1722 m_updatingTopology =
true;
1729 if (type >= QPlatformMediaPlayer::NTrackTypes)
1731 return m_trackInfo[type].currentIndex;
1736 if (type >= QPlatformMediaPlayer::NTrackTypes)
1738 return m_trackInfo[type].metaData.count();
1741QMediaMetaData
MFPlayerSession::trackMetaData(QPlatformMediaPlayer::TrackType type,
int trackNumber)
1743 if (type >= QPlatformMediaPlayer::NTrackTypes)
1746 if (trackNumber < 0 || trackNumber >= m_trackInfo[type].metaData.count())
1749 return m_trackInfo[type].metaData.at(trackNumber);
1754#include "moc_mfplayersession_p.cpp"
void seekableUpdate(bool seekable)
void setPlaybackRate(qreal rate)
void setPosition(qint64 position)
void setAudioOutput(QPlatformAudioOutput *device)
void setVideoSink(QVideoSink *sink)
void bufferProgressChanged(float percentFilled)
qreal playbackRate() const
void stop(bool immediate=false)
STDMETHODIMP Invoke(IMFAsyncResult *pResult) override
void load(const QUrl &media, QIODevice *stream)
void changeStatus(QMediaPlayer::MediaStatus newStatus)
QMediaTimeRange availablePlaybackRanges()
void setMuted(bool muted)
void updateOutputRouting()
void setSink(QVideoSink *surface)
HRESULT BindOutputNode(IMFTopologyNode *pNode)
HRESULT BindOutputNodes(IMFTopology *pTopology)
Combined button and popup list for selecting options.