diff options
author | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2013-02-19 17:14:43 +0100 |
---|---|---|
committer | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2013-02-19 17:15:56 +0100 |
commit | d396262bc9771cacbc71e79215768984fa1974ff (patch) | |
tree | e029399524feabed6ebe0ee1e010af9f5e58932d /src/plugins/wmf/player/mfplayersession.cpp | |
parent | 0ce2cb3ebe6289e3db14438560fa186d9e5186b8 (diff) | |
parent | 2db2cde0d8433ad1aed59412717c4cddf496ba66 (diff) |
Merge remote-tracking branch 'origin/stable' into dev
Conflicts:
src/multimedia/doc/qtmultimedia.qdocconf
src/plugins/blackberry/mediaplayer/bbmetadata.cpp
src/plugins/blackberry/mediaplayer/bbmetadata.h
tests/auto/unit/qpaintervideosurface/tst_qpaintervideosurface.cpp
Change-Id: I447c297ea15a94d1d2feb0fb5f9edac8c5d4505a
Diffstat (limited to 'src/plugins/wmf/player/mfplayersession.cpp')
-rw-r--r-- | src/plugins/wmf/player/mfplayersession.cpp | 400 |
1 files changed, 319 insertions, 81 deletions
diff --git a/src/plugins/wmf/player/mfplayersession.cpp b/src/plugins/wmf/player/mfplayersession.cpp index d9ff0e7cb..c52498f63 100644 --- a/src/plugins/wmf/player/mfplayersession.cpp +++ b/src/plugins/wmf/player/mfplayersession.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Mobility Components. @@ -65,6 +65,7 @@ #include "sourceresolver.h" #include "samplegrabber.h" #include "mftvideo.h" +#include <wmcodecdsp.h> //#define DEBUG_MEDIAFOUNDATION //#define TEST_STREAMING @@ -436,7 +437,6 @@ MFPlayerSession::MFPlayerSession(MFPlayerService *playerService) m_request.rate = 1.0f; m_audioSampleGrabber = new AudioSampleGrabberCallback; - m_videoProbeMFT = new MFTransform; } void MFPlayerSession::close() @@ -472,6 +472,18 @@ void MFPlayerSession::close() m_sourceResolver->Release(); m_sourceResolver = 0; } + if (m_videoProbeMFT) { + m_videoProbeMFT->Release(); + m_videoProbeMFT = 0; + } + + if (m_playerService->videoRendererControl()) { + m_playerService->videoRendererControl()->releaseActivate(); +#ifndef Q_WS_SIMULATOR + } else if (m_playerService->videoWindowControl()) { + m_playerService->videoWindowControl()->releaseActivate(); +#endif + } if (m_session) m_session->Release(); @@ -493,18 +505,26 @@ void MFPlayerSession::removeProbe(MFAudioProbeControl *probe) void MFPlayerSession::addProbe(MFVideoProbeControl* probe) { - m_videoProbeMFT->addProbe(probe); + if (m_videoProbes.contains(probe)) + return; + + m_videoProbes.append(probe); + + if (m_videoProbeMFT) + m_videoProbeMFT->addProbe(probe); } void MFPlayerSession::removeProbe(MFVideoProbeControl* probe) { - m_videoProbeMFT->removeProbe(probe); + m_videoProbes.removeOne(probe); + + if (m_videoProbeMFT) + m_videoProbeMFT->removeProbe(probe); } MFPlayerSession::~MFPlayerSession() { m_audioSampleGrabber->Release(); - m_videoProbeMFT->Release(); } @@ -985,76 +1005,105 @@ IMFTopology *MFPlayerSession::insertMFT(IMFTopology *topology, TOPOID outputNode if (FAILED(MFCreateTopoLoader(&topoLoader))) break; - if (FAILED(topoLoader->Load(topology, &resolvedTopology, NULL))) - break; + if (FAILED(topoLoader->Load(topology, &resolvedTopology, NULL))) { + // Topology could not be resolved, adding ourselves a color converter + // to the topology might solve the problem + insertColorConverter(topology, outputNodeId); + if (FAILED(topoLoader->Load(topology, &resolvedTopology, NULL))) + break; + } -// FIXME!! VideoProbe disabled in Qt 5.0 because it is unstable. -// Commented out the following code to skip inserting the transform node -// getting the video frames. + if (insertResizer(resolvedTopology)) + isNewTopology = true; // Get all output nodes and search for video output node. -// if (FAILED(resolvedTopology->GetOutputNodeCollection(&outputNodes))) -// break; + if (FAILED(resolvedTopology->GetOutputNodeCollection(&outputNodes))) + break; -// DWORD elementCount = 0; -// if (FAILED(outputNodes->GetElementCount(&elementCount))) -// break; + DWORD elementCount = 0; + if (FAILED(outputNodes->GetElementCount(&elementCount))) + break; -// for (DWORD n = 0; n < elementCount; n++) { -// IUnknown *element = 0; -// IMFTopologyNode *node = 0; -// IMFTopologyNode *inputNode = 0; -// IMFTopologyNode *mftNode = 0; + for (DWORD n = 0; n < elementCount; n++) { + IUnknown *element = 0; + IMFTopologyNode *node = 0; + IUnknown *outputObject = 0; + IMFMediaTypeHandler *videoSink = 0; + IMFTopologyNode *inputNode = 0; + IMFTopologyNode *mftNode = 0; + bool mftAdded = false; -// do { -// if (FAILED(outputNodes->GetElement(n, &element))) -// break; + do { + if (FAILED(outputNodes->GetElement(n, &element))) + break; -// if (FAILED(element->QueryInterface(IID_IMFTopologyNode, (void**)&node))) -// break; + if (FAILED(element->QueryInterface(IID_IMFTopologyNode, (void**)&node))) + break; -// TOPOID id; -// if (FAILED(node->GetTopoNodeID(&id))) -// break; + TOPOID id; + if (FAILED(node->GetTopoNodeID(&id))) + break; -// if (id != outputNodeId) -// break; + if (id != outputNodeId) + break; -// // Insert MFT between the output node and the node connected to it. -// DWORD outputIndex = 0; -// if (FAILED(node->GetInput(0, &inputNode, &outputIndex))) -// break; + // Use output supported media types for the MFT + if (FAILED(node->GetObject(&outputObject))) + break; -// if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mftNode))) -// break; + if (FAILED(outputObject->QueryInterface(IID_IMFMediaTypeHandler, (void**)&videoSink))) + break; -// if (FAILED(mftNode->SetObject(m_videoProbeMFT))) -// break; + DWORD mtCount; + if (FAILED(videoSink->GetMediaTypeCount(&mtCount))) + break; -// if (FAILED(resolvedTopology->AddNode(mftNode))) -// break; + for (DWORD i = 0; i < mtCount; ++i) { + IMFMediaType *type = 0; + if (SUCCEEDED(videoSink->GetMediaTypeByIndex(i, &type))) + m_videoProbeMFT->addSupportedMediaType(type); + } + + // Insert MFT between the output node and the node connected to it. + DWORD outputIndex = 0; + if (FAILED(node->GetInput(0, &inputNode, &outputIndex))) + break; + + if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mftNode))) + break; + + if (FAILED(mftNode->SetObject(m_videoProbeMFT))) + break; -// if (FAILED(inputNode->ConnectOutput(0, mftNode, 0))) -// break; + if (FAILED(resolvedTopology->AddNode(mftNode))) + break; -// if (FAILED(mftNode->ConnectOutput(0, node, 0))) -// break; + if (FAILED(inputNode->ConnectOutput(0, mftNode, 0))) + break; -// isNewTopology = true; -// } while (false); + if (FAILED(mftNode->ConnectOutput(0, node, 0))) + break; -// if (mftNode) -// mftNode->Release(); -// if (inputNode) -// inputNode->Release(); -// if (node) -// node->Release(); -// if (element) -// element->Release(); + mftAdded = true; + isNewTopology = true; + } while (false); -// if (isNewTopology) -// break; -// } + if (mftNode) + mftNode->Release(); + if (inputNode) + inputNode->Release(); + if (node) + node->Release(); + if (element) + element->Release(); + if (videoSink) + videoSink->Release(); + if (outputObject) + outputObject->Release(); + + if (mftAdded) + break; + } } while (false); if (outputNodes) @@ -1074,6 +1123,177 @@ IMFTopology *MFPlayerSession::insertMFT(IMFTopology *topology, TOPOID outputNode return topology; } +// This method checks if the topology contains a color converter transform (CColorConvertDMO), +// if it does it inserts a resizer transform (CResizerDMO) to handle dynamic frame size change +// of the video stream. +// Returns true if it inserted a resizer +bool MFPlayerSession::insertResizer(IMFTopology *topology) +{ + bool inserted = false; + WORD elementCount = 0; + IMFTopologyNode *node = 0; + IUnknown *object = 0; + IWMColorConvProps *colorConv = 0; + IMFTransform *resizer = 0; + IMFTopologyNode *resizerNode = 0; + IMFTopologyNode *inputNode = 0; + + HRESULT hr = topology->GetNodeCount(&elementCount); + if (FAILED(hr)) + return false; + + for (WORD i = 0; i < elementCount; ++i) { + if (node) { + node->Release(); + node = 0; + } + if (object) { + object->Release(); + object = 0; + } + + if (FAILED(topology->GetNode(i, &node))) + break; + + MF_TOPOLOGY_TYPE nodeType; + if (FAILED(node->GetNodeType(&nodeType))) + break; + + if (nodeType != MF_TOPOLOGY_TRANSFORM_NODE) + continue; + + if (FAILED(node->GetObject(&object))) + break; + + if (FAILED(object->QueryInterface(&colorConv))) + continue; + + if (FAILED(CoCreateInstance(CLSID_CResizerDMO, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform, (void**)&resizer))) + break; + + if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &resizerNode))) + break; + + if (FAILED(resizerNode->SetObject(resizer))) + break; + + if (FAILED(topology->AddNode(resizerNode))) + break; + + DWORD outputIndex = 0; + if (FAILED(node->GetInput(0, &inputNode, &outputIndex))) { + topology->RemoveNode(resizerNode); + break; + } + + if (FAILED(inputNode->ConnectOutput(0, resizerNode, 0))) { + topology->RemoveNode(resizerNode); + break; + } + + if (FAILED(resizerNode->ConnectOutput(0, node, 0))) { + inputNode->ConnectOutput(0, node, 0); + topology->RemoveNode(resizerNode); + break; + } + + inserted = true; + break; + } + + if (node) + node->Release(); + if (object) + object->Release(); + if (colorConv) + colorConv->Release(); + if (resizer) + resizer->Release(); + if (resizerNode) + resizerNode->Release(); + if (inputNode) + inputNode->Release(); + + return inserted; +} + +// This method inserts a color converter (CColorConvertDMO) in the topology, +// typically to convert to RGB format. +// Usually this converter is automatically inserted when the topology is resolved but +// for some reason it fails to do so in some cases, we then do it ourselves. +void MFPlayerSession::insertColorConverter(IMFTopology *topology, TOPOID outputNodeId) +{ + IMFCollection *outputNodes = 0; + + if (FAILED(topology->GetOutputNodeCollection(&outputNodes))) + return; + + DWORD elementCount = 0; + if (FAILED(outputNodes->GetElementCount(&elementCount))) + goto done; + + for (DWORD n = 0; n < elementCount; n++) { + IUnknown *element = 0; + IMFTopologyNode *node = 0; + IMFTopologyNode *inputNode = 0; + IMFTopologyNode *mftNode = 0; + IMFTransform *converter = 0; + + do { + if (FAILED(outputNodes->GetElement(n, &element))) + break; + + if (FAILED(element->QueryInterface(IID_IMFTopologyNode, (void**)&node))) + break; + + TOPOID id; + if (FAILED(node->GetTopoNodeID(&id))) + break; + + if (id != outputNodeId) + break; + + DWORD outputIndex = 0; + if (FAILED(node->GetInput(0, &inputNode, &outputIndex))) + break; + + if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mftNode))) + break; + + if (FAILED(CoCreateInstance(CLSID_CColorConvertDMO, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform, (void**)&converter))) + break; + + if (FAILED(mftNode->SetObject(converter))) + break; + + if (FAILED(topology->AddNode(mftNode))) + break; + + if (FAILED(inputNode->ConnectOutput(0, mftNode, 0))) + break; + + if (FAILED(mftNode->ConnectOutput(0, node, 0))) + break; + + } while (false); + + if (mftNode) + mftNode->Release(); + if (inputNode) + inputNode->Release(); + if (node) + node->Release(); + if (element) + element->Release(); + if (converter) + converter->Release(); + } + +done: + if (outputNodes) + outputNodes->Release(); +} + void MFPlayerSession::stop(bool immediate) { #ifdef DEBUG_MEDIAFOUNDATION @@ -1181,6 +1401,10 @@ void MFPlayerSession::createSession() QObject::connect(m_sourceResolver, SIGNAL(mediaSourceReady()), this, SLOT(handleMediaSourceReady())); QObject::connect(m_sourceResolver, SIGNAL(error(long)), this, SLOT(handleSourceError(long))); + m_videoProbeMFT = new MFTransform; + for (int i = 0; i < m_videoProbes.size(); ++i) + m_videoProbeMFT->addProbe(m_videoProbes.at(i)); + Q_ASSERT(m_session == NULL); HRESULT hr = MFCreateMediaSession(NULL, &m_session); if (FAILED(hr)) { @@ -1334,29 +1558,41 @@ void MFPlayerSession::commitRateChange(qreal rate, BOOL isThin) // Negative <-> zero: Stopped // Postive <-> zero: Paused or stopped if ((rate > 0 && m_state.rate <= 0) || (rate < 0 && m_state.rate >= 0)) { - // Transition to stopped. if (cmdNow == CmdStart) { // Get the current clock position. This will be the restart time. m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime); Q_ASSERT(hnsSystemTime != 0); - // Stop and set the rate - stop(); + // We need to stop only when dealing with negative rates + if (rate >= 0 && m_state.rate >= 0) + pause(); + else + stop(); - //Cache Request: Restart from stop. - m_request.setCommand(CmdSeekResume); + // If we deal with negative rates, we stopped the session and consequently + // reset the position to zero. We then need to resume to the current position. + m_request.setCommand(rate < 0 || m_state.rate < 0 ? CmdSeekResume : CmdStart); m_request.start = hnsClockTime / 10000; } else if (cmdNow == CmdPause) { - // The current state is paused. - // For this rate change, the session must be stopped. However, the - // session cannot transition back from stopped to paused. - // Therefore, this rate transition is not supported while paused. if (rate < 0 || m_state.rate < 0) { + // The current state is paused. + // For this rate change, the session must be stopped. However, the + // session cannot transition back from stopped to paused. + // Therefore, this rate transition is not supported while paused. qWarning() << "Unable to change rate from positive to negative or vice versa in paused state"; return; } + + // This happens when resuming playback after scrubbing in pause mode. + // This transition requires the session to be paused. Even though our + // internal state is set to paused, the session might not be so we need + // to enforce it + if (rate > 0 && m_state.rate == 0) { + m_state.setCommand(CmdNone); + pause(); + } } - } else if (rate == 0 && m_state.rate != 0) { + } else if (rate == 0 && m_state.rate > 0) { if (cmdNow != CmdPause) { // Transition to paused. // This transisition requires the paused state. @@ -1366,6 +1602,15 @@ void MFPlayerSession::commitRateChange(qreal rate, BOOL isThin) // Request: Switch back to current state. m_request.setCommand(cmdNow); } + } else if (rate == 0 && m_state.rate < 0) { + // Changing rate from negative to zero requires to stop the session + m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime); + + stop(); + + // Resumte to the current position (stop() will reset the position to 0) + m_request.setCommand(CmdSeekResume); + m_request.start = hnsClockTime / 10000; } // Set the rate. @@ -1569,7 +1814,9 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent) break; case MESourceUnknown: changeStatus(QMediaPlayer::InvalidMedia); + break; case MEError: + changeStatus(QMediaPlayer::UnknownMediaStatus); qWarning() << "handleSessionEvent: serious error = " << hrStatus; emit error(QMediaPlayer::ResourceError, tr("Media session serious error."), true); break; @@ -1590,8 +1837,7 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent) updatePendingCommands(CmdStart); break; case MESessionStarted: - if (!m_scrubbing) - updatePendingCommands(CmdStart); + updatePendingCommands(CmdStart); #ifndef Q_WS_SIMULATOR // playback started, we can now set again the procAmpValues if they have been // changed previously (these are lost when loading a new media) @@ -1605,10 +1851,10 @@ void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent) m_varStart.vt = VT_I8; m_varStart.hVal.QuadPart = 0; - //only change to loadedMedia when not loading a new media source - if (m_status != QMediaPlayer::LoadingMedia) { + // Reset to Loaded status unless we are loading a new media + // or if the media is buffered (to avoid restarting the video surface) + if (m_status != QMediaPlayer::LoadingMedia && m_status != QMediaPlayer::BufferedMedia) changeStatus(QMediaPlayer::LoadedMedia); - } } updatePendingCommands(CmdStop); break; @@ -1751,15 +1997,7 @@ void MFPlayerSession::updatePendingCommands(Command command) // The current pending command has completed. if (m_pendingState == SeekPending && m_state.prevCmd == CmdPause) { m_pendingState = NoPending; - //if we have pending seek request, - //then we just keep current state to paused and continue the seek request, - //otherwise we will restore to pause state - if (m_request.command == CmdSeek) { - m_state.setCommand(CmdPause); - } else { - pause(); - return; - } + m_state.setCommand(CmdPause); } m_pendingState = NoPending; |