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>
21#include <private/qwindows_scopedpropvariant_p.h>
28#include <wmcodecdsp.h>
31#include <mmdeviceapi.h>
32#include <propvarutil.h>
34#include <functiondiscoverykeys_devpkey.h>
42 m_playerControl(playerControl),
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 (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 != status() || !m_sourceResolver
201 || m_sourceResolver.Get() != sender())
203#ifdef DEBUG_MEDIAFOUNDATION
204 qDebug() <<
"handleMediaSourceReady";
207 ComPtr<IMFMediaSource> mediaSource = m_sourceResolver->mediaSource();
209 DWORD dwCharacteristics = 0;
210 mediaSource->GetCharacteristics(&dwCharacteristics);
213 ComPtr<IMFPresentationDescriptor> sourcePD;
214 hr = mediaSource->CreatePresentationDescriptor(&sourcePD);
217 m_metaData = MFMetaData::fromNative(mediaSource.Get());
219 sourcePD->GetUINT64(MF_PD_DURATION, &m_duration);
221 durationUpdate(qint64(m_duration / 10000));
222 setupPlaybackTopology(mediaSource.Get(), 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 TrackType trackType = (mediaType == Audio) ?
320 TrackType::AudioStream
321 : TrackType::VideoStream;
323 QLocale::Language lang = streamLanguage.isEmpty() ?
324 QLocale::Language::AnyLanguage : QLocale(streamLanguage).language();
326 QMediaMetaData metaData;
327 metaData.insert(QMediaMetaData::Title, streamName);
328 metaData.insert(QMediaMetaData::Language, lang);
330 m_trackInfo[trackType].metaData.append(metaData);
331 m_trackInfo[trackType].nativeIndexes.append(i);
332 m_trackInfo[trackType].format = format;
334 if (((m_mediaTypes & mediaType) == 0) && selected) {
335 ComPtr<IMFTopologyNode> sourceNode =
336 addSourceNode(topology.Get(), source, sourcePD, streamDesc.Get());
338 ComPtr<IMFTopologyNode> outputNode =
339 addOutputNode(mediaType, topology.Get(), 0);
341 sourceNode->GetTopoNodeID(&m_trackInfo[trackType].sourceNodeId);
342 outputNode->GetTopoNodeID(&m_trackInfo[trackType].outputNodeId);
344 hr = sourceNode->ConnectOutput(0, outputNode.Get(), 0);
347 error(QMediaPlayer::FormatError, tr(
"Unable to play any stream."),
false);
349 m_trackInfo[trackType].currentIndex = m_trackInfo[trackType].nativeIndexes.count() - 1;
352 m_mediaTypes |= mediaType;
366 topology->RemoveNode(sourceNode.Get());
372 if (selected && !streamAdded)
373 sourcePD->DeselectStream(i);
377 if (succeededCount == 0) {
378 changeStatus(QMediaPlayer::InvalidMedia);
379 error(QMediaPlayer::ResourceError, tr(
"Unable to play."),
true);
381 if (m_trackInfo[TrackType::VideoStream].outputNodeId != TOPOID(-1))
382 topology = insertMFT(
384 m_trackInfo[TrackType::VideoStream].outputNodeId);
386 hr = m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology.Get());
388 m_updatingTopology =
true;
390 changeStatus(QMediaPlayer::InvalidMedia);
391 error(QMediaPlayer::ResourceError, tr(
"Failed to set topology."),
true);
396ComPtr<IMFTopologyNode>
MFPlayerSession::addSourceNode(IMFTopology *topology,
397 IMFMediaSource *source,
398 IMFPresentationDescriptor *presentationDesc,
399 IMFStreamDescriptor *streamDesc)
401 ComPtr<IMFTopologyNode> node;
402 HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node);
404 hr = node->SetUnknown(MF_TOPONODE_SOURCE, source);
406 hr = node->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, presentationDesc);
408 hr = node->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, streamDesc);
410 hr = topology->AddNode(node.Get());
420ComPtr<IMFTopologyNode>
MFPlayerSession::addOutputNode(MediaType mediaType, IMFTopology *topology,
423 ComPtr<IMFTopologyNode> node;
424 if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node)))
427 ComPtr<IMFActivate> activate;
428 if (mediaType == Audio) {
430 auto id = m_audioOutput->device.id();
432 qInfo() <<
"No audio output";
436 HRESULT hr = MFCreateAudioRendererActivate(&activate);
438 qWarning() <<
"Failed to create audio renderer activate";
442 QString s = QString::fromUtf8(id);
443 hr = activate->SetString(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID, (LPCWSTR)s.utf16());
445 qWarning() <<
"Failed to set attribute for audio device"
446 << m_audioOutput->device.description();
450 }
else if (mediaType == Video) {
451 activate = m_videoRendererControl->createActivate();
453 QSize resolution = m_metaData.value(QMediaMetaData::Resolution).toSize();
455 if (resolution.isValid())
456 m_videoRendererControl->setCropRect(QRect(QPoint(), resolution));
460 error(QMediaPlayer::FormatError, tr(
"Unknown stream type."),
false);
463 if (!activate || FAILED(node->SetObject(activate.Get()))
464 || FAILED(node->SetUINT32(MF_TOPONODE_STREAMID, sinkID))
465 || FAILED(node->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE))
466 || FAILED(topology->AddNode(node.Get()))) {
470 if (activate && mediaType == Audio)
482 ComPtr<IUnknown> nodeObject;
483 ComPtr<IMFActivate> activate;
484 ComPtr<IMFStreamSink> stream;
485 ComPtr<IMFMediaSink> sink;
487 HRESULT hr = pNode->GetObject(&nodeObject);
491 hr = nodeObject->QueryInterface(IID_PPV_ARGS(&activate));
493 DWORD dwStreamID = 0;
496 hr = activate->ActivateObject(IID_PPV_ARGS(&sink));
498 dwStreamID = MFGetAttributeUINT32(pNode, MF_TOPONODE_STREAMID, 0);
502 hr = sink->GetStreamSinkById(dwStreamID, &stream);
505 hr = sink->AddStreamSink(dwStreamID, NULL, &stream);
511 hr = pNode->SetObject(stream.Get());
514 hr = nodeObject->QueryInterface(IID_PPV_ARGS(&stream));
524 ComPtr<IMFCollection> collection;
527 HRESULT hr = pTopology->GetOutputNodeCollection(&collection);
532 hr = collection->GetElementCount(&cNodes);
535 for (DWORD i = 0; i < cNodes; i++) {
536 ComPtr<IUnknown> element;
537 hr = collection->GetElement(i, &element);
541 ComPtr<IMFTopologyNode> node;
542 hr = element->QueryInterface(IID_IMFTopologyNode, &node);
547 hr = BindOutputNode(node.Get());
560ComPtr<IMFTopology>
MFPlayerSession::insertMFT(
const ComPtr<IMFTopology> &topology,
563 bool isNewTopology =
false;
565 ComPtr<IMFTopoLoader> topoLoader;
566 ComPtr<IMFTopology> resolvedTopology;
567 ComPtr<IMFCollection> outputNodes;
570 if (FAILED(BindOutputNodes(topology.Get())))
573 if (FAILED(MFCreateTopoLoader(&topoLoader)))
576 if (FAILED(topoLoader->Load(topology.Get(), &resolvedTopology, NULL))) {
579 insertColorConverter(topology.Get(), outputNodeId);
580 if (FAILED(topoLoader->Load(topology.Get(), &resolvedTopology, NULL)))
584 if (insertResizer(resolvedTopology.Get()))
585 isNewTopology =
true;
589 return resolvedTopology;
601 bool inserted =
false;
602 WORD elementCount = 0;
603 ComPtr<IMFTopologyNode> node;
604 ComPtr<IUnknown> object;
605 ComPtr<IWMColorConvProps> colorConv;
606 ComPtr<IMFTransform> resizer;
607 ComPtr<IMFTopologyNode> resizerNode;
608 ComPtr<IMFTopologyNode> inputNode;
610 HRESULT hr = topology->GetNodeCount(&elementCount);
614 for (WORD i = 0; i < elementCount; ++i) {
618 if (FAILED(topology->GetNode(i, &node)))
621 MF_TOPOLOGY_TYPE nodeType;
622 if (FAILED(node->GetNodeType(&nodeType)))
625 if (nodeType != MF_TOPOLOGY_TRANSFORM_NODE)
628 if (FAILED(node->GetObject(&object)))
631 if (FAILED(object->QueryInterface(IID_PPV_ARGS(&colorConv))))
634 if (FAILED(CoCreateInstance(CLSID_CResizerDMO, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform,
638 if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &resizerNode)))
641 if (FAILED(resizerNode->SetObject(resizer.Get())))
644 if (FAILED(topology->AddNode(resizerNode.Get())))
647 DWORD outputIndex = 0;
648 if (FAILED(node->GetInput(0, &inputNode, &outputIndex))) {
649 topology->RemoveNode(resizerNode.Get());
653 if (FAILED(inputNode->ConnectOutput(0, resizerNode.Get(), 0))) {
654 topology->RemoveNode(resizerNode.Get());
658 if (FAILED(resizerNode->ConnectOutput(0, node.Get(), 0))) {
659 inputNode->ConnectOutput(0, node.Get(), 0);
660 topology->RemoveNode(resizerNode.Get());
675void MFPlayerSession::insertColorConverter(IMFTopology *topology, TOPOID outputNodeId)
677 ComPtr<IMFCollection> outputNodes;
679 if (FAILED(topology->GetOutputNodeCollection(&outputNodes)))
682 DWORD elementCount = 0;
683 if (FAILED(outputNodes->GetElementCount(&elementCount)))
686 for (DWORD n = 0; n < elementCount; n++) {
687 ComPtr<IUnknown> element;
688 ComPtr<IMFTopologyNode> node;
689 ComPtr<IMFTopologyNode> inputNode;
690 ComPtr<IMFTopologyNode> mftNode;
691 ComPtr<IMFTransform> converter;
694 if (FAILED(outputNodes->GetElement(n, &element)))
697 if (FAILED(element->QueryInterface(IID_IMFTopologyNode, &node)))
701 if (FAILED(node->GetTopoNodeID(&id)))
704 if (id != outputNodeId)
707 DWORD outputIndex = 0;
708 if (FAILED(node->GetInput(0, &inputNode, &outputIndex)))
711 if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mftNode)))
714 if (FAILED(CoCreateInstance(CLSID_CColorConvertDMO, NULL, CLSCTX_INPROC_SERVER,
715 IID_IMFTransform, &converter)))
718 if (FAILED(mftNode->SetObject(converter.Get())))
721 if (FAILED(topology->AddNode(mftNode.Get())))
724 if (FAILED(inputNode->ConnectOutput(0, mftNode.Get(), 0)))
727 if (FAILED(mftNode->ConnectOutput(0, node.Get(), 0)))
736#ifdef DEBUG_MEDIAFOUNDATION
739 if (!immediate && m_pendingState != NoPending) {
740 m_request.setCommand(CmdStop);
742 if (m_state.command == CmdStop)
748 if (SUCCEEDED(m_session->Stop())) {
750 m_state.setCommand(CmdStop);
751 m_pendingState = CmdPending;
752 if (status() != QMediaPlayer::EndOfMedia) {
757 error(QMediaPlayer::ResourceError, tr(
"Failed to stop."),
true);
764 if (status() == QMediaPlayer::LoadedMedia && m_updateRoutingOnStart) {
765 m_updateRoutingOnStart =
false;
769 if (status() == QMediaPlayer::EndOfMedia) {
774#ifdef DEBUG_MEDIAFOUNDATION
778 if (m_pendingState != NoPending) {
779 m_request.setCommand(CmdStart);
781 if (m_state.command == CmdStart)
786 m_position = position() * 10000;
789 if (m_restorePosition >= 0) {
790 m_position = m_restorePosition;
791 if (!m_updatingTopology)
792 m_restorePosition = -1;
795 QtMultimediaPrivate::ScopedPropVariant varStart;
796 InitPropVariantFromInt64(m_position, varStart.get());
798 if (SUCCEEDED(m_session->Start(&GUID_NULL, varStart.get()))) {
799 m_state.setCommand(CmdStart);
800 m_pendingState = CmdPending;
802 error(QMediaPlayer::ResourceError, tr(
"failed to start playback"),
true);
809#ifdef DEBUG_MEDIAFOUNDATION
817 if (status() == QMediaPlayer::LoadingMedia) {
818 m_deferredPause =
true;
822 if (m_pendingState != NoPending) {
823 m_request.setCommand(CmdPause);
825 if (m_state.command == CmdPause)
828 if (SUCCEEDED(m_session->Pause())) {
829 m_state.setCommand(CmdPause);
830 m_pendingState = CmdPending;
832 error(QMediaPlayer::ResourceError, tr(
"Failed to pause."),
false);
834 if (status() == QMediaPlayer::EndOfMedia) {
843 if (!m_playerControl)
845#ifdef DEBUG_MEDIAFOUNDATION
846 qDebug() <<
"MFPlayerSession::changeStatus" << newStatus;
849 statusChanged(newStatus);
854 if (!m_playerControl)
855 return QMediaPlayer::NoMedia;
856 return m_playerControl->mediaStatus();
863 Q_ASSERT(m_session == NULL);
865 HRESULT hr = MFCreateMediaSession(NULL, &m_session);
867 changeStatus(QMediaPlayer::InvalidMedia);
868 error(QMediaPlayer::ResourceError, tr(
"Unable to create mediasession."),
true);
872 m_hCloseEvent = EventHandle{ CreateEvent(NULL, FALSE, FALSE, NULL) };
874 hr = m_session->BeginGetEvent(
this, m_session.Get());
876 changeStatus(QMediaPlayer::InvalidMedia);
877 error(QMediaPlayer::ResourceError, tr(
"Unable to pull session events."),
false);
882 m_sourceResolver = makeComObject<SourceResolver>();
883 QObject::connect(m_sourceResolver.Get(), &SourceResolver::mediaSourceReady,
this,
884 &MFPlayerSession::handleMediaSourceReady);
885 QObject::connect(m_sourceResolver.Get(), &SourceResolver::error,
this,
886 &MFPlayerSession::handleSourceError);
894 if (m_request.command == CmdSeek || m_request.command == CmdSeekResume)
895 return m_request.start;
897 if (m_pendingState == SeekPending)
898 return m_state.start;
900 if (m_state.command == CmdStop)
901 return m_position / 10000;
903 if (m_presentationClock) {
904 MFTIME time, sysTime;
905 if (FAILED(m_presentationClock->GetCorrelatedTime(0, &time, &sysTime)))
906 return m_position / 10000;
907 return qint64(time / 10000);
909 return m_position / 10000;
914#ifdef DEBUG_MEDIAFOUNDATION
915 qDebug() <<
"setPosition";
917 if (m_pendingState != NoPending) {
918 m_request.setCommand(CmdSeek);
919 m_request.start = position;
921 setPositionInternal(position, CmdNone);
925void MFPlayerSession::setPositionInternal(qint64 position, Command requestCmd)
927 if (status() == QMediaPlayer::EndOfMedia)
928 changeStatus(QMediaPlayer::LoadedMedia);
929 if (m_state.command == CmdStop && requestCmd != CmdSeekResume) {
930 m_position = position * 10000;
933 positionChanged(
this->position());
937 if (m_state.command == CmdPause)
940#ifdef DEBUG_MEDIAFOUNDATION
941 qDebug() <<
"setPositionInternal";
944 QtMultimediaPrivate::ScopedPropVariant varStart;
945 varStart->vt = VT_I8;
946 varStart->hVal.QuadPart = LONGLONG(position * 10000);
947 if (SUCCEEDED(m_session->Start(NULL, varStart.get()))) {
949 m_state.setCommand(CmdStart);
950 m_state.start = position;
951 m_pendingState = SeekPending;
953 error(QMediaPlayer::ResourceError, tr(
"Failed to seek."),
true);
960 return m_restoreRate;
967 m_restoreRate = rate;
968 playbackRateChanged(rate);
971 setPlaybackRateInternal(rate);
976 if (rate == m_request.rate)
979 m_pendingRate = rate;
983#ifdef DEBUG_MEDIAFOUNDATION
984 qDebug() <<
"setPlaybackRate";
993 if (FAILED(m_rateSupport->IsRateSupported(FALSE, rate, NULL))) {
995 if (FAILED(m_rateSupport->IsRateSupported(isThin, rate, NULL))) {
996 qWarning() <<
"unable to set playbackrate = " << rate;
997 m_pendingRate = m_request.rate = m_state.rate;
1001 if (m_pendingState != NoPending) {
1002 m_request.rate = rate;
1003 m_request.isThin = isThin;
1007 if (m_request.command == CmdNone)
1008 m_request.setCommand(m_state.command);
1011 commitRateChange(rate, isThin);
1017#ifdef DEBUG_MEDIAFOUNDATION
1018 qDebug() <<
"commitRateChange";
1020 Q_ASSERT(m_pendingState == NoPending);
1021 MFTIME hnsSystemTime = 0;
1022 MFTIME hnsClockTime = 0;
1023 Command cmdNow = m_state.command;
1024 bool resetPosition =
false;
1029 if ((rate > 0 && m_state.rate <= 0) || (rate < 0 && m_state.rate >= 0)) {
1030 if (cmdNow == CmdStart) {
1032 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1033 Q_ASSERT(hnsSystemTime != 0);
1035 if (rate < 0 || m_state.rate < 0)
1036 m_request.setCommand(CmdSeekResume);
1037 else if (isThin || m_state.isThin)
1038 m_request.setCommand(CmdStartAndSeek);
1040 m_request.setCommand(CmdStart);
1043 if (rate >= 0 && m_state.rate >= 0)
1050 m_request.start = hnsClockTime / 10000;
1051 }
else if (cmdNow == CmdPause) {
1052 if (rate < 0 || m_state.rate < 0) {
1057 qWarning() <<
"Unable to change rate from positive to negative or vice versa in paused state";
1058 rate = m_state.rate;
1059 isThin = m_state.isThin;
1067 if (rate > 0 && m_state.rate == 0) {
1068 m_state.setCommand(CmdNone);
1072 }
else if (rate == 0 && m_state.rate > 0) {
1073 if (cmdNow != CmdPause) {
1080 m_request.setCommand(cmdNow);
1082 }
else if (rate == 0 && m_state.rate < 0) {
1084 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1086 m_request.setCommand(CmdSeekResume);
1091 m_request.start = hnsClockTime / 10000;
1092 }
else if (!isThin && m_state.isThin) {
1093 if (cmdNow == CmdStart) {
1098 resetPosition =
true;
1099 }
else if (cmdNow == CmdPause) {
1102 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1103 m_request.setCommand(CmdSeekResume);
1104 m_request.start = hnsClockTime / 10000;
1110 if (FAILED(m_rateControl->SetRate(isThin, rate))) {
1111 qWarning() <<
"failed to set playbackrate = " << rate;
1112 rate = m_state.rate;
1113 isThin = m_state.isThin;
1117 if (resetPosition) {
1118 m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime);
1119 setPosition(hnsClockTime / 10000);
1124 m_pendingRate = m_request.rate = m_state.rate = rate;
1126 m_state.isThin = isThin;
1127 playbackRateChanged(rate);
1132 if (m_scrubbing == enableScrub)
1135 m_scrubbing = enableScrub;
1139 m_pendingRate = m_restoreRate;
1145 m_restoreRate = m_request.rate;
1146 setPlaybackRateInternal(0.0f);
1149 setPlaybackRateInternal(m_restoreRate);
1155 if (m_volume == volume)
1160 setVolumeInternal(volume);
1165 if (m_muted == muted)
1169 setVolumeInternal(muted ? 0 : m_volume);
1174 if (m_volumeControl) {
1175 quint32 channelCount = 0;
1176 if (!SUCCEEDED(m_volumeControl->GetChannelCount(&channelCount))
1177 || channelCount == 0)
1180 for (quint32 i = 0; i < channelCount; ++i)
1181 m_volumeControl->SetChannelVolume(i, volume);
1187 if (!m_netsourceStatistics)
1191 key.fmtid = MFNETSOURCE_STATISTICS;
1192 key.pid = MFNETSOURCE_BUFFERPROGRESS_ID;
1196 QtMultimediaPrivate::ScopedPropVariant var;
1197 if (m_netsourceStatistics->GetValue(key, var.get()) == S_OK)
1198 progress = var->lVal;
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 key.fmtid = MFNETSOURCE_STATISTICS;
1216 key.pid = MFNETSOURCE_SEEKRANGESTART_ID;
1219 QtMultimediaPrivate::ScopedPropVariant startVar;
1220 if (m_netsourceStatistics->GetValue(key, startVar.get()) == S_OK) {
1221 start = qint64(startVar->uhVal.QuadPart / 10000);
1222 key.pid = MFNETSOURCE_SEEKRANGEEND_ID;
1223 QtMultimediaPrivate::ScopedPropVariant endVar;
1224 if (m_netsourceStatistics->GetValue(key, endVar.get()) == S_OK)
1225 end = qint64(endVar->uhVal.QuadPart / 10000);
1229 return QMediaTimeRange(start, end);
1236 if (riid == IID_IMFAsyncCallback) {
1237 *ppvObject =
static_cast<IMFAsyncCallback*>(
this);
1238 }
else if (riid == IID_IUnknown) {
1239 *ppvObject =
static_cast<IUnknown*>(
this);
1242 return E_NOINTERFACE;
1249 return InterlockedIncrement(&m_cRef);
1254 LONG cRef = InterlockedDecrement(&m_cRef);
1260 m_playerControl =
nullptr;
1267 if (pResult->GetStateNoAddRef() != m_session.Get())
1270 ComPtr<IMFMediaEvent> pEvent;
1272 HRESULT hr = m_session->EndGetEvent(pResult, &pEvent);
1277 MediaEventType meType = MEUnknown;
1278 hr = pEvent->GetType(&meType);
1283 if (meType == MESessionClosed) {
1284 SetEvent(m_hCloseEvent.get());
1287 hr = m_session->BeginGetEvent(
this, m_session.Get());
1294 emit sessionEvent(pEvent);
1299void MFPlayerSession::handleSessionEvent(
const ComPtr<IMFMediaEvent> &sessionEvent)
1301 HRESULT hrStatus = S_OK;
1302 HRESULT hr = sessionEvent->GetStatus(&hrStatus);
1303 if (FAILED(hr) || !m_session) {
1307 MediaEventType meType = MEUnknown;
1308 hr = sessionEvent->GetType(&meType);
1309#ifdef DEBUG_MEDIAFOUNDATION
1310 if (FAILED(hrStatus))
1311 qDebug() <<
"handleSessionEvent: MediaEventType = " << meType <<
"Failed";
1313 qDebug() <<
"handleSessionEvent: MediaEventType = " << meType;
1317 case MENonFatalError: {
1318 QtMultimediaPrivate::ScopedPropVariant var;
1319 sessionEvent->GetValue(var.get());
1320 qWarning() <<
"handleSessionEvent: non fatal error = " << var->ulVal;
1321 error(QMediaPlayer::ResourceError, tr(
"Media session non-fatal error."),
false);
1324 case MESourceUnknown:
1325 changeStatus(QMediaPlayer::InvalidMedia);
1328 if (hrStatus == MF_E_ALREADY_INITIALIZED) {
1331#ifdef DEBUG_MEDIAFOUNDATION
1332 qDebug() <<
"handleSessionEvent: ignoring MF_E_ALREADY_INITIALIZED";
1336 changeStatus(QMediaPlayer::InvalidMedia);
1337 qWarning() <<
"handleSessionEvent: serious error = "
1338 << Qt::showbase << Qt::hex << Qt::uppercasedigits <<
static_cast<quint32>(hrStatus);
1341 error(QMediaPlayer::NetworkError, tr(
"Error reading from the network."),
true);
1343 case MF_E_NET_WRITE:
1344 error(QMediaPlayer::NetworkError, tr(
"Error writing to the network."),
true);
1347 error(QMediaPlayer::NetworkError, tr(
"Network packets might be blocked by a firewall."),
true);
1349 case MF_E_MEDIAPROC_WRONGSTATE:
1350 error(QMediaPlayer::ResourceError, tr(
"Media session state error."),
true);
1352 case MF_E_INVALID_STREAM_DATA:
1353 error(QMediaPlayer::ResourceError, tr(
"Invalid stream data."),
true);
1356 error(QMediaPlayer::ResourceError, tr(
"Media session serious error."),
true);
1360 case MESessionRateChanged:
1363 if (FAILED(hrStatus)) {
1364 QtMultimediaPrivate::ScopedPropVariant var;
1365 if (SUCCEEDED(sessionEvent->GetValue(var.get())) && (var->vt == VT_R4))
1366 m_state.rate = var->fltVal;
1367 playbackRateChanged(playbackRate());
1370 case MESessionScrubSampleComplete :
1372 updatePendingCommands(CmdStart);
1374 case MESessionStarted:
1375 if (status() == QMediaPlayer::EndOfMedia
1376 || status() == QMediaPlayer::LoadedMedia) {
1378 changeStatus(QMediaPlayer::BufferedMedia);
1381 updatePendingCommands(CmdStart);
1387 m_signalPositionChangeTimer.start();
1389 case MESessionStopped:
1390 if (status() != QMediaPlayer::EndOfMedia) {
1395 if (status() != QMediaPlayer::LoadingMedia && m_request.command != CmdSeekResume)
1396 changeStatus(QMediaPlayer::LoadedMedia);
1398 updatePendingCommands(CmdStop);
1399 m_signalPositionChangeTimer.stop();
1401 case MESessionPaused:
1402 m_position = position() * 10000;
1403 updatePendingCommands(CmdPause);
1404 m_signalPositionChangeTimer.stop();
1405 if (status() == QMediaPlayer::LoadedMedia)
1406 setPosition(position());
1408 case MEReconnectStart:
1409#ifdef DEBUG_MEDIAFOUNDATION
1410 qDebug() <<
"MEReconnectStart" << ((hrStatus == S_OK) ?
"OK" :
"Failed");
1413 case MEReconnectEnd:
1414#ifdef DEBUG_MEDIAFOUNDATION
1415 qDebug() <<
"MEReconnectEnd" << ((hrStatus == S_OK) ?
"OK" :
"Failed");
1418 case MESessionTopologySet:
1419 if (FAILED(hrStatus)) {
1420 changeStatus(QMediaPlayer::InvalidMedia);
1421 error(QMediaPlayer::FormatError, tr(
"Unsupported media, a codec is missing."),
true);
1425 m_lastPosition = -1;
1428 changeStatus(QMediaPlayer::LoadedMedia);
1433 if (FAILED(hrStatus)) {
1438 case MEBufferingStarted:
1439 changeStatus(QMediaPlayer::StalledMedia);
1442 case MEBufferingStopped:
1443 changeStatus(QMediaPlayer::BufferedMedia);
1446 case MESessionEnded:
1447 m_pendingState = NoPending;
1448 m_state.command = CmdStop;
1449 m_state.prevCmd = CmdNone;
1450 m_request.command = CmdNone;
1451 m_request.prevCmd = CmdNone;
1454 m_position = qint64(m_duration);
1455 positionChanged(position());
1457 changeStatus(QMediaPlayer::EndOfMedia);
1459 case MEEndOfPresentationSegment:
1461 case MESessionTopologyStatus: {
1463 if (SUCCEEDED(sessionEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &status))) {
1464 if (status == MF_TOPOSTATUS_READY) {
1465 ComPtr<IMFClock> clock;
1466 if (SUCCEEDED(m_session->GetClock(&clock))) {
1467 clock->QueryInterface(IID_IMFPresentationClock, &m_presentationClock);
1470 if (SUCCEEDED(MFGetService(m_session.Get(), MF_RATE_CONTROL_SERVICE,
1471 IID_PPV_ARGS(&m_rateControl)))) {
1472 if (SUCCEEDED(MFGetService(m_session.Get(), MF_RATE_CONTROL_SERVICE,
1473 IID_PPV_ARGS(&m_rateSupport)))) {
1474 if (SUCCEEDED(m_rateSupport->IsRateSupported(TRUE, 0, NULL)))
1477 BOOL isThin = FALSE;
1479 if (SUCCEEDED(m_rateControl->GetRate(&isThin, &rate))) {
1480 if (m_pendingRate != rate) {
1481 m_state.rate = m_request.rate = rate;
1482 setPlaybackRate(m_pendingRate);
1486 MFGetService(m_session.Get(), MFNETSOURCE_STATISTICS_SERVICE,
1487 IID_PPV_ARGS(&m_netsourceStatistics));
1489 if (SUCCEEDED(MFGetService(m_session.Get(), MR_STREAM_VOLUME_SERVICE,
1490 IID_PPV_ARGS(&m_volumeControl))))
1491 setVolumeInternal(m_muted ? 0 : m_volume);
1493 m_updatingTopology =
false;
1496 if (m_deferredPause) {
1497 m_deferredPause =
false;
1511 positionChanged(position());
1512 if (m_state.command != command || m_pendingState == NoPending)
1516 if (m_pendingState == SeekPending && m_state.prevCmd == CmdPause) {
1517 m_pendingState = NoPending;
1525 m_state.setCommand(CmdPause);
1528 m_pendingState = NoPending;
1531 if (m_request.rate != m_state.rate) {
1532 commitRateChange(m_request.rate, m_request.isThin);
1536 if (m_pendingState == NoPending) {
1537 switch (m_request.command) {
1549 setPositionInternal(m_request.start, m_request.command);
1551 case CmdStartAndSeek:
1553 setPositionInternal(m_request.start, m_request.command);
1558 m_request.setCommand(CmdNone);
1565 return m_canScrub && m_rateSupport && m_rateControl;
1570#ifdef DEBUG_MEDIAFOUNDATION
1571 qDebug() <<
"MFPlayerSession::clear";
1575 m_deferredPause =
false;
1577 m_pendingState = NoPending;
1578 m_state.command = CmdStop;
1579 m_state.prevCmd = CmdNone;
1580 m_request.command = CmdNone;
1581 m_request.prevCmd = CmdNone;
1583 for (size_t i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
1584 m_trackInfo[i].metaData.clear();
1585 m_trackInfo[i].nativeIndexes.clear();
1586 m_trackInfo[i].currentIndex = -1;
1587 m_trackInfo[i].sourceNodeId = TOPOID(-1);
1588 m_trackInfo[i].outputNodeId = TOPOID(-1);
1589 m_trackInfo[i].format = GUID_NULL;
1592 if (!m_metaData.isEmpty()) {
1597 m_presentationClock.Reset();
1598 m_rateControl.Reset();
1599 m_rateSupport.Reset();
1600 m_volumeControl.Reset();
1601 m_netsourceStatistics.Reset();
1606 if (m_audioOutput == device)
1610 m_audioOutput->q->disconnect(
this);
1612 m_audioOutput = device;
1613 if (m_audioOutput) {
1615 setVolume(m_audioOutput->q->volume());
1618 connect(m_audioOutput->q, &QAudioOutput::volumeChanged,
this, &MFPlayerSession::setVolume);
1625 int currentAudioTrack =
1626 m_trackInfo[TrackType::AudioStream].currentIndex;
1627 if (currentAudioTrack > -1)
1628 setActiveTrack(TrackType::AudioStream, currentAudioTrack);
1642 if (type != TrackType::AudioStream)
1645 const auto &nativeIndexes = m_trackInfo[type].nativeIndexes;
1647 if (index < -1 || index >= nativeIndexes.count())
1652 if (m_trackInfo[TrackType::VideoStream].format == MFVideoFormat_HEVC)
1655 ComPtr<IMFTopology> topology;
1657 if (SUCCEEDED(m_session->GetFullTopology(MFSESSION_GETFULLTOPOLOGY_CURRENT, 0, &topology))) {
1659 m_restorePosition = position() * 10000;
1661 if (m_state.command == CmdStart)
1664 if (m_trackInfo[type].outputNodeId != TOPOID(-1)) {
1665 ComPtr<IMFTopologyNode> node;
1666 if (SUCCEEDED(topology->GetNodeByID(m_trackInfo[type].outputNodeId, &node))) {
1667 topology->RemoveNode(node.Get());
1668 m_trackInfo[type].outputNodeId = TOPOID(-1);
1671 if (m_trackInfo[type].sourceNodeId != TOPOID(-1)) {
1672 ComPtr<IMFTopologyNode> node;
1673 if (SUCCEEDED(topology->GetNodeByID(m_trackInfo[type].sourceNodeId, &node))) {
1674 topology->RemoveNode(node.Get());
1675 m_trackInfo[type].sourceNodeId = TOPOID(-1);
1679 ComPtr<IMFMediaSource> mediaSource = m_sourceResolver->mediaSource();
1681 ComPtr<IMFPresentationDescriptor> sourcePD;
1682 if (SUCCEEDED(mediaSource->CreatePresentationDescriptor(&sourcePD))) {
1684 if (m_trackInfo[type].currentIndex >= 0 && m_trackInfo[type].currentIndex < nativeIndexes.count())
1685 sourcePD->DeselectStream(nativeIndexes.at(m_trackInfo[type].currentIndex));
1687 m_trackInfo[type].currentIndex = index;
1690 m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology.Get());
1692 int nativeIndex = nativeIndexes.at(index);
1693 sourcePD->SelectStream(nativeIndex);
1695 ComPtr<IMFStreamDescriptor> streamDesc;
1696 BOOL selected = FALSE;
1698 if (SUCCEEDED(sourcePD->GetStreamDescriptorByIndex(nativeIndex, &selected, &streamDesc))) {
1699 ComPtr<IMFTopologyNode> sourceNode = addSourceNode(
1700 topology.Get(), mediaSource.Get(), sourcePD.Get(), streamDesc.Get());
1702 ComPtr<IMFTopologyNode> outputNode =
1703 addOutputNode(MFPlayerSession::Audio, topology.Get(), 0);
1705 if (SUCCEEDED(sourceNode->ConnectOutput(0, outputNode.Get(), 0))) {
1706 sourceNode->GetTopoNodeID(&m_trackInfo[type].sourceNodeId);
1707 outputNode->GetTopoNodeID(&m_trackInfo[type].outputNodeId);
1708 m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE,
1715 m_updatingTopology =
true;
1722 return m_trackInfo[type].currentIndex;
1727 return m_trackInfo[type].metaData.count();
1732 if (trackNumber < 0 || trackNumber >= m_trackInfo[type].metaData.count())
1735 return m_trackInfo[type].metaData.at(trackNumber);
1740#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)
void setActiveTrack(TrackType type, int index)
qreal playbackRate() const
void stop(bool immediate=false)
int trackCount(TrackType)
STDMETHODIMP Invoke(IMFAsyncResult *pResult) override
void load(const QUrl &media, QIODevice *stream)
void changeStatus(QMediaPlayer::MediaStatus newStatus)
QMediaTimeRange availablePlaybackRanges()
void setMuted(bool muted)
int activeTrack(TrackType type)
QMediaMetaData trackMetaData(TrackType type, int trackNumber)
void updateOutputRouting()
void setSink(QVideoSink *surface)
HRESULT BindOutputNode(IMFTopologyNode *pNode)
HRESULT BindOutputNodes(IMFTopology *pTopology)
Combined button and popup list for selecting options.