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),
47 m_status(QMediaPlayer::NoMedia)
50 connect(
this, &MFPlayerSession::sessionEvent,
this, &MFPlayerSession::handleSessionEvent);
52 m_signalPositionChangeTimer.setInterval(10);
53 m_signalPositionChangeTimer.setTimerType(Qt::PreciseTimer);
54 m_signalPositionChangeTimer.callOnTimeout(
this, &MFPlayerSession::timeout);
56 m_pendingState = NoPending;
57 ZeroMemory(&m_state,
sizeof(m_state));
58 m_state.command = CmdStop;
59 m_state.prevCmd = CmdNone;
61 ZeroMemory(&m_request,
sizeof(m_request));
62 m_request.command = CmdNone;
63 m_request.prevCmd = CmdNone;
64 m_request.rate = 1.0f;
66 m_videoRendererControl =
new MFVideoRendererControl(
this);
71 const qint64 pos = position();
73 if (pos != m_lastPosition) {
74 const bool updatePos = m_timeCounter++ % 10 == 0;
75 if (pos >= qint64(m_duration / 10000 - 20)) {
76 if (m_playerControl->doLoop()) {
94#ifdef DEBUG_MEDIAFOUNDATION
98 m_signalPositionChangeTimer.stop();
106 hr = m_session->Close();
108 DWORD dwWaitResult = WaitForSingleObject(m_hCloseEvent.get(), 2000);
109 if (dwWaitResult == WAIT_TIMEOUT) {
110 qWarning() <<
"session close time out!";
118 m_session->Shutdown();
119 if (m_sourceResolver)
120 m_sourceResolver->shutdown();
122 m_sourceResolver.Reset();
137#ifdef DEBUG_MEDIAFOUNDATION
142 if (m_status == QMediaPlayer::LoadingMedia && m_sourceResolver)
143 m_sourceResolver->cancel();
145 if (url.isEmpty() && !stream) {
147 changeStatus(QMediaPlayer::NoMedia);
148 }
else if (stream && (!stream->isReadable())) {
150 changeStatus(QMediaPlayer::InvalidMedia);
151 error(QMediaPlayer::ResourceError, tr(
"Invalid stream source."),
true);
152 }
else if (createSession()) {
153 changeStatus(QMediaPlayer::LoadingMedia);
154 m_sourceResolver->load(url, stream);
155 if (url.isLocalFile())
156 m_updateRoutingOnStart =
true;
158 positionChanged(position());
164 QMediaPlayer::Error errorCode = QMediaPlayer::ResourceError;
166 case QMediaPlayer::FormatError:
167 errorCode = QMediaPlayer::FormatError;
168 errorString = tr(
"Attempting to play invalid Qt resource.");
170 case NS_E_FILE_NOT_FOUND:
171 errorString = tr(
"The system cannot find the file specified.");
173 case NS_E_SERVER_NOT_FOUND:
174 errorString = tr(
"The specified server could not be found.");
176 case MF_E_UNSUPPORTED_BYTESTREAM_TYPE:
177 errorCode = QMediaPlayer::FormatError;
178 errorString = tr(
"Unsupported media type.");
180 case MF_E_UNSUPPORTED_SCHEME:
181 errorCode = QMediaPlayer::ResourceError;
182 errorString = tr(
"Unsupported URL scheme.");
184 case INET_E_CANNOT_CONNECT:
185 errorCode = QMediaPlayer::NetworkError;
186 errorString = tr(
"Connection to server could not be established.");
189 qWarning() <<
"handleSourceError:"
190 << Qt::showbase << Qt::hex << Qt::uppercasedigits <<
static_cast<quint32>(hr);
191 errorString = tr(
"Failed to load source.");
194 changeStatus(QMediaPlayer::InvalidMedia);
195 error(errorCode, errorString,
true);
200 if (QMediaPlayer::LoadingMedia != m_status || !m_sourceResolver
201 || m_sourceResolver.Get() != sender())
203#ifdef DEBUG_MEDIAFOUNDATION
204 qDebug() <<
"handleMediaSourceReady";
207 IMFMediaSource* mediaSource = m_sourceResolver->mediaSource();
209 DWORD dwCharacteristics = 0;
210 mediaSource->GetCharacteristics(&dwCharacteristics);
211 seekableUpdate(MFMEDIASOURCE_CAN_SEEK & dwCharacteristics);
213 ComPtr<IMFPresentationDescriptor> sourcePD;
214 hr = mediaSource->CreatePresentationDescriptor(&sourcePD);
217 m_metaData = MFMetaData::fromNative(mediaSource);
219 sourcePD->GetUINT64(MF_PD_DURATION, &m_duration);
221 durationUpdate(qint64(m_duration / 10000));
222 setupPlaybackTopology(mediaSource, sourcePD.Get());
225 changeStatus(QMediaPlayer::InvalidMedia);
226 error(QMediaPlayer::ResourceError, tr(
"Cannot create presentation descriptor."),
true);
236 if (!stream || !type || !name || !language || !format)
241 *language = QString();
243 ComPtr<IMFMediaTypeHandler> typeHandler;
245 if (SUCCEEDED(stream->GetMediaTypeHandler(&typeHandler))) {
248 if (SUCCEEDED(stream->GetStringLength(MF_SD_STREAM_NAME, &len)) && len > 0) {
249 WCHAR *wstr =
new WCHAR[len+1];
250 if (SUCCEEDED(stream->GetString(MF_SD_STREAM_NAME, wstr, len + 1, &len))) {
251 *name = QString::fromUtf16(
reinterpret_cast<
const char16_t *>(wstr));
255 if (SUCCEEDED(stream->GetStringLength(MF_SD_LANGUAGE, &len)) && len > 0) {
256 WCHAR *wstr =
new WCHAR[len+1];
257 if (SUCCEEDED(stream->GetString(MF_SD_LANGUAGE, wstr, len + 1, &len))) {
258 *language = QString::fromUtf16(
reinterpret_cast<
const char16_t *>(wstr));
264 if (SUCCEEDED(typeHandler->GetMajorType(&guidMajorType))) {
265 if (guidMajorType == MFMediaType_Audio)
267 else if (guidMajorType == MFMediaType_Video)
271 ComPtr<IMFMediaType> mediaType;
272 if (SUCCEEDED(typeHandler->GetCurrentMediaType(&mediaType))) {
273 mediaType->GetGUID(MF_MT_SUBTYPE, format);
277 return *type != Unknown;
280void MFPlayerSession::setupPlaybackTopology(IMFMediaSource *source, IMFPresentationDescriptor *sourcePD)
284 DWORD cSourceStreams = 0;
285 hr = sourcePD->GetStreamDescriptorCount(&cSourceStreams);
287 changeStatus(QMediaPlayer::InvalidMedia);
288 error(QMediaPlayer::ResourceError, tr(
"Failed to get stream count."),
true);
292 ComPtr<IMFTopology> topology;
293 hr = MFCreateTopology(&topology);
295 changeStatus(QMediaPlayer::InvalidMedia);
296 error(QMediaPlayer::ResourceError, tr(
"Failed to create topology."),
true);
301 DWORD succeededCount = 0;
302 for (DWORD i = 0; i < cSourceStreams; i++) {
303 BOOL selected = FALSE;
304 bool streamAdded =
false;
305 ComPtr<IMFStreamDescriptor> streamDesc;
307 HRESULT hr = sourcePD->GetStreamDescriptorByIndex(i, &selected, &streamDesc);
311 MediaType mediaType = Unknown;
313 QString streamLanguage;
314 GUID format = GUID_NULL;
316 if (getStreamInfo(streamDesc.Get(), &mediaType, &streamName, &streamLanguage,
319 QPlatformMediaPlayer::TrackType trackType = (mediaType == Audio) ?
320 QPlatformMediaPlayer::AudioStream : QPlatformMediaPlayer::VideoStream;
322 QLocale::Language lang = streamLanguage.isEmpty() ?
323 QLocale::Language::AnyLanguage : QLocale(streamLanguage).language();
325 QMediaMetaData metaData;
326 metaData.insert(QMediaMetaData::Title, streamName);
327 metaData.insert(QMediaMetaData::Language, lang);
329 m_trackInfo[trackType].metaData.append(metaData);
330 m_trackInfo[trackType].nativeIndexes.append(i);
331 m_trackInfo[trackType].format = format;
333 if (((m_mediaTypes & mediaType) == 0) && selected) {
334 ComPtr<IMFTopologyNode> sourceNode =
335 addSourceNode(topology.Get(), source, sourcePD, streamDesc.Get());
337 ComPtr<IMFTopologyNode> outputNode =
338 addOutputNode(mediaType, topology.Get(), 0);
340 sourceNode->GetTopoNodeID(&m_trackInfo[trackType].sourceNodeId);
341 outputNode->GetTopoNodeID(&m_trackInfo[trackType].outputNodeId);
343 hr = sourceNode->ConnectOutput(0, outputNode.Get(), 0);
346 error(QMediaPlayer::FormatError, tr(
"Unable to play any stream."),
false);
348 m_trackInfo[trackType].currentIndex = m_trackInfo[trackType].nativeIndexes.count() - 1;
351 m_mediaTypes |= mediaType;
365 topology->RemoveNode(sourceNode.Get());
371 if (selected && !streamAdded)
372 sourcePD->DeselectStream(i);
376 if (succeededCount == 0) {
377 changeStatus(QMediaPlayer::InvalidMedia);
378 error(QMediaPlayer::ResourceError, tr(
"Unable to play."),
true);
380 if (m_trackInfo[QPlatformMediaPlayer::VideoStream].outputNodeId != TOPOID(-1))
381 topology = insertMFT(topology, m_trackInfo[QPlatformMediaPlayer::VideoStream].outputNodeId);
383 hr = m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology.Get());
385 m_updatingTopology =
true;
387 changeStatus(QMediaPlayer::InvalidMedia);
388 error(QMediaPlayer::ResourceError, tr(
"Failed to set topology."),
true);
393ComPtr<IMFTopologyNode>
MFPlayerSession::addSourceNode(IMFTopology *topology,
394 IMFMediaSource *source,
395 IMFPresentationDescriptor *presentationDesc,
396 IMFStreamDescriptor *streamDesc)
398 ComPtr<IMFTopologyNode> node;
399 HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node);
401 hr = node->SetUnknown(MF_TOPONODE_SOURCE, source);
403 hr = node->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, presentationDesc);
405 hr = node->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, streamDesc);
407 hr = topology->AddNode(node.Get());
417ComPtr<IMFTopologyNode>
MFPlayerSession::addOutputNode(MediaType mediaType, IMFTopology *topology,
420 ComPtr<IMFTopologyNode> node;
421 if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node)))
424 ComPtr<IMFActivate> activate;
425 if (mediaType == Audio) {
427 auto id = m_audioOutput->device.id();
429 qInfo() <<
"No audio output";
433 HRESULT hr = MFCreateAudioRendererActivate(&activate);
435 qWarning() <<
"Failed to create audio renderer activate";
439 QString s = QString::fromUtf8(id);
440 hr = activate->SetString(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID, (LPCWSTR)s.utf16());
442 qWarning() <<
"Failed to set attribute for audio device"
443 << m_audioOutput->device.description();
447 }
else if (mediaType == Video) {
448 activate = m_videoRendererControl->createActivate();
450 QSize resolution = m_metaData.value(QMediaMetaData::Resolution).toSize();
452 if (resolution.isValid())
453 m_videoRendererControl->setCropRect(QRect(QPoint(), resolution));
457 error(QMediaPlayer::FormatError, tr(
"Unknown stream type."),
false);
460 if (!activate || FAILED(node->SetObject(activate.Get()))
461 || FAILED(node->SetUINT32(MF_TOPONODE_STREAMID, sinkID))
462 || FAILED(node->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE))
463 || FAILED(topology->AddNode(node.Get()))) {
467 if (activate && mediaType == Audio)
479 ComPtr<IUnknown> nodeObject;
480 ComPtr<IMFActivate> activate;
481 ComPtr<IMFStreamSink> stream;
482 ComPtr<IMFMediaSink> sink;
484 HRESULT hr = pNode->GetObject(&nodeObject);
488 hr = nodeObject->QueryInterface(IID_PPV_ARGS(&activate));
490 DWORD dwStreamID = 0;
493 hr = activate->ActivateObject(IID_PPV_ARGS(&sink));
495 dwStreamID = MFGetAttributeUINT32(pNode, MF_TOPONODE_STREAMID, 0);
499 hr = sink->GetStreamSinkById(dwStreamID, &stream);
502 hr = sink->AddStreamSink(dwStreamID, NULL, &stream);
508 hr = pNode->SetObject(stream.Get());
511 hr = nodeObject->QueryInterface(IID_PPV_ARGS(&stream));
521 ComPtr<IMFCollection> collection;
524 HRESULT hr = pTopology->GetOutputNodeCollection(&collection);
529 hr = collection->GetElementCount(&cNodes);
532 for (DWORD i = 0; i < cNodes; i++) {
533 ComPtr<IUnknown> element;
534 hr = collection->GetElement(i, &element);
538 ComPtr<IMFTopologyNode> node;
539 hr = element->QueryInterface(IID_IMFTopologyNode, &node);
544 hr = BindOutputNode(node.Get());
557ComPtr<IMFTopology>
MFPlayerSession::insertMFT(
const ComPtr<IMFTopology> &topology,
560 bool isNewTopology =
false;
562 ComPtr<IMFTopoLoader> topoLoader;
563 ComPtr<IMFTopology> resolvedTopology;
564 ComPtr<IMFCollection> outputNodes;
567 if (FAILED(BindOutputNodes(topology.Get())))
570 if (FAILED(MFCreateTopoLoader(&topoLoader)))
573 if (FAILED(topoLoader->Load(topology.Get(), &resolvedTopology, NULL))) {
576 insertColorConverter(topology.Get(), outputNodeId);
577 if (FAILED(topoLoader->Load(topology.Get(), &resolvedTopology, NULL)))
581 if (insertResizer(resolvedTopology.Get()))
582 isNewTopology =
true;
586 return resolvedTopology;
598 bool inserted =
false;
599 WORD elementCount = 0;
600 ComPtr<IMFTopologyNode> node;
601 ComPtr<IUnknown> object;
602 ComPtr<IWMColorConvProps> colorConv;
603 ComPtr<IMFTransform> resizer;
604 ComPtr<IMFTopologyNode> resizerNode;
605 ComPtr<IMFTopologyNode> inputNode;
607 HRESULT hr = topology->GetNodeCount(&elementCount);
611 for (WORD i = 0; i < elementCount; ++i) {
615 if (FAILED(topology->GetNode(i, &node)))
618 MF_TOPOLOGY_TYPE nodeType;
619 if (FAILED(node->GetNodeType(&nodeType)))
622 if (nodeType != MF_TOPOLOGY_TRANSFORM_NODE)
625 if (FAILED(node->GetObject(&object)))
628 if (FAILED(object->QueryInterface(IID_PPV_ARGS(&colorConv))))
631 if (FAILED(CoCreateInstance(CLSID_CResizerDMO, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform,
635 if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &resizerNode)))
638 if (FAILED(resizerNode->SetObject(resizer.Get())))
641 if (FAILED(topology->AddNode(resizerNode.Get())))
644 DWORD outputIndex = 0;
645 if (FAILED(node->GetInput(0, &inputNode, &outputIndex))) {
646 topology->RemoveNode(resizerNode.Get());
650 if (FAILED(inputNode->ConnectOutput(0, resizerNode.Get(), 0))) {
651 topology->RemoveNode(resizerNode.Get());
655 if (FAILED(resizerNode->ConnectOutput(0, node.Get(), 0))) {
656 inputNode->ConnectOutput(0, node.Get(), 0);
657 topology->RemoveNode(resizerNode.Get());
672void MFPlayerSession::insertColorConverter(IMFTopology *topology, TOPOID outputNodeId)
674 ComPtr<IMFCollection> outputNodes;
676 if (FAILED(topology->GetOutputNodeCollection(&outputNodes)))
679 DWORD elementCount = 0;
680 if (FAILED(outputNodes->GetElementCount(&elementCount)))
683 for (DWORD n = 0; n < elementCount; n++) {
684 ComPtr<IUnknown> element;
685 ComPtr<IMFTopologyNode> node;
686 ComPtr<IMFTopologyNode> inputNode;
687 ComPtr<IMFTopologyNode> mftNode;
688 ComPtr<IMFTransform> converter;
691 if (FAILED(outputNodes->GetElement(n, &element)))
694 if (FAILED(element->QueryInterface(IID_IMFTopologyNode, &node)))
698 if (FAILED(node->GetTopoNodeID(&id)))
701 if (id != outputNodeId)
704 DWORD outputIndex = 0;
705 if (FAILED(node->GetInput(0, &inputNode, &outputIndex)))
708 if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mftNode)))
711 if (FAILED(CoCreateInstance(CLSID_CColorConvertDMO, NULL, CLSCTX_INPROC_SERVER,
712 IID_IMFTransform, &converter)))
715 if (FAILED(mftNode->SetObject(converter.Get())))
718 if (FAILED(topology->AddNode(mftNode.Get())))
721 if (FAILED(inputNode->ConnectOutput(0, mftNode.Get(), 0)))
724 if (FAILED(mftNode->ConnectOutput(0, node.Get(), 0)))
733#ifdef DEBUG_MEDIAFOUNDATION
736 if (!immediate && m_pendingState != NoPending) {
737 m_request.setCommand(CmdStop);
739 if (m_state.command == CmdStop)
745 if (SUCCEEDED(m_session->Stop())) {
747 m_state.setCommand(CmdStop);
748 m_pendingState = CmdPending;
749 if (m_status != QMediaPlayer::EndOfMedia) {
754 error(QMediaPlayer::ResourceError, tr(
"Failed to stop."),
true);
761 if (status() == QMediaPlayer::LoadedMedia && m_updateRoutingOnStart) {
762 m_updateRoutingOnStart =
false;
766 if (m_status == QMediaPlayer::EndOfMedia) {
771#ifdef DEBUG_MEDIAFOUNDATION
775 if (m_pendingState != NoPending) {
776 m_request.setCommand(CmdStart);
778 if (m_state.command == CmdStart)
783 m_position = position() * 10000;
786 if (m_restorePosition >= 0) {
787 m_position = m_restorePosition;
788 if (!m_updatingTopology)
789 m_restorePosition = -1;
792 PROPVARIANT varStart;
793 InitPropVariantFromInt64(m_position, &varStart);
795 if (SUCCEEDED(m_session->Start(&GUID_NULL, &varStart))) {
796 m_state.setCommand(CmdStart);
797 m_pendingState = CmdPending;
799 error(QMediaPlayer::ResourceError, tr(
"failed to start playback"),
true);
801 PropVariantClear(&varStart);
807#ifdef DEBUG_MEDIAFOUNDATION
810 if (m_pendingState != NoPending) {
811 m_request.setCommand(CmdPause);
813 if (m_state.command == CmdPause)
816 if (SUCCEEDED(m_session->Pause())) {
817 m_state.setCommand(CmdPause);
818 m_pendingState = CmdPending;
820 error(QMediaPlayer::ResourceError, tr(
"Failed to pause."),
false);
822 if (m_status == QMediaPlayer::EndOfMedia) {
831 if (m_status == newStatus)
833#ifdef DEBUG_MEDIAFOUNDATION
834 qDebug() <<
"MFPlayerSession::changeStatus" << newStatus;
836 m_status = newStatus;
849 Q_ASSERT(m_session == NULL);
851 HRESULT hr = MFCreateMediaSession(NULL, &m_session);
853 changeStatus(QMediaPlayer::InvalidMedia);
854 error(QMediaPlayer::ResourceError, tr(
"Unable to create mediasession."),
true);
858 m_hCloseEvent = EventHandle{ CreateEvent(NULL, FALSE, FALSE, NULL) };
860 hr = m_session->BeginGetEvent(
this, m_session.Get());
862 changeStatus(QMediaPlayer::InvalidMedia);
863 error(QMediaPlayer::ResourceError, tr(
"Unable to pull session events."),
false);
868 m_sourceResolver = makeComObject<SourceResolver>();
869 QObject::connect(m_sourceResolver.Get(), &SourceResolver::mediaSourceReady,
this,
870 &MFPlayerSession::handleMediaSourceReady);
871 QObject::connect(m_sourceResolver.Get(), &SourceResolver::error,
this,
872 &MFPlayerSession::handleSourceError);
880 if (m_request.command == CmdSeek || m_request.command == CmdSeekResume)
881 return m_request.start;
883 if (m_pendingState == SeekPending)
884 return m_state.start;
886 if (m_state.command == CmdStop)
887 return m_position / 10000;
889 if (m_presentationClock) {
890 MFTIME time, sysTime;
891 if (FAILED(m_presentationClock->GetCorrelatedTime(0, &time, &sysTime)))
892 return m_position / 10000;
893 return qint64(time / 10000);
895 return m_position / 10000;
900#ifdef DEBUG_MEDIAFOUNDATION
901 qDebug() <<
"setPosition";
903 if (m_pendingState != NoPending) {
904 m_request.setCommand(CmdSeek);
905 m_request.start = position;
907 setPositionInternal(position, CmdNone);
911void MFPlayerSession::setPositionInternal(qint64 position, Command requestCmd)
913 if (m_status == QMediaPlayer::EndOfMedia)
914 changeStatus(QMediaPlayer::LoadedMedia);
915 if (m_state.command == CmdStop && requestCmd != CmdSeekResume) {
916 m_position = position * 10000;
919 positionChanged(
this->position());
923 if (m_state.command == CmdPause)
926#ifdef DEBUG_MEDIAFOUNDATION
927 qDebug() <<
"setPositionInternal";
930 PROPVARIANT varStart;
932 varStart.hVal.QuadPart = LONGLONG(position * 10000);
933 if (SUCCEEDED(m_session->Start(NULL, &varStart))) {
934 PropVariantClear(&varStart);
936 m_state.setCommand(CmdStart);
937 m_state.start = position;
938 m_pendingState = SeekPending;
940 error(QMediaPlayer::ResourceError, tr(
"Failed to seek."),
true);
947 return m_restoreRate;
954 m_restoreRate = rate;
955 playbackRateChanged(rate);
958 setPlaybackRateInternal(rate);
963 if (rate == m_request.rate)
966 m_pendingRate = rate;
970#ifdef DEBUG_MEDIAFOUNDATION
971 qDebug() <<
"setPlaybackRate";
980 if (FAILED(m_rateSupport->IsRateSupported(FALSE, rate, NULL))) {
982 if (FAILED(m_rateSupport->IsRateSupported(isThin, rate, NULL))) {
983 qWarning() <<
"unable to set playbackrate = " << rate;
984 m_pendingRate = m_request.rate = m_state.rate;
988 if (m_pendingState != NoPending) {
989 m_request.rate = rate;
990 m_request.isThin = isThin;
994 if (m_request.command == CmdNone)
995 m_request.setCommand(m_state.command);
998 commitRateChange(rate, isThin);
1004#ifdef DEBUG_MEDIAFOUNDATION
1005 qDebug() <<
"commitRateChange";
1007 Q_ASSERT(m_pendingState == NoPending);
1008 MFTIME hnsSystemTime = 0;
1009 MFTIME hnsClockTime = 0;
1010 Command cmdNow = m_state.command;
1011 bool resetPosition =
false;
1016 if ((rate > 0 && m_state.rate <= 0) || (rate < 0 && m_state.rate >= 0)) {
1017 if (cmdNow == CmdStart) {
1019 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1020 Q_ASSERT(hnsSystemTime != 0);
1022 if (rate < 0 || m_state.rate < 0)
1023 m_request.setCommand(CmdSeekResume);
1024 else if (isThin || m_state.isThin)
1025 m_request.setCommand(CmdStartAndSeek);
1027 m_request.setCommand(CmdStart);
1030 if (rate >= 0 && m_state.rate >= 0)
1037 m_request.start = hnsClockTime / 10000;
1038 }
else if (cmdNow == CmdPause) {
1039 if (rate < 0 || m_state.rate < 0) {
1044 qWarning() <<
"Unable to change rate from positive to negative or vice versa in paused state";
1045 rate = m_state.rate;
1046 isThin = m_state.isThin;
1054 if (rate > 0 && m_state.rate == 0) {
1055 m_state.setCommand(CmdNone);
1059 }
else if (rate == 0 && m_state.rate > 0) {
1060 if (cmdNow != CmdPause) {
1067 m_request.setCommand(cmdNow);
1069 }
else if (rate == 0 && m_state.rate < 0) {
1071 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1073 m_request.setCommand(CmdSeekResume);
1078 m_request.start = hnsClockTime / 10000;
1079 }
else if (!isThin && m_state.isThin) {
1080 if (cmdNow == CmdStart) {
1085 resetPosition =
true;
1086 }
else if (cmdNow == CmdPause) {
1089 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1090 m_request.setCommand(CmdSeekResume);
1091 m_request.start = hnsClockTime / 10000;
1097 if (FAILED(m_rateControl->SetRate(isThin, rate))) {
1098 qWarning() <<
"failed to set playbackrate = " << rate;
1099 rate = m_state.rate;
1100 isThin = m_state.isThin;
1104 if (resetPosition) {
1105 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1106 setPosition(hnsClockTime / 10000);
1111 m_pendingRate = m_request.rate = m_state.rate = rate;
1113 m_state.isThin = isThin;
1114 playbackRateChanged(rate);
1119 if (m_scrubbing == enableScrub)
1122 m_scrubbing = enableScrub;
1126 m_pendingRate = m_restoreRate;
1132 m_restoreRate = m_request.rate;
1133 setPlaybackRateInternal(0.0f);
1136 setPlaybackRateInternal(m_restoreRate);
1142 if (m_volume == volume)
1147 setVolumeInternal(volume);
1152 if (m_muted == muted)
1156 setVolumeInternal(muted ? 0 : m_volume);
1161 if (m_volumeControl) {
1162 quint32 channelCount = 0;
1163 if (!SUCCEEDED(m_volumeControl->GetChannelCount(&channelCount))
1164 || channelCount == 0)
1167 for (quint32 i = 0; i < channelCount; ++i)
1168 m_volumeControl->SetChannelVolume(i, volume);
1174 if (!m_netsourceStatistics)
1177 PropVariantInit(&var);
1179 key.fmtid = MFNETSOURCE_STATISTICS;
1180 key.pid = MFNETSOURCE_BUFFERPROGRESS_ID;
1184 if (m_netsourceStatistics->GetValue(key, &var) == S_OK) {
1185 progress = var.lVal;
1186 PropVariantClear(&var);
1189#ifdef DEBUG_MEDIAFOUNDATION
1190 qDebug() <<
"bufferProgress: progress = " << progress;
1193 return progress/100.;
1200 qint64 end = qint64(m_duration / 10000);
1202 if (m_netsourceStatistics) {
1204 PropVariantInit(&var);
1206 key.fmtid = MFNETSOURCE_STATISTICS;
1207 key.pid = MFNETSOURCE_SEEKRANGESTART_ID;
1210 if (m_netsourceStatistics->GetValue(key, &var) == S_OK) {
1211 start = qint64(var.uhVal.QuadPart / 10000);
1212 PropVariantClear(&var);
1213 PropVariantInit(&var);
1214 key.pid = MFNETSOURCE_SEEKRANGEEND_ID;
1215 if (m_netsourceStatistics->GetValue(key, &var) == S_OK) {
1216 end = qint64(var.uhVal.QuadPart / 10000);
1217 PropVariantClear(&var);
1222 return QMediaTimeRange(start, end);
1229 if (riid == IID_IMFAsyncCallback) {
1230 *ppvObject =
static_cast<IMFAsyncCallback*>(
this);
1231 }
else if (riid == IID_IUnknown) {
1232 *ppvObject =
static_cast<IUnknown*>(
this);
1235 return E_NOINTERFACE;
1242 return InterlockedIncrement(&m_cRef);
1247 LONG cRef = InterlockedDecrement(&m_cRef);
1253 m_playerControl =
nullptr;
1260 if (pResult->GetStateNoAddRef() != m_session.Get())
1263 ComPtr<IMFMediaEvent> pEvent;
1265 HRESULT hr = m_session->EndGetEvent(pResult, &pEvent);
1270 MediaEventType meType = MEUnknown;
1271 hr = pEvent->GetType(&meType);
1276 if (meType == MESessionClosed) {
1277 SetEvent(m_hCloseEvent.get());
1280 hr = m_session->BeginGetEvent(
this, m_session.Get());
1287 emit sessionEvent(pEvent);
1292void MFPlayerSession::handleSessionEvent(
const ComPtr<IMFMediaEvent> &sessionEvent)
1294 HRESULT hrStatus = S_OK;
1295 HRESULT hr = sessionEvent->GetStatus(&hrStatus);
1296 if (FAILED(hr) || !m_session) {
1300 MediaEventType meType = MEUnknown;
1301 hr = sessionEvent->GetType(&meType);
1302#ifdef DEBUG_MEDIAFOUNDATION
1303 if (FAILED(hrStatus))
1304 qDebug() <<
"handleSessionEvent: MediaEventType = " << meType <<
"Failed";
1306 qDebug() <<
"handleSessionEvent: MediaEventType = " << meType;
1310 case MENonFatalError: {
1312 PropVariantInit(&var);
1313 sessionEvent->GetValue(&var);
1314 qWarning() <<
"handleSessionEvent: non fatal error = " << var.ulVal;
1315 PropVariantClear(&var);
1316 error(QMediaPlayer::ResourceError, tr(
"Media session non-fatal error."),
false);
1319 case MESourceUnknown:
1320 changeStatus(QMediaPlayer::InvalidMedia);
1323 if (hrStatus == MF_E_ALREADY_INITIALIZED) {
1326#ifdef DEBUG_MEDIAFOUNDATION
1327 qDebug() <<
"handleSessionEvent: ignoring MF_E_ALREADY_INITIALIZED";
1331 changeStatus(QMediaPlayer::InvalidMedia);
1332 qWarning() <<
"handleSessionEvent: serious error = "
1333 << Qt::showbase << Qt::hex << Qt::uppercasedigits <<
static_cast<quint32>(hrStatus);
1336 error(QMediaPlayer::NetworkError, tr(
"Error reading from the network."),
true);
1338 case MF_E_NET_WRITE:
1339 error(QMediaPlayer::NetworkError, tr(
"Error writing to the network."),
true);
1342 error(QMediaPlayer::NetworkError, tr(
"Network packets might be blocked by a firewall."),
true);
1344 case MF_E_MEDIAPROC_WRONGSTATE:
1345 error(QMediaPlayer::ResourceError, tr(
"Media session state error."),
true);
1347 case MF_E_INVALID_STREAM_DATA:
1348 error(QMediaPlayer::ResourceError, tr(
"Invalid stream data."),
true);
1351 error(QMediaPlayer::ResourceError, tr(
"Media session serious error."),
true);
1355 case MESessionRateChanged:
1358 if (FAILED(hrStatus)) {
1360 PropVariantInit(&var);
1361 if (SUCCEEDED(sessionEvent->GetValue(&var)) && (var.vt == VT_R4)) {
1362 m_state.rate = var.fltVal;
1364 playbackRateChanged(playbackRate());
1367 case MESessionScrubSampleComplete :
1369 updatePendingCommands(CmdStart);
1371 case MESessionStarted:
1372 if (m_status == QMediaPlayer::EndOfMedia
1373 || m_status == QMediaPlayer::LoadedMedia) {
1375 changeStatus(QMediaPlayer::BufferedMedia);
1378 updatePendingCommands(CmdStart);
1384 m_signalPositionChangeTimer.start();
1386 case MESessionStopped:
1387 if (m_status != QMediaPlayer::EndOfMedia) {
1392 if (m_status != QMediaPlayer::LoadingMedia && m_request.command != CmdSeekResume)
1393 changeStatus(QMediaPlayer::LoadedMedia);
1395 updatePendingCommands(CmdStop);
1396 m_signalPositionChangeTimer.stop();
1398 case MESessionPaused:
1399 m_position = position() * 10000;
1400 updatePendingCommands(CmdPause);
1401 m_signalPositionChangeTimer.stop();
1402 if (m_status == QMediaPlayer::LoadedMedia)
1403 setPosition(position());
1405 case MEReconnectStart:
1406#ifdef DEBUG_MEDIAFOUNDATION
1407 qDebug() <<
"MEReconnectStart" << ((hrStatus == S_OK) ?
"OK" :
"Failed");
1410 case MEReconnectEnd:
1411#ifdef DEBUG_MEDIAFOUNDATION
1412 qDebug() <<
"MEReconnectEnd" << ((hrStatus == S_OK) ?
"OK" :
"Failed");
1415 case MESessionTopologySet:
1416 if (FAILED(hrStatus)) {
1417 changeStatus(QMediaPlayer::InvalidMedia);
1418 error(QMediaPlayer::FormatError, tr(
"Unsupported media, a codec is missing."),
true);
1422 m_lastPosition = -1;
1425 changeStatus(QMediaPlayer::LoadedMedia);
1430 if (FAILED(hrStatus)) {
1435 case MEBufferingStarted:
1436 changeStatus(QMediaPlayer::StalledMedia);
1439 case MEBufferingStopped:
1440 changeStatus(QMediaPlayer::BufferedMedia);
1443 case MESessionEnded:
1444 m_pendingState = NoPending;
1445 m_state.command = CmdStop;
1446 m_state.prevCmd = CmdNone;
1447 m_request.command = CmdNone;
1448 m_request.prevCmd = CmdNone;
1451 m_position = qint64(m_duration);
1452 positionChanged(position());
1454 changeStatus(QMediaPlayer::EndOfMedia);
1456 case MEEndOfPresentationSegment:
1458 case MESessionTopologyStatus: {
1460 if (SUCCEEDED(sessionEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &status))) {
1461 if (status == MF_TOPOSTATUS_READY) {
1462 ComPtr<IMFClock> clock;
1463 if (SUCCEEDED(m_session->GetClock(&clock))) {
1464 clock->QueryInterface(IID_IMFPresentationClock, &m_presentationClock);
1467 if (SUCCEEDED(MFGetService(m_session.Get(), MF_RATE_CONTROL_SERVICE,
1468 IID_PPV_ARGS(&m_rateControl)))) {
1469 if (SUCCEEDED(MFGetService(m_session.Get(), MF_RATE_CONTROL_SERVICE,
1470 IID_PPV_ARGS(&m_rateSupport)))) {
1471 if (SUCCEEDED(m_rateSupport->IsRateSupported(TRUE, 0, NULL)))
1474 BOOL isThin = FALSE;
1476 if (SUCCEEDED(m_rateControl->GetRate(&isThin, &rate))) {
1477 if (m_pendingRate != rate) {
1478 m_state.rate = m_request.rate = rate;
1479 setPlaybackRate(m_pendingRate);
1483 MFGetService(m_session.Get(), MFNETSOURCE_STATISTICS_SERVICE,
1484 IID_PPV_ARGS(&m_netsourceStatistics));
1486 if (SUCCEEDED(MFGetService(m_session.Get(), MR_STREAM_VOLUME_SERVICE,
1487 IID_PPV_ARGS(&m_volumeControl))))
1488 setVolumeInternal(m_muted ? 0 : m_volume);
1490 m_updatingTopology =
false;
1503 positionChanged(position());
1504 if (m_state.command != command || m_pendingState == NoPending)
1508 if (m_pendingState == SeekPending && m_state.prevCmd == CmdPause) {
1509 m_pendingState = NoPending;
1517 m_state.setCommand(CmdPause);
1520 m_pendingState = NoPending;
1523 if (m_request.rate != m_state.rate) {
1524 commitRateChange(m_request.rate, m_request.isThin);
1528 if (m_pendingState == NoPending) {
1529 switch (m_request.command) {
1541 setPositionInternal(m_request.start, m_request.command);
1543 case CmdStartAndSeek:
1545 setPositionInternal(m_request.start, m_request.command);
1550 m_request.setCommand(CmdNone);
1557 return m_canScrub && m_rateSupport && m_rateControl;
1562#ifdef DEBUG_MEDIAFOUNDATION
1563 qDebug() <<
"MFPlayerSession::clear";
1568 m_pendingState = NoPending;
1569 m_state.command = CmdStop;
1570 m_state.prevCmd = CmdNone;
1571 m_request.command = CmdNone;
1572 m_request.prevCmd = CmdNone;
1574 for (
int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
1575 m_trackInfo[i].metaData.clear();
1576 m_trackInfo[i].nativeIndexes.clear();
1577 m_trackInfo[i].currentIndex = -1;
1578 m_trackInfo[i].sourceNodeId = TOPOID(-1);
1579 m_trackInfo[i].outputNodeId = TOPOID(-1);
1580 m_trackInfo[i].format = GUID_NULL;
1583 if (!m_metaData.isEmpty()) {
1588 m_presentationClock.Reset();
1589 m_rateControl.Reset();
1590 m_rateSupport.Reset();
1591 m_volumeControl.Reset();
1592 m_netsourceStatistics.Reset();
1597 if (m_audioOutput == device)
1601 m_audioOutput->q->disconnect(
this);
1603 m_audioOutput = device;
1604 if (m_audioOutput) {
1606 setVolume(m_audioOutput->q->volume());
1608 connect(m_audioOutput->q, &QAudioOutput::deviceChanged,
this, &MFPlayerSession::updateOutputRouting);
1609 connect(m_audioOutput->q, &QAudioOutput::volumeChanged,
this, &MFPlayerSession::setVolume);
1610 connect(m_audioOutput->q, &QAudioOutput::mutedChanged,
this, &MFPlayerSession::setMuted);
1616 int currentAudioTrack = m_trackInfo[QPlatformMediaPlayer::AudioStream].currentIndex;
1617 if (currentAudioTrack > -1)
1618 setActiveTrack(QPlatformMediaPlayer::AudioStream, currentAudioTrack);
1632 if (type != QPlatformMediaPlayer::AudioStream)
1635 const auto &nativeIndexes = m_trackInfo[type].nativeIndexes;
1637 if (index < -1 || index >= nativeIndexes.count())
1642 if (m_trackInfo[QPlatformMediaPlayer::VideoStream].format == MFVideoFormat_HEVC)
1645 ComPtr<IMFTopology> topology;
1647 if (SUCCEEDED(m_session->GetFullTopology(MFSESSION_GETFULLTOPOLOGY_CURRENT, 0, &topology))) {
1649 m_restorePosition = position() * 10000;
1651 if (m_state.command == CmdStart)
1654 if (m_trackInfo[type].outputNodeId != TOPOID(-1)) {
1655 ComPtr<IMFTopologyNode> node;
1656 if (SUCCEEDED(topology->GetNodeByID(m_trackInfo[type].outputNodeId, &node))) {
1657 topology->RemoveNode(node.Get());
1658 m_trackInfo[type].outputNodeId = TOPOID(-1);
1661 if (m_trackInfo[type].sourceNodeId != TOPOID(-1)) {
1662 ComPtr<IMFTopologyNode> node;
1663 if (SUCCEEDED(topology->GetNodeByID(m_trackInfo[type].sourceNodeId, &node))) {
1664 topology->RemoveNode(node.Get());
1665 m_trackInfo[type].sourceNodeId = TOPOID(-1);
1669 IMFMediaSource *mediaSource = m_sourceResolver->mediaSource();
1671 ComPtr<IMFPresentationDescriptor> sourcePD;
1672 if (SUCCEEDED(mediaSource->CreatePresentationDescriptor(&sourcePD))) {
1674 if (m_trackInfo[type].currentIndex >= 0 && m_trackInfo[type].currentIndex < nativeIndexes.count())
1675 sourcePD->DeselectStream(nativeIndexes.at(m_trackInfo[type].currentIndex));
1677 m_trackInfo[type].currentIndex = index;
1680 m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology.Get());
1682 int nativeIndex = nativeIndexes.at(index);
1683 sourcePD->SelectStream(nativeIndex);
1685 ComPtr<IMFStreamDescriptor> streamDesc;
1686 BOOL selected = FALSE;
1688 if (SUCCEEDED(sourcePD->GetStreamDescriptorByIndex(nativeIndex, &selected, &streamDesc))) {
1689 ComPtr<IMFTopologyNode> sourceNode = addSourceNode(
1690 topology.Get(), mediaSource, sourcePD.Get(), streamDesc.Get());
1692 ComPtr<IMFTopologyNode> outputNode =
1693 addOutputNode(MFPlayerSession::Audio, topology.Get(), 0);
1695 if (SUCCEEDED(sourceNode->ConnectOutput(0, outputNode.Get(), 0))) {
1696 sourceNode->GetTopoNodeID(&m_trackInfo[type].sourceNodeId);
1697 outputNode->GetTopoNodeID(&m_trackInfo[type].outputNodeId);
1698 m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE,
1705 m_updatingTopology =
true;
1712 if (type >= QPlatformMediaPlayer::NTrackTypes)
1714 return m_trackInfo[type].currentIndex;
1719 if (type >= QPlatformMediaPlayer::NTrackTypes)
1721 return m_trackInfo[type].metaData.count();
1724QMediaMetaData
MFPlayerSession::trackMetaData(QPlatformMediaPlayer::TrackType type,
int trackNumber)
1726 if (type >= QPlatformMediaPlayer::NTrackTypes)
1729 if (trackNumber < 0 || trackNumber >= m_trackInfo[type].metaData.count())
1732 return m_trackInfo[type].metaData.at(trackNumber);
1737#include "moc_mfplayersession_p.cpp"
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)