Click here to Skip to main content
15,860,861 members
Articles / Multimedia / DirectX

Streaming Server using Direct Show and Windows Media Format

Rate me:
Please Sign up or sign in to vote.
4.92/5 (31 votes)
13 May 2010CPOL3 min read 355.6K   5.8K   79   68
A simple streaming server using Direct Show and Windows Media Format SDK
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.

C++
#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:

C++
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.

C++
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:

C++
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.

C++
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.

C++
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.

C++
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.

C++
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.

C++
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.

C++
    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.

C++
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:

C++
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;
C++
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.

C++
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.

C++
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:

C++
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)


Written By
Architect
India India
I am an iOS\VC++\.net programmer. I am currently employed as an Architect in a software development company. The main technologies I used are
Objective C, Swift,VC++,MFC,C#.NET,DirectShow,COM,Windows media Format etc...

I'm looking to advance my career by learning new technologies and extending my programming experience.

EMail : rajeevkr.asr@gmail.com
Skype : rajeevkr_asr

Comments and Discussions

 
QuestionConverting to C# WMWriterAdvanced2 problrm Pin
dwk0012-Oct-23 5:04
dwk0012-Oct-23 5:04 
QuestionAdmin privileges Pin
swayz7-Jun-19 22:03
swayz7-Jun-19 22:03 
Questionstreaming over the Internet. Pin
Member 999284031-Dec-18 10:40
Member 999284031-Dec-18 10:40 
QuestionIs it possible to directly go from a video capture filter directly into the network filter without using a file at all? Pin
luckiejacky74569691-Jan-18 15:09
luckiejacky74569691-Jan-18 15:09 
QuestionHow to use IConfigAsfWriter change the output resolution? Pin
martin.liang23-Feb-15 1:13
martin.liang23-Feb-15 1:13 
QuestionHow to set the output resolution Pin
martin.liang30-Aug-14 3:45
martin.liang30-Aug-14 3:45 
AnswerRe: How to set the output resolution Pin
R@jeev K R1-Sep-14 22:29
R@jeev K R1-Sep-14 22:29 
Question•CodeProject: Save image from streaming URL Pin
TheCPUWizard14-Jun-14 6:57
TheCPUWizard14-Jun-14 6:57 
QuestionSome good resources!! Pin
shaima'19-Oct-13 9:14
shaima'19-Oct-13 9:14 
GeneralThanks Pin
shaima'14-Oct-13 8:42
shaima'14-Oct-13 8:42 
Questionset size video Pin
Đức Huỳnh Văn10-Oct-13 23:07
Đức Huỳnh Văn10-Oct-13 23:07 
AnswerRe: set size video Pin
R@jeev K R1-Sep-14 22:30
R@jeev K R1-Sep-14 22:30 
QuestionDo a own client Pin
Allan Pereira16-Jul-13 7:19
Allan Pereira16-Jul-13 7:19 
QuestionHi Rajeev could you post me how to use this in c#.Net.. Pin
sabarinew7904-Jun-13 0:54
sabarinew7904-Jun-13 0:54 
Questionwonderful Pin
gmarrero9-Mar-13 6:41
gmarrero9-Mar-13 6:41 
Questionh264 codec or rtsp Pin
magic.on6-Jun-12 7:46
magic.on6-Jun-12 7:46 
AnswerRe: h264 codec or rtsp Pin
jfriedman11-Dec-12 11:47
jfriedman11-Dec-12 11:47 
QuestionHow to play online streaming audio file at client pc Pin
Member 88978911-May-12 7:19
Member 88978911-May-12 7:19 
QuestionHow to write part of application widow to vga or usb out as video stream Pin
jayachanakya14-Mar-12 20:30
jayachanakya14-Mar-12 20:30 
GeneralMy vote of 5 Pin
code_junkie29-Feb-12 16:33
code_junkie29-Feb-12 16:33 
QuestionStrmbase.lib Pin
wave_hkman7-Feb-12 3:51
wave_hkman7-Feb-12 3:51 
AnswerRe: Strmbase.lib Pin
R@jeev K R8-Feb-12 4:38
R@jeev K R8-Feb-12 4:38 
GeneralRe: Strmbase.lib Pin
wave_hkman8-Feb-12 21:55
wave_hkman8-Feb-12 21:55 
GeneralRe: Strmbase.lib Pin
R@jeev K R14-Feb-12 1:40
R@jeev K R14-Feb-12 1:40 
GeneralRe: Strmbase.lib Pin
wave_hkman20-Feb-12 1:38
wave_hkman20-Feb-12 1:38 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.