Click here to Skip to main content
15,881,248 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 357K   5.8K   79  
A simple streaming server using Direct Show and Windows Media Format SDK
// ServerDlg.cpp : implementation file
//

#include "stdafx.h"
#include "Server.h"
#include "ServerDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

#define ID_PAUSE_STATE_PAUSE "Pause"
#define ID_PAUSE_STATE_RUN "Run"


class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CServerDlg dialog

CServerDlg::CServerDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CServerDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CServerDlg)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	m_pWriter2 = NULL;
	m_pNetSink = NULL;
	m_pProvider = NULL;
	m_pGraph=NULL;
}

void CServerDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CServerDlg)
	DDX_Control(pDX, IDC_STATIC_URL, m_StaticUrl);
	DDX_Control(pDX, IDC_EDIT_PORT, m_EditPort);
	DDX_Control(pDX, IDC_BUTTON_STOP, m_btnStop);
	DDX_Control(pDX, IDC_BUTTON_PAUSE, m_btnPause);
	DDX_Control(pDX, IDC_BUTTON_START, m_btnStart);
	DDX_Control(pDX, IDC_COMBO_AUDEO_DEVICES, m_CboAudioDevices);
	DDX_Control(pDX, IDC_COMBO_VIDEO_DEVICES, m_CboVideoDevices);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CServerDlg, CDialog)
	//{{AFX_MSG_MAP(CServerDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON_START, OnButtonStart)
	ON_BN_CLICKED(IDC_BUTTON_PAUSE, OnButtonPause)
	ON_BN_CLICKED(IDC_BUTTON_STOP, OnButtonStop)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CServerDlg message handlers

BOOL CServerDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	GetVideoDevices("",NULL);//Fill video device combo
	GetAudeoDevices("",NULL);//Fill audeo device combo
	
	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CServerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CServerDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CServerDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

void CServerDlg::OnButtonStart() 
{

	if(StartStreaming())
	{
		m_btnStart.EnableWindow(FALSE);
		m_btnPause.EnableWindow(TRUE);
		m_btnStop.EnableWindow(TRUE);
		AfxMessageBox("Streaming started successfully");
	}
	else
	{
		AfxMessageBox("Start streaming failed");
	}
}


CServerDlg::GetVideoDevices(CString strDevName,IBaseFilter **pFilter)
{
	try
	{
		CoInitialize(NULL);
		int id = 0;
		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;
		hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
									&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=="")
					{
						m_CboVideoDevices.AddString(strdev);
					}
					else
					{
						if(strDevName==strdev)
						{
							pM->BindToObject(0, 0, IID_IBaseFilter, (void**)pFilter);
							break;
						}
					}

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

CServerDlg::GetAudeoDevices(CString strDevName,IBaseFilter **pFilter)
{
	try
	{
		

		CoInitialize(NULL);
		int id = 0;
		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 (hr != NOERROR)
		{

			return FALSE;
		}

		CComPtr<IEnumMoniker> pEm;
		hr = pCreateDevEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory,
									&pEm, 0);
		if (hr != NOERROR) 
		{
			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=="")
					{
						m_CboAudioDevices.AddString(strDev);
					}
					else
					{
						if(strDevName==strDev)
						{
							pM->BindToObject(0, 0, IID_IBaseFilter, (void**)pFilter);
							break;
						}
					}

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


HRESULT CServerDlg::ConnectFilters(IGraphBuilder *pGraph, 
        IBaseFilter *pFirst, IBaseFilter *pSecond)
{
	try
	{
		//CString strAsfVideoPin="Video Input 01";
		 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 CServerDlg::GetPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin)
{
	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;
	}
}

BOOL CServerDlg::StartStreaming()
{

///////////////////////////////////////////////////////////////////////////////////
	//CREATING 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;

///////////////////////////////////////////////////////////////////////////////////

	/*///////////////////////////////////////////////////////
	Get the Device and add to graph
	///////////////////////////////////////////////////////*/
	CString strVidDevName,strAudDevice;
	m_CboVideoDevices.GetWindowText(strVidDevName);
	m_CboAudioDevices.GetWindowText(strAudDevice);

	if(!GetVideoDevices(strVidDevName, &m_pVidDeviceFilter))
		return FALSE;

	if(!GetAudeoDevices(strAudDevice, &m_pAudDeviceFilter))
		return FALSE;


	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;
	


	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;
	
	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;

	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 form IServiceProvider failed");
		return FALSE;
	}

	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 removing the default sink...
	{
		AfxMessageBox (" Remove Sink failed");
		return FALSE;
	}

	m_pProvider->Release();

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


	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;
	}
	

///////////////////////////////////////////////////////////////////////////
//Run the graph
///////////////////////////////////////////////////////////////////////////

	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;

	

	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.

	return TRUE;

}


IPin* CServerDlg::GetPinByName(IBaseFilter *pFilter, LPCOLESTR pinname)
{
	try
	{
		// GET PIN BY NAME

		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;
	}
}


HRESULT CServerDlg::ConnectFilters(IGraphBuilder *pGraph, 
        IBaseFilter *pFirst, IBaseFilter *pSecond,CString strAsfFilterPin)
{
	try
	{
		 IPin *pOut = NULL, *pIn = NULL;
		 HRESULT hr=0;
		//HRESULT hr = GetPin(pSecond, PINDIR_INPUT, &pIn);
		 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;
	}
}


void CServerDlg::OnButtonPause() 
{
	HRESULT hr=0;
	CString strPauseState;
	m_btnPause.GetWindowText(strPauseState);
	if(strPauseState==ID_PAUSE_STATE_PAUSE)
	{
		if(m_pMediaControl)
		{
			hr=m_pMediaControl->Pause();
			if(!(FAILED(hr)))
			{
				m_btnPause.SetWindowText(ID_PAUSE_STATE_RUN);
				m_btnStop.EnableWindow(TRUE);
			}
		}
	}
	else if(strPauseState==ID_PAUSE_STATE_RUN)
	{
		if(m_pMediaControl)
		{
			hr=m_pMediaControl->Run();
			if(!(FAILED(hr)))
			{
				m_btnPause.SetWindowText(ID_PAUSE_STATE_PAUSE);
				m_btnStop.EnableWindow(TRUE);
			}
		}
	}
}

void CServerDlg::OnButtonStop() 
{
	HRESULT hr=0;
	if(m_pMediaControl)
	{
		hr=m_pMediaControl->Stop();	
		if((!FAILED(hr)))
		{
			m_btnPause.EnableWindow(FALSE);
			m_btnStop.EnableWindow(FALSE);
			m_btnStart.EnableWindow(FALSE);
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

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