Hello,
After spent week on the problem I need some help. Here the problem:
1. I have MJPEG camera and I successfully created source. It works great and produces MEDIATYPE_Video/MFVideoFormat_MJPG
So far so good.
2. Next step I wanted to use this source to make output to ASF file and it also works great. From traces I can see the topo:
MySource:MFVideoFormat_MJPEG->mfmjpegdec:MFVideoFormat_YUY2->MF:MFVideoFormat_IYUY->wmvencod:MFVideoFormat_WMV3,
I can see the file, play it, great!
So last thing I need to make HTTP stream and be able to re-stream MJPEG as ASF. And here I am failed. I use sample from great book "Developing MS MF Applications"
First how topo looks:
MySource:MFVideoFormat_MJPEG->TEE->MFVideoFormat_MJPEG->MF and this is it!!! Strange, why it didnt add ASF encoder?
I also tried to add manually MJPG/WMV transcorers but from traces they were not properly resolved which is also strange
I would be greatly appreciate it anybody have at least guess what is wrong!!!
Aleksey
Here the code, quite a lot, but it is really staitforward:
start(){
hr = CreateMediaSource(url);
hr = CreateNetworkSink(8080);
hr = CreateTopology();
}
CreateMediaSource() - so far it is not interesting
HRESULT CTopoBuilder::CreateNetworkSink(DWORD requestPort)
{
HRESULT hr = S_OK;
CComPtr<IMFPresentationDescriptor> pPresDescriptor;
CComPtr<IMFASFProfile> pAsfProfile;
CComQIPtr<IMFASFContentInfo> pAsfContentInfo;
CComPtr<IMFActivate> pByteStreamActivate;
CComPtr<IMFActivate> pNetSinkActivate;
do
{
BREAK_ON_NULL(m_pSource, E_UNEXPECTED);
// create an HTTP activator for the custom HTTP output byte stream object
pByteStreamActivate = new (std::nothrow) CHttpOutputStreamActivate(requestPort);
BREAK_ON_NULL(pByteStreamActivate, E_OUTOFMEMORY);
// create the presentation descriptor for the source
hr = m_pSource->CreatePresentationDescriptor(&pPresDescriptor);
BREAK_ON_FAIL(hr);
// create the ASF profile from the presentation descriptor
hr = MFCreateASFProfileFromPresentationDescriptor(pPresDescriptor, &pAsfProfile);
BREAK_ON_FAIL(hr);
// create the ContentInfo object for the ASF profile
hr = MFCreateASFContentInfo(&pAsfContentInfo);
BREAK_ON_FAIL(hr);
// set the profile on the content info object
hr = pAsfContentInfo->SetProfile(pAsfProfile);
BREAK_ON_FAIL(hr);
// create an activator object for an ASF streaming sink
hr = MFCreateASFStreamingMediaSinkActivate(pByteStreamActivate, pAsfContentInfo,
&m_pNetworkSinkActivate);
BREAK_ON_FAIL(hr);
}
while(false);
return hr;
}
HRESULT CTopoBuilder::CreateTopology(void)
{
HRESULT hr = S_OK;
CComQIPtr<IMFPresentationDescriptor> pPresDescriptor;
DWORD nSourceStreams = 0;
do
{
// release the old topology if there was one
m_pTopology.Release();
// Create a new topology.
hr = MFCreateTopology(&m_pTopology);
BREAK_ON_FAIL(hr);
// Create the presentation descriptor for the media source - a container object that
// holds a list of the streams and allows selection of streams that will be used.
hr = m_pSource->CreatePresentationDescriptor(&pPresDescriptor);
BREAK_ON_FAIL(hr);
// Get the number of streams in the media source
hr = pPresDescriptor->GetStreamDescriptorCount(&nSourceStreams);
BREAK_ON_FAIL(hr);
// For each stream, create source and sink nodes and add them to the topology.
for (DWORD x = 0; x < nSourceStreams; x++)
{
hr = AddBranchToPartialTopology(pPresDescriptor, x);
// if we failed to build a branch for this stream type, then deselect it
// that will cause the stream to be disabled, and the source will not produce
// any data for it
if(FAILED(hr))
{
hr = pPresDescriptor->DeselectStream(x);
BREAK_ON_FAIL(hr);
}
}
}
while(false);
return hr;
}
HRESULT CTopoBuilder::AddBranchToPartialTopology(
CComPtr<IMFPresentationDescriptor> pPresDescriptor,
DWORD nStream)
{
HRESULT hr = S_OK;
CComPtr<IMFStreamDescriptor> pStreamDescriptor;
CComPtr<IMFTopologyNode> pSourceNode;
CComPtr<IMFTopologyNode> pOutputNode;
BOOL streamSelected = FALSE;
do
{
BREAK_ON_NULL(m_pTopology, E_UNEXPECTED);
// Get the stream descriptor for this stream (information about stream).
hr = pPresDescriptor->GetStreamDescriptorByIndex(nStream, &streamSelected, &pStreamDescriptor);
BREAK_ON_FAIL(hr);
// Create the topology branch only if the stream is selected - IE if the user wants to play it.
if (streamSelected)
{
// Create a source node for this stream.
hr = CreateSourceStreamNode(pPresDescriptor, pStreamDescriptor, pSourceNode);
BREAK_ON_FAIL(hr);
// Create the output node for the renderer.
hr = CreateOutputNode(pStreamDescriptor, m_videoHwnd, pSourceNode, &pOutputNode);
BREAK_ON_FAIL(hr);
// Add the source and sink nodes to the topology.
hr = m_pTopology->AddNode(pSourceNode);
BREAK_ON_FAIL(hr);
hr = m_pTopology->AddNode(pOutputNode);
BREAK_ON_FAIL(hr);
// Connect the source node to the output node. The topology will find the
// intermediate nodes needed to convert media types.
hr = pSourceNode->ConnectOutput(0, pOutputNode, 0);
}
}
while(false);
return hr;
}
HRESULT CTopoBuilder::CreateSourceStreamNode(
CComPtr<IMFPresentationDescriptor> pPresDescriptor,
CComPtr<IMFStreamDescriptor> pStreamDescriptor,
CComPtr<IMFTopologyNode> &pNode)
{
HRESULT hr = S_OK;
do
{
BREAK_ON_NULL(pPresDescriptor, E_POINTER);
BREAK_ON_NULL(pStreamDescriptor, E_POINTER);
// Create the topology node, indicating that it must be a source node.
hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode);
BREAK_ON_FAIL(hr);
// Associate the node with the source by passing in a pointer to the media source,
// and indicating that it is the source
hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, m_pSource);
BREAK_ON_FAIL(hr);
// Set the node presentation descriptor attribute of the node by passing
// in a pointer to the presentation descriptor
hr = pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPresDescriptor);
BREAK_ON_FAIL(hr);
// Set the node stream descriptor attribute by passing in a pointer to the stream
// descriptor
hr = pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pStreamDescriptor);
BREAK_ON_FAIL(hr);
}
while(false);
return hr;
}
HRESULT CTopoBuilder::CreateOutputNode(
CComPtr<IMFStreamDescriptor> pStreamDescriptor,
HWND hwndVideo,
IMFTopologyNode* pSNode,
IMFTopologyNode** ppOutputNode)
{
HRESULT hr = S_OK;
CComPtr<IMFMediaTypeHandler> pHandler = NULL;
CComPtr<IMFActivate> pRendererActivate = NULL;
CComPtr<IMFTopologyNode> pSourceNode = pSNode;
CComPtr<IMFTopologyNode> pOutputNode;
GUID majorType = GUID_NULL;
do
{
if(m_videoHwnd != NULL)
{
// Get the media type handler for the stream which will be used to process
// the media types of the stream. The handler stores the media type.
hr = pStreamDescriptor->GetMediaTypeHandler(&pHandler);
BREAK_ON_FAIL(hr);
// Get the major media type (e.g. video or audio)
hr = pHandler->GetMajorType(&majorType);
BREAK_ON_FAIL(hr);
// Create an IMFActivate controller object for the renderer, based on the media type.
// The activation objects are used by the session in order to create the renderers only when
// they are needed - IE only right before starting playback. The activation objects are also
// used to shut down the renderers.
if (majorType == MFMediaType_Audio)
{
// if the stream major type is audio, create the audio renderer.
hr = MFCreateAudioRendererActivate(&pRendererActivate);
}
else if (majorType == MFMediaType_Video)
{
// if the stream major type is video, create the video renderer, passing in the video
// window handle - that's where the video will be playing.
hr = MFCreateVideoRendererActivate(hwndVideo, &pRendererActivate);
}
else
{
// fail if the stream type is not video or audio. For example fail
// if we encounter a CC stream.
hr = E_FAIL;
}
BREAK_ON_FAIL(hr);
// Create the node which will represent the renderer
hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pOutputNode);
BREAK_ON_FAIL(hr);
// Store the IActivate object in the sink node - it will be extracted later by the
// media session during the topology render phase.
hr = pOutputNode->SetObject(pRendererActivate);
BREAK_ON_FAIL(hr);
}
if(m_pNetworkSinkActivate != NULL)
{
CComPtr<IMFTopologyNode> pOldOutput = pOutputNode;
pOutputNode = NULL;
hr = CreateTeeNetworkTwig(pStreamDescriptor, pOldOutput, &pOutputNode);
BREAK_ON_FAIL(hr);
}
*ppOutputNode = pOutputNode.Detach();
}
while(false);
return hr;
}
HRESULT CTopoBuilder::CreateTeeNetworkTwig(IMFStreamDescriptor* pStreamDescriptor,
IMFTopologyNode* pRendererNode, IMFTopologyNode** ppTeeNode)
{
HRESULT hr = S_OK;
CComPtr<IMFTopologyNode> pNetworkOutputNode;
CComPtr<IMFTopologyNode> pTeeNode;
DWORD streamId = 0;
do
{
BREAK_ON_NULL(ppTeeNode, E_POINTER);
// if the network sink is not configured, just exit
if(m_pNetworkSinkActivate == NULL)
break;
// get the stream ID
hr = pStreamDescriptor->GetStreamIdentifier(&streamId);
BREAK_ON_FAIL(hr);
// create the output topology node for one of the streams on the network sink
hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNetworkOutputNode);
BREAK_ON_FAIL(hr);
// set the output stream ID on the stream sink topology node
hr = pNetworkOutputNode->SetUINT32(MF_TOPONODE_STREAMID, streamId);
BREAK_ON_FAIL(hr);
// associate the output network topology node with the network sink
hr = pNetworkOutputNode->SetObject(m_pNetworkSinkActivate);
BREAK_ON_FAIL(hr);
// add the network output topology node to the topology
m_pTopology->AddNode(pNetworkOutputNode);
BREAK_ON_FAIL(hr);
// create the topology Tee node
hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &pTeeNode);
BREAK_ON_FAIL(hr);
// connect the first Tee node output to the network sink node
hr = pTeeNode->ConnectOutput(0, pNetworkOutputNode, 0);
BREAK_ON_FAIL(hr);
// if a renderer node was created and passed in, add it to the topology
if(pRendererNode != NULL)
{
// add the renderer node to the topology
hr = m_pTopology->AddNode(pRendererNode);
BREAK_ON_FAIL(hr);
// connect the second Tee node output to the renderer sink node
hr = pTeeNode->ConnectOutput(1, pRendererNode, 0);
BREAK_ON_FAIL(hr);
}
// detach the Tee node and return it as the output node
*ppTeeNode = pTeeNode.Detach();
}
while(false);
return hr;
}