Adicionando um decodificador a uma topologia
Este tópico descreve como adicionar um decodificador de áudio ou vídeo a uma topologia.
Para a maioria dos aplicativos de reprodução, você pode omitir os decodificadores da topologia parcial que você envia para a Sessão de Mídia. A Sessão de Mídia usa o carregador de topologia para concluir a topologia e o carregador de topologia insere todos os decodificadores necessários. No entanto, se você quiser selecionar um decodificador específico, poderá adicionar manualmente um decodificador à topologia.
Aqui estão as etapas gerais para adicionar um decodificador a uma topologia.
- Localize o CLSID do decodificador.
- Adicione um nó para o decodificador na topologia.
- Para um decodificador de vídeo, habilite a Aceleração de Vídeo DirectX. Esta etapa não é necessária para decodificadores de áudio.
Localizar o CLSID do decodificador
Se você quiser usar um decodificador específico, talvez já saiba o CLSID do decodificador. Nesse caso, você pode ignorar esta etapa. Caso contrário, use a função MFTEnum para pesquisar o CLSID no registro. Essa função usa vários critérios de pesquisa como entrada. Para localizar um decodificador, você precisa especificar apenas o formato de entrada (tipo principal e subtipo). Você pode obtê-los no descritor de fluxo, conforme mostrado no código a seguir.
// Returns the MFT decoder based on the major type GUID.
HRESULT GetDecoderCategory(const GUID& majorType, GUID *pCategory)
{
if (majorType == MFMediaType_Video)
{
*pCategory = MFT_CATEGORY_VIDEO_DECODER;
}
else if (majorType == MFMediaType_Audio)
{
*pCategory = MFT_CATEGORY_AUDIO_DECODER;
}
else
{
return MF_E_INVALIDMEDIATYPE;
}
return S_OK;
}
// Finds a decoder for a stream.
//
// If the stream is not compressed, pCLSID receives the value GUID_NULL.
HRESULT FindDecoderForStream(
IMFStreamDescriptor *pSD, // Stream descriptor for the stream.
CLSID *pCLSID // Receives the CLSID of the decoder.
)
{
BOOL bIsCompressed = FALSE;
GUID guidMajorType = GUID_NULL;
GUID guidSubtype = GUID_NULL;
GUID guidDecoderCategory = GUID_NULL;
CLSID *pDecoderCLSIDs = NULL; // Pointer to an array of CLISDs.
UINT32 cDecoderCLSIDs = NULL; // Size of the array.
IMFMediaTypeHandler *pHandler = NULL;
IMFMediaType *pMediaType = NULL;
// Find the media type for the stream.
HRESULT hr = pSD->GetMediaTypeHandler(&pHandler);
if (SUCCEEDED(hr))
{
hr = pHandler->GetCurrentMediaType(&pMediaType);
}
// Get the major type and subtype.
if (SUCCEEDED(hr))
{
hr = pMediaType->GetMajorType(&guidMajorType);
}
if (SUCCEEDED(hr))
{
hr = pMediaType->GetGUID(MF_MT_SUBTYPE, &guidSubtype);
}
// Check whether the stream is compressed.
if (SUCCEEDED(hr))
{
hr = pMediaType->IsCompressedFormat(&bIsCompressed);
}
#if (WINVER < _WIN32_WINNT_WIN7)
// Starting in Windows 7, you can connect an uncompressed video source
// directly to the EVR. In earlier versions of Media Foundation, this
// is not supported.
if (SUCCEEDED(hr))
{
if (!bIsCompressed && (guidMajorType == MFMediaType_Video))
{
hr = MF_E_INVALIDMEDIATYPE;
}
}
#endif
// If the stream is compressed, find a decoder.
if (SUCCEEDED(hr))
{
if (bIsCompressed)
{
// Select the decoder category from the major type (audio/video).
hr = GetDecoderCategory(guidMajorType, &guidDecoderCategory);
// Look for a decoder.
if (SUCCEEDED(hr))
{
MFT_REGISTER_TYPE_INFO tinfo;
tinfo.guidMajorType = guidMajorType;
tinfo.guidSubtype = guidSubtype;
hr = MFTEnum(
guidDecoderCategory,
0, // Reserved
&tinfo, // Input type to match. (Encoded type.)
NULL, // Output type to match. (Don't care.)
NULL, // Attributes to match. (None.)
&pDecoderCLSIDs, // Receives a pointer to an array of CLSIDs.
&cDecoderCLSIDs // Receives the size of the array.
);
}
// MFTEnum can return zero matches.
if (SUCCEEDED(hr) && (cDecoderCLSIDs == 0))
{
hr = MF_E_TOPO_CODEC_NOT_FOUND;
}
// Return the first CLSID in the list to the caller.
if (SUCCEEDED(hr))
{
*pCLSID = pDecoderCLSIDs[0];
}
}
else
{
// Uncompressed. A decoder is not required.
*pCLSID = GUID_NULL;
}
}
SafeRelease(&pHandler);
SafeRelease(&pMediaType);
CoTaskMemFree(pDecoderCLSIDs);
return hr;
}
Para obter mais informações sobre descritores de fluxo, consulte Descritores de apresentação.
A função MFTEnum retorna um ponteiro para uma matriz de CLSIDs. A ordem da matriz retornada é arbitrária. Neste exemplo, a função usa o primeiro CLSID na matriz. Você pode obter mais informações sobre um decodificador, incluindo o nome amigável do decodificador, chamando MFTGetInfo. Além disso, observe que MFTEnum pode ter êxito, mas retornar uma matriz vazia, portanto, é importante marcar o tamanho da matriz, que é retornado no último parâmetro.
Adicionar o nó de decodificador à topologia
Depois de ter o CLSID para o decodificador, crie um novo nó de transformação chamando MFCreateTopology. Especifique o CLSID definindo o atributo MF_TOPONODE_TRANSFORM_OBJECTID no nó. Para obter um exemplo de como criar um nó de transformação, consulte Criando nós de transformação. Em seguida, conecte o nó de origem ao nó do decodificador e o nó do decodificador ao nó de saída, chamando IMFTopologyNode::ConnectOutput.
O exemplo a seguir mostra como criar os nós e conectá-los. O exemplo é muito semelhante à função de exemplo chamada AddBranchToPartialTopology
que é mostrada no tópico Criando topologias de reprodução. A única diferença é que este exemplo adiciona o nó extra para o decodificador.
HRESULT AddBranchToPartialTopologyWithDecoder(
IMFTopology *pTopology, // Topology.
IMFMediaSource *pSource, // Media source.
IMFPresentationDescriptor *pPD, // Presentation descriptor.
DWORD iStream, // Stream index.
HWND hVideoWnd // Window for video playback.
)
{
IMFStreamDescriptor *pSD = NULL;
IMFActivate *pSinkActivate = NULL;
IMFTopologyNode *pSourceNode = NULL;
IMFTopologyNode *pOutputNode = NULL;
IMFTopologyNode *pDecoderNode = NULL;
BOOL fSelected = FALSE;
CLSID clsidDecoder = GUID_NULL;
// Get the stream descriptor.
HRESULT hr = pPD->GetStreamDescriptorByIndex(iStream, &fSelected, &pSD);
if (FAILED(hr))
{
return hr;
}
if (fSelected)
{
// Add a source node for this stream.
hr = AddSourceNode(pTopology, pSource, pPD, pSD, &pSourceNode);
// Create the media sink activation object.
if (SUCCEEDED(hr))
{
hr = CreateMediaSinkActivate(pSD, hVideoWnd, &pSinkActivate);
}
// Create the output node for the renderer.
if (SUCCEEDED(hr))
{
hr = AddOutputNode(pTopology, pSinkActivate, 0, &pOutputNode);
}
// Find a decoder.
if (SUCCEEDED(hr))
{
hr = FindDecoderForStream(pSD, &clsidDecoder);
}
if (SUCCEEDED(hr))
{
if (clsidDecoder == GUID_NULL)
{
// No decoder is required.
// Connect the source node to the output node.
hr = pSourceNode->ConnectOutput(0, pOutputNode, 0);
}
else
{
// Add a decoder node.
hr = AddTransformNode(pTopology, clsidDecoder, &pDecoderNode);
// Connect the source node to the decoder node.
if (SUCCEEDED(hr))
{
hr = pSourceNode->ConnectOutput(0, pDecoderNode, 0);
}
// Connect the decoder node to the output node.
if (SUCCEEDED(hr))
{
hr = pDecoderNode->ConnectOutput(0, pOutputNode, 0);
}
}
}
// Mark this branch as not requiring a decoder.
if (SUCCEEDED(hr))
{
hr = pOutputNode->SetUINT32(
MF_TOPONODE_CONNECT_METHOD,
MF_CONNECT_ALLOW_CONVERTER
);
}
if (SUCCEEDED(hr))
{
hr = pDecoderNode->SetUINT32(
MF_TOPONODE_CONNECT_METHOD,
MF_CONNECT_ALLOW_CONVERTER
);
}
}
// else: If not selected, don't add the branch.
SafeRelease(&pSD);
SafeRelease(&pSinkActivate);
SafeRelease(&pSourceNode);
SafeRelease(&pOutputNode);
SafeRelease(&pDecoderNode);
return hr;
}
Habilitar Aceleração de Vídeo
A próxima etapa na adição de um decodificador de áudio ou vídeo a uma topologia se aplica somente a decodificadores de vídeo. Para obter o melhor desempenho para reprodução de vídeo, você deve habilitar a Aceleração de Vídeo DirectX (DXVA) se o decodificador de vídeo der suporte a ele. Normalmente, essa etapa é executada pelo carregador de topologia, mas se você adicionar o decodificador à topologia manualmente, deverá executar essa etapa por conta própria.
Como pré-condição para essa etapa, todos os nós de saída na topologia devem estar associados a coletores de mídia. Para obter mais informações, consulte Associando nós de saída a coletores de mídia.
Primeiro, localize o objeto na topologia que hospeda o gerenciador de dispositivos Direct3D. Para fazer isso, obtenha o ponteiro do objeto de cada nó e consulte o objeto para o serviço IDirect3DDeviceManager9 . Normalmente, o EVR (renderizador de vídeo avançado) atende a essa função. O código a seguir mostra uma função que localiza o gerenciador de dispositivos:
// Finds the node in the topology that provides the Direct3D device manager.
HRESULT FindDeviceManager(
IMFTopology *pTopology, // Topology to search.
IUnknown **ppDeviceManager, // Receives a pointer to the device manager.
IMFTopologyNode **ppNode // Receives a pointer to the node.
)
{
HRESULT hr = S_OK;
WORD cNodes = 0;
BOOL bFound = FALSE;
IMFTopologyNode *pNode = NULL;
IUnknown *pNodeObject = NULL;
IDirect3DDeviceManager9 *pD3DManager = NULL;
// Search all of the nodes in the topology.
hr = pTopology->GetNodeCount(&cNodes);
if (FAILED(hr))
{
return hr;
}
for (WORD i = 0; i < cNodes; i++)
{
// For each of the following calls, failure just means we
// did not find the node we're looking for, so keep looking.
hr = pTopology->GetNode(i, &pNode);
// Get the node's object pointer.
if (SUCCEEDED(hr))
{
hr = pNode->GetObject(&pNodeObject);
}
// Query the node object for the device manager service.
if (SUCCEEDED(hr))
{
hr = MFGetService(
pNodeObject,
MR_VIDEO_ACCELERATION_SERVICE,
IID_PPV_ARGS(&pD3DManager)
);
}
if (SUCCEEDED(hr))
{
// Found the right node. Return the pointers to the caller.
*ppDeviceManager = pD3DManager;
(*ppDeviceManager)->AddRef();
*ppNode = pNode;
(*ppNode)->AddRef();
bFound = TRUE;
break;
}
SafeRelease(&pNodeObject);
SafeRelease(&pD3DManager);
SafeRelease(&pNode);
} // End of for loop.
SafeRelease(&pNodeObject);
SafeRelease(&pD3DManager);
SafeRelease(&pNode);
return bFound ? S_OK : E_FAIL;
}
Em seguida, localize o nó de transformação diretamente upstream do nó que contém o gerenciador de dispositivos Direct3D. Obtenha o ponteiro IMFTransform deste nó de transformação. O ponteiro IMFTransform representa uma transformação da Media Foundation (MFT). Dependendo de como o nó foi criado, talvez seja necessário criar o MFT chamando CoCreateInstance ou ativar o MFT de um objeto de ativação. O código a seguir manipula todos os vários casos:
// Returns the MFT for a transform node.
HRESULT GetTransformFromNode(
IMFTopologyNode *pNode,
IMFTransform **ppMFT
)
{
MF_TOPOLOGY_TYPE type = MF_TOPOLOGY_MAX;
IUnknown *pNodeObject = NULL;
IMFTransform *pMFT = NULL;
IMFActivate *pActivate = NULL;
IMFAttributes *pAttributes = NULL;
// Is this a transform node?
HRESULT hr = pNode->GetNodeType(&type);
if (FAILED(hr))
{
return hr;
}
if (type != MF_TOPOLOGY_TRANSFORM_NODE)
{
// Wrong node type.
return E_FAIL;
}
// Check whether the node has an object pointer.
hr = pNode->GetObject(&pNodeObject);
if (SUCCEEDED(hr))
{
// The object pointer should be one of the following:
// 1. Pointer to an MFT.
// 2. Pointer to an activation object.
// Is it an MFT? Query for IMFTransform.
hr = pNodeObject->QueryInterface(IID_IMFTransform, (void**)&pMFT);
if (FAILED(hr))
{
// It is not an MFT, so it should be an activation object.
hr = pNodeObject->QueryInterface(IID_PPV_ARGS(&pActivate));
// Use the activation object to create the MFT.
if (SUCCEEDED(hr))
{
hr = pActivate->ActivateObject(IID_PPV_ARGS(&pMFT));
}
// Replace the node's object pointer with the MFT.
if (SUCCEEDED(hr))
{
hr = pNode->SetObject(pMFT);
}
// If the activation object has the MF_ACTIVATE_MFT_LOCKED
// attribute, transfer this value to the
// MF_TOPONODE_MFT_LOCKED attribute on the node.
// However, don't fail if this attribute is not found.
if (SUCCEEDED(hr))
{
BOOL bLocked = MFGetAttributeUINT32(
pActivate, MF_ACTIVATE_MFT_LOCKED, FALSE);
hr = pNode->SetUINT32(MF_TOPONODE_LOCKED, bLocked);
}
}
}
else
{
GUID clsidMFT;
// The node does not have an object pointer. Look for a CLSID.
hr = pNode->GetGUID(MF_TOPONODE_TRANSFORM_OBJECTID, &clsidMFT);
// Create the MFT.
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(
clsidMFT, NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pMFT)
);
}
// If the MFT supports attributes, copy the node attributes to the
// MFT attribute store.
if (SUCCEEDED(hr))
{
if (SUCCEEDED(pMFT->GetAttributes(&pAttributes)))
{
// Copy from pNode to pAttributes.
hr = pNode->CopyAllItems(pAttributes);
}
}
// Set the object on the node.
if (SUCCEEDED(hr))
{
hr = pNode->SetObject(pMFT);
}
}
// Return the IMFTransform pointer to the caller.
if (SUCCEEDED(hr))
{
*ppMFT = pMFT;
(*ppMFT)->AddRef();
}
SafeRelease(&pNodeObject);
SafeRelease(&pMFT);
SafeRelease(&pActivate);
SafeRelease(&pAttributes);
return hr;
}
Se o MFT tiver o atributo MF_SA_D3D_AWARE com o valor TRUE, isso significa que o MFT dá suporte à Aceleração de Vídeo DirectX. A função a seguir testa esse atributo:
// Returns TRUE is an MFT supports DirectX Video Acceleration.
BOOL IsTransformD3DAware(IMFTransform *pMFT)
{
BOOL bD3DAware = FALSE;
IMFAttributes *pAttributes = NULL;
HRESULT hr = pMFT->GetAttributes(&pAttributes);
if (SUCCEEDED(hr))
{
bD3DAware = MFGetAttributeUINT32(pAttributes, MF_SA_D3D_AWARE, FALSE);
pAttributes->Release();
}
return bD3DAware;
}
Para habilitar a aceleração de vídeo neste MFT, chame IMFTransform::P rocessMessage com a mensagem MFT_MESSAGE_SET_D3D_MANAGER. Defina também o atributo MF_TOPONODE_D3DAWARE como TRUE no nó de transformação. Esse atributo informa ao pipeline que a aceleração de vídeo foi habilitada. O código a seguir executa estas etapas:
// Enables or disables DirectX Video Acceleration in a topology.
HRESULT EnableVideoAcceleration(IMFTopology *pTopology, BOOL bEnable)
{
IMFTopologyNode *pD3DManagerNode = NULL;
IMFTopologyNode *pUpstreamNode = NULL;
IUnknown *pD3DManager = NULL;
IMFTransform *pMFT = NULL;
// Look for the node that supports the Direct3D Manager.
HRESULT hr = FindDeviceManager(pTopology, &pD3DManager, &pD3DManagerNode);
if (FAILED(hr))
{
// There is no Direct3D device manager in the topology.
// This is not a failure case.
return S_OK;
}
DWORD dwOutputIndex = 0;
// Get the node upstream from the device manager node.
hr = pD3DManagerNode->GetInput(0, &pUpstreamNode, &dwOutputIndex);
// Get the MFT from the upstream node.
if (SUCCEEDED(hr))
{
hr = GetTransformFromNode(pUpstreamNode, &pMFT);
}
// If the MFT is Direct3D-aware, notify the MFT of the device
// manager and mark the topology node as Direct3D-aware.
if (SUCCEEDED(hr))
{
if (IsTransformD3DAware(pMFT))
{
ULONG_PTR ptr = bEnable ? (ULONG_PTR)pD3DManager : NULL;
hr = pMFT->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, ptr);
// Mark the node.
if (SUCCEEDED(hr))
{
hr = pUpstreamNode->SetUINT32(MF_TOPONODE_D3DAWARE, bEnable);
}
}
}
SafeRelease(&pD3DManagerNode);
SafeRelease(&pUpstreamNode);
SafeRelease(&pD3DManager);
SafeRelease(&pMFT);
return hr;
}
Para obter mais informações sobre o DXVA no Media Foundation, consulte Aceleração de Vídeo DirectX 2.0.
Tópicos relacionados