Click here to Skip to main content
Click here to Skip to main content

Streaming Server using Direct Show and Windows Media Format

By , 13 May 2010
 
Server.JPG

Introduction

This article discusses how to stream video over network using DirectShow and Windows Media Format SDK. I am using WMAsfWriter to write the media sample to the port. Here I tried to explain streaming live video source like Webcam over network.

Background

In my recent article, I have explained how to capture an image from a streaming URL using DShow. After that, I tried to create my own streaming server like Windows Media encoder. Also I saw some queries about the same on the internet too. At last, I implemented this using some references from MSDN and the web. I hope my sample code will support someone in solving their problem.

Setting Up Your Visual Studio Project

You need to add header files from the Windows Platform SDK and the DShow base classes to your include path. The project has to be linked with Strmbase.lib and WMVCORE.lib.

#include "atlbase.h" 	// for using atl support 
#include "dshow.h"   	// DirectShow header 
#include "wmsdk.h" 	// Windows media format SDK 
#include "Dshowasf.h" 	// For asf support 

Using the Code

The following DShow interfaces will be used in this article:

IGraphBuilder  *m_pGraph ; 		//Graph builder 
IBaseFilter *m_pVidDeviceFilter; 	//video device filter 
IBaseFilter *m_pAudDeviceFilter; 	//Audio device filter 
IMediaControl *m_pMediaControl; 	//Media control for running the graph 
IMediaEvent *m_pEvent ; 		//Media Event interface for capture the media events
IBaseFilter *m_pWMASFWritter ; 	//The WMASFWriter filter is a wrapper for the 
				//WMWriter object(from WMF) which can multiplux 
				//the streams into an ASF file and write the ASF 
				//stream to any IWMWriterSink object.
				//It is from wmf SDK. 
IFileSinkFilter* m_pFileSinkFilter;	//A file sink filter in a video capture filter graph,
				//for instance, writes the output of the video 
				//compression filter to a file. 
				//If a filter needs the name of an output file, 
				//it should expose this interface to allow 
				//an application to set the file name. 
IWMWriterAdvanced2 *m_pWriter2 ; 	//provides the ability to set and retrieve 
				//named settings for an input. It is from WMF SDK. 
IWMWriterNetworkSink *m_pNetSink;	//is used to deliver streams to the network. 
IServiceProvider *m_pProvider ;	//Defines a mechanism for retrieving a service object;
				//that is, an object that provides custom support to 
				//other objects. Here we used to get the 
				//IWMWriterAdvanced2 interface instance. 

We can start with the CoInitialize because here we are going to work with COM. Then we need a filter graph to creating the capture graph.

HRESULT hr = CoInitialize(NULL); 
if(FAILED(hr)) 
	return FALSE;

hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, 
	IID_IGraphBuilder, (void **)&m_pGraph); 

if(FAILED(hr)) 
	return FALSE;

Here we need the input devices for getting the streams for streaming. The following function will retrieve the audio and video devices:

GetDevices(CString strDevName,IBaseFilter **pFilter,BOOL bVideo)
{
	try
	{
		CoInitialize(NULL);
		ULONG cRetrieved;
		IMoniker *pM;
		
		// enumerate all video capture devices
		CComPtr<icreatedevenum> pCreateDevEnum;
		HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, 
				NULL, CLSCTX_INPROC_SERVER,
				IID_ICreateDevEnum, (void**)&pCreateDevEnum);
		if (FAILED(hr)) 
		{
			return FALSE;
		}

		CComPtr<ienummoniker> pEm;

		if(bVideo)
		{
			hr = pCreateDevEnum->CreateClassEnumerator
				(CLSID_VideoInputDeviceCategory,
				&pEm, 0);
		}
		else
		{
	 		hr = pCreateDevEnum->CreateClassEnumerator
				(CLSID_AudioInputDeviceCategory,s
					&pEm, 0);
		}

		if (FAILED(hr)) 
		{

			return FALSE;
		}

		pEm->Reset();
		
		while(hr = pEm->Next(1, &pM, &cRetrieved), hr==S_OK)
		{
			IPropertyBag *pBag;
			hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
			if(SUCCEEDED(hr)) 
			{
				VARIANT var;
				var.vt = VT_BSTR;
				hr = pBag->Read(L"FriendlyName", &var, NULL);
				if (hr == NOERROR) 
				{
					TCHAR strdev[2048];		
					WideCharToMultiByte
					(CP_ACP,0,var.bstrVal, -1, strdev, 
					2048, NULL, NULL);
					if(strDevName==strdev)
					{
						pM->BindToObject(0, 0, 
						IID_IBaseFilter, (void**)pFilter);
						break;
					}

					SysFreeString(var.bstrVal);
				}
				pBag->Release();
			}
			pM->Release();
		}
		return TRUE;
	}
	catch(...)
	{
		return FALSE;
	}
}

if(!GetDevices(strVidDevName, &m_pVidDeviceFilter,TRUE))
	return FALSE;

if(!GetDevices(strAudDevName, &m_pAudDeviceFilter,FALSE))
	return FALSE;

and add these filters to graph.

hr=m_pGraph->AddFilter(m_pVidDeviceFilter,L"Vid Capture Filter");
	if(FAILED(hr)) 
		return FALSE;

	hr=m_pGraph->AddFilter(m_pAudDeviceFilter,L"Aud Capture Filter");
	if(FAILED(hr)) 
		return FALSE;

Create an instance of WMAsfWriter for writing the streams to writer sink.

hr = CoCreateInstance(CLSID_WMAsfWriter,NULL,CLSCTX_INPROC_SERVER, 
                      IID_IBaseFilter, (void **) &m_pWMASFWritter); 

	if(FAILED(hr)) 
		return FALSE;

	hr=m_pGraph->AddFilter(m_pWMASFWritter,L"ASF Writter");
	if(FAILED(hr)) 
		return FALSE;

Then query the file sink filter from asf writer for setting up the file name where the streams temporarily write. Once you completed the streaming, you will get an empty file with size 0 bytes.

hr = m_pWMASFWritter->QueryInterface
	( IID_IFileSinkFilter, (void**)&m_pFileSinkFilter );
	if(FAILED(hr)) 
		return FALSE;

	hr = m_pFileSinkFilter->SetFileName(L"C:\\test.wmv", NULL);
	if(FAILED(hr)) 
		return FALSE;

Here we can use the IServiceProvider interface to get the IWMWriterAdvanced2 instance for creating the writer network sink.

if (FAILED(hr = m_pWMASFWritter->QueryInterface(IID_IServiceProvider, 
	(void**)&m_pProvider)))
	{
		AfxMessageBox("Getting service provider failed");
		return FALSE;
	}

	if (FAILED(hr = m_pProvider->QueryService(IID_IWMWriterAdvanced2, 
		IID_IWMWriterAdvanced2, (void**)&m_pWriter2)))
	{
		AfxMessageBox ("Query Service failed");
		return FALSE;
	}

Setting the live source as true.IWMWriterAdvanced::SetLiveSource tells the writer whether the source of the data is expected to be running in real time or not. Also remove the default sink in the writer.

if (FAILED(hr = m_pWriter2->SetLiveSource(TRUE)))
	{
		AfxMessageBox ("Setting live source failed");
		return FALSE;
	}

	if (FAILED(hr = m_pWriter2->RemoveSink(0)))	//For the time being,
						//we are removing the 
						//default sink...
	{
		AfxMessageBox (" Remove Sink failed");
		return FALSE;
	}

	m_pProvider->Release(); 			//Dispose the object after use

Create the network sink object by calling the WMCreateWriterNetworkSink function, which returns an IWMWriterNetworkSink pointer.

        if (FAILED(hr = WMCreateWriterNetworkSink(&m_pNetSink)))
	{
		AfxMessageBox ("WMCreateWriterNetworkSink failed");
		return FALSE;
	}

Call IWMWriterNetworkSink::Open on the network sink and specify the port number to open; for example, 8080. Optionally, call IWMWriterNetworkSink::GetHostURL to get the URL of the host. Clients will access the content from this URL. You can also call IWMWriterNetworkSink::SetMaximumClients to restrict the number of clients.

CString strPort="";
m_EditPort.GetWindowText(strPort);

DWORD dwPort = 8080;

if(strPort!="")
{
	dwPort=atoi(strPort);
}

if (FAILED(hr = m_pNetSink->Open(&dwPort)))
{
	AfxMessageBox ("Port opening failed");
	return FALSE;
}

WCHAR url[256];
DWORD len = 256;

hr = m_pNetSink->GetHostURL(url, &len);

if(SUCCEEDED(hr))
{
	CString strUrl(url);
	m_StaticUrl.SetWindowText(strUrl);
}

if (FAILED(m_pWriter2->AddSink(m_pNetSink)))
{
	AfxMessageBox ("AddSink failed");
	return FALSE;
}

Now we will connect the Video device filter and audio device filter to WMASFWriter. The following functions will connect these filters together:

hr=ConnectFilters(m_pGraph, m_pVidDeviceFilter,m_pWMASFWritter,"Video Input 01");
if(FAILED(hr)) 
	return FALSE;

hr=ConnectFilters(m_pGraph, m_pAudDeviceFilter,m_pWMASFWritter);
if(FAILED(hr)) 
	return FALSE;
HRESULT ConnectFilters(IGraphBuilder *pGraph, 
        IBaseFilter *pFirst, IBaseFilter *pSecond)
{
	try
	{
		 IPin *pOut = NULL, *pIn = NULL;
		HRESULT hr = GetPin(pSecond, PINDIR_INPUT, &pIn);

		if (FAILED(hr)) return hr;
		// The previous filter may have multiple outputs, 
		// so try each one!
		IEnumPins  *pEnum;
		pFirst->EnumPins(&pEnum);
		while(pEnum->Next(1, &pOut, 0) == S_OK)
		{
				PIN_DIRECTION PinDirThis;
				pOut->QueryDirection(&PinDirThis);
				if (PINDIR_OUTPUT == PinDirThis)
				{
					hr = pGraph->Connect(pOut, pIn);
					switch(hr)
					{
					case S_OK:
							break;
					case VFW_S_PARTIAL_RENDER:
						AfxMessageBox
						("VFW_S_PARTIAL_RENDER");
						break;
					case E_ABORT:
						  AfxMessageBox("E_ABORT");
						break;
					case E_POINTER:
						AfxMessageBox("E_POINTER");
						break;
					case VFW_E_CANNOT_CONNECT:
						 AfxMessageBox
						("VFW_E_CANNOT_CONNECT");
						break;
					case VFW_E_NOT_IN_GRAPH:
						  AfxMessageBox
						("VFW_E_NOT_IN_GRAPH");
						break;
					}		
					
				   if(!FAILED(hr))
					{
						   break;
					}
				}
				pOut->Release();
		}
		pEnum->Release();
		pIn->Release();
		pOut->Release();
		return hr;
	}
	catch(...)
	{
		return -1;
	}
}

HRESULT ConnectFilters(IGraphBuilder *pGraph, 
        IBaseFilter *pFirst, IBaseFilter *pSecond,CString strAsfFilterPin)
{
	try
	{
		 IPin *pOut = NULL, *pIn = NULL;
		 HRESULT hr=0;
		 pIn=GetPinByName(pSecond,
			strAsfFilterPin.AllocSysString()); //Get pin by name...

		if (FAILED(hr)) return hr;
		// The previous filter may have multiple outputs, so try each one!
		IEnumPins  *pEnum;
		pFirst->EnumPins(&pEnum);
		while(pEnum->Next(1, &pOut, 0) == S_OK)
		{
				PIN_DIRECTION PinDirThis;
				pOut->QueryDirection(&PinDirThis);
				if (PINDIR_OUTPUT == PinDirThis)
				{
					hr = pGraph->Connect(pOut, pIn);
					switch(hr)
					{
					case S_OK:
						//AfxMessageBox
						//("Connection Success");
						break;
					case VFW_S_PARTIAL_RENDER:
						AfxMessageBox
						("VFW_S_PARTIAL_RENDER");
						break;
					case E_ABORT:
						AfxMessageBox("E_ABORT");
						break;
					case E_POINTER:
						AfxMessageBox("E_POINTER");
						break;
					case VFW_E_CANNOT_CONNECT:
						AfxMessageBox
						("VFW_E_CANNOT_CONNECT");
						break;
					case VFW_E_NOT_IN_GRAPH:
						AfxMessageBox
						("VFW_E_NOT_IN_GRAPH");
						break;
					}		
					
				   if(!FAILED(hr))
					{
						   break;
					}
				}
				pOut->Release();
		}
		pEnum->Release();
		pIn->Release();
		pOut->Release();
		return hr;
	}
	catch(...)
	{
		return -1;
	}
}

The following functions will retrieve the pin by direction and pin by name respectively.

HRESULT GetPin(IBaseFilter *pFilter, 
	PIN_DIRECTION PinDir, IPin **ppPin) //get pin by direction
{
	try
	{
		int i=0;
		IEnumPins  *pEnum;
		IPin       *pPin;
		pFilter->EnumPins(&pEnum);
		while(pEnum->Next(1, &pPin, 0) == S_OK)
		{
			 PIN_DIRECTION PinDirThis;
			 pPin->QueryDirection(&PinDirThis);
			 if (PinDir == PinDirThis)
			 {
				pEnum->Release();
				*ppPin = pPin;
				return S_OK;

			 }
			 pPin->Release();
		}
		pEnum->Release();
		return E_FAIL;  
	}
	catch(...)
	{
		return E_FAIL;
	}
}

IPin* ::GetPinByName(IBaseFilter *pFilter, LPCOLESTR pinname) //get pin by name
{
	try
	{
		IEnumPins* pEnum;
		IPin*      pPin;

		HRESULT hr = pFilter->EnumPins(&pEnum);
		if (FAILED(hr))
			return NULL;

		while(pEnum->Next(1, &pPin, 0) == S_OK)
		{
			PIN_INFO pinfo;
			pPin->QueryPinInfo(&pinfo);
			BOOL found = !_wcsicmp(pinname, pinfo.achName);

			if (pinfo.pFilter) 
				pinfo.pFilter->Release();

			if (found) 
			{
				return pPin;
			};
			pPin->Release();
		}
		return NULL;
	}
	catch(...)
	{
		return NULL;
	}
}

There we can run the graph.

hr = m_pGraph->QueryInterface(IID_IMediaControl, (void **)&m_pMediaControl);
if(FAILED(hr)) 
	return FALSE;

hr = m_pGraph->QueryInterface(IID_IMediaEvent, (void **)&m_pEvent); 
if(FAILED(hr)) 
	return FALSE;

hr = m_pMediaControl -> Run( ); 

if(FAILED(hr))
{
	AfxMessageBox ("Media Control Run failed");
	return FALSE;
}
long evCode=0;
hr=pEvent->WaitForCompletion(INFINITE, &evCode); // Wait till its done. 
//please be careful when using INFINITE, 
//this may block the entire app indefinitely.
//It will cause the application hang up.

You can pause or stop the streaming using the following methods:

hr=m_pMediaControl->Pause();
hr=m_pMediaControl->Stop();	

There we go. All are done from the streaming side. You can use the Windows Media player as the client. Just open the media player and select the Openurl from file menu and put the URL displayed in the server GUI there and start.

If you need your own client, you can use IWMASFReader to open this network location. You can capture image from this URL by using my following article:

Have a nice coding.... :)

Reference

History

  • First version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Rajeev K R Pala
Technical Lead
India India
Member
I am an iOS\VC++\.net software developer from India. I am currently employed as Software Engineer in a Product company. The main technologies I used are Objective C,VC++,MFC,C#.NET,DirectShow,COM,Windows media Format etc...
 
- 2+ Years in iOS Development
- 3+ years in VC++\MFC programming
- 3+ years in .NET programming
- 2+ years in COM,DirectShow,Windows media Format
 
I'm looking to advance my Software Engineering career by learning new technologies and extending my programming experience.
 
EMail : rajeevkr.asr@gmail.com
Skype : rajeevkr_asr

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionwonderfulmembergmarrero9 Mar '13 - 6:41 
Questionh264 codec or rtspmembermagic.on6 Jun '12 - 7:46 
AnswerRe: h264 codec or rtspmemberjfriedman11 Dec '12 - 11:47 
QuestionHow to play online streaming audio file at client pcmemberMember 88978911 May '12 - 7:19 
QuestionHow to write part of application widow to vga or usb out as video streammemberjayachanakya14 Mar '12 - 20:30 
GeneralMy vote of 5membercode_junkie29 Feb '12 - 16:33 
QuestionStrmbase.libmemberwave_hkman7 Feb '12 - 3:51 
AnswerRe: Strmbase.libmemberRajeev K R Pala8 Feb '12 - 4:38 
GeneralRe: Strmbase.libmemberwave_hkman8 Feb '12 - 21:55 
GeneralRe: Strmbase.libmemberRajeev K R Pala14 Feb '12 - 1:40 
GeneralRe: Strmbase.lib [modified]memberwave_hkman20 Feb '12 - 1:38 
Questionblocked after some secondsmembernever138881 Jan '12 - 2:33 
AnswerRe: blocked after some secondsmemberRajeev K R Pala8 Feb '12 - 4:44 
Strange issue !!!. It may be because of your server system configuration. Check the memory status in TASK MANAGER. Just try with VLC player also, then you can ensure it is not an issue with Media player(config).
Questionit´s possible to deactivate the buffering?memberpipols8318 Oct '11 - 23:37 
AnswerRe: it´s possible to deactivate the buffering?memberRajeev K R Pala29 Nov '11 - 22:15 
QuestionMedia Control Run failedmembermapartha11 Aug '11 - 20:33 
AnswerRe: Media Control Run failedmemberRajeev K R Pala11 Aug '11 - 20:46 
GeneralRe: Media Control Run failedmembermapartha11 Aug '11 - 22:29 
GeneralRe: Media Control Run failedmemberRajeev K R Pala11 Aug '11 - 23:14 
GeneralRe: Media Control Run failedmembermapartha18 Aug '11 - 18:29 
GeneralRe: Media Control Run failedmemberRajeev K R Pala23 Aug '11 - 0:42 
GeneralRe: Media Control Run failedmemberRajeev K R Pala23 Aug '11 - 0:43 
QuestionI can't build your project on VS2008memberHyoung-Joon Kim13 Apr '11 - 14:32 
AnswerRe: I can't build your project on VS2008memberHyoung-Joon Kim13 Apr '11 - 16:29 
GeneralRe: I can't build your project on VS2008memberRajeev K R Pala18 Apr '11 - 3:32 
GeneralMy vote of 5memberAriesLL15 Dec '10 - 6:18 
QuestionNo video from client side?memberAriesLL14 Dec '10 - 6:23 
AnswerRe: No video from client side?memberRajeev K R Pala14 Dec '10 - 20:16 
GeneralRe: No video from client side?memberAriesLL15 Dec '10 - 2:58 
GeneralRe: No video from client side?memberRajeev K R Pala15 Dec '10 - 4:30 
GeneralRe: No video from client side?memberAriesLL15 Dec '10 - 7:00 
GeneralRe: No video from client side?memberGaurav I Pandey24 Jan '11 - 14:10 
GeneralRe: No video from client side?memberRajeev K R Pala24 Jan '11 - 17:20 
GeneralMy vote of 5member王宇isthebest4 Aug '10 - 16:24 
GeneralMemory leakmemberhuangwanzhang26 Jul '10 - 16:16 
GeneralRe: Memory leakmemberRajeev K R Pala26 Jul '10 - 18:28 
GeneralRe: Memory leakmemberhuangwanzhang28 Jul '10 - 15:46 
GeneralRe: Memory leakmemberRajeev K R Pala6 Aug '10 - 4:16 
QuestionScaled?memberStephen Elliott22 Jul '10 - 14:16 
AnswerRe: Scaled?memberRajeev K R Pala23 Jul '10 - 22:01 
GeneralRe: Scaled?memberStephen Elliott25 Jul '10 - 9:16 
AnswerRe: Scaled?memberRajeev K R Pala25 Jul '10 - 19:56 
AnswerRe: Scaled?memberAriesLL15 Dec '10 - 2:51 
GeneralRe: Scaled?memberRajeev K R Pala15 Dec '10 - 4:22 
QuestionIs there a way to support hi-def with this? (H264)? What about flash?memberthready23 May '10 - 4:42 
AnswerRe: Is there a way to support hi-def with this? (H264)? What about flash?memberRajeev K R Pala24 May '10 - 2:40 
GeneralStream in Windows MobilememberMember 435511518 May '10 - 10:21 
GeneralRe: Stream in Windows MobilememberRajeev K R Pala18 May '10 - 20:25 
Questionclient ?memberMember 429555918 May '10 - 0:37 
AnswerRe: client ?memberRajeev K R Pala18 May '10 - 0:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 13 May 2010
Article Copyright 2010 by Rajeev K R Pala
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid