locked
capturing using topology RRS feed

  • Question

  • Hi,

    i've got troubles creating a media capturing application using MF/Win7. Basically i want to capture video from the webcam and store it in a mpeg4-file using h264 encoding.

    First i tried to create a transcoding topology, but then i decided to create a normal topology manually.

    Therefore i perform following steps:

    1. create topology
    2. retrieve symbolic link of the webcam (source)
    3. create media source for the webcam using the symbolic link
    4. create source node for the media source
    5. create a color converter mft
    6. create a transform node for the color converter mft (the subtypes supplied by the webcam do not match the ones requested by the h264 encoder)
    7. connect transform node to source node
    8. create a h264 encoder mft
    9. create a transform node for the h264 encoder mft
    10. connect the h264 transform node to the color converter transform node
    11. create a bytestream for the file
    12. create an mpeg4 media sink using the bytestream
    13. create an output node for the media sink
    14. create a media session
    15. set the topology on the media session
    Currently i'm stuck with a MF_E_TOPO_CODEC_NOT_FOUND error when calling mediaSession->SetTopology().

    So i've got following questions:
    1. Do i have to specify input/output media types for each node manually?
    2. Do i have to configure the color converter mft manually?
    3. Can i use the current media type of the video media source as video media type for the file sink (except that i change the subtype to h264)?
    br, alexander spengler
    Tuesday, February 9, 2010 10:29 AM

Answers

  • 1) No, the topology loader will do this for you.  The one exception is the output type of the encoder.  You should set this to be equal to the input type of the sink, otherwise the encoder will not know what type you want to output.
    2) No.  In fact, you do not even have to insert a color converter yourself.  If the topology loader finds unmatching uncompressed types, it will automatically insert a color converter for you.
    3) This is where things get a little tricky.  This is generally the right thing to do, but if the source produces uncompressed data you will probably need to touch up the bitrate value.  Also, the encoder or sink may require other attributes that the source does not set.

    Have you looked into some of the more convenient transcoding APIs that MF provides in Win7?  MFCreateTranscodeTopology will set up the encoding topology for you, resolving all of the difficulties in setting the correct attributes on the encoder and sink.  Also, the source reader and sink writer make it pretty easy to do transcoding without having to do detailed topology setup.  The MF blog has a sample that uses MFCreateTranscodeTopology and a sample that uses the sink writer to do transcode.
    Wednesday, February 10, 2010 3:06 AM
  • Meanwhile I solved the task using SourceReader/SinkWriter. thx
    • Marked as answer by The March Hare Friday, February 12, 2010 2:19 PM
    Friday, February 12, 2010 9:10 AM

All replies

  • 1) No, the topology loader will do this for you.  The one exception is the output type of the encoder.  You should set this to be equal to the input type of the sink, otherwise the encoder will not know what type you want to output.
    2) No.  In fact, you do not even have to insert a color converter yourself.  If the topology loader finds unmatching uncompressed types, it will automatically insert a color converter for you.
    3) This is where things get a little tricky.  This is generally the right thing to do, but if the source produces uncompressed data you will probably need to touch up the bitrate value.  Also, the encoder or sink may require other attributes that the source does not set.

    Have you looked into some of the more convenient transcoding APIs that MF provides in Win7?  MFCreateTranscodeTopology will set up the encoding topology for you, resolving all of the difficulties in setting the correct attributes on the encoder and sink.  Also, the source reader and sink writer make it pretty easy to do transcoding without having to do detailed topology setup.  The MF blog has a sample that uses MFCreateTranscodeTopology and a sample that uses the sink writer to do transcode.
    Wednesday, February 10, 2010 3:06 AM
  • I removed the code for setting the input/output types manually and only set the output of the encoder mft. But now SetTopology keeps resulting in 0xC00D5212 (MF_E_TOPO_CODEC_NOT_FOUND) - regardless whether i add a color converter mft or not.

    	void CCaptureEngine::Start()
    	{
    		//create topology
    		IMFTopology *pTopology = CreateTopology();
    
    		//video device source node
    		String^ videoSymbolicLink = GetVideoSymbolicLink();
    		IMFMediaSource *pVideoMediaSource = CreateVideoDeviceSource(videoSymbolicLink);
    		IMFTopologyNode *pVideoSourceNode = CreateVideoSourceNode(pTopology, pVideoMediaSource);
    
    		IMFMediaType *pVideoSourceOutputMediaType = GetSourceMediaType(pVideoMediaSource);
    
    		//color converter mft node
    		IMFTransform *pColorConverterMFT = CreateColorConverterMFT();
    		IMFTopologyNode *pColorConverterNode = CreateColorConverterNode(pTopology, pColorConverterMFT);
    
    		CHECK_HR(pVideoSourceNode->ConnectOutput(0, pColorConverterNode, 0));
    
    		//h264 encoder mft node
    		IMFTransform *pVideoEncoderMFT = CreateVideoEncoderMFT();
    		IMFTopologyNode *pVideoEncoderNode = CreateVideoEncoderNode(pTopology, pVideoEncoderMFT);
    
    		IMFMediaType *pVideoEncoderOutputMediaType = CopyMediaType(pVideoSourceOutputMediaType, MFVideoFormat_H264);
    
    		CHECK_HR(pColorConverterNode->ConnectOutput(0, pVideoEncoderNode, 0));
    
    		//media types for file container
    		IMFMediaType *pVideoEncoderMediaType = pVideoEncoderOutputMediaType;
    		IMFMediaType *pAudioEncoderMediaType = NULL;
    
    		//file sink
    		IMFByteStream *pFileByteStream = CreateFileByteStream("D:\\capture.mp4");
    		IMFMediaSink *pMediaSink = CreateMediaSink(pFileByteStream, pVideoEncoderMediaType, pAudioEncoderMediaType);
    		IMFTopologyNode *pVideoOutputNode = CreateVideoOutputNode(pTopology, pMediaSink, 0);
    
    		CHECK_HR(pVideoEncoderNode->ConnectOutput(0, pVideoOutputNode, 0));
    
    		//create session
    		this->pSession = CreateMediaSession(m_pEventHandler);
    
    		HRESULT hrEventStatus = S_OK;
    		m_pEventHandler->SetWaitingEvent(MESessionTopologySet);
    		CHECK_HR(pSession->SetTopology(0, pTopology));
    		m_pEventHandler->Wait(&hrEventStatus, MAX_EVENT_WAIT_TIME);
    		CHECK_HR(hrEventStatus);
    
    		StartSession();
    	}
    
    	IMFMediaSource* CCaptureEngine::CreateVideoDeviceSource(String^ symbolicLink)
    	{
    		IMFAttributes *pAttributes = NULL;
    		IMFMediaSource *pSource = NULL;
    
    		try
    		{
    			CHECK_HR(MFCreateAttributes(&pAttributes, 2));
    
    			// Set the device type to video.
    			CHECK_HR(pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID));
    
    			// Set the symbolic link.
    			pin_ptr<const wchar_t> str = PtrToStringChars(symbolicLink);
    			CHECK_HR(pAttributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, str));
    
    			CHECK_HR(MFCreateDeviceSource(pAttributes, &pSource));
    
    			return pSource;
    		}
    		finally
    		{
    			SAFE_RELEASE(pAttributes);
    		}
    	}
    
    	IMFTopologyNode* CCaptureEngine::CreateVideoSourceNode(IMFTopology* pTopology, IMFMediaSource *pVideoMediaSource)
    	{
    		BOOL selected = FALSE;
    		IMFPresentationDescriptor *pVideoSourcePD = NULL;
    		IMFStreamDescriptor *pVideoSourceSD = NULL;
    
    		try
    		{
    			CHECK_HR(pVideoMediaSource->CreatePresentationDescriptor(&pVideoSourcePD));
    			CHECK_HR(pVideoSourcePD->GetStreamDescriptorByIndex(0, &selected, &pVideoSourceSD));
    
    			IMFTopologyNode *pNode = AddSourceNode(pTopology, pVideoMediaSource, pVideoSourcePD, pVideoSourceSD);
    			return pNode;
    		}
    		finally
    		{
    			SAFE_RELEASE(pVideoSourcePD);
    			SAFE_RELEASE(pVideoSourceSD);
    		}
    	}
    
    	IMFTransform* CCaptureEngine::CreateColorConverterMFT()
    	{
    		//register color converter locally
    		CHECK_HR(MFTRegisterLocalByCLSID(__uuidof(CColorConvertDMO), MFT_CATEGORY_VIDEO_PROCESSOR, L"", MFT_ENUM_FLAG_SYNCMFT, 0, NULL, 0, NULL));
    		
    		//create color converter
    		IMFTransform *pColorConverterMFT = NULL;
    		CHECK_HR(CoCreateInstance(__uuidof(CColorConvertDMO), NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform, (void**)&pColorConverterMFT));
    
    		return pColorConverterMFT;
    	}
    
    	IMFTopologyNode* CCaptureEngine::CreateColorConverterNode(IMFTopology* pTopology, IMFTransform *pColorConverterMFT)
    	{
    		IMFTopologyNode *pNode = AddTransformNode(pTopology, pColorConverterMFT);
    		return pNode;
    	}
    
    	IMFTransform* CCaptureEngine::CreateVideoEncoderMFT()
    	{
    		UINT32 count = 0;
    
    		IMFActivate **ppActivate = NULL;
    
    		MFT_REGISTER_TYPE_INFO info = { 0 };
    
    		info.guidMajorType = MFMediaType_Video;
    		info.guidSubtype = MFVideoFormat_H264;
    
    		try
    		{
    			CHECK_HR(MFTEnumEx(
    				MFT_CATEGORY_VIDEO_ENCODER,
    				MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_LOCALMFT | MFT_ENUM_FLAG_SORTANDFILTER,
    				NULL,       // Input type
    				&info,      // Output type
    				&ppActivate,
    				&count
    				));
    
    			if (count == 0)
    			{
    				CHECK_HR(MF_E_TOPO_CODEC_NOT_FOUND);
    			}
    
    			IMFTransform *pEncoder;
    			// Create the first encoder in the list.
    			CHECK_HR(ppActivate[0]->ActivateObject(__uuidof(IMFTransform),(void**)&pEncoder));
    
    			return pEncoder;
    		}
    		finally
    		{
    			for (UINT32 i = 0; i < count; i++)
    			{
    				SAFE_RELEASE(ppActivate[i]);
    			}
    			CoTaskMemFree(ppActivate);
    		}
    	}
    
    	IMFTopologyNode* CCaptureEngine::CreateVideoEncoderNode(IMFTopology* pTopology, IMFTransform *pVideoEncoderMFT)
    	{
    		IMFTopologyNode *pNode = AddTransformNode(pTopology, pVideoEncoderMFT);
    		return pNode;
    	}
    
    	IMFByteStream* CCaptureEngine::CreateFileByteStream(String^ filename)
    	{
    		//create file byte stream
    		pin_ptr<const wchar_t> str = PtrToStringChars(filename);
    		IMFByteStream *pByteStream = NULL;
    		CHECK_HR(MFCreateFile(MF_ACCESSMODE_WRITE, MF_OPENMODE_DELETE_IF_EXIST, MF_FILEFLAGS_NONE, str, &pByteStream));
    		return pByteStream;
    	}
    
    	IMFMediaSink* CCaptureEngine::CreateMediaSink(IMFByteStream *pByteStream, IMFMediaType *pVideoMediaType, IMFMediaType *pAudioMediaType)
    	{
    		IMFMediaSink *pMediaSink = NULL;
    		CHECK_HR(MFCreateMPEG4MediaSink(pByteStream, pVideoMediaType, pAudioMediaType, &pMediaSink));
    		return pMediaSink;
    	}
    
    	IMFTopologyNode* CCaptureEngine::CreateVideoOutputNode(IMFTopology* pTopology, IMFMediaSink *pMediaSink, int streamIndex)
    	{
    		IMFStreamSink *pVideoStreamSink = NULL;
    		CHECK_HR(pMediaSink->GetStreamSinkByIndex(streamIndex, &pVideoStreamSink));
    
    		//create video output node
    		IMFTopologyNode *pNode = AddOutputNode(pTopology, pVideoStreamSink);
    		return pNode;
    	}
    

    Wednesday, February 10, 2010 8:52 AM
  • Meanwhile I solved the task using SourceReader/SinkWriter. thx
    • Marked as answer by The March Hare Friday, February 12, 2010 2:19 PM
    Friday, February 12, 2010 9:10 AM