4262 lines
107 KiB
C++
4262 lines
107 KiB
C++
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "tededit.h"
|
|
#include "tedvis.h"
|
|
#include "topoviewerwindow.h"
|
|
#include "tedmemo.h"
|
|
#include "dmoinfo.h"
|
|
#include "propertyview.h"
|
|
|
|
#include "mediaobj.h"
|
|
#include <dmo.h>
|
|
#include <evr.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include "Logger.h"
|
|
|
|
CLSID CLSID_AudioRenderActivate = { /* d23e6476-b104-4707-81cb-e1ca19a07016 */
|
|
0xd23e6476,
|
|
0xb104,
|
|
0x4707,
|
|
{ 0x81, 0xcb, 0xe1, 0xca, 0x19, 0xa0, 0x70, 0x16 }
|
|
};
|
|
|
|
CLSID CLSID_VideoRenderActivate = { /* d23e6477-b104-4707-81cb-e1ca19a07016 */
|
|
0xd23e6477,
|
|
0xb104,
|
|
0x4707,
|
|
{ 0x81, 0xcb, 0xe1, 0xca, 0x19, 0xa0, 0x70, 0x16 }
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
class CMoveComponentHandler : public CCommandHandler
|
|
{
|
|
public:
|
|
CMoveComponentHandler(CVisualTree* pTree, ITedPropertyController* pController);
|
|
|
|
BOOL OnLButtonDown(CVisualObject* pObj, CVisualPoint& pt);
|
|
BOOL OnLButtonUp(CVisualObject* pObj, CVisualPoint& pt);
|
|
BOOL OnMouseMove(CVisualObject* pObj, CVisualPoint& pt);
|
|
BOOL OnLButtonDoubleClick(CVisualObject* pObj, CVisualPoint& pt);
|
|
BOOL OnFocus(CVisualObject* pObj);
|
|
|
|
void SetEditable(BOOL fEditable);
|
|
|
|
private:
|
|
HRESULT ShowOTA(IMFTopologyNode* pNode);
|
|
|
|
BOOL m_fCapture;
|
|
BOOL m_fEditable;
|
|
|
|
// offset from the left/top of component to mouse
|
|
CVisualPoint m_Offset;
|
|
|
|
CVisualTree * m_pTree;
|
|
CComPtr<ITedPropertyController> m_spController;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
class CConnectPinHandler : public CCommandHandler
|
|
{
|
|
public:
|
|
CConnectPinHandler(CVisualTree* pTree, CTedTopologyEditor* pEditor, ITedPropertyController* pController);
|
|
|
|
BOOL OnLButtonDown(CVisualObject* pObj, CVisualPoint& pt);
|
|
BOOL OnLButtonUp(CVisualObject* pObj, CVisualPoint& pt);
|
|
BOOL OnMouseMove(CVisualObject* pObj, CVisualPoint& pt);
|
|
BOOL OnLButtonDoubleClick(CVisualObject* pObj, CVisualPoint& pt);
|
|
BOOL OnFocus(CVisualObject* pObj);
|
|
|
|
void SetEditable(BOOL fEditable);
|
|
|
|
protected:
|
|
void RemoveOldConnectors(CVisualPin* pPin, CVisualPin* pOtherPin);
|
|
void ShowMediaTypeProperties(IMFTopologyNode* pOutputNode, DWORD dwOutputPinIndex, IMFTopologyNode* pInputNode, DWORD dwInputPinIndex);
|
|
CComPtr<IMFMediaType> GetNodeMFMediaType(IMFTopologyNode* pNode, DWORD dwPinIndex, bool fOutput);
|
|
bool IsValidConnection(CVisualObject* pSourcePin, CVisualObject* pTargetPin);
|
|
|
|
private:
|
|
BOOL m_fCapture;
|
|
BOOL m_fEditable;
|
|
|
|
CTedTopologyEditor * m_pEditor;
|
|
CVisualTree * m_pTree;
|
|
CComPtr<ITedPropertyController> m_spController;
|
|
|
|
CVisualConnector * m_pNew;
|
|
CVisualPin* m_pLastOverPin;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
CMoveComponentHandler::CMoveComponentHandler(CVisualTree* pTree, ITedPropertyController* pController)
|
|
: m_fCapture(FALSE)
|
|
, m_fEditable(TRUE)
|
|
, m_pTree(pTree)
|
|
, m_spController(pController)
|
|
{
|
|
}
|
|
|
|
BOOL CMoveComponentHandler::OnLButtonDown(CVisualObject* pObj, CVisualPoint& pt)
|
|
{
|
|
m_Offset = pt;
|
|
|
|
if(pObj->GetContainer())
|
|
{
|
|
m_Offset.Add(-pObj->GetContainer()->Rect().x(), -pObj->GetContainer()->Rect().y());
|
|
}
|
|
else
|
|
{
|
|
m_Offset.Add(-pObj->Rect().x(), -pObj->Rect().y());
|
|
}
|
|
|
|
m_fCapture = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CMoveComponentHandler::OnLButtonUp(CVisualObject* pObj, CVisualPoint& pt)
|
|
{
|
|
m_fCapture = FALSE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CMoveComponentHandler::OnMouseMove(CVisualObject* pObj, CVisualPoint& pt)
|
|
{
|
|
if(!m_fCapture)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if(m_fEditable)
|
|
{
|
|
if(pObj->GetContainer())
|
|
{
|
|
pObj->GetContainer()->Move(pt.x() - m_Offset.x(), pt.y() - m_Offset.y());
|
|
}
|
|
else
|
|
{
|
|
pObj->Move(pt.x() - m_Offset.x(), pt.y() - m_Offset.y());
|
|
}
|
|
|
|
m_pTree->RouteAllConnectors();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CMoveComponentHandler::OnLButtonDoubleClick(CVisualObject * pObj, CVisualPoint & pt)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CMoveComponentHandler::OnFocus(CVisualObject* pObj)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CTedTopologyNode* pNode = (CTedTopologyNode*) pObj->GetData();
|
|
|
|
if(pNode->GetMFNodeCount() <= pObj->GetIndex()) return FALSE;
|
|
|
|
if(m_spController.p) m_spController->ClearProperties();
|
|
|
|
IMFTopologyNode* pMFNode = pNode->GetMFNode(pObj->GetIndex());
|
|
|
|
MF_TOPOLOGY_TYPE nodeType;
|
|
pMFNode->GetNodeType(&nodeType);
|
|
|
|
if(m_spController.p)
|
|
{
|
|
CNodePropertyInfo* pNodePropertyInfo = new CNodePropertyInfo(pMFNode);
|
|
m_spController->AddPropertyInfo(pNodePropertyInfo);
|
|
|
|
// For source nodes, we also add presentation descriptor attributes
|
|
if(nodeType == MF_TOPOLOGY_SOURCESTREAM_NODE)
|
|
{
|
|
CComPtr<IMFPresentationDescriptor> spPD;
|
|
hr = pMFNode->GetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, IID_IMFPresentationDescriptor, (void**) &spPD);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
CAttributesPropertyInfo* pPDInfo = new CAttributesPropertyInfo(spPD.p, LoadAtlString(IDS_PD_ATTRIBS), TED_ATTRIBUTE_CATEGORY_PRESENTATIONDESCRIPTOR);
|
|
m_spController->AddPropertyInfo(pPDInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
// For transforms and sinks, we also add output trust authority information
|
|
if(nodeType == MF_TOPOLOGY_TRANSFORM_NODE || nodeType == MF_TOPOLOGY_OUTPUT_NODE)
|
|
{
|
|
IFC( ShowOTA(pMFNode) );
|
|
}
|
|
Cleanup:
|
|
return TRUE;
|
|
}
|
|
|
|
void CMoveComponentHandler::SetEditable(BOOL fEditable)
|
|
{
|
|
m_fEditable = fEditable;
|
|
}
|
|
|
|
HRESULT CMoveComponentHandler::ShowOTA(IMFTopologyNode* pNode)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CComPtr<IUnknown> spUnkObj;
|
|
CComPtr<IMFTrustedOutput> spTrustedOutput;
|
|
DWORD dwOTACount;
|
|
|
|
IFC( pNode->GetObject(&spUnkObj) );
|
|
IFC( spUnkObj->QueryInterface(IID_IMFTrustedOutput, (void**) &spTrustedOutput) );
|
|
IFC( spTrustedOutput->GetOutputTrustAuthorityCount(&dwOTACount) );
|
|
|
|
CComPtr<IMFOutputTrustAuthority>* arrOTA = new CComPtr<IMFOutputTrustAuthority>[dwOTACount];
|
|
CHECK_ALLOC(arrOTA);
|
|
|
|
for(DWORD i =0; i < dwOTACount; i++)
|
|
{
|
|
CComPtr<IMFOutputTrustAuthority> spOTA;
|
|
|
|
spTrustedOutput->GetOutputTrustAuthorityByIndex(i, &arrOTA[i]);
|
|
}
|
|
|
|
if(m_spController.p)
|
|
{
|
|
COTAPropertyInfo* pOTAPropertyInfo = new COTAPropertyInfo(arrOTA, dwOTACount);
|
|
if(NULL == pOTAPropertyInfo)
|
|
{
|
|
delete[] arrOTA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
m_spController->AddPropertyInfo(pOTAPropertyInfo);
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
CConnectPinHandler::CConnectPinHandler(CVisualTree * pTree, CTedTopologyEditor* pEditor, ITedPropertyController* pController)
|
|
: m_pTree(pTree)
|
|
, m_fEditable(TRUE)
|
|
, m_pEditor(pEditor)
|
|
, m_spController(pController)
|
|
, m_pNew(NULL)
|
|
, m_pLastOverPin(NULL)
|
|
{
|
|
assert(m_pEditor != NULL);
|
|
assert(m_pTree != NULL);
|
|
}
|
|
|
|
BOOL CConnectPinHandler::OnLButtonDown(CVisualObject * pObj, CVisualPoint & pt)
|
|
{
|
|
if(m_fEditable)
|
|
{
|
|
// Begin a connection
|
|
m_fCapture = TRUE;
|
|
|
|
CVisualPin * pPin = (CVisualPin*)pObj;
|
|
|
|
m_pNew = new CVisualConnector;
|
|
|
|
m_pTree->AddVisual(m_pNew);
|
|
|
|
m_pNew->Left() = pPin->GetConnectorPoint();
|
|
m_pNew->Right() = pPin->GetConnectorPoint();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CConnectPinHandler::OnLButtonUp(CVisualObject* pObj, CVisualPoint& pt)
|
|
{
|
|
CVisualObject * pHitObj;
|
|
|
|
assert(pObj != NULL);
|
|
|
|
if(!m_fCapture || !m_fEditable)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CVisualPin * pPin = (CVisualPin*)pObj;
|
|
|
|
if(!m_pTree->HitTest(pt, &pHitObj))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if(!IsValidConnection(pObj, pHitObj))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
CVisualPin * pOtherPin = (CVisualPin*)pHitObj;
|
|
|
|
CVisualObject::CONNECTION_TYPE connType = pOtherPin->GetConnectionType();
|
|
if(CVisualObject::INPUT == connType)
|
|
{
|
|
m_pNew->Right() = pOtherPin->GetConnectorPoint();
|
|
}
|
|
else
|
|
{
|
|
m_pNew->Left() = pOtherPin->GetConnectorPoint();
|
|
|
|
// pPin needs to be the input pin
|
|
CVisualPin* temp = pPin;
|
|
pPin = pOtherPin;
|
|
pOtherPin = temp;
|
|
}
|
|
|
|
assert(pPin != pOtherPin);
|
|
|
|
CTedTopologyNode* outputterNode = (CTedTopologyNode*) (pPin->GetData());
|
|
CTedTopologyNode* acceptorNode = (CTedTopologyNode*) (pOtherPin->GetData());
|
|
|
|
m_pEditor->FullConnectNodes(outputterNode, pPin->GetPinId(), acceptorNode, pOtherPin->GetPinId());
|
|
|
|
Cleanup:
|
|
|
|
if(NULL != m_pNew)
|
|
{
|
|
m_pTree->RemoveVisual(m_pNew);
|
|
m_pNew = NULL;
|
|
}
|
|
|
|
if(m_pLastOverPin)
|
|
{
|
|
m_pLastOverPin->Highlight(false);
|
|
m_pLastOverPin = NULL;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CConnectPinHandler::OnMouseMove(CVisualObject * pObj, CVisualPoint & pt)
|
|
{
|
|
if(!m_fCapture || !m_fEditable)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
assert(m_pNew);
|
|
|
|
CVisualPin * pPin = (CVisualPin*)pObj;
|
|
|
|
if(pPin->GetConnectionType() == CVisualObject::INPUT)
|
|
{
|
|
m_pNew->Left() = pt;
|
|
}
|
|
else
|
|
{
|
|
m_pNew->Right() = pt;
|
|
}
|
|
|
|
CVisualObject* pOverObject;
|
|
|
|
if(m_pLastOverPin)
|
|
{
|
|
m_pLastOverPin->Highlight(false);
|
|
m_pLastOverPin = NULL;
|
|
}
|
|
|
|
if(m_pTree->HitTest(pt, &pOverObject))
|
|
{
|
|
if(pOverObject->GetConnectionType() != CVisualObject::NONE && IsValidConnection(pPin, (CVisualPin*) pOverObject))
|
|
{
|
|
CVisualPin* pOverPin = (CVisualPin*) pOverObject;
|
|
|
|
pOverPin->Highlight(true);
|
|
|
|
m_pLastOverPin = pOverPin;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CConnectPinHandler::OnLButtonDoubleClick(CVisualObject* pObj, CVisualPoint& pt)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CConnectPinHandler::OnFocus(CVisualObject* pObj)
|
|
{
|
|
CVisualObject::CONNECTION_TYPE connType;
|
|
connType = pObj->GetConnectionType();
|
|
|
|
if(m_spController.p) m_spController->ClearProperties();
|
|
|
|
// If we have a connection, we want to display the media types for it
|
|
if(connType != CVisualObject::NONE)
|
|
{
|
|
CVisualPin* pPin = (CVisualPin*) pObj;
|
|
|
|
CTedTopologyNode* pNode = (CTedTopologyNode*) (pPin->GetData());
|
|
|
|
// For source streams, we also want to display stream descriptor attributes
|
|
if(pNode->GetType() == CTedTopologyNode::TED_SOURCE_NODE && m_spController.p)
|
|
{
|
|
IMFTopologyNode* pMFNode = pNode->GetMFNode(pPin->GetPinId());
|
|
|
|
CComPtr<IMFStreamDescriptor> spSD;
|
|
pMFNode->GetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, IID_IMFStreamDescriptor, (void**) &spSD);
|
|
|
|
CComPtr<IMFAttributes> spAttr = spSD.p;
|
|
|
|
CAttributesPropertyInfo* pAttrInfo = new CAttributesPropertyInfo(spAttr, LoadAtlString(IDS_SD_ATTRIBS), TED_ATTRIBUTE_CATEGORY_STREAMDESCRIPTOR);
|
|
if(m_spController.p) m_spController->AddPropertyInfo(pAttrInfo);
|
|
}
|
|
|
|
if(pPin->GetConnector() == NULL)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
CTedTopologyConnection* pConn = NULL;
|
|
CTedTopologyNode* pOtherNode = NULL;
|
|
|
|
if(connType == CVisualObject::INPUT)
|
|
{
|
|
pConn = m_pEditor->FindUpstreamConnection(pNode->GetID(), pPin->GetPinId());
|
|
assert(pConn);
|
|
pOtherNode = m_pEditor->FindNode(pConn->GetOutputNodeID());
|
|
assert(pOtherNode);
|
|
|
|
CTedTopologyPin outputPin;
|
|
CTedTopologyPin inputPin;
|
|
|
|
pNode->GetPin(pConn->GetInputPinID(), inputPin, true);
|
|
pOtherNode->GetPin(pConn->GetOutputPinID(), outputPin, false);
|
|
|
|
IMFTopologyNode* pMFInputNode = inputPin.PNode();
|
|
IMFTopologyNode* pMFOutputNode = outputPin.PNode();
|
|
|
|
ShowMediaTypeProperties(pMFOutputNode, outputPin.Index(), pMFInputNode, inputPin.Index());
|
|
}
|
|
else
|
|
{
|
|
pConn = m_pEditor->FindDownstreamConnection(pNode->GetID(), pPin->GetPinId());
|
|
assert(pConn);
|
|
pOtherNode = m_pEditor->FindNode(pConn->GetInputNodeID());
|
|
assert(pOtherNode);
|
|
|
|
CTedTopologyPin outputPin;
|
|
CTedTopologyPin inputPin;
|
|
|
|
pOtherNode->GetPin(pConn->GetInputPinID(), inputPin, true);
|
|
pNode->GetPin(pConn->GetOutputPinID(), outputPin, false);
|
|
|
|
IMFTopologyNode* pMFInputNode = inputPin.PNode();
|
|
IMFTopologyNode* pMFOutputNode = outputPin.PNode();
|
|
|
|
ShowMediaTypeProperties(pMFOutputNode, outputPin.Index(), pMFInputNode, inputPin.Index());
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void CConnectPinHandler::ShowMediaTypeProperties(IMFTopologyNode* pOutputNode, DWORD dwOutputPinIndex, IMFTopologyNode* pInputNode, DWORD dwInputPinIndex)
|
|
{
|
|
if(m_spController)
|
|
{
|
|
HRESULT hr;
|
|
CComPtr<IMFMediaType> spOutputType = GetNodeMFMediaType(pOutputNode, dwOutputPinIndex, true);
|
|
CComPtr<IMFMediaType> spInputType = GetNodeMFMediaType(pInputNode, dwInputPinIndex, false);
|
|
|
|
if(spOutputType.p)
|
|
{
|
|
CComPtr<IMFAttributes> spOutAttr;
|
|
hr = spOutputType->QueryInterface(IID_IMFAttributes, (void**) &spOutAttr);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
CAttributesPropertyInfo* pOutInfo = new CAttributesPropertyInfo(spOutAttr, LoadAtlString(IDS_MT_UPSTREAM), TED_ATTRIBUTE_CATEGORY_MEDIATYPE);
|
|
if(pOutInfo) m_spController->AddPropertyInfo(pOutInfo);
|
|
}
|
|
}
|
|
|
|
if(spInputType.p)
|
|
{
|
|
CComPtr<IMFAttributes> spInAttr;
|
|
hr = spInputType->QueryInterface(IID_IMFAttributes, (void**) &spInAttr);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
CAttributesPropertyInfo* pInInfo = new CAttributesPropertyInfo(spInAttr, LoadAtlString(IDS_MT_DOWNSTREAM), TED_ATTRIBUTE_CATEGORY_MEDIATYPE);
|
|
if(pInInfo) m_spController->AddPropertyInfo(pInInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CComPtr<IMFMediaType> CConnectPinHandler::GetNodeMFMediaType(IMFTopologyNode* pNode, DWORD dwPinIndex, bool fOutput)
|
|
{
|
|
MF_TOPOLOGY_TYPE nodeType;
|
|
CComPtr<IMFMediaType> spType;
|
|
|
|
pNode->GetNodeType(&nodeType);
|
|
|
|
if(nodeType == MF_TOPOLOGY_SOURCESTREAM_NODE)
|
|
{
|
|
CComPtr<IMFStreamDescriptor> spSD;
|
|
CComPtr<IMFMediaTypeHandler> spMTH;
|
|
|
|
pNode->GetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, IID_IMFStreamDescriptor, (void**) &spSD);
|
|
spSD->GetMediaTypeHandler(&spMTH);
|
|
|
|
spMTH->GetCurrentMediaType(&spType);
|
|
}
|
|
else if(nodeType == MF_TOPOLOGY_TRANSFORM_NODE)
|
|
{
|
|
CComPtr<IUnknown> spTransformUnk;
|
|
CComPtr<IMFTransform> spTransform;
|
|
|
|
pNode->GetObject(&spTransformUnk);
|
|
spTransformUnk->QueryInterface(IID_IMFTransform, (void**) &spTransform);
|
|
|
|
if(fOutput)
|
|
{
|
|
spTransform->GetOutputCurrentType(dwPinIndex, &spType);
|
|
}
|
|
else
|
|
{
|
|
spTransform->GetInputCurrentType(dwPinIndex, &spType);
|
|
}
|
|
}
|
|
else if(nodeType == MF_TOPOLOGY_OUTPUT_NODE)
|
|
{
|
|
HRESULT hr;
|
|
CComPtr<IUnknown> spUnknown;
|
|
CComPtr<IMFStreamSink> spStreamSink;
|
|
|
|
pNode->GetObject(&spUnknown);
|
|
|
|
hr = spUnknown->QueryInterface(IID_IMFStreamSink, (void**) &spStreamSink);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
CComPtr<IMFMediaTypeHandler> spMTH;
|
|
|
|
spStreamSink->GetMediaTypeHandler(&spMTH);
|
|
|
|
spMTH->GetCurrentMediaType(&spType);
|
|
}
|
|
|
|
if(NULL == spType.p)
|
|
{
|
|
pNode->GetInputPrefType(0, &spType);
|
|
}
|
|
}
|
|
|
|
return spType;
|
|
}
|
|
|
|
bool CConnectPinHandler::IsValidConnection(CVisualObject* pSource, CVisualObject* pTarget)
|
|
{
|
|
CVisualObject::CONNECTION_TYPE connType = pSource->GetConnectionType();
|
|
CVisualObject::CONNECTION_TYPE targetConnType = pTarget->GetConnectionType();
|
|
|
|
if( CVisualObject::NONE == connType /* don't process objects that cannot be connected */
|
|
|| CVisualObject::NONE == targetConnType /* ditto */
|
|
||targetConnType == connType /* Don't connect two inputs or two outputs together */
|
|
|| pSource == pTarget /* Don't connect an object to itself */
|
|
)
|
|
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CConnectPinHandler::SetEditable(BOOL fEditable)
|
|
{
|
|
m_fEditable = fEditable;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
CTedTopologyPin::CTedTopologyPin()
|
|
: m_pNode(NULL)
|
|
, m_nIndex(0)
|
|
{
|
|
}
|
|
|
|
CTedTopologyPin::CTedTopologyPin(IMFTopologyNode * pNode, DWORD nIndex)
|
|
: m_pNode(pNode)
|
|
, m_nIndex(nIndex)
|
|
{
|
|
}
|
|
|
|
CTedTopologyPin::~CTedTopologyPin()
|
|
{
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
CTedTopologyConnection::CTedTopologyConnection(int nOutputNodeID, int nOutputPinID, int nInputNodeID, int nInputPinID)
|
|
: m_nOutputNodeID(nOutputNodeID), m_nOutputPinID(nOutputPinID), m_nInputNodeID(nInputNodeID), m_nInputPinID(nInputPinID)
|
|
{
|
|
assert(m_nOutputNodeID >= 0);
|
|
assert(m_nOutputPinID >= 0);
|
|
assert(m_nInputNodeID >= 0);
|
|
assert(m_nInputPinID >= 0);
|
|
}
|
|
|
|
CTedTopologyConnection::CTedTopologyConnection(CTedConnectionMemo* pMemo)
|
|
: m_nOutputNodeID(pMemo->m_nOutputNodeID), m_nOutputPinID(pMemo->m_nOutputPinID)
|
|
, m_nInputNodeID(pMemo->m_nInputNodeID), m_nInputPinID(pMemo->m_nInputPinID)
|
|
{
|
|
}
|
|
|
|
int CTedTopologyConnection::GetOutputNodeID() const
|
|
{
|
|
return m_nOutputNodeID;
|
|
}
|
|
|
|
int CTedTopologyConnection::GetInputNodeID() const
|
|
{
|
|
return m_nInputNodeID;
|
|
}
|
|
|
|
int CTedTopologyConnection::GetOutputPinID() const
|
|
{
|
|
return m_nOutputPinID;
|
|
}
|
|
|
|
int CTedTopologyConnection::GetInputPinID() const
|
|
{
|
|
return m_nInputPinID;
|
|
}
|
|
|
|
CTedConnectionMemo* CTedTopologyConnection::CreateMemo() const
|
|
{
|
|
return new CTedConnectionMemo(m_nOutputNodeID, m_nOutputPinID, m_nInputNodeID, m_nInputPinID);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CTedTopologyNode
|
|
|
|
// Initialization //
|
|
|
|
int CTedTopologyNode::ms_nNextID = 0;
|
|
|
|
CTedTopologyNode::CTedTopologyNode()
|
|
{
|
|
m_nID = ms_nNextID++;
|
|
}
|
|
|
|
CTedTopologyNode::~CTedTopologyNode()
|
|
{
|
|
}
|
|
|
|
HRESULT CTedTopologyNode::Init(const CAtlStringW& label, bool fAutoInserted)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
m_pVisual = new CVisualNode(label, fAutoInserted);
|
|
CHECK_ALLOC( m_pVisual );
|
|
|
|
m_pVisual->Move(20, 20);
|
|
m_pVisual->SetData((LONG_PTR) this);
|
|
|
|
m_strLabel = label;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTopologyNode::InitContainer(const CAtlStringW& label, bool fAutoinserted)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
m_pVisual = new CVisualContainer(label);
|
|
CHECK_ALLOC( m_pVisual );
|
|
|
|
m_pVisual->Move(20, 20);
|
|
m_pVisual->SetData((LONG_PTR) this);
|
|
|
|
m_strLabel = label;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
void CTedTopologyNode::Init(CTedNodeMemo* pMemo)
|
|
{
|
|
Init(pMemo->m_strLabel);
|
|
|
|
m_nID = pMemo->m_nID;
|
|
|
|
if(ms_nNextID <= m_nID)
|
|
{
|
|
ms_nNextID = m_nID + 1;
|
|
}
|
|
|
|
m_pVisual->Move(pMemo->m_x, pMemo->m_y);
|
|
}
|
|
|
|
void CTedTopologyNode::InitContainer(CTedNodeMemo* pMemo)
|
|
{
|
|
InitContainer(pMemo->m_strLabel);
|
|
|
|
m_nID = pMemo->m_nID;
|
|
|
|
if(ms_nNextID <= m_nID)
|
|
{
|
|
ms_nNextID = m_nID + 1;
|
|
}
|
|
|
|
m_pVisual->Move(pMemo->m_x, pMemo->m_y);
|
|
}
|
|
|
|
// Accessors //
|
|
|
|
HWND CTedTopologyNode::GetVideoWindow() const
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
CVisualComponent* CTedTopologyNode::GetVisual() const
|
|
{
|
|
return m_pVisual;
|
|
}
|
|
|
|
CVisualPin* CTedTopologyNode::GetVisualInputPin(int pinID)
|
|
{
|
|
return ((CVisualComponent*) m_pVisual)->GetInputPin(pinID);
|
|
}
|
|
|
|
CVisualPin* CTedTopologyNode::GetVisualOutputPin(int pinID)
|
|
{
|
|
return ((CVisualComponent*) m_pVisual)->GetOutputPin(pinID);
|
|
}
|
|
|
|
int CTedTopologyNode::GetID() const
|
|
{
|
|
return m_nID;
|
|
}
|
|
|
|
HRESULT CTedTopologyNode::GetNodeID(DWORD dwIndex, TOPOID& NodeID)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CComPtr<IMFTopologyNode> spNode = GetMFNode(dwIndex);
|
|
|
|
IFC( spNode->GetTopoNodeID(&NodeID) );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
CAtlStringW CTedTopologyNode::GetLabel() const
|
|
{
|
|
return m_strLabel;
|
|
}
|
|
|
|
bool CTedTopologyNode::IsOrphaned()
|
|
{
|
|
bool fHasInputConnections = false;
|
|
bool fHasOutputConnections = false;
|
|
|
|
for(DWORD i = 0; i < GetMFNodeCount(); i++)
|
|
{
|
|
IMFTopologyNode* pNode = GetMFNode(i);
|
|
|
|
DWORD cInputs = 0;
|
|
pNode->GetInputCount(&cInputs);
|
|
for(DWORD j = 0; j < cInputs; j++)
|
|
{
|
|
CComPtr<IMFTopologyNode> spUpNode;
|
|
DWORD dwUpIndex;
|
|
if(SUCCEEDED( pNode->GetInput(j, &spUpNode, &dwUpIndex) ))
|
|
{
|
|
fHasInputConnections = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!fHasInputConnections) break;
|
|
|
|
DWORD cOutputs = 0;
|
|
pNode->GetOutputCount(&cOutputs);
|
|
for(DWORD j = 0; j < cOutputs; j++)
|
|
{
|
|
CComPtr<IMFTopologyNode> spDownNode;
|
|
DWORD dwDownIndex;
|
|
if(SUCCEEDED( pNode->GetOutput(j, &spDownNode, &dwDownIndex) ))
|
|
{
|
|
fHasOutputConnections = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (!fHasInputConnections || !fHasOutputConnections);
|
|
}
|
|
|
|
// Mutators //
|
|
HRESULT CTedTopologyNode::CopyAttributes(IMFTopologyNode* pNode, DWORD dwIndex)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IMFTopologyNode* pTargetNode = GetMFNode(dwIndex);
|
|
CComPtr<IMFMediaType> spInputPrefType;
|
|
|
|
IFC( pNode->CopyAllItems(pTargetNode) );
|
|
|
|
m_fErrorNode = false;
|
|
|
|
if( SUCCEEDED(pNode->GetItem(MF_TOPONODE_ERRORCODE, NULL)) )
|
|
{
|
|
m_fErrorNode = true;
|
|
|
|
TOPOID tidNode;
|
|
IFC( pNode->GetTopoNodeID(&tidNode) );
|
|
|
|
for(DWORD j = 0; j < GetMFNodeCount(); ++j)
|
|
{
|
|
IMFTopologyNode* pTedNode = GetMFNode(j);
|
|
|
|
TOPOID tidTedNode;
|
|
IFC( pTedNode->GetTopoNodeID(&tidTedNode) );
|
|
|
|
if(tidNode == tidTedNode)
|
|
{
|
|
m_pVisual->FlagTopoLoadError(j, true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!m_fErrorNode)
|
|
{
|
|
for(DWORD i = 0; i < GetMFNodeCount(); i++)
|
|
{
|
|
m_pVisual->FlagTopoLoadError(i, false);
|
|
}
|
|
}
|
|
|
|
DWORD cInputs;
|
|
IFC( pNode->GetInputCount(&cInputs) );
|
|
for(DWORD i = 0; i < cInputs; i++)
|
|
{
|
|
CComPtr<IMFMediaType> spPrefType;
|
|
if(SUCCEEDED(pNode->GetInputPrefType(i, &spPrefType)))
|
|
{
|
|
pTargetNode->SetInputPrefType(i, spPrefType);
|
|
}
|
|
}
|
|
|
|
DWORD cOutputs;
|
|
IFC( pNode->GetOutputCount(&cOutputs) );
|
|
for(DWORD i = 0; i < cOutputs; i++)
|
|
{
|
|
CComPtr<IMFMediaType> spPrefType;
|
|
if(SUCCEEDED(pNode->GetOutputPrefType(i, &spPrefType)))
|
|
{
|
|
pTargetNode->SetOutputPrefType(i, spPrefType);
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
// Non-public Helpers //
|
|
void CTedTopologyNode::AddPin(bool bIsInput, const CAtlStringW& strLabel, int nID)
|
|
{
|
|
assert(m_pVisual->Type() == CVisualObject::NODE);
|
|
((CVisualNode*) m_pVisual)->AddPin(bIsInput, (LONG_PTR) const_cast<CTedTopologyNode*>(this), strLabel, nID);
|
|
}
|
|
|
|
HRESULT CTedTopologyNode::PostInitFromMemoCopyAttributes(CTedNodeMemo* pMemo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
for(DWORD i = 0; i < pMemo->GetNodeAttributeCount() && i < GetMFNodeCount(); i++)
|
|
{
|
|
IMFAttributes* pAttributes = pMemo->GetNodeAttributes(i);
|
|
|
|
UINT32 cItems;
|
|
IFC( pAttributes->GetCount(&cItems) );
|
|
|
|
for(UINT32 j = 0; j < cItems; j++)
|
|
{
|
|
GUID gidKey;
|
|
PROPVARIANT var;
|
|
PropVariantInit(&var);
|
|
|
|
IFC( pAttributes->GetItemByIndex(j, &gidKey, &var) );
|
|
IFC( GetMFNode(i)->SetItem(gidKey, var) );
|
|
|
|
PropVariantClear(&var);
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CTedSourceNode
|
|
|
|
// Initializers //
|
|
|
|
CComPtr<IMFSourceResolver> CTedSourceNode::ms_spResolver = NULL;
|
|
bool CTedSourceNode::m_bIsResolverCreated = false;
|
|
|
|
CTedSourceNode::CTedSourceNode()
|
|
: m_fIsProtected(false)
|
|
, m_fExternalShutdownRequired(false)
|
|
, m_bInitializedFromMFSource(false)
|
|
{
|
|
}
|
|
|
|
CTedSourceNode::~CTedSourceNode()
|
|
{
|
|
m_Nodes.RemoveAll();
|
|
}
|
|
|
|
HRESULT CTedSourceNode::Init(const CAtlStringW& strSourceURL, const CAtlStringW& label)
|
|
{
|
|
CTedTopologyNode::InitContainer(label);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
m_strSourceURL = strSourceURL;
|
|
|
|
IFC( CreateMFSource() );
|
|
IFC( CreateSourceNodes() );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedSourceNode::Init(IMFMediaSource* pSource, const CAtlStringW& label)
|
|
{
|
|
CTedTopologyNode::InitContainer(label);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
m_strSourceURL = label;
|
|
|
|
IFC( CreateMFCaptureSource( pSource ) );
|
|
IFC( CreateSourceNodes() );
|
|
|
|
m_bInitializedFromMFSource = true;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedSourceNode::Init(CTedSourceMemo* pMemo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CTedTopologyNode::InitContainer(pMemo);
|
|
|
|
m_strSourceURL = pMemo->m_strSourceURL;
|
|
|
|
IFC( CreateMFSource() );
|
|
IFC( CreateSourceNodes() );
|
|
|
|
IFC( PostInitFromMemoCopyAttributes(pMemo) );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedSourceNode::Init(CTedSourceNode* pNode)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CTedTopologyNode::InitContainer(pNode->GetLabel());
|
|
|
|
m_spSource = pNode->m_spSource;
|
|
m_spPD = pNode->m_spPD;
|
|
m_strSourceURL = pNode->m_strSourceURL;
|
|
m_fExternalShutdownRequired = pNode->m_fExternalShutdownRequired;
|
|
|
|
for(DWORD i = 0; i < pNode->GetMFNodeCount(); i++)
|
|
{
|
|
CComPtr<IMFTopologyNode> spNode;
|
|
|
|
IFC( MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &spNode) );
|
|
IFC( spNode->CloneFrom(pNode->GetMFNode(i)) );
|
|
IFC( InitMFSourceNode(spNode, i) );
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedSourceNode::Init(const CAtlStringW& strSourceURL, const CAtlStringW& label, IMFMediaSource* pSource)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CTedTopologyNode::InitContainer(label);
|
|
|
|
m_strSourceURL = strSourceURL;
|
|
m_spSource = pSource;
|
|
IFC( m_spSource->CreatePresentationDescriptor(&m_spPD) );
|
|
|
|
IFC( CreateSourceNodes() );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedSourceNode::InitIndirect(const CAtlStringW& strSourceURL, CAtlArray<IMFTopologyNode*>& sourceNodes)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
m_strSourceURL = strSourceURL;
|
|
|
|
CTedTopologyNode::InitContainer(m_strSourceURL);
|
|
|
|
for(DWORD i = 0; i < sourceNodes.GetCount(); i++)
|
|
{
|
|
CComPtr<IMFTopologyNode> spNode;
|
|
IFC( MFCreateTopologyNode( MF_TOPOLOGY_SOURCESTREAM_NODE,
|
|
&spNode ));
|
|
|
|
spNode->CloneFrom(sourceNodes.GetAt(i));
|
|
|
|
IFC(InitMFSourceNode(spNode, i));
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
// Accessors //
|
|
|
|
HRESULT CTedSourceNode::GetPin(DWORD nInput, CTedTopologyPin & Pin, bool inputPin)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
assert(!inputPin);
|
|
assert(nInput < m_Nodes.GetCount());
|
|
|
|
assert(m_Nodes.GetAt(nInput));
|
|
Pin.SetPNode(m_Nodes.GetAt(nInput));
|
|
Pin.SetIndex(0);
|
|
|
|
return hr;
|
|
}
|
|
|
|
bool CTedSourceNode::IsProtected() const
|
|
{
|
|
return m_fIsProtected;
|
|
}
|
|
|
|
CTedNodeMemo* CTedSourceNode::CreateMemo() const
|
|
{
|
|
CTedSourceMemo* pMemo = new CTedSourceMemo(GetVisual()->Rect().x(), GetVisual()->Rect().y(), GetLabel(), GetID(), m_strSourceURL);
|
|
|
|
for(size_t i = 0; i < m_Nodes.GetCount(); i++)
|
|
{
|
|
pMemo->AddNodeAttributes(m_Nodes.GetAt(i));
|
|
}
|
|
|
|
return pMemo;
|
|
}
|
|
|
|
DWORD CTedSourceNode::GetMFNodeCount() const
|
|
{
|
|
return (DWORD) m_Nodes.GetCount();
|
|
}
|
|
|
|
IMFTopologyNode* CTedSourceNode::GetMFNode(DWORD nIndex) const
|
|
{
|
|
return m_Nodes.GetAt(nIndex);
|
|
}
|
|
|
|
CTedTopologyNode::TED_NODE_TYPE CTedSourceNode::GetType()
|
|
{
|
|
return TED_SOURCE_NODE;
|
|
}
|
|
|
|
DWORD CTedSourceNode::GetIndexOf(TOPOID nodeID)
|
|
{
|
|
for(DWORD i = 0; i < m_Nodes.GetCount(); i++)
|
|
{
|
|
TOPOID curNodeID;
|
|
m_Nodes.GetAt(i)->GetTopoNodeID(&curNodeID);
|
|
|
|
if(nodeID == curNodeID) return i;
|
|
}
|
|
|
|
return (DWORD) -1;
|
|
}
|
|
|
|
CVisualPin* CTedSourceNode::GetVisualInputPin(int pinID)
|
|
{
|
|
return GetVisual()->GetInputPin(pinID);
|
|
}
|
|
|
|
CVisualPin* CTedSourceNode::GetVisualOutputPin(int pinID)
|
|
{
|
|
return GetVisual()->GetOutputPin(pinID);
|
|
}
|
|
|
|
// Mutators //
|
|
|
|
void CTedSourceNode::ReleaseResolver()
|
|
{
|
|
ms_spResolver.Release();
|
|
m_bIsResolverCreated = false;
|
|
}
|
|
|
|
void CTedSourceNode::ShutdownMFSource()
|
|
{
|
|
if(!m_fExternalShutdownRequired)
|
|
{
|
|
m_spSource->Shutdown();
|
|
}
|
|
}
|
|
|
|
HRESULT CTedSourceNode::CopyAttributes(IMFTopologyNode* pNode, DWORD dwIndex)
|
|
{
|
|
HRESULT hr;
|
|
BOOL fSelected;
|
|
CComPtr<IMFStreamDescriptor> spSD;
|
|
|
|
IFC( CTedTopologyNode::CopyAttributes(pNode, dwIndex) );
|
|
|
|
IMFTopologyNode* pOurNode = GetMFNode(dwIndex);
|
|
|
|
// Do not replace source, presentation descriptor, or stream descriptor
|
|
IFC( pOurNode->SetUnknown(MF_TOPONODE_SOURCE, m_spSource) );
|
|
IFC( pOurNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, m_spPD) );
|
|
IFC( m_spPD->GetStreamDescriptorByIndex(dwIndex, &fSelected, &spSD) );
|
|
IFC( pOurNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, spSD) );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
void CTedSourceNode::FlagExternalShutdownRequired()
|
|
{
|
|
m_fExternalShutdownRequired = true;
|
|
}
|
|
|
|
HRESULT CTedSourceNode::SelectValidStreams()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
for(size_t i = 0; i < m_Nodes.GetCount(); i++)
|
|
{
|
|
IMFTopologyNode* pNode = m_Nodes.GetAt(i);
|
|
CComPtr<IMFTopologyNode> spDownNode;
|
|
DWORD dwDownIndex;
|
|
|
|
HRESULT hrOutput = pNode->GetOutput(0, &spDownNode, &dwDownIndex);
|
|
if(SUCCEEDED(hrOutput))
|
|
{
|
|
IFC( m_spPD->SelectStream((DWORD) i) );
|
|
}
|
|
else
|
|
{
|
|
IFC( m_spPD->DeselectStream((DWORD) i) );
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
// Non-public Helpers //
|
|
|
|
HRESULT CTedSourceNode::CreateMFSource()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
MF_OBJECT_TYPE ObjectType;
|
|
|
|
if(!m_bIsResolverCreated)
|
|
{
|
|
IFC( MFCreateSourceResolver( &ms_spResolver ) );
|
|
m_bIsResolverCreated = true;
|
|
}
|
|
|
|
IFC( ms_spResolver->CreateObjectFromURL(m_strSourceURL,
|
|
MF_RESOLUTION_MEDIASOURCE,
|
|
NULL,
|
|
&ObjectType,
|
|
(IUnknown**)&m_spSource) );
|
|
|
|
IFC( m_spSource->CreatePresentationDescriptor(&m_spPD) );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedSourceNode::CreateMFCaptureSource( IMFMediaSource* pSource )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
m_spSource = pSource;
|
|
IFC( m_spSource->CreatePresentationDescriptor(&m_spPD) );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedSourceNode::InitMFSourceNode(CComPtr<IMFTopologyNode> spNode, int nPinIndex)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CComPtr<IMFStreamDescriptor> spSD;
|
|
CComPtr<IMFMediaTypeHandler> spMediaTypeHandler;
|
|
BOOL fSelected = FALSE;
|
|
GUID guidMajorType = GUID_NULL;
|
|
CAtlString strPinLabel;
|
|
|
|
IFC( spNode->GetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, IID_IMFStreamDescriptor, (void**) &spSD) );
|
|
|
|
IFC( spSD->GetMediaTypeHandler( &spMediaTypeHandler ) );
|
|
IFC( spMediaTypeHandler->GetMajorType( &guidMajorType ) );
|
|
|
|
if(MFMediaType_Audio == guidMajorType)
|
|
{
|
|
strPinLabel = LoadAtlString(IDS_AUDIO);
|
|
}
|
|
else if(MFMediaType_Video == guidMajorType)
|
|
{
|
|
strPinLabel = LoadAtlString(IDS_VIDEO);
|
|
}
|
|
|
|
InitIsProtected();
|
|
|
|
CVisualNode* pVisualNode = new CVisualNode(strPinLabel, false);
|
|
CHECK_ALLOC( pVisualNode );
|
|
|
|
pVisualNode->SetData((LONG_PTR) this);
|
|
pVisualNode->SetIndex(nPinIndex);
|
|
|
|
pVisualNode->AddPin(false, (LONG_PTR) const_cast<CTedSourceNode*>(this), L"", nPinIndex);
|
|
|
|
((CVisualContainer*) GetVisual())->AddComponent(pVisualNode);
|
|
|
|
|
|
assert(spNode != NULL);
|
|
|
|
m_Nodes.Add(spNode);
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedSourceNode::CreateSourceNodes()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// For each stream, create a source topology node
|
|
//
|
|
DWORD cSourceStreams = 0;
|
|
IFC( m_spPD->GetStreamDescriptorCount( &cSourceStreams ));
|
|
|
|
DWORD i = 0;
|
|
for ( i = 0; i < cSourceStreams; i++ )
|
|
{
|
|
CComPtr<IMFStreamDescriptor> spSD;
|
|
CComPtr<IMFTopologyNode> spNode;
|
|
BOOL fSelected = FALSE;
|
|
|
|
IFC(m_spPD->GetStreamDescriptorByIndex(
|
|
i,
|
|
&fSelected,
|
|
&spSD ));
|
|
|
|
IFC( MFCreateTopologyNode( MF_TOPOLOGY_SOURCESTREAM_NODE,
|
|
&spNode ));
|
|
|
|
IFC( spNode->SetUnknown(MF_TOPONODE_SOURCE, m_spSource) );
|
|
IFC( spNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, m_spPD) );
|
|
IFC( spNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, spSD) );
|
|
|
|
IFC( InitMFSourceNode(spNode, i) );
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedSourceNode::InitIsProtected()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = MFRequireProtectedEnvironment(m_spPD);
|
|
|
|
if(S_FALSE == hr)
|
|
{
|
|
m_fIsProtected = false;
|
|
}
|
|
else
|
|
{
|
|
m_fIsProtected = true;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CTedOutputNode
|
|
|
|
// Initialization //
|
|
|
|
CTedOutputNode::CTedOutputNode()
|
|
: m_fExternalShutdownRequired(false)
|
|
{
|
|
}
|
|
|
|
CTedOutputNode::~CTedOutputNode()
|
|
{
|
|
m_arrStreamSinkNodes.RemoveAll();
|
|
}
|
|
|
|
HRESULT CTedOutputNode::Init(IMFActivate* pActivate, const CAtlStringW& label)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CComPtr<IMFTopologyNode> spNode;
|
|
|
|
assert(pActivate != NULL);
|
|
|
|
IFC( MFCreateTopologyNode( MF_TOPOLOGY_OUTPUT_NODE, &spNode ));
|
|
IFC(spNode->SetObject(pActivate));
|
|
|
|
m_arrStreamSinkNodes.Add(spNode);
|
|
|
|
CTedTopologyNode::Init(label);
|
|
AddPin(true, L"", 0);
|
|
Cleanup:
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedOutputNode::Init(IMFMediaSink* pSink, const CAtlStringW& label)
|
|
{
|
|
assert(pSink != NULL);
|
|
|
|
CTedTopologyNode::InitContainer(label);
|
|
|
|
return InitFromSink(pSink);
|
|
}
|
|
|
|
HRESULT CTedOutputNode::Init(IMFActivate* pActivate, CTedOutputMemo* pMemo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
assert(pActivate != NULL);
|
|
|
|
CTedTopologyNode::Init(pMemo);
|
|
|
|
CComPtr<IMFTopologyNode> spNode;
|
|
|
|
IFC( MFCreateTopologyNode( MF_TOPOLOGY_OUTPUT_NODE, &spNode ) );
|
|
IFC( spNode->SetObject(pActivate) );
|
|
|
|
m_arrStreamSinkNodes.Add(spNode);
|
|
|
|
AddPin(true, L"", 0);
|
|
|
|
IFC( PostInitFromMemoCopyAttributes(pMemo) );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedOutputNode::Init(IMFMediaSink* pSink, CTedOutputMemo* pMemo)
|
|
{
|
|
assert (pSink != NULL);
|
|
assert(pMemo != NULL);
|
|
|
|
CTedTopologyNode::InitContainer(pMemo);
|
|
|
|
HRESULT hr = InitFromSink(pSink);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = PostInitFromMemoCopyAttributes(pMemo);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedOutputNode::Init(IMFStreamSink* pStreamSink, const CAtlStringW& label)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CComPtr<IMFTopologyNode> spNode;
|
|
|
|
assert(pStreamSink != NULL);
|
|
|
|
IFC( MFCreateTopologyNode( MF_TOPOLOGY_OUTPUT_NODE, &spNode ) );
|
|
IFC( spNode->SetObject(pStreamSink) );
|
|
|
|
m_arrStreamSinkNodes.Add(spNode);
|
|
|
|
CTedTopologyNode::Init(label);
|
|
AddPin(true, L"", 0);
|
|
Cleanup:
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Accessors //
|
|
|
|
HRESULT CTedOutputNode::GetPin(DWORD nInput, CTedTopologyPin & Pin, bool inputPin)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
assert(inputPin);
|
|
assert(nInput < m_arrStreamSinkNodes.GetCount());
|
|
|
|
Pin.SetPNode(m_arrStreamSinkNodes.GetAt(nInput));
|
|
Pin.SetIndex(0);
|
|
|
|
return hr;
|
|
}
|
|
|
|
DWORD CTedOutputNode::GetMFNodeCount() const
|
|
{
|
|
return (DWORD) m_arrStreamSinkNodes.GetCount();
|
|
}
|
|
|
|
IMFTopologyNode* CTedOutputNode::GetMFNode(DWORD nIndex) const
|
|
{
|
|
assert(nIndex < m_arrStreamSinkNodes.GetCount());
|
|
|
|
return m_arrStreamSinkNodes.GetAt(nIndex);
|
|
}
|
|
|
|
CTedTopologyNode::TED_NODE_TYPE CTedOutputNode::GetType()
|
|
{
|
|
return TED_OUTPUT_NODE;
|
|
}
|
|
|
|
CVisualPin* CTedOutputNode::GetVisualInputPin(int pinID)
|
|
{
|
|
return GetVisual()->GetInputPin(pinID);
|
|
}
|
|
|
|
CVisualPin* CTedOutputNode::GetVisualOutputPin(int pinID)
|
|
{
|
|
return GetVisual()->GetOutputPin(pinID);
|
|
}
|
|
|
|
// Mutators //
|
|
|
|
HRESULT CTedOutputNode::WrapStreamSink(CLogger* pLogger)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CComPtr<IUnknown> spActivateUnk;
|
|
CComPtr<IMFActivate> spActivate;
|
|
CComPtr<IMFMediaSink> spSink;
|
|
CComPtr<IMFStreamSink> spStreamSink;
|
|
CMFStreamSinkWrapper* pWrapper = NULL;
|
|
|
|
IFC( m_arrStreamSinkNodes.GetAt(0)->GetObject(&spActivateUnk) );
|
|
hr = spActivateUnk->QueryInterface(IID_IMFActivate, (void**) &spActivate);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
IFC( spActivate->ActivateObject(IID_IMFMediaSink, (void**) &spSink) );
|
|
IFC( spSink->GetStreamSinkById(0, &spStreamSink) );
|
|
}
|
|
else
|
|
{
|
|
IFC( spActivateUnk->QueryInterface(IID_IMFStreamSink, (void**) &spStreamSink) );
|
|
}
|
|
|
|
pWrapper = new CMFStreamSinkWrapper(spStreamSink, pLogger);
|
|
CHECK_ALLOC( pWrapper );
|
|
pWrapper->AddRef();
|
|
|
|
IFC( m_arrStreamSinkNodes.GetAt(0)->SetObject(pWrapper) );
|
|
|
|
Cleanup:
|
|
if(pWrapper) pWrapper->Release();
|
|
return hr;
|
|
}
|
|
|
|
void CTedOutputNode::ShutdownMFSink()
|
|
{
|
|
if(!m_fExternalShutdownRequired)
|
|
{
|
|
if(m_arrStreamSinkNodes.GetCount() > 0)
|
|
{
|
|
IMFTopologyNode* pNode = m_arrStreamSinkNodes.GetAt(0);
|
|
CComPtr<IUnknown> spStreamSinkUnk;
|
|
CComPtr<IMFStreamSink> spStreamSink;
|
|
|
|
HRESULT hr = pNode->GetObject(&spStreamSinkUnk);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
HRESULT hr = spStreamSinkUnk->QueryInterface(IID_IMFStreamSink, (void**) &spStreamSink);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
CComPtr<IMFMediaSink> spSink;
|
|
hr = spStreamSink->GetMediaSink(&spSink);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
spSink->Shutdown();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTedOutputNode::FlagExternalShutdownRequired()
|
|
{
|
|
m_fExternalShutdownRequired = true;
|
|
}
|
|
|
|
// Non-public Helpers //
|
|
|
|
HRESULT CTedOutputNode::InitFromSink(IMFMediaSink* pSink)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CVisualContainer* pContainer = (CVisualContainer*) GetVisual();
|
|
|
|
DWORD dwStreamSinkCount;
|
|
IFC( pSink->GetStreamSinkCount(&dwStreamSinkCount) );
|
|
|
|
for(DWORD i = 0; i < dwStreamSinkCount; ++i)
|
|
{
|
|
CComPtr<IMFStreamSink> spStreamSink;
|
|
CComPtr<IMFTopologyNode> spStreamSinkNode;
|
|
|
|
IFC( pSink->GetStreamSinkByIndex(i, &spStreamSink) );
|
|
|
|
IFC( MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &spStreamSinkNode) );
|
|
IFC( spStreamSinkNode->SetObject(spStreamSink) );
|
|
m_arrStreamSinkNodes.Add(spStreamSinkNode);
|
|
|
|
CVisualNode* pVisualNode = new CVisualNode(L"", false);
|
|
CHECK_ALLOC( pVisualNode );
|
|
|
|
pVisualNode->SetData((LONG_PTR) this);
|
|
pVisualNode->SetIndex(i);
|
|
|
|
pVisualNode->AddPin(true, (LONG_PTR) const_cast<CTedOutputNode*>(this), L"", i);
|
|
|
|
pContainer->AddComponent(pVisualNode);
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CTedAudioOutputNode
|
|
|
|
// Initialization //
|
|
HRESULT CTedAudioOutputNode::Init(const CAtlStringW& label)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CComPtr<IMFActivate> spSARAct;
|
|
IFC(MFCreateAudioRendererActivate(&spSARAct));
|
|
|
|
CTedOutputNode::Init(spSARAct, label);
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedAudioOutputNode::Init(CTedAudioOutputMemo* pMemo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CComPtr<IMFActivate> spSARAct;
|
|
IFC(MFCreateAudioRendererActivate(&spSARAct));
|
|
|
|
IFC(CTedOutputNode::Init(spSARAct, pMemo));
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedAudioOutputNode::Init(const CAtlStringW& label, IMFStreamSink* pStreamSink)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
assert(pStreamSink != NULL);
|
|
|
|
IFC( CTedOutputNode::Init(pStreamSink, label) );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
// Accessors //
|
|
|
|
CTedNodeMemo* CTedAudioOutputNode::CreateMemo() const
|
|
{
|
|
CTedAudioOutputMemo* pMemo = new CTedAudioOutputMemo(GetVisual()->Rect().x(), GetVisual()->Rect().y(), GetLabel(), GetID());
|
|
pMemo->AddNodeAttributes(GetMFNode(0));
|
|
GetMFNode(0)->AddRef();
|
|
|
|
return pMemo;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CTedVideoOutputNode
|
|
|
|
// Initialization //
|
|
|
|
HRESULT CTedVideoOutputNode::Init(const CAtlStringW& label, HWND hVideoOutWindow)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CComPtr<IMFActivate> spEVRAct;
|
|
IFC(MFCreateVideoRendererActivate(hVideoOutWindow, &spEVRAct));
|
|
|
|
IFC( CTedOutputNode::Init(spEVRAct, label) );
|
|
|
|
m_hWnd = hVideoOutWindow;
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedVideoOutputNode::Init(HWND hVideoOutWindow, CTedVideoOutputMemo* pMemo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
assert(pMemo != NULL);
|
|
|
|
CComPtr<IMFActivate> spEVRAct;
|
|
IFC(MFCreateVideoRendererActivate(hVideoOutWindow, &spEVRAct));
|
|
|
|
IFC(CTedOutputNode::Init(spEVRAct, pMemo));
|
|
|
|
m_hWnd = hVideoOutWindow;
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedVideoOutputNode::Init(const CAtlStringW& label, HWND hVideoOutWindow, IMFStreamSink* pStreamSink)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
assert(pStreamSink != NULL);
|
|
|
|
IFC( CTedOutputNode::Init(pStreamSink, label) );
|
|
|
|
m_hWnd = hVideoOutWindow;
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
// Accessors //
|
|
|
|
HWND CTedVideoOutputNode::GetVideoWindow() const
|
|
{
|
|
return m_hWnd;
|
|
}
|
|
|
|
CTedNodeMemo* CTedVideoOutputNode::CreateMemo() const
|
|
{
|
|
CTedVideoOutputMemo* pMemo = new CTedVideoOutputMemo(GetVisual()->Rect().x(), GetVisual()->Rect().y(), GetLabel(), GetID());
|
|
pMemo->AddNodeAttributes(GetMFNode(0));
|
|
|
|
return pMemo;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CTedCustomOutputNode
|
|
|
|
// Initialization //
|
|
|
|
HRESULT CTedCustomOutputNode::Init(GUID gidCustomSinkID, const CAtlStringW& label)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
m_gidCustomSinkID = gidCustomSinkID;
|
|
|
|
CComPtr<IMFMediaSink> spSink;
|
|
IFC( CoCreateInstance(m_gidCustomSinkID, NULL, CLSCTX_INPROC_SERVER, IID_IMFMediaSink, (void**) &spSink) );
|
|
|
|
IFC( CTedOutputNode::Init(spSink, label) );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedCustomOutputNode::Init(IMFMediaSink* pSink, const CAtlStringW& label)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
m_gidCustomSinkID = GUID_NULL;
|
|
|
|
IFC( CTedOutputNode::Init(pSink, label) );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedCustomOutputNode::Init(CTedCustomOutputMemo* pMemo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
assert(pMemo != NULL);
|
|
|
|
m_gidCustomSinkID = pMemo->m_gidCustomSinkID;
|
|
|
|
CComPtr<IMFMediaSink> spSink;
|
|
IFC( CoCreateInstance(m_gidCustomSinkID, NULL, CLSCTX_INPROC_SERVER, IID_IMFMediaSink, (void**) &spSink) );
|
|
|
|
IFC( CTedOutputNode::Init(spSink, pMemo) );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
CTedNodeMemo* CTedCustomOutputNode::CreateMemo() const
|
|
{
|
|
CTedCustomOutputMemo* pMemo = new CTedCustomOutputMemo(GetVisual()->Rect().x(), GetVisual()->Rect().y(), GetLabel(), GetID(), m_gidCustomSinkID);
|
|
|
|
for(DWORD i = 0; i < GetMFNodeCount(); i++)
|
|
{
|
|
pMemo->AddNodeAttributes(GetMFNode(0));
|
|
}
|
|
|
|
return pMemo;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CTedTransformNode
|
|
|
|
// Initialization //
|
|
|
|
HRESULT CTedTransformNode::Init(CLSID dmoCLSID, const CAtlStringW& label, bool fAutoInserted)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CTedTopologyNode::Init(label, fAutoInserted);
|
|
|
|
IFC( InitTransform(dmoCLSID) );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTransformNode::Init(IMFTransform* pTransform, CLSID dmoCLSID, const CAtlStringW& label, bool fAutoInserted)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CTedTopologyNode::Init(label, fAutoInserted);
|
|
|
|
IFC( InitTransform(pTransform) );
|
|
|
|
m_clsid = dmoCLSID;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTransformNode::Init(IMFActivate* pTransformActivate, const CAtlStringW& label, bool fAutoInserted)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CTedTopologyNode::Init(label, fAutoInserted);
|
|
|
|
IFC( InitTransform(pTransformActivate) );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTransformNode::Init(CTedTransformMemo* pMemo)
|
|
{
|
|
assert(pMemo != NULL);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CTedTopologyNode::Init(pMemo);
|
|
|
|
IFC( InitTransform(pMemo->m_clsid) );
|
|
|
|
IFC( PostInitFromMemoCopyAttributes(pMemo) );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
// Accessors //
|
|
|
|
HRESULT CTedTransformNode::GetPin(DWORD nInput, CTedTopologyPin & Pin, bool inputPin)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
Pin.SetPNode(m_spTransformNode);
|
|
Pin.SetIndex(nInput);
|
|
|
|
//Cleanup:
|
|
|
|
return hr;
|
|
}
|
|
|
|
CTedNodeMemo* CTedTransformNode::CreateMemo() const
|
|
{
|
|
CTedTransformMemo* pMemo = new CTedTransformMemo(GetVisual()->Rect().x(), GetVisual()->Rect().y(), GetLabel(), GetID(), m_clsid);
|
|
pMemo->AddNodeAttributes(GetMFNode(0));
|
|
|
|
return pMemo;
|
|
}
|
|
|
|
DWORD CTedTransformNode::GetMFNodeCount() const
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
IMFTopologyNode* CTedTransformNode::GetMFNode(DWORD nIndex) const
|
|
{
|
|
assert(nIndex == 0);
|
|
|
|
return m_spTransformNode;
|
|
}
|
|
|
|
bool CTedTransformNode::HasSameTransform(const CLSID& transformCLSID)
|
|
{
|
|
if(transformCLSID == m_clsid)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
CTedTopologyNode::TED_NODE_TYPE CTedTransformNode::GetType()
|
|
{
|
|
return TED_TRANSFORM_NODE;
|
|
}
|
|
|
|
// Mutators //
|
|
|
|
HRESULT CTedTransformNode::CopyMediaTypes(IMFTopologyNode* pOldNode)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CComPtr<IUnknown> spOldTransformUnk, spNewTransformUnk;
|
|
CComPtr<IMFTransform> spNewTransform, spOldTransform;
|
|
DWORD dwNumInputs, dwNumOutputs;
|
|
DWORD* pInputStreamIDs = NULL;
|
|
DWORD* pOutputStreamIDs = NULL;
|
|
|
|
CComPtr<IMFTopologyNode> spNewNode = m_spTransformNode;
|
|
|
|
IFC( spNewNode->GetObject(&spNewTransformUnk) );
|
|
IFC( spNewTransformUnk->QueryInterface(IID_IMFTransform, (void**) &spNewTransform) );
|
|
|
|
IFC( pOldNode->GetObject(&spOldTransformUnk) );
|
|
IFC( spOldTransformUnk->QueryInterface(IID_IMFTransform, (void**) &spOldTransform) );
|
|
|
|
IFC( spNewTransform->GetStreamCount(&dwNumInputs, &dwNumOutputs) );
|
|
|
|
pInputStreamIDs = new DWORD[dwNumInputs];
|
|
CHECK_ALLOC( pInputStreamIDs );
|
|
pOutputStreamIDs = new DWORD[dwNumOutputs];
|
|
CHECK_ALLOC( pOutputStreamIDs );
|
|
|
|
hr = spNewTransform->GetStreamIDs(dwNumInputs, pInputStreamIDs, dwNumOutputs, pOutputStreamIDs);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
if(hr == E_NOTIMPL)
|
|
{
|
|
for(DWORD i = 0; i < dwNumInputs; ++i)
|
|
{
|
|
pInputStreamIDs[i] = i;
|
|
pOutputStreamIDs[i] = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IFC(hr);
|
|
}
|
|
}
|
|
|
|
if ( spOldTransform != spNewTransform )
|
|
{
|
|
for(DWORD i = 0; i < dwNumInputs; ++i)
|
|
{
|
|
CComPtr<IMFMediaType> spType;
|
|
|
|
IFC( spOldTransform->GetInputCurrentType(pInputStreamIDs[i], &spType) );
|
|
IFC( spNewTransform->SetInputType(pInputStreamIDs[i], spType, 0) );
|
|
}
|
|
|
|
for(DWORD i = 0; i < dwNumOutputs; ++i)
|
|
{
|
|
CComPtr<IMFMediaType> spType;
|
|
|
|
IFC( spOldTransform->GetOutputCurrentType(pOutputStreamIDs[i], &spType) );
|
|
IFC( spNewTransform->SetOutputType(pOutputStreamIDs[i], spType, 0) );
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
delete[] pInputStreamIDs;
|
|
delete[] pOutputStreamIDs;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTransformNode::WrapTransform(CLogger* pLogger)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CMFTransformWrapper* pTransformWrapper = NULL;
|
|
CComPtr<IUnknown> spTransformUnk;
|
|
CComPtr<IMFTransform> spTransform;
|
|
|
|
IFC( m_spTransformNode->GetObject(&spTransformUnk) );
|
|
IFC( spTransformUnk->QueryInterface(IID_IMFTransform, (void**) &spTransform) );
|
|
|
|
pTransformWrapper = new CMFTransformWrapper(spTransform, pLogger);
|
|
CHECK_ALLOC( pTransformWrapper );
|
|
pTransformWrapper->AddRef();
|
|
|
|
IFC( m_spTransformNode->SetObject(pTransformWrapper) );
|
|
|
|
Cleanup:
|
|
if(pTransformWrapper) pTransformWrapper->Release();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTransformNode::ResetD3DManager()
|
|
{
|
|
HRESULT hr;
|
|
|
|
IFC( m_spTransform->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, NULL) );
|
|
IFC( m_spTransformNode->SetUINT32(MF_TOPONODE_D3DAWARE, 0) );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
// Non-public Helpers //
|
|
|
|
HRESULT CTedTransformNode::InitTransform(CLSID dmoCLSID)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CComPtr<IUnknown> spMFTUnk;
|
|
CComPtr<IMFTransform> spTransform;
|
|
DWORD dwNumInputs = 0, dwNumOutputs = 0;
|
|
|
|
m_clsid = dmoCLSID;
|
|
|
|
CComPtr<IMFTopologyNode> spNode;
|
|
|
|
IFC( MFCreateTopologyNode( MF_TOPOLOGY_TRANSFORM_NODE, &spNode ));
|
|
|
|
IFC( CoCreateInstance(m_clsid, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **) &spMFTUnk) );
|
|
|
|
IFC( spNode->SetObject(spMFTUnk) );
|
|
|
|
IFC( spNode->SetGUID(MF_TOPONODE_TRANSFORM_OBJECTID, m_clsid) );
|
|
|
|
IFC( spMFTUnk->QueryInterface(IID_IMFTransform, (void**) &m_spTransform) );
|
|
IFC( m_spTransform->GetStreamCount(&dwNumInputs, &dwNumOutputs) );
|
|
|
|
m_spTransformNode = spNode;
|
|
|
|
for(size_t i = 0; i < dwNumInputs; i++)
|
|
{
|
|
AddPin(true, L"", (DWORD) i);
|
|
}
|
|
|
|
for(size_t i = 0; i < dwNumOutputs; i++)
|
|
{
|
|
AddPin(false, L"", (DWORD) i);
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTransformNode::InitTransform(IMFTransform* pTransform)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwNumInputs = 0, dwNumOutputs = 0;
|
|
|
|
m_spTransform = pTransform;
|
|
|
|
CComPtr<IMFTopologyNode> spNode;
|
|
IFC( MFCreateTopologyNode( MF_TOPOLOGY_TRANSFORM_NODE, &spNode ));
|
|
IFC(spNode->SetObject(pTransform));
|
|
|
|
m_spTransformNode = spNode;
|
|
|
|
IFC(pTransform->GetStreamCount(&dwNumInputs, &dwNumOutputs));
|
|
for(size_t i = 0; i < dwNumInputs; i++)
|
|
{
|
|
AddPin(true, L"", (DWORD) i);
|
|
}
|
|
|
|
for(size_t i = 0; i < dwNumOutputs; i++)
|
|
{
|
|
AddPin(false, L"", (DWORD) i);
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTransformNode::InitTransform(IMFActivate* pTransformActivate)
|
|
{
|
|
HRESULT hr;
|
|
CComPtr<IMFTransform> spTransform;
|
|
|
|
pTransformActivate->GetGUID(MFT_TRANSFORM_CLSID_Attribute, &m_clsid);
|
|
|
|
IFC( pTransformActivate->ActivateObject(IID_IMFTransform, (void**) &spTransform) );
|
|
IFC( InitTransform(spTransform) );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CTedTeeNode
|
|
|
|
// Initialization //
|
|
|
|
HRESULT CTedTeeNode::Init()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CTedTopologyNode::Init(LoadAtlString(IDS_TEE));
|
|
|
|
IFC( MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &m_spTeeNode ));
|
|
|
|
AddPin(true, L"", 0);
|
|
AddPin(false, L"", 0);
|
|
|
|
m_nNextOutputIndex = 1;
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTeeNode::Init(CTedTeeMemo* pMemo)
|
|
{
|
|
assert(pMemo != NULL);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CTedTopologyNode::Init(pMemo);
|
|
|
|
IFC( MFCreateTopologyNode( MF_TOPOLOGY_TEE_NODE, &m_spTeeNode ));
|
|
|
|
AddPin(true, L"", 0);
|
|
|
|
m_nNextOutputIndex = pMemo->m_nNextOutputIndex;
|
|
|
|
for(DWORD i = 0; i < m_nNextOutputIndex; i++)
|
|
{
|
|
AddPin(false, L"", i);
|
|
}
|
|
|
|
IFC( PostInitFromMemoCopyAttributes(pMemo) );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
};
|
|
|
|
// Accessors //
|
|
|
|
HRESULT CTedTeeNode::GetPin(DWORD nInput, CTedTopologyPin & Pin, bool inputPin)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
Pin.SetPNode(m_spTeeNode);
|
|
Pin.SetIndex(nInput);
|
|
|
|
return hr;
|
|
}
|
|
|
|
CTedNodeMemo* CTedTeeNode::CreateMemo() const
|
|
{
|
|
CTedTeeMemo* pMemo = new CTedTeeMemo(GetVisual()->Rect().x(), GetVisual()->Rect().y(), GetLabel(), GetID(), m_nNextOutputIndex);
|
|
pMemo->AddNodeAttributes(GetMFNode(0));
|
|
|
|
return pMemo;
|
|
}
|
|
|
|
DWORD CTedTeeNode::GetMFNodeCount() const
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
IMFTopologyNode* CTedTeeNode::GetMFNode(DWORD nIndex) const
|
|
{
|
|
assert(nIndex == 0);
|
|
|
|
return m_spTeeNode;
|
|
}
|
|
|
|
CTedTopologyNode::TED_NODE_TYPE CTedTeeNode::GetType()
|
|
{
|
|
return TED_TEE_NODE;
|
|
}
|
|
|
|
// Mutators //
|
|
|
|
void CTedTeeNode::NotifyConnection()
|
|
{
|
|
DWORD outputCount;
|
|
|
|
m_spTeeNode->GetOutputCount(&outputCount);
|
|
|
|
if(outputCount >= m_nNextOutputIndex)
|
|
{
|
|
AddPin(false, L"", m_nNextOutputIndex++);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CTedEditorVisualObjectEventHandler
|
|
|
|
CTedEditorVisualObjectEventHandler::CTedEditorVisualObjectEventHandler(CTedTopologyEditor* pEditor)
|
|
: m_pEditor(pEditor)
|
|
{
|
|
}
|
|
|
|
void CTedEditorVisualObjectEventHandler::NotifyObjectDeleted(CVisualObject* pVisualObj)
|
|
{
|
|
if(pVisualObj->Type() == CVisualObject::CONNECTOR)
|
|
{
|
|
CTedTopologyConnection* pConn = (CTedTopologyConnection*) pVisualObj->GetData();
|
|
if(pConn == NULL) return;
|
|
|
|
CTedTopologyNode* pNode = m_pEditor->FindNode(pConn->GetOutputNodeID());
|
|
|
|
HRESULT hr = m_pEditor->FullDisconnectNodes(pNode, pConn->GetOutputPinID());
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
CAtlString strErr;
|
|
strErr.Format(L"%x", hr);
|
|
MessageBox(NULL, LoadAtlString(IDS_E_DELETE_CONNECTION), strErr, MB_OK);
|
|
}
|
|
}
|
|
else if(pVisualObj->Type() == CVisualObject::NODE || pVisualObj->Type() == CVisualObject::CONTAINER)
|
|
{
|
|
HRESULT hr = m_pEditor->RemoveNodeWithVisual(pVisualObj);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
CAtlString strErr;
|
|
strErr.Format(L"%x", hr);
|
|
MessageBox(NULL, LoadAtlString(IDS_E_DELETE_NODE), strErr, MB_OK);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CTedNodeCreator
|
|
|
|
CTedNodeCreator CTedNodeCreator::m_Singleton;
|
|
|
|
CTedNodeCreator::CTedNodeCreator()
|
|
{
|
|
}
|
|
|
|
CTedNodeCreator::~CTedNodeCreator()
|
|
{
|
|
}
|
|
|
|
CTedNodeCreator* CTedNodeCreator::GetSingleton()
|
|
{
|
|
return &m_Singleton;
|
|
}
|
|
|
|
HRESULT CTedNodeCreator::CreateSource(const CAtlStringW& strSourceURL, IMFMediaSource* pFromSource, CTedSourceNode** ppSourceNode)
|
|
{
|
|
if(NULL == ppSourceNode)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CAtlStringW strLabel;
|
|
int charLoc = strSourceURL.ReverseFind('\\');
|
|
if(charLoc != -1)
|
|
{
|
|
strLabel = strSourceURL.Mid(charLoc + 1);
|
|
}
|
|
else
|
|
{
|
|
strLabel = strSourceURL;
|
|
}
|
|
|
|
CTedSourceNode* pSourceNode = new CTedSourceNode;
|
|
CHECK_ALLOC( pSourceNode );
|
|
|
|
if( pFromSource )
|
|
{
|
|
hr = pSourceNode->Init(strSourceURL, strLabel, pFromSource);
|
|
}
|
|
else
|
|
{
|
|
hr = pSourceNode->Init(strSourceURL, strLabel);
|
|
}
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
delete pSourceNode;
|
|
}
|
|
else
|
|
{
|
|
*ppSourceNode = pSourceNode;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedNodeCreator::CreateCaptureSource(IMFMediaSource* pSource, CTedSourceNode** ppSourceNode)
|
|
{
|
|
if(NULL == ppSourceNode)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if(NULL == pSource)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CTedSourceNode* pSourceNode = new CTedSourceNode;
|
|
CHECK_ALLOC( pSourceNode );
|
|
|
|
//
|
|
// Get the friendly name
|
|
//
|
|
{
|
|
WCHAR* pwsz = NULL;
|
|
HRESULT hrName = S_OK;
|
|
CComPtr<IMFAttributes> spAttributes = NULL;
|
|
UINT32 nLen = 0;
|
|
|
|
hr = pSource->QueryInterface( __uuidof(IMFAttributes), (LPVOID*)&spAttributes );
|
|
|
|
hrName = spAttributes->GetStringLength( MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
|
|
&nLen );
|
|
|
|
if ( SUCCEEDED( hrName ) )
|
|
{
|
|
pwsz = new WCHAR[ nLen + 1 ];
|
|
if ( pwsz == NULL )
|
|
{
|
|
hrName = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if ( SUCCEEDED( hrName ) )
|
|
{
|
|
hrName = spAttributes->GetString( MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
|
|
pwsz,
|
|
nLen + 1,
|
|
&nLen );
|
|
}
|
|
|
|
if ( SUCCEEDED( hrName ) )
|
|
{
|
|
hr = pSourceNode->Init(pSource, pwsz);
|
|
}
|
|
else
|
|
{
|
|
hr = pSourceNode->Init(pSource, LoadAtlString(IDS_CAPTURE_SOURCE));
|
|
}
|
|
|
|
delete[] pwsz;
|
|
}
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
delete pSourceNode;
|
|
}
|
|
else
|
|
{
|
|
*ppSourceNode = pSourceNode;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedNodeCreator::CreateSAR(CTedAudioOutputNode** ppSAR)
|
|
{
|
|
if(NULL == ppSAR)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
CTedAudioOutputNode * pNode = NULL;
|
|
|
|
pNode = new CTedAudioOutputNode();
|
|
CHECK_ALLOC( pNode );
|
|
hr = pNode->Init(LoadAtlString(IDS_AUDIO_RENDERER));
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
delete pNode;
|
|
}
|
|
else
|
|
{
|
|
*ppSAR = pNode;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedNodeCreator::CreateEVR(ITedVideoWindowHandler* pVideoHandler, CTedVideoOutputNode** ppEVR)
|
|
{
|
|
if(NULL == ppEVR)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
CTedVideoOutputNode * pNode = NULL;
|
|
HWND hWnd = NULL;
|
|
|
|
if(pVideoHandler)
|
|
{
|
|
IFC( pVideoHandler->GetVideoWindow((LONG_PTR*) &hWnd) );
|
|
}
|
|
|
|
pNode = new CTedVideoOutputNode();
|
|
CHECK_ALLOC( pNode );
|
|
hr = pNode->Init(LoadAtlString(IDS_VIDEO_RENDERER), hWnd);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
delete pNode;
|
|
if(hWnd != NULL) pVideoHandler->ReleaseVideoWindow((LONG_PTR) hWnd);
|
|
}
|
|
else
|
|
{
|
|
*ppEVR = pNode;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedNodeCreator::CreateCustomSink(IMFMediaSink* pSink, CTedCustomOutputNode** ppSinkNode)
|
|
{
|
|
if(NULL == ppSinkNode)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
CTedCustomOutputNode* pNode = NULL;
|
|
HWND hWnd = NULL;
|
|
|
|
pNode = new CTedCustomOutputNode();
|
|
CHECK_ALLOC( pNode );
|
|
hr = pNode->Init(pSink, LoadAtlString(IDS_CUSTOM_SINK));
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
delete pNode;
|
|
}
|
|
else
|
|
{
|
|
*ppSinkNode = pNode;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedNodeCreator::CreateCustomSink(GUID gidCustomSinkID, CTedCustomOutputNode** ppSinkNode)
|
|
{
|
|
if(NULL == ppSinkNode)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CTedCustomOutputNode* pNode = new CTedCustomOutputNode();
|
|
CHECK_ALLOC( pNode );
|
|
hr = pNode->Init(gidCustomSinkID, LoadAtlString(IDS_CUSTOM_SINK));
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
delete pNode;
|
|
}
|
|
else
|
|
{
|
|
*ppSinkNode = pNode;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedNodeCreator::CreateTransform(CLSID clsidDMO, const CAtlStringW& strName, CTedTransformNode** ppTransformNode)
|
|
{
|
|
if(NULL == ppTransformNode)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
CTedTransformNode * pNode = NULL;
|
|
|
|
pNode = new CTedTransformNode();
|
|
CHECK_ALLOC( pNode );
|
|
hr = pNode->Init(clsidDMO, strName);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
delete pNode;
|
|
}
|
|
else
|
|
{
|
|
*ppTransformNode = pNode;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedNodeCreator::CreateTransform(IMFActivate* pTransformActivate, CTedTransformNode** ppTransformNode)
|
|
{
|
|
if(NULL == ppTransformNode)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
CTedTransformNode * pNode = NULL;
|
|
LPWSTR szName = NULL;
|
|
|
|
pNode = new CTedTransformNode();
|
|
CHECK_ALLOC( pNode );
|
|
IFC( pTransformActivate->GetAllocatedString(MFT_FRIENDLY_NAME_Attribute, &szName, NULL) );
|
|
hr = pNode->Init(pTransformActivate, szName);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
delete pNode;
|
|
}
|
|
else
|
|
{
|
|
*ppTransformNode = pNode;
|
|
}
|
|
|
|
Cleanup:
|
|
CoTaskMemFree(szName);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedNodeCreator::CreateTee(CTedTeeNode** ppTeeNode)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CTedTeeNode * pNode = NULL;
|
|
|
|
pNode = new CTedTeeNode;
|
|
CHECK_ALLOC( pNode );
|
|
hr = pNode->Init();
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
delete pNode;
|
|
}
|
|
else
|
|
{
|
|
*ppTeeNode = pNode;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CTedTopologyEditor
|
|
|
|
// Initialization //
|
|
const int CTedTopologyEditor::MARGIN_SIZE = 20;
|
|
|
|
CTedTopologyEditor::CTedTopologyEditor()
|
|
: m_pMoveHandler(NULL)
|
|
, m_pConnectHandler(NULL)
|
|
, m_nProtectedSourceCount(0)
|
|
, m_fSaved(true)
|
|
, m_fShutdownSources(true)
|
|
, m_VisualEventHandler(this)
|
|
, m_LastSeqID(0)
|
|
{
|
|
MFCreateSequencerSource(NULL, &m_spSequencer);
|
|
}
|
|
|
|
CTedTopologyEditor::~CTedTopologyEditor()
|
|
{
|
|
for(size_t i = 0; i < m_Nodes.GetCount(); ++i)
|
|
{
|
|
if(m_fShutdownSources)
|
|
{
|
|
if(m_Nodes.GetAt(i)->GetType() == CTedTopologyNode::TED_SOURCE_NODE)
|
|
{
|
|
CTedSourceNode* pSource = (CTedSourceNode*) m_Nodes.GetAt(i);
|
|
pSource->ShutdownMFSource();
|
|
}
|
|
else if(m_Nodes.GetAt(i)->GetType() == CTedTopologyNode::TED_OUTPUT_NODE)
|
|
{
|
|
CTedOutputNode* pOutput = (CTedOutputNode*) m_Nodes.GetAt(i);
|
|
pOutput->ShutdownMFSink();
|
|
}
|
|
}
|
|
|
|
delete m_Nodes.GetAt(i);
|
|
}
|
|
|
|
for(size_t i = 0; i < m_Connections.GetCount(); ++i)
|
|
{
|
|
delete m_Connections.GetAt(i);
|
|
}
|
|
|
|
delete m_pMoveHandler;
|
|
delete m_pConnectHandler;
|
|
|
|
for(size_t i = 0; i < m_arrLoggers.GetCount(); ++i)
|
|
{
|
|
m_arrLoggers.GetAt(i)->Release();
|
|
}
|
|
|
|
if(m_spSequencer.p)
|
|
{
|
|
CComPtr<IMFMediaSource> spSource;
|
|
HRESULT hr = m_spSequencer->QueryInterface(IID_IMFMediaSource, (void**) &spSource);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
spSource->Shutdown();
|
|
}
|
|
}
|
|
|
|
CTedSourceNode::ReleaseResolver();
|
|
}
|
|
|
|
HRESULT CTedTopologyEditor::Init(ITedVideoWindowHandler* pVideoCallback, ITedPropertyController* pPropertyCallback, ITedTopoEventHandler* pEventCallback, CTopoViewerWindow* pView)
|
|
{
|
|
assert(pView != NULL);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
m_spVideoCallback = pVideoCallback;
|
|
m_spPropertyCallback = pPropertyCallback;
|
|
m_spEventCallback = pEventCallback;
|
|
m_pView = pView;
|
|
|
|
IFC( NewTopology() );
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTopologyEditor::NewTopology()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
m_spTopology.Release();
|
|
IFC( MFCreateTopology(&m_spTopology) );
|
|
|
|
for(size_t i = 0; i < m_Connections.GetCount(); ++i)
|
|
{
|
|
delete m_Connections.GetAt(i);
|
|
}
|
|
|
|
for(size_t i = 0; i < m_Nodes.GetCount(); ++i)
|
|
{
|
|
CTedTopologyNode* pNode = m_Nodes.GetAt(i);
|
|
|
|
if(pNode->GetType() == CTedTopologyNode::TED_SOURCE_NODE && m_fShutdownSources)
|
|
{
|
|
CTedSourceNode* pSource = (CTedSourceNode*) pNode;
|
|
pSource->ShutdownMFSource();
|
|
}
|
|
else if(pNode->GetType() == CTedTopologyNode::TED_OUTPUT_NODE && m_fShutdownSources)
|
|
{
|
|
CTedOutputNode* pOutput = (CTedOutputNode*) pNode;
|
|
pOutput->ShutdownMFSink();
|
|
|
|
if(pNode->GetVideoWindow() != NULL)
|
|
{
|
|
m_spVideoCallback->ReleaseVideoWindow((LONG_PTR) pNode->GetVideoWindow());
|
|
}
|
|
}
|
|
|
|
delete pNode;
|
|
}
|
|
|
|
m_Nodes.RemoveAll();
|
|
m_Connections.RemoveAll();
|
|
|
|
delete m_pMoveHandler;
|
|
delete m_pConnectHandler;
|
|
|
|
m_pMoveHandler = new CMoveComponentHandler(m_pView->PTree(), m_spPropertyCallback);
|
|
CHECK_ALLOC( m_pMoveHandler );
|
|
|
|
m_pConnectHandler = new CConnectPinHandler(m_pView->PTree(), this, m_spPropertyCallback);
|
|
CHECK_ALLOC( m_pConnectHandler );
|
|
|
|
CVisualNode::SetPinHandler(m_pConnectHandler);
|
|
|
|
m_nProtectedSourceCount = 0;
|
|
|
|
for(size_t i = 0; i < m_arrLoggers.GetCount(); ++i)
|
|
{
|
|
m_arrLoggers.GetAt(i)->Release();
|
|
}
|
|
|
|
m_arrLoggers.RemoveAll();
|
|
|
|
m_fSaved = true;
|
|
m_fShutdownSources = true;
|
|
|
|
if(m_spPropertyCallback.p) m_spPropertyCallback->ClearProperties();
|
|
|
|
m_pView->PTree()->SetEventHandler(&m_VisualEventHandler);
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
// Accessors - General //
|
|
|
|
HRESULT CTedTopologyEditor::GetTopology(IMFTopology** ppTopo, BOOL* pfIsProtected)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CComPtr<IMFTopology> spNewTopo;
|
|
|
|
if(NULL == ppTopo || NULL == pfIsProtected)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
bool fMultipleSrc = HasMultipleSources();
|
|
|
|
IFC( MFCreateTopology(&spNewTopo) );
|
|
IFC( spNewTopo->CloneFrom(m_spTopology) );
|
|
|
|
*pfIsProtected = false;
|
|
for(size_t i = 0; i < m_Nodes.GetCount(); i++)
|
|
{
|
|
CTedTopologyNode::TED_NODE_TYPE NodeType = m_Nodes.GetAt(i)->GetType();
|
|
if(NodeType == CTedTopologyNode::TED_SOURCE_NODE)
|
|
{
|
|
CTedSourceNode* pSourceNode = (CTedSourceNode*) m_Nodes.GetAt(i);
|
|
|
|
if(pSourceNode->IsProtected())
|
|
{
|
|
*pfIsProtected = true;
|
|
}
|
|
|
|
IFC( pSourceNode->SelectValidStreams() );
|
|
|
|
// Source nodes that get passed out cannot be shut down here as we are unsure
|
|
// if the external app has finished using them
|
|
pSourceNode->FlagExternalShutdownRequired();
|
|
}
|
|
else if(NodeType == CTedTopologyNode::TED_OUTPUT_NODE)
|
|
{
|
|
CTedOutputNode* pOutputNode = (CTedOutputNode*) m_Nodes.GetAt(i);
|
|
|
|
pOutputNode->FlagExternalShutdownRequired();
|
|
}
|
|
}
|
|
|
|
WORD cNodes;
|
|
IFC( spNewTopo->GetNodeCount(&cNodes) );
|
|
for(WORD i = 0; i < cNodes; i++)
|
|
{
|
|
CComPtr<IMFTopologyNode> spNode;
|
|
IFC( spNewTopo->GetNode(i, &spNode) );
|
|
|
|
spNode->DeleteItem(MF_TOPONODE_ERRORCODE);
|
|
spNode->DeleteItem(MF_TOPONODE_ERROR_MAJORTYPE);
|
|
spNode->DeleteItem(MF_TOPONODE_ERROR_SUBTYPE);
|
|
|
|
BOOL fConnected = FALSE;
|
|
|
|
DWORD cInputs;
|
|
IFC( spNode->GetInputCount(&cInputs) )
|
|
for(DWORD j = 0; j < cInputs; j++)
|
|
{
|
|
CComPtr<IMFTopologyNode> spUpNode;
|
|
DWORD dwUpIndex;
|
|
HRESULT hrPin = spNode->GetInput(j, &spUpNode, &dwUpIndex);
|
|
if(SUCCEEDED(hrPin))
|
|
{
|
|
fConnected = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!fConnected)
|
|
{
|
|
DWORD cOutputs;
|
|
IFC( spNode->GetOutputCount(&cOutputs) );
|
|
for(DWORD j = 0; j < cOutputs; j++)
|
|
{
|
|
CComPtr<IMFTopologyNode> spDownNode;
|
|
DWORD dwDownIndex;
|
|
HRESULT hrPin = spNode->GetOutput(j, &spDownNode, &dwDownIndex);
|
|
if(SUCCEEDED(hrPin))
|
|
{
|
|
fConnected = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!fConnected)
|
|
{
|
|
IFC( spNewTopo->RemoveNode(spNode) );
|
|
cNodes--;
|
|
i--;
|
|
}
|
|
}
|
|
|
|
if(fMultipleSrc)
|
|
{
|
|
MFSequencerElementId NewID;
|
|
IFC( m_spSequencer->AppendTopology(spNewTopo, SequencerTopologyFlags_Last, &NewID) );
|
|
|
|
if(m_LastSeqID != 0)
|
|
{
|
|
IFC( m_spSequencer->DeleteTopology(m_LastSeqID) );
|
|
}
|
|
|
|
m_LastSeqID = NewID;
|
|
|
|
CComPtr<IMFMediaSource> spSrc;
|
|
IFC( m_spSequencer->QueryInterface(IID_IMFMediaSource, (void**) &spSrc) );
|
|
|
|
CComPtr<IMFPresentationDescriptor> spPD;
|
|
IFC( spSrc->CreatePresentationDescriptor(&spPD) );
|
|
|
|
CComPtr<IMFMediaSourceTopologyProvider> spSrcTopoProvider;
|
|
IFC( m_spSequencer->QueryInterface(IID_IMFMediaSourceTopologyProvider, (void**) &spSrcTopoProvider) );
|
|
|
|
spNewTopo.Release();
|
|
IFC( spSrcTopoProvider->GetMediaSourceTopology(spPD, &spNewTopo) );
|
|
}
|
|
|
|
*ppTopo = spNewTopo;
|
|
(*ppTopo)->AddRef();
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
bool CTedTopologyEditor::HasSource()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WORD cNodes;
|
|
WORD n;
|
|
|
|
IFC(m_spTopology->GetNodeCount(&cNodes));
|
|
|
|
for(n = 0; n < cNodes; n++)
|
|
{
|
|
CComPtr<IMFTopologyNode> spNode;
|
|
MF_TOPOLOGY_TYPE Type;
|
|
|
|
IFC( m_spTopology->GetNode(n, &spNode) );
|
|
IFC( spNode->GetNodeType(&Type) );
|
|
|
|
if(Type == MF_TOPOLOGY_SOURCESTREAM_NODE)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return false;
|
|
}
|
|
|
|
DWORD CTedTopologyEditor::GetNodeCount()
|
|
{
|
|
return (DWORD) m_Nodes.GetCount();
|
|
}
|
|
|
|
CTedTopologyNode* CTedTopologyEditor::GetNode(DWORD dwIndex)
|
|
{
|
|
return m_Nodes.GetAt(dwIndex);
|
|
}
|
|
|
|
// Accessors - Find //
|
|
|
|
CTedTopologyConnection* CTedTopologyEditor::FindDownstreamConnection(int nBeginNodeID, int nBeginPinID)
|
|
{
|
|
for(size_t i = 0; i < m_Connections.GetCount(); ++i)
|
|
{
|
|
CTedTopologyConnection* pConn = m_Connections.GetAt(i);
|
|
if(pConn->GetOutputNodeID() == nBeginNodeID && pConn->GetOutputPinID() == nBeginPinID)
|
|
{
|
|
return pConn;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CTedTopologyConnection* CTedTopologyEditor::FindUpstreamConnection(int nBeginNodeID, int nBeginPinID)
|
|
{
|
|
for(size_t i = 0; i < m_Connections.GetCount(); ++i)
|
|
{
|
|
CTedTopologyConnection* pConn = m_Connections.GetAt(i);
|
|
if(pConn->GetInputNodeID() == nBeginNodeID && pConn->GetInputPinID() == nBeginPinID)
|
|
{
|
|
return pConn;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CTedTopologyNode* CTedTopologyEditor::FindNode(int nodeID)
|
|
{
|
|
for(size_t i = 0; i < m_Nodes.GetCount(); ++i)
|
|
{
|
|
CTedTopologyNode* pNode = m_Nodes.GetAt(i);
|
|
|
|
if(pNode->GetID() == nodeID)
|
|
{
|
|
return pNode;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Mutators - General //
|
|
|
|
HRESULT CTedTopologyEditor::MergeTopology(IMFTopology* pTopo)
|
|
{
|
|
assert(pTopo != NULL);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
typedef struct _SourcePair
|
|
{
|
|
CTedSourceNode* m_pOldSource;
|
|
CTedSourceNode* m_pNewSource;
|
|
} SourcePair;
|
|
|
|
// All connectors are invalidated by the new topology
|
|
RemoveAllConnectors();
|
|
|
|
int xPos, yPos;
|
|
CAtlArray<SourcePair> arrSourcePairs;
|
|
|
|
CComPtr<IMFCollection> spSourceNodeCollection;
|
|
IFC(pTopo->GetSourceNodeCollection(&spSourceNodeCollection) );
|
|
|
|
DWORD dwSourceNodeCount = 0;
|
|
IFC( spSourceNodeCollection->GetElementCount(&dwSourceNodeCount) );
|
|
|
|
for(DWORD i = 0; i < dwSourceNodeCount; i++)
|
|
{
|
|
CComPtr<IUnknown> spSourceNodeUnk;
|
|
CComPtr<IMFTopologyNode> spSourceNode;
|
|
CComPtr<IMFTopologyNode> spDownstreamNode;
|
|
DWORD nNextIndex;
|
|
|
|
// Get the next source stream node; find out if we have added this source already by looking up in our source table
|
|
IFC( spSourceNodeCollection->GetElement(i, &spSourceNodeUnk) );
|
|
IFC( spSourceNodeUnk->QueryInterface(IID_IMFTopologyNode, (void**) &spSourceNode) );
|
|
|
|
TOPOID idSourceNode;
|
|
IFC( spSourceNode->GetTopoNodeID(&idSourceNode) );
|
|
|
|
CTedSourceNode* pCurrentSource = NULL;
|
|
CTedSourceNode* pNewSource = NULL;
|
|
for(size_t j = 0; j < arrSourcePairs.GetCount(); j++)
|
|
{
|
|
if(arrSourcePairs.GetAt(j).m_pOldSource->GetIndexOf(idSourceNode) != (DWORD) -1)
|
|
{
|
|
pCurrentSource = arrSourcePairs.GetAt(j).m_pOldSource;
|
|
pNewSource = arrSourcePairs.GetAt(j).m_pNewSource;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If there was not a source already in our source table, create a new one
|
|
if(!pCurrentSource)
|
|
{
|
|
pCurrentSource = FindSourceNode(idSourceNode);
|
|
|
|
if(!pCurrentSource) continue;
|
|
|
|
pNewSource = new CTedSourceNode();
|
|
CHECK_ALLOC( pNewSource );
|
|
|
|
if( pCurrentSource->IsInitializedFromMFSource() )
|
|
{
|
|
pNewSource->Init(pCurrentSource->GetMFSource(), pCurrentSource->GetLabel());
|
|
}
|
|
else
|
|
{
|
|
pNewSource->Init(pCurrentSource->GetURL(), pCurrentSource->GetLabel());
|
|
}
|
|
pNewSource->GetVisual()->Move(pCurrentSource->GetVisual()->Rect().x(), pCurrentSource->GetVisual()->Rect().y());
|
|
|
|
SourcePair sourcePair = {pCurrentSource, pNewSource};
|
|
arrSourcePairs.Add(sourcePair);
|
|
}
|
|
|
|
DWORD realIndex = pCurrentSource->GetIndexOf(idSourceNode);
|
|
|
|
xPos = int(pCurrentSource->GetVisual()->Rect().x() + pCurrentSource->GetVisual()->Rect().w() + MARGIN_SIZE);
|
|
yPos = int(pCurrentSource->GetVisual()->Rect().y() + CVisualContainer::TOP_MARGIN * (realIndex + 1) + CVisualContainer::COMP_DEF_HEIGHT * realIndex);
|
|
|
|
IFC( pNewSource->CopyAttributes(spSourceNode, realIndex) );
|
|
|
|
hr = spSourceNode->GetOutput(0, &spDownstreamNode, &nNextIndex);
|
|
if(FAILED(hr) || spDownstreamNode.p == NULL) continue;
|
|
|
|
IFC( MergeBranch(pNewSource, realIndex, spDownstreamNode, nNextIndex, xPos, yPos, true) );
|
|
}
|
|
|
|
for(size_t i = 0; i < arrSourcePairs.GetCount(); i++)
|
|
{
|
|
RemoveNode(arrSourcePairs.GetAt(i).m_pOldSource);
|
|
IFC( AddComponent(arrSourcePairs.GetAt(i).m_pNewSource) );
|
|
}
|
|
|
|
m_pView->PTree()->RouteAllConnectors();
|
|
m_pView->NotifyNewVisuals();
|
|
m_pView->InvalidateRect(NULL);
|
|
|
|
RemoveOrphanTransforms();
|
|
|
|
m_fShutdownSources = true;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CTedTopologyEditor::SpyNodeWithVisual(CVisualObject* pVisual)
|
|
{
|
|
assert(pVisual != NULL);
|
|
|
|
CTedTopologyNode* pFoundNode = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
for(size_t i = 0; i < m_Nodes.GetCount(); ++i)
|
|
{
|
|
if(m_Nodes.GetAt(i)->GetVisual() == pVisual)
|
|
{
|
|
pFoundNode = m_Nodes.GetAt(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(NULL != pFoundNode)
|
|
{
|
|
switch(pFoundNode->GetType())
|
|
{
|
|
case CTedTopologyNode::TED_TRANSFORM_NODE:
|
|
{
|
|
CTedTransformNode* pTedTransform = (CTedTransformNode*) pFoundNode;
|
|
|
|
TOPOID nodeID;
|
|
IFC( pTedTransform->GetNodeID(0, nodeID) );
|
|
|
|
CAtlStringW fileName;
|
|
fileName.Format(L"%s-%d.txt", pTedTransform->GetLabel(), nodeID);
|
|
|
|
CLogger* pLogger = new CLogger(fileName);
|
|
CHECK_ALLOC( pLogger );
|
|
|
|
IFC( pTedTransform->WrapTransform(pLogger) );
|
|
|
|
m_arrLoggers.Add(pLogger);
|
|
pLogger->AddRef();
|
|
|
|
break;
|
|
}
|
|
case CTedTopologyNode::TED_OUTPUT_NODE:
|
|
{
|
|
CTedOutputNode* pTedOutput = (CTedOutputNode*) pFoundNode;
|
|
|
|
TOPOID nodeID;
|
|
IFC( pTedOutput->GetNodeID(0, nodeID) );
|
|
|
|
CAtlStringW fileName;
|
|
fileName.Format(L"%s-%d.txt", pTedOutput->GetLabel(), nodeID);
|
|
|
|
CLogger* pLogger = new CLogger(fileName);
|
|
CHECK_ALLOC( pLogger );
|
|
|
|
IFC( pTedOutput->WrapStreamSink(pLogger) );
|
|
|
|
m_arrLoggers.Add(pLogger);
|
|
pLogger->AddRef();
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTopologyEditor::ShowTopology(IMFTopology* pTopology, LPCWSTR szSourceURL)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwSourceNodeCount = 0;
|
|
CComPtr<IUnknown> spSourceNodeUnk;
|
|
CComPtr<IMFTopologyNode> spSourceNode;
|
|
CComPtr<IMFMediaSource> spSource;
|
|
CTedNodeCreator* pNodeCreator = CTedNodeCreator::GetSingleton();
|
|
|
|
NewTopology();
|
|
|
|
CComPtr<IMFCollection> spSourceNodeCollection;
|
|
IFC(pTopology->GetSourceNodeCollection(&spSourceNodeCollection) );
|
|
|
|
IFC( spSourceNodeCollection->GetElementCount(&dwSourceNodeCount) );
|
|
if(dwSourceNodeCount == 0) IFC(E_INVALIDARG);
|
|
|
|
IFC( spSourceNodeCollection->GetElement(0, &spSourceNodeUnk) );
|
|
IFC( spSourceNodeUnk->QueryInterface(IID_IMFTopologyNode, (void**) &spSourceNode) );
|
|
IFC( spSourceNode->GetUnknown(MF_TOPONODE_SOURCE, IID_IMFMediaSource, (void**) &spSource) );
|
|
|
|
CTedSourceNode* pNewSource;
|
|
IFC( pNodeCreator->CreateSource(szSourceURL, spSource, &pNewSource) );
|
|
|
|
pNewSource->GetVisual()->Move(10, 10);
|
|
|
|
int xPos = 10;
|
|
int yPos = 10;
|
|
|
|
xPos += int(pNewSource->GetVisual()->Rect().w() + MARGIN_SIZE);
|
|
|
|
for(DWORD i = 0; i < dwSourceNodeCount; i++)
|
|
{
|
|
CComPtr<IUnknown> spSourceNodeUnk;
|
|
CComPtr<IMFTopologyNode> spSourceNode;
|
|
CComPtr<IMFTopologyNode> spDownstreamNode;
|
|
DWORD nNextIndex;
|
|
|
|
yPos = int(pNewSource->GetVisual()->Rect().y() + CVisualContainer::TOP_MARGIN * (i + 1) + CVisualContainer::COMP_DEF_HEIGHT * i);
|
|
|
|
IFC( spSourceNodeCollection->GetElement(i, (IUnknown**) &spSourceNodeUnk) );
|
|
IFC( spSourceNodeUnk->QueryInterface(IID_IMFTopologyNode, (void**) &spSourceNode) );
|
|
IFC( spSourceNode->GetOutput(0, &spDownstreamNode, &nNextIndex) );
|
|
|
|
if(spDownstreamNode.p == NULL) continue;
|
|
|
|
CComPtr<IMFPresentationDescriptor> spPD;
|
|
IFC( spSourceNode->GetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, IID_IMFPresentationDescriptor, (LPVOID*) &spPD) );
|
|
|
|
DWORD dwSDCount;
|
|
IFC( spPD->GetStreamDescriptorCount(&dwSDCount) );
|
|
|
|
CComPtr<IMFStreamDescriptor> spNodeSD;
|
|
IFC( spSourceNode->GetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, IID_IMFStreamDescriptor, (LPVOID*) &spNodeSD) );
|
|
|
|
DWORD dwOutputIndex = 0;
|
|
for(DWORD j = 0; j < dwSDCount; j++)
|
|
{
|
|
CComPtr<IMFStreamDescriptor> spSD;
|
|
BOOL fSelected;
|
|
IFC( spPD->GetStreamDescriptorByIndex(j, &fSelected, &spSD) );
|
|
|
|
if(spSD.p == spNodeSD.p)
|
|
{
|
|
dwOutputIndex = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
IFC( MergeBranch(pNewSource, dwOutputIndex, spDownstreamNode, nNextIndex, xPos, yPos, false) );
|
|
|
|
yPos += int(pNewSource->GetVisual()->Rect().h() + MARGIN_SIZE);
|
|
}
|
|
|
|
IFC( AddComponent(pNewSource) );
|
|
|
|
m_pView->PTree()->RouteAllConnectors();
|
|
m_pView->NotifyNewVisuals();
|
|
m_pView->InvalidateRect(NULL);
|
|
|
|
m_fShutdownSources = true;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
void CTedTopologyEditor::SetEditable(BOOL fEditable)
|
|
{
|
|
m_fEditable = fEditable;
|
|
|
|
m_pMoveHandler->SetEditable(fEditable);
|
|
m_pConnectHandler->SetEditable(fEditable);
|
|
}
|
|
|
|
// Mutators - Create New Node //
|
|
|
|
HRESULT CTedTopologyEditor::AddNode(CTedTopologyNode* pNode)
|
|
{
|
|
HRESULT hr;
|
|
|
|
IFC( AddComponent(pNode) );
|
|
m_pView->InvalidateRect(NULL);
|
|
m_fSaved = false;
|
|
|
|
if(m_spEventCallback.p)
|
|
{
|
|
m_spEventCallback->NotifyAddedNode(pNode->GetID());
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
// Mutators - Connection //
|
|
|
|
// Connect the two nodes both in the topology and the visual representation
|
|
HRESULT CTedTopologyEditor::FullConnectNodes(CTedTopologyNode* pOutputNode, long nOutputPin,
|
|
CTedTopologyNode* pInputNode, long nInputPin)
|
|
{
|
|
assert(pOutputNode != NULL);
|
|
assert(pInputNode != NULL);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CVisualPin* pPin = pOutputNode->GetVisualOutputPin(nOutputPin);
|
|
CVisualPin* pOtherPin = pInputNode->GetVisualInputPin(nInputPin);
|
|
|
|
assert(pPin != NULL);
|
|
assert(pOtherPin != NULL);
|
|
|
|
IFC( ConnectMFNodes(pOutputNode, nOutputPin, pInputNode, nInputPin) );
|
|
|
|
RemoveOldConnectors(pPin, pOtherPin);
|
|
|
|
CVisualConnector* pConn = new CVisualConnector();
|
|
CHECK_ALLOC( pConn );
|
|
m_pView->PTree()->AddVisual(pConn);
|
|
|
|
pConn->Left() = pPin->GetConnectorPoint();
|
|
pConn->Right() = pOtherPin->GetConnectorPoint();
|
|
|
|
pPin->SetConnector(pConn);
|
|
pOtherPin->SetConnector(pConn);
|
|
|
|
CTedTopologyConnection* pTedConn = new CTedTopologyConnection(pOutputNode->GetID(), pPin->GetPinId(),
|
|
pInputNode->GetID(), pOtherPin->GetPinId());
|
|
CHECK_ALLOC( pTedConn );
|
|
m_Connections.Add(pTedConn);
|
|
|
|
pConn->SetData((LONG_PTR) pTedConn);
|
|
|
|
CTedTopologyNode::TED_NODE_TYPE nodeType = pOutputNode->GetType();
|
|
if(nodeType == CTedTopologyNode::TED_TEE_NODE)
|
|
{
|
|
CTedTeeNode* pTee = (CTedTeeNode*) pOutputNode;
|
|
pTee->NotifyConnection();
|
|
m_pView->PTree()->RouteAllConnectors();
|
|
}
|
|
|
|
m_fSaved = false;
|
|
|
|
if(m_spEventCallback.p)
|
|
{
|
|
m_spEventCallback->NotifyConnection(pOutputNode->GetID(), pInputNode->GetID());
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTopologyEditor::FullDisconnectNodes(CTedTopologyNode* pOutputNode, long nOutputPin)
|
|
{
|
|
assert(pOutputNode != NULL);
|
|
|
|
HRESULT hr = S_OK;
|
|
IMFTopologyNode* pNode;
|
|
|
|
if(pOutputNode->GetType() == CTedTopologyNode::TED_SOURCE_NODE || pOutputNode->GetType() == CTedTopologyNode::TED_OUTPUT_NODE)
|
|
{
|
|
pNode = pOutputNode->GetMFNode(nOutputPin);
|
|
|
|
(void)pNode->DisconnectOutput(0);
|
|
}
|
|
else
|
|
{
|
|
pNode = pOutputNode->GetMFNode(0);
|
|
(void)pNode->DisconnectOutput(nOutputPin);
|
|
}
|
|
|
|
int nEndNodeID;
|
|
RemoveConnectionWithBegin(pOutputNode->GetID(), nOutputPin, &nEndNodeID);
|
|
|
|
m_fSaved = false;
|
|
|
|
if(m_spEventCallback.p)
|
|
{
|
|
m_spEventCallback->NotifyDisconnection(pOutputNode->GetID(), nEndNodeID);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Mutators - Removal //
|
|
|
|
HRESULT CTedTopologyEditor::RemoveNode(CTedTopologyNode* pNode, bool fRemoveVisual)
|
|
{
|
|
assert(pNode != NULL);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
for(size_t i = 0; i < m_Nodes.GetCount(); ++i)
|
|
{
|
|
if(m_Nodes.GetAt(i) == pNode)
|
|
{
|
|
m_Nodes.RemoveAt(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
DWORD nNodes = pNode->GetMFNodeCount();
|
|
for(DWORD i = 0; i < nNodes; ++i)
|
|
{
|
|
IFC( m_spTopology->RemoveNode(pNode->GetMFNode(i)) );
|
|
}
|
|
|
|
if(pNode->GetType() == CTedTopologyNode::TED_SOURCE_NODE)
|
|
{
|
|
CTedSourceNode* pSourceNode = (CTedSourceNode*) pNode;
|
|
pSourceNode->ShutdownMFSource();
|
|
}
|
|
else if(pNode->GetType() == CTedTopologyNode::TED_OUTPUT_NODE)
|
|
{
|
|
CTedOutputNode* pOutputNode = (CTedOutputNode*) pNode;
|
|
pOutputNode->ShutdownMFSink();
|
|
}
|
|
|
|
HWND hWnd = pNode->GetVideoWindow();
|
|
if(hWnd != NULL)
|
|
{
|
|
m_spVideoCallback->ReleaseVideoWindow((LONG_PTR) hWnd);
|
|
|
|
// If a video renderer was removed, any D3D managers
|
|
// associated with that video renderer are invalid
|
|
(void)ResetD3DManagers();
|
|
}
|
|
|
|
m_fSaved = false;
|
|
if(m_spEventCallback.p)
|
|
{
|
|
m_spEventCallback->NotifyRemovedNode(pNode->GetID());
|
|
}
|
|
|
|
Cleanup:
|
|
m_pView->Unselect();
|
|
if(fRemoveVisual) m_pView->PTree()->RemoveVisual(pNode->GetVisual());
|
|
delete pNode;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTopologyEditor::RemoveNodeWithVisual(CVisualObject* pVisual)
|
|
{
|
|
assert(pVisual != NULL);
|
|
|
|
CTedTopologyNode* pFoundNode = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
// Iterate through the nodes and find the one that has the given visual
|
|
for(size_t i = 0; i < m_Nodes.GetCount(); ++i)
|
|
{
|
|
if(m_Nodes.GetAt(i)->GetVisual()->ContainsVisual(pVisual))
|
|
{
|
|
pFoundNode = m_Nodes.GetAt(i);
|
|
}
|
|
}
|
|
|
|
if(pFoundNode)
|
|
{
|
|
hr = RemoveNode(pFoundNode, false);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
void CTedTopologyEditor::RemoveConnectionWithBegin(int nBeginNodeID, int nBeginPinID, int* pEndNodeID) {
|
|
for(size_t i = 0; i < m_Connections.GetCount(); ++i)
|
|
{
|
|
CTedTopologyConnection* pConn = m_Connections.GetAt(i);
|
|
if(pConn->GetOutputNodeID() == nBeginNodeID && pConn->GetOutputPinID() == nBeginPinID)
|
|
{
|
|
if(pEndNodeID) *pEndNodeID = pConn->GetInputNodeID();
|
|
m_Connections.RemoveAt(i);
|
|
delete pConn;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTedTopologyEditor::RemoveConnectionWithEnd(int nEndNodeID, int nEndPinID, int* pBeginNodeID)
|
|
{
|
|
for(size_t i = 0; i < m_Connections.GetCount(); ++i)
|
|
{
|
|
CTedTopologyConnection* pConn = m_Connections.GetAt(i);
|
|
if(pConn->GetInputNodeID() == nEndNodeID && pConn->GetInputPinID() == nEndPinID)
|
|
{
|
|
if(pBeginNodeID) *pBeginNodeID = pConn->GetOutputNodeID();
|
|
m_Connections.RemoveAt(i);
|
|
delete pConn;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CTedTopologyEditor::RemoveOldConnectors(CVisualPin* pPin, CVisualPin* pOtherPin)
|
|
{
|
|
assert(pPin != NULL);
|
|
assert(pOtherPin != NULL);
|
|
|
|
CVisualConnector* pOriginConnector = pPin->GetConnector();
|
|
CVisualConnector* pDestConnector = pOtherPin->GetConnector();
|
|
|
|
CTedTopologyNode* pOutputterNode = (CTedTopologyNode*) (pPin->GetData());
|
|
CTedTopologyNode* pAcceptorNode = (CTedTopologyNode*) (pOtherPin->GetData());
|
|
|
|
if(pOriginConnector && pOriginConnector == pDestConnector)
|
|
{
|
|
m_pView->PTree()->RemoveVisual(pOriginConnector);
|
|
RemoveConnectionWithBegin(pOutputterNode->GetID(), pPin->GetPinId(), NULL);
|
|
}
|
|
else
|
|
{
|
|
if(pOriginConnector)
|
|
{
|
|
m_pView->PTree()->RemoveVisual(pOriginConnector);
|
|
RemoveConnectionWithBegin(pOutputterNode->GetID(), pPin->GetPinId(), NULL);
|
|
}
|
|
|
|
if(pDestConnector)
|
|
{
|
|
m_pView->PTree()->RemoveVisual(pDestConnector);
|
|
RemoveConnectionWithEnd(pAcceptorNode->GetID(), pOtherPin->GetPinId(), NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTedTopologyEditor::RemoveAllConnectors()
|
|
{
|
|
for(size_t i = 0; i < m_Nodes.GetCount(); i++)
|
|
{
|
|
RemoveAllConnectorsForComponent(m_Nodes.GetAt(i)->GetVisual());
|
|
}
|
|
|
|
for(size_t i = 0; i < m_Connections.GetCount(); i++)
|
|
{
|
|
delete m_Connections.GetAt(i);
|
|
}
|
|
m_Connections.RemoveAll();
|
|
}
|
|
|
|
void CTedTopologyEditor::RemoveAllConnectorsForComponent(CVisualComponent* pComponent)
|
|
{
|
|
for(size_t i = 0; i < pComponent->GetInputPinCount(); i++)
|
|
{
|
|
CVisualPin* pPin = pComponent->GetInputPinByIndex((DWORD) i);
|
|
|
|
if(pPin->GetConnector())
|
|
{
|
|
m_pView->PTree()->RemoveVisual(pPin->GetConnector());
|
|
}
|
|
}
|
|
|
|
for(size_t i = 0; i < pComponent->GetOutputPinCount(); i++)
|
|
{
|
|
CVisualPin* pPin = pComponent->GetOutputPinByIndex((DWORD) i);
|
|
|
|
if(pPin->GetConnector())
|
|
{
|
|
m_pView->PTree()->RemoveVisual(pPin->GetConnector());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mutators - Serialization //
|
|
|
|
HRESULT CTedTopologyEditor::SaveTopology(const CAtlStringW& fileName)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CComPtr<ITedDataSaver> spSaver;
|
|
IFC( CoCreateInstance(CLSID_CXMLDataSaver, NULL, CLSCTX_INPROC_SERVER, IID_ITedDataSaver, (void**) &spSaver) );
|
|
|
|
IFC(spSaver->Init(L"TedDocument"));
|
|
|
|
for(size_t i = 0; i < m_Nodes.GetCount(); ++i)
|
|
{
|
|
CAutoPtr<CTedNodeMemo> memo(m_Nodes.GetAt(i)->CreateMemo());
|
|
IFC(memo->Serialize(spSaver));
|
|
}
|
|
|
|
for(size_t i = 0; i < m_Connections.GetCount(); ++i)
|
|
{
|
|
CAutoPtr<CTedConnectionMemo> memo(m_Connections.GetAt(i)->CreateMemo());
|
|
IFC(memo->Serialize(spSaver));
|
|
}
|
|
|
|
IFC(spSaver->SaveToFile(fileName));
|
|
|
|
m_fSaved = true;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTopologyEditor::LoadTopology(const CAtlStringW& fileName)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPWSTR strNextObject = NULL;
|
|
|
|
CComPtr<ITedDataLoader> spLoader;
|
|
IFC( CoCreateInstance(CLSID_CXMLDataLoader, NULL, CLSCTX_INPROC_SERVER, IID_ITedDataLoader, (void**) &spLoader) );
|
|
|
|
NewTopology();
|
|
|
|
IFC( spLoader->LoadFromFile(fileName, L"TedDocument") );
|
|
|
|
BOOL fHasNextObject;
|
|
IFC( spLoader->HasNextObject(&fHasNextObject) );
|
|
while(fHasNextObject)
|
|
{
|
|
// Get the string that represents the serialized object
|
|
hr = spLoader->GetNextObject(&strNextObject);
|
|
|
|
if(hr == S_FALSE)
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
IFC(hr);
|
|
}
|
|
|
|
IFC( LoadTopologyObject(spLoader, strNextObject) );
|
|
|
|
CoTaskMemFree(strNextObject);
|
|
strNextObject = NULL;
|
|
|
|
IFC( spLoader->HasNextObject(&fHasNextObject) );
|
|
}
|
|
|
|
// We loaded a bunch of connections; now we need to actually connect the nodes in MF and the visual layer.
|
|
ApplyLoadedConnections();
|
|
|
|
m_pView->InvalidateRect(NULL);
|
|
|
|
m_fSaved = true;
|
|
|
|
Cleanup:
|
|
if(strNextObject)
|
|
{
|
|
CoTaskMemFree(strNextObject);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Non-public Helpers - General //
|
|
|
|
HRESULT CTedTopologyEditor::AddComponent(CTedTopologyNode* pNode)
|
|
{
|
|
assert(pNode != NULL);
|
|
HRESULT hr = S_OK;
|
|
|
|
m_Nodes.Add(pNode);
|
|
|
|
CVisualObject* pVisual = pNode->GetVisual();
|
|
pVisual->SetHandler(m_pMoveHandler);
|
|
|
|
m_pView->PTree()->AddVisual(pNode->GetVisual());
|
|
m_pView->NotifyNewVisuals();
|
|
|
|
DWORD cNodes = pNode->GetMFNodeCount();
|
|
for(DWORD i = 0; i < cNodes; i++)
|
|
{
|
|
IFC( m_spTopology->AddNode(pNode->GetMFNode(i)) );
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CTedTopologyEditor::LoadTopologyObject(ITedDataLoader* pLoader, const CAtlStringW& strObjName)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if(strObjName == L"CTedSourceMemo")
|
|
{
|
|
CAutoPtr<CTedSourceMemo> memo(new CTedSourceMemo());
|
|
IFC( memo->Deserialize(pLoader) );
|
|
|
|
CTedSourceNode* node = new CTedSourceNode;
|
|
CHECK_ALLOC( node );
|
|
IFC( node->Init(memo) );
|
|
|
|
IFC( AddComponent(node) );
|
|
}
|
|
else if(strObjName == L"CTedAudioOutputMemo")
|
|
{
|
|
CAutoPtr<CTedAudioOutputMemo> memo(new CTedAudioOutputMemo());
|
|
IFC( memo->Deserialize(pLoader) );
|
|
|
|
CTedAudioOutputNode* node = new CTedAudioOutputNode;
|
|
CHECK_ALLOC( node );
|
|
IFC( node->Init(memo) );
|
|
|
|
IFC( AddComponent(node) );
|
|
}
|
|
else if(strObjName == L"CTedVideoOutputMemo")
|
|
{
|
|
CAutoPtr<CTedVideoOutputMemo> memo(new CTedVideoOutputMemo());
|
|
HWND hVideoWindow = NULL;
|
|
|
|
IFC( memo->Deserialize(pLoader) );
|
|
|
|
if(m_spVideoCallback.p) IFC( m_spVideoCallback->GetVideoWindow((LONG_PTR*) &hVideoWindow) );
|
|
|
|
CTedVideoOutputNode* node = new CTedVideoOutputNode;
|
|
CHECK_ALLOC( node );
|
|
IFC( node->Init(hVideoWindow, memo) );
|
|
|
|
IFC( AddComponent(node) );
|
|
}
|
|
else if(strObjName == L"CTedTransformMemo")
|
|
{
|
|
CAutoPtr<CTedTransformMemo> memo(new CTedTransformMemo());
|
|
|
|
IFC( memo->Deserialize(pLoader) );
|
|
|
|
CTedTransformNode* node = new CTedTransformNode;
|
|
CHECK_ALLOC( node );
|
|
IFC( node->Init(memo) );
|
|
|
|
IFC( AddComponent(node) );
|
|
}
|
|
else if(strObjName == L"CTedTeeMemo")
|
|
{
|
|
CAutoPtr<CTedTeeMemo> memo(new CTedTeeMemo());
|
|
|
|
IFC(memo->Deserialize(pLoader));
|
|
|
|
CTedTeeNode* node = new CTedTeeNode;
|
|
CHECK_ALLOC( node );
|
|
IFC( node->Init(memo) );
|
|
|
|
IFC( AddComponent(node) );
|
|
}
|
|
else if(strObjName == L"CTedConnectionMemo")
|
|
{
|
|
CAutoPtr<CTedConnectionMemo> memo(new CTedConnectionMemo());
|
|
|
|
IFC( memo->Deserialize(pLoader) );
|
|
|
|
CTedTopologyConnection* pConnection = new CTedTopologyConnection(memo);
|
|
CHECK_ALLOC( pConnection );
|
|
|
|
m_Connections.Add(pConnection);
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CTedTopologyEditor::ApplyLoadedConnections()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
for(size_t i = 0; i < m_Connections.GetCount(); i++)
|
|
{
|
|
CTedTopologyConnection* conn = m_Connections.GetAt(i);
|
|
|
|
CTedTopologyNode* outputterNode = FindNodeWithID(conn->GetOutputNodeID());
|
|
CTedTopologyNode* acceptorNode = FindNodeWithID(conn->GetInputNodeID());
|
|
|
|
if(outputterNode == NULL || acceptorNode == NULL)
|
|
{
|
|
assert(false);
|
|
continue;
|
|
}
|
|
|
|
IFC( ConnectMFNodes(outputterNode, conn->GetOutputPinID(), acceptorNode, conn->GetInputPinID()) );
|
|
|
|
CVisualPin* outputterPin = outputterNode->GetVisualOutputPin(conn->GetOutputPinID());
|
|
CVisualPin* acceptorPin = acceptorNode->GetVisualInputPin(conn->GetInputPinID());
|
|
|
|
if(outputterPin == NULL || acceptorPin == NULL)
|
|
{
|
|
assert(false);
|
|
continue;
|
|
}
|
|
|
|
m_pView->PTree()->MakeConnector(outputterPin, acceptorPin);
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTopologyEditor::ConnectMFNodes(CTedTopologyNode * pUpNode, long nUpPin,
|
|
CTedTopologyNode * pDownNode, long nDownPin)
|
|
{
|
|
assert(pUpNode != NULL);
|
|
assert(pDownNode != NULL);
|
|
|
|
HRESULT hr = S_OK;
|
|
CTedTopologyPin UpPin;
|
|
CTedTopologyPin DownPin;
|
|
|
|
IFC(pUpNode->GetPin(nUpPin, UpPin, false));
|
|
IFC(pDownNode->GetPin(nDownPin, DownPin, true));
|
|
|
|
assert(UpPin.PNode() != NULL);
|
|
assert(DownPin.PNode() != NULL);
|
|
|
|
IFC(UpPin.PNode()->ConnectOutput(UpPin.Index(), DownPin.PNode(), DownPin.Index()));
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
bool CTedTopologyEditor::HasMultipleSources()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
bool fMultipleSrc = false;
|
|
|
|
CComPtr<IMFCollection> spSrcNodeCollection;
|
|
IFC( m_spTopology->GetSourceNodeCollection(&spSrcNodeCollection) );
|
|
|
|
DWORD cElementCount;
|
|
IFC( spSrcNodeCollection->GetElementCount(&cElementCount) );
|
|
|
|
|
|
IMFMediaSource* pSrc = NULL;
|
|
for(DWORD i = 0; i < cElementCount; i++)
|
|
{
|
|
CComPtr<IUnknown> spSrcNodeUnk;
|
|
IFC( spSrcNodeCollection->GetElement(i, &spSrcNodeUnk) );
|
|
|
|
CComPtr<IMFTopologyNode> spSrcNode;
|
|
IFC( spSrcNodeUnk->QueryInterface(IID_IMFTopologyNode, (void**) &spSrcNode) );
|
|
|
|
CComPtr<IMFMediaSource> spSrc;
|
|
IFC( spSrcNode->GetUnknown(MF_TOPONODE_SOURCE, IID_IMFMediaSource, (void**) &spSrc) );
|
|
|
|
if(pSrc != NULL && spSrc != pSrc)
|
|
{
|
|
fMultipleSrc = true;
|
|
break;
|
|
}
|
|
|
|
pSrc = spSrc;
|
|
}
|
|
|
|
Cleanup:
|
|
return fMultipleSrc;
|
|
}
|
|
|
|
HRESULT CTedTopologyEditor::ResetD3DManagers()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
for(DWORD i = 0; i < m_Nodes.GetCount(); i++)
|
|
{
|
|
if(m_Nodes.GetAt(i)->GetType() == CTedTopologyNode::TED_TRANSFORM_NODE)
|
|
{
|
|
IFC( ((CTedTransformNode*) m_Nodes.GetAt(i))->ResetD3DManager() );
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
// Non-public Helpers - Merge //
|
|
|
|
HRESULT CTedTopologyEditor::MergeBranch(CTedTopologyNode* pBeginTedNode, long nOutputPin,
|
|
IMFTopologyNode* pNextNode, long nInputPin,
|
|
int xPos, int yPos, bool fMarkNew)
|
|
{
|
|
assert(pBeginTedNode != NULL);
|
|
assert(pNextNode != NULL);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
MF_TOPOLOGY_TYPE nodeType;
|
|
CComPtr<IMFTopologyNode> spNode;
|
|
DWORD nNewIndex;
|
|
DWORD outputCount;
|
|
|
|
IFC( pNextNode->GetNodeType(&nodeType) );
|
|
IFC( pNextNode->GetOutputCount(&outputCount) );
|
|
|
|
TOPOID idNode;
|
|
IFC( pNextNode->GetTopoNodeID(&idNode) );
|
|
|
|
CTedTopologyNode* pTedNode = FindNodeByTopoID(idNode);
|
|
if(NULL == pTedNode)
|
|
{
|
|
|
|
if(MF_TOPOLOGY_TRANSFORM_NODE == nodeType)
|
|
{
|
|
IFC( MergeTransformNode(pBeginTedNode, nOutputPin, pNextNode, nInputPin, xPos, yPos, fMarkNew, &pTedNode) );
|
|
}
|
|
else if(MF_TOPOLOGY_OUTPUT_NODE == nodeType)
|
|
{
|
|
IFC( MergeOutputNode(pBeginTedNode, nOutputPin, pNextNode, nInputPin, xPos, yPos, &pTedNode) );
|
|
}
|
|
else if(MF_TOPOLOGY_TEE_NODE == nodeType)
|
|
{
|
|
TOPOID nodeID;
|
|
IFC( pNextNode->GetTopoNodeID(&nodeID) );
|
|
|
|
CTedTeeNode* pTeeNode = (CTedTeeNode*) FindNodeByTopoID(nodeID);
|
|
if(pTeeNode) RemoveNode(pTeeNode);
|
|
|
|
pTeeNode = new CTedTeeNode();
|
|
CHECK_ALLOC( pTeeNode );
|
|
pTeeNode->Init();
|
|
pTedNode = pTeeNode;
|
|
|
|
}
|
|
}
|
|
|
|
// If we did not generate a new node, use this iter's upstream node as the next iter's upstream node
|
|
if(!pTedNode)
|
|
{
|
|
pTedNode = pBeginTedNode;
|
|
}
|
|
else
|
|
{
|
|
pTedNode->CopyAttributes(pNextNode);
|
|
}
|
|
|
|
pTedNode->GetVisual()->Move(xPos, yPos);
|
|
|
|
xPos += int(pTedNode->GetVisual()->Rect().w() + MARGIN_SIZE);
|
|
yPos -= int((pTedNode->GetVisual()->Rect().h() / 2 + MARGIN_SIZE) * (outputCount - 1));
|
|
|
|
if(yPos < 0) yPos = 0;
|
|
|
|
for(DWORD i = 0; i < outputCount; i++)
|
|
{
|
|
spNode.Release();
|
|
IFC(pNextNode->GetOutput(i, &spNode, &nNewIndex));
|
|
|
|
// If a new node was not generated, use the upstream node's output pin
|
|
if(pTedNode == pBeginTedNode)
|
|
{
|
|
IFC( MergeBranch(pTedNode, nOutputPin, spNode, nNewIndex, xPos, yPos, fMarkNew) );
|
|
}
|
|
else
|
|
{
|
|
IFC( MergeBranch(pTedNode, i, spNode, nNewIndex, xPos, yPos, fMarkNew) );
|
|
}
|
|
|
|
yPos += int(pTedNode->GetVisual()->Rect().h() + MARGIN_SIZE);
|
|
}
|
|
|
|
if(pBeginTedNode != pTedNode)
|
|
{
|
|
if(pTedNode->GetMFNodeCount() > 1)
|
|
{
|
|
TOPOID tidNodeID;
|
|
IFC( pNextNode->GetTopoNodeID(&tidNodeID) );
|
|
|
|
// Find which pin corresponds to the TopoID of the current node
|
|
for(DWORD i = 0; i < pTedNode->GetMFNodeCount(); i++)
|
|
{
|
|
TOPOID tidNodeID2;
|
|
IFC( pTedNode->GetMFNode(i)->GetTopoNodeID(&tidNodeID2) );
|
|
|
|
if(tidNodeID == tidNodeID2)
|
|
{
|
|
nInputPin = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = FullConnectNodes(pBeginTedNode, nOutputPin, pTedNode, nInputPin);
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTopologyEditor::MergeTransformNode(CTedTopologyNode* pBeginTedNode, long nOutputPin,
|
|
IMFTopologyNode* pNextNode, long nInputPin,
|
|
int xPos, int yPos, bool fMarkNew, CTedTopologyNode** ppNewNode)
|
|
{
|
|
assert(pBeginTedNode != NULL);
|
|
assert(pNextNode != NULL);
|
|
assert(ppNewNode != NULL);
|
|
|
|
HRESULT hr = S_OK;
|
|
HRESULT hrTransform, hrActivate;
|
|
CTedTransformNode* pTedNode = NULL;
|
|
CComPtr<IUnknown> spTransformUnk;
|
|
CComPtr<IMFTransform> spTransform;
|
|
CComPtr<IMFActivate> spActivate;
|
|
|
|
GUID gidTransform = GUID_NULL;
|
|
pNextNode->GetGUID(MF_TOPONODE_TRANSFORM_OBJECTID, &gidTransform);
|
|
|
|
DMOInfo* pDMOInfo = DMOInfo::GetSingleton();
|
|
CAtlStringW strLabel = pDMOInfo->GetCLSIDName(gidTransform);
|
|
|
|
if(strLabel.IsEmpty())
|
|
{
|
|
// Cannot find a registered DMO with this CLSID, so just convert the CLSID
|
|
// directly to a string and display it to the user
|
|
LPOLESTR strClsid = NULL;
|
|
StringFromCLSID(gidTransform, &strClsid);
|
|
|
|
strLabel = OLE2W(strClsid);
|
|
|
|
CoTaskMemFree(strClsid);
|
|
}
|
|
|
|
|
|
hr = pNextNode->GetObject(&spTransformUnk);
|
|
if(FAILED(hr))
|
|
{
|
|
if(gidTransform == GUID_NULL)
|
|
{
|
|
// Probably a decrypter; just remove it
|
|
*ppNewNode = NULL;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
IFC( hr );
|
|
}
|
|
}
|
|
|
|
hrTransform = spTransformUnk->QueryInterface(IID_IMFTransform, (void**) &spTransform);
|
|
if(FAILED(hrTransform))
|
|
{
|
|
if(gidTransform == GUID_NULL)
|
|
{
|
|
*ppNewNode = NULL;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
hrActivate = spTransformUnk->QueryInterface(IID_IMFActivate, (void**) &spActivate);
|
|
if(FAILED(hrActivate))
|
|
{
|
|
IFC( hrActivate );
|
|
}
|
|
}
|
|
}
|
|
|
|
if(SUCCEEDED(hrTransform))
|
|
{
|
|
if(gidTransform == GUID_NULL)
|
|
{
|
|
CTedTransformNode* pTransformNode = new CTedTransformNode();
|
|
CHECK_ALLOC( pTransformNode );
|
|
pTransformNode->Init(spTransform, GUID_NULL, LoadAtlString(IDS_MFT_UNKNOWN), true);
|
|
pTedNode = pTransformNode;
|
|
IFC( AddComponent(pTedNode) );
|
|
|
|
pTedNode->CopyMediaTypes(pNextNode);
|
|
*ppNewNode = pTedNode;
|
|
return hr;
|
|
}
|
|
|
|
TOPOID tidNodeID;
|
|
pNextNode->GetTopoNodeID(&tidNodeID);
|
|
pTedNode = (CTedTransformNode*) FindNodeByTopoID(tidNodeID);
|
|
|
|
if(NULL == pTedNode)
|
|
{
|
|
CTedTransformNode* pTransformNode = new CTedTransformNode();
|
|
CHECK_ALLOC( pTransformNode );
|
|
pTransformNode->Init(spTransform, gidTransform, strLabel, fMarkNew);
|
|
pTedNode = pTransformNode;
|
|
IFC( AddComponent(pTedNode) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LPWSTR szFriendlyName = NULL;
|
|
if(SUCCEEDED( spActivate->GetAllocatedString(MFT_FRIENDLY_NAME_Attribute, &szFriendlyName, NULL) ))
|
|
{
|
|
strLabel = szFriendlyName;
|
|
CoTaskMemFree(szFriendlyName);
|
|
}
|
|
|
|
CTedTransformNode* pTransformNode = new CTedTransformNode();
|
|
CHECK_ALLOC( pTransformNode );
|
|
pTransformNode->Init(spActivate, strLabel, fMarkNew);
|
|
pTedNode = pTransformNode;
|
|
IFC( AddComponent(pTedNode) );
|
|
}
|
|
|
|
*ppNewNode = pTedNode;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTopologyEditor::MergeOutputNode(CTedTopologyNode* pBeginTedNode, long nOutputPin,
|
|
IMFTopologyNode* pNextNode, long nInputPin,
|
|
int xPos, int yPos, CTedTopologyNode** ppNewNode)
|
|
{
|
|
assert(pBeginTedNode != NULL);
|
|
assert(pNextNode != NULL);
|
|
assert(ppNewNode != NULL);
|
|
|
|
HRESULT hr = S_OK;
|
|
CTedTopologyNode* pTedNode = NULL;
|
|
HWND hWndOld = NULL;
|
|
|
|
TOPOID tidNodeID;
|
|
IFC( pNextNode->GetTopoNodeID(&tidNodeID) );
|
|
|
|
pTedNode = FindNodeByTopoID(tidNodeID);
|
|
if(pTedNode)
|
|
{
|
|
RemoveNode(pTedNode);
|
|
}
|
|
|
|
{
|
|
CComPtr<IUnknown> spStreamSinkUnk;
|
|
CComPtr<IMFStreamSink> spStreamSink;
|
|
CComPtr<IMFActivate> spActivate;
|
|
CComPtr<IMFMediaType> spPrefType;
|
|
bool fIsEVR, fIsSAR;
|
|
|
|
IFC( IsEVR(pNextNode, &fIsEVR) );
|
|
IFC( IsSAR(pNextNode, &fIsSAR) );
|
|
|
|
IFC( pNextNode->GetObject(&spStreamSinkUnk) );
|
|
HRESULT hrHasStreamSink = spStreamSinkUnk->QueryInterface(IID_IMFStreamSink, (void**) &spStreamSink);
|
|
HRESULT hrHasActivate = spStreamSinkUnk->QueryInterface(IID_IMFActivate, (void**) &spActivate );
|
|
|
|
if(fIsEVR)
|
|
{
|
|
CTedVideoOutputNode* pVideoNode = new CTedVideoOutputNode();
|
|
CHECK_ALLOC( pVideoNode );
|
|
CAtlString str = LoadAtlString(IDS_VIDEO_RENDERER);
|
|
|
|
if(hrHasStreamSink == S_OK)
|
|
{
|
|
CComPtr<IMFGetService> spGetService;
|
|
CComPtr<IMFVideoDisplayControl> spDisplayControl;
|
|
HWND hCurrentWnd = NULL;
|
|
|
|
IFC( spStreamSink->QueryInterface(IID_IMFGetService, (void**) &spGetService) );
|
|
IFC( spGetService->GetService(MR_VIDEO_RENDER_SERVICE, IID_IMFVideoDisplayControl, (void**) &spDisplayControl) );
|
|
IFC( spDisplayControl->GetVideoWindow(&hCurrentWnd) );
|
|
|
|
HWND hVideoWnd = hCurrentWnd;
|
|
if(hCurrentWnd == NULL && m_spVideoCallback.p != NULL)
|
|
{
|
|
IFC( m_spVideoCallback->GetVideoWindow((LONG_PTR*) &hVideoWnd) );
|
|
IFC( spDisplayControl->SetVideoWindow(hVideoWnd) );
|
|
}
|
|
|
|
pVideoNode->Init(str, hVideoWnd, spStreamSink);
|
|
}
|
|
else
|
|
{
|
|
HWND hVideoWnd = NULL;
|
|
if(m_spVideoCallback.p != NULL)
|
|
{
|
|
IFC( m_spVideoCallback->GetVideoWindow((LONG_PTR*) &hVideoWnd) );
|
|
}
|
|
|
|
pVideoNode->Init(str, hVideoWnd);
|
|
}
|
|
|
|
IFC( pVideoNode->CopyAttributes(pNextNode) );
|
|
|
|
pTedNode = pVideoNode;
|
|
IFC( AddComponent(pTedNode) );
|
|
}
|
|
else if(fIsSAR)
|
|
{
|
|
CTedAudioOutputNode* pAudioNode = new CTedAudioOutputNode();
|
|
CHECK_ALLOC( pAudioNode );
|
|
|
|
CAtlString str = LoadAtlString(IDS_AUDIO_RENDERER);
|
|
if(hrHasStreamSink == S_OK)
|
|
{
|
|
pAudioNode->Init(str, spStreamSink);
|
|
}
|
|
else
|
|
{
|
|
pAudioNode->Init(str);
|
|
}
|
|
|
|
IFC( pAudioNode->CopyAttributes(pNextNode) );
|
|
|
|
pTedNode = pAudioNode;
|
|
IFC( AddComponent(pTedNode) );
|
|
}
|
|
else if(hrHasStreamSink == S_OK)
|
|
{
|
|
CComPtr<IMFMediaSink> spSink;
|
|
|
|
IFC( spStreamSink->GetMediaSink(&spSink) );
|
|
|
|
CAtlString str = LoadAtlString(IDS_CUSTOM_SINK);
|
|
|
|
CTedCustomOutputNode* pCustomNode = new CTedCustomOutputNode();
|
|
CHECK_ALLOC( pCustomNode );
|
|
pCustomNode->Init(spSink, str);
|
|
|
|
IFC( pCustomNode->CopyAttributes(pNextNode) );
|
|
|
|
pTedNode = pCustomNode;
|
|
IFC( AddComponent(pTedNode) );
|
|
|
|
}
|
|
else if(hrHasActivate == S_OK)
|
|
{
|
|
CTedOutputNode* pActivateNode = new CTedOutputNode();
|
|
CHECK_ALLOC( pActivateNode );
|
|
pActivateNode->Init(spActivate, L"Sink Activate");
|
|
|
|
IFC( pActivateNode->CopyAttributes(pNextNode) );
|
|
|
|
pTedNode = pActivateNode;
|
|
IFC( AddComponent(pTedNode) );
|
|
}
|
|
else
|
|
{
|
|
// Ignore this node if it has no stream sink
|
|
}
|
|
}
|
|
|
|
*ppNewNode = pTedNode;
|
|
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
// Ugly hack to figure out if a given topology node is an EVR stream -- check for IMFVideoRenderer interface
|
|
HRESULT CTedTopologyEditor::IsEVR(IMFTopologyNode* pNode, bool* fIsEVR)
|
|
{
|
|
if(NULL == fIsEVR)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
*fIsEVR = false;
|
|
|
|
HRESULT hr = S_OK;
|
|
CComPtr<IUnknown> spStreamSinkUnk;
|
|
CComPtr<IMFStreamSink> spStreamSink;
|
|
CComPtr<IMFMediaSink> spSink;
|
|
CComPtr<IMFVideoRenderer> spVR;
|
|
|
|
GUID gidActivateID;
|
|
hr = pNode->GetGUID(MF_TOPONODE_TRANSFORM_OBJECTID, &gidActivateID);
|
|
|
|
if(SUCCEEDED(hr) && gidActivateID == CLSID_VideoRenderActivate)
|
|
{
|
|
*fIsEVR = true;
|
|
return S_OK;
|
|
}
|
|
|
|
IFC( pNode->GetObject(&spStreamSinkUnk) );
|
|
IFC( spStreamSinkUnk->QueryInterface(IID_IMFStreamSink, (void**) &spStreamSink) );
|
|
IFC( spStreamSink->GetMediaSink(&spSink) );
|
|
hr = spSink->QueryInterface(IID_IMFVideoRenderer, (void**) &spVR);
|
|
|
|
*fIsEVR = SUCCEEDED(hr);
|
|
|
|
Cleanup:
|
|
return S_OK;
|
|
}
|
|
|
|
// Ugly hack to figure out if a given topology node is a SAR stream -- check for IMFAudioStreamVolume interface
|
|
HRESULT CTedTopologyEditor::IsSAR(IMFTopologyNode* pNode, bool* fIsSAR)
|
|
{
|
|
if(NULL == fIsSAR)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
*fIsSAR = false;
|
|
|
|
HRESULT hr = S_OK;
|
|
CComPtr<IUnknown> spStreamSinkUnk;
|
|
CComPtr<IMFStreamSink> spStreamSink;
|
|
CComPtr<IMFAudioStreamVolume> spASV;
|
|
|
|
GUID gidActivateID;
|
|
hr = pNode->GetGUID(MF_TOPONODE_TRANSFORM_OBJECTID, &gidActivateID);
|
|
|
|
if(SUCCEEDED(hr) && gidActivateID == CLSID_AudioRenderActivate)
|
|
{
|
|
*fIsSAR = true;
|
|
return S_OK;
|
|
}
|
|
|
|
IFC( pNode->GetObject(&spStreamSinkUnk) );
|
|
IFC( spStreamSinkUnk->QueryInterface(IID_IMFStreamSink, (void**) &spStreamSink) );
|
|
hr = spStreamSink->QueryInterface(IID_IMFAudioStreamVolume, (void**) &spASV);
|
|
|
|
*fIsSAR = SUCCEEDED(hr);
|
|
|
|
Cleanup:
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CTedTopologyEditor::RemoveOrphanTransforms()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
for(size_t i = 0; i < m_Nodes.GetCount(); i++)
|
|
{
|
|
CTedTopologyNode* pNode = m_Nodes.GetAt(i);
|
|
|
|
if(pNode->GetType() == CTedTopologyNode::TED_TRANSFORM_NODE && pNode->IsOrphaned())
|
|
{
|
|
IFC( RemoveNode(pNode) );
|
|
--i;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTedTopologyEditor::RemoveNodesNotInTopology(IMFTopology* pTopology)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
for(size_t i = 0; i < m_Nodes.GetCount(); i++)
|
|
{
|
|
CTedTopologyNode* pTedNode = m_Nodes.GetAt(i);
|
|
CComPtr<IMFTopologyNode> spNode;
|
|
TOPOID tidTedNode;
|
|
|
|
IFC( pTedNode->GetMFNode(0)->GetTopoNodeID(&tidTedNode) );
|
|
if( FAILED( pTopology->GetNodeByID(tidTedNode, &spNode) ) )
|
|
{
|
|
IFC( RemoveNode(pTedNode) );
|
|
i--;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
// Non-public Helpers - Find //
|
|
|
|
CTedTopologyNode* CTedTopologyEditor::FindNodeWithID(int id)
|
|
{
|
|
for(size_t i = 0; i < m_Nodes.GetCount(); i++)
|
|
{
|
|
if(m_Nodes.GetAt(i)->GetID() == id) return m_Nodes.GetAt(i);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CTedSourceNode* CTedTopologyEditor::FindSourceNode(TOPOID nodeID)
|
|
{
|
|
for(size_t i = 0; i < m_Nodes.GetCount(); i++)
|
|
{
|
|
if(m_Nodes.GetAt(i)->GetType() == CTedTopologyNode::TED_SOURCE_NODE)
|
|
{
|
|
CTedSourceNode* pSourceNode = (CTedSourceNode*) m_Nodes.GetAt(i);
|
|
|
|
DWORD dwIndex = pSourceNode->GetIndexOf(nodeID);
|
|
|
|
if(dwIndex != (DWORD) -1)
|
|
{
|
|
return pSourceNode;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CTedTopologyNode* CTedTopologyEditor::FindNodeByTopoID(TOPOID nodeID)
|
|
{
|
|
for(size_t i = 0; i < m_Nodes.GetCount(); i++)
|
|
{
|
|
for(DWORD j = 0; j < m_Nodes.GetAt(i)->GetMFNodeCount(); j++)
|
|
{
|
|
TOPOID currNodeID;
|
|
m_Nodes.GetAt(i)->GetNodeID(j, currNodeID);
|
|
|
|
if(currNodeID == nodeID)
|
|
{
|
|
return m_Nodes.GetAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|