程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 最簡單的基於DirectShow的示例:視頻播放器自定義版

最簡單的基於DirectShow的示例:視頻播放器自定義版

編輯:C++入門知識

最簡單的基於DirectShow的示例:視頻播放器自定義版


本文記錄一個簡單的基於DirectShow的自定義的視頻播放器。這裡所說的“自定義播放器”,實際上指的是自己在Filter Graph中手動逐個添加Filter,並且連接這些Filter的後運行的播放器。這麼做相對於使用RenderFile()這種“智能”創建Filter Graph的方法來說要復雜不少,但是可以讓我們更加了解DirectShow的體系。
/

流程圖

最簡單的基於DirectShow的自定義的視頻播放器的流程如下圖所示。

/

該流程圖中包含如下變量:
IGraphBuilder *pGraph:繼承自IFilterGraph,用於構建Filter Graph。
IMediaControl *pControl:提供和播放控制有關的一些接口。
IMediaEvent *pEvent:用來處理Filter Graph發出的事件。
IBaseFilter *pF_source:源Filter。
IFileSourceFilter* pFileSource:源Filter的暴露的接口,用於設置輸入文件的路徑。
IBaseFilter *pF_demuxer:解復用Filter。
IBaseFilter *pF_decoder:解碼Filter。
IBaseFilter *pF_render:渲染Filter。
IPin *pOut:輸出Pin。
IPin *pIn:輸入Pin。
IPin **pPin:內部變量Pin。
該流程圖大體上可以分成以下步驟:
(1) 初始化DirectShow
包括以下幾個步驟:
a) CoInitialize():初始化COM運行環境。
b) CoCreateInstance(…,pGraph):用指定的類標識符創建一個Com對象。在這裡創建IGraphBuilder。
c) pGraph->QueryInterface(…,pControl):通過QueryInterface()查詢某個組件是否支持某個特定的接口。在這裡查詢IMediaControl接口。
d) pGraph->QueryInterface(…,pEvent):同上。在這裡查詢IMediaEvent接口。
(2) 添加Source Filter
包括以下幾個步驟:
a) CoCreateInstance(…,pF_source):創建Source Filter。
b) pGraph->AddFilter(pF_source,…):將Source Filter加入Filter Graph。
c) pF_source->QueryInterface(…,pFileSource):查找Source Filter的IFileSourceFilter接口。
d) pFileSource->Load(Lxxx.mpg,pF_source):調用IFileSourceFilter的Load()方法加載視頻文件。
(3) 添加Demuxer Filter
包括以下幾個步驟:
a) CoCreateInstance(…,pF_demuxer):創建Demuxer Filter。
b) pGraph->AddFilter(pF_demuxer,…):將Demuxer Filter加入Filter Graph。
(4) 添加Decoder Filter
包括以下幾個步驟:
a) CoCreateInstance(…,pF_decoder):創建Decoder Filter。
b) pGraph->AddFilter(pF_decoder,…):將Decoder Filter加入Filter Graph。
(5) 添加Render Filter
包括以下幾個步驟:
a) CoCreateInstance(…,pF_render):創建Render Filter。
b) pGraph->AddFilter(pF_render,…):將Render Filter加入Filter Graph。
(6) 連接Source Filter和Demuxer Filter
調用了一個函數connect_filters()用於連接2個Filter。
connect_filters()的執行步驟如下:
a) 調用get_unconnected_pin()從源Filter中選擇一個沒有鏈接的輸出Pin。
b) 調用get_unconnected_pin()從目的Filter中選擇一個沒有鏈接的輸入Pin。
c) 連接這兩個Pin
get_unconnected_pin()的執行步驟如下:
a) 枚舉Filter上的Pin。
b) 遍歷這些Pin,查找符合輸出方向(通過IPin的QueryDirection()方法),而且沒有在使用的Pin(通過IPin的ConnectedTo()方法)。
(7) 連接Demuxer Filter和Decoder Filter
過程同上。
(8) 連接Decoder Filter和Render Filter
過程同上。
(9) 開始播放
包括以下步驟:
pControl->Run():開始運行Filter Graph中的所有Filter。
pEvent->WaitForCompletion():等待Filter Graph處理完所有數據。

上述步驟可以理解為在GraphEdit軟件中分別按照步驟添加以下控件。其中(1)、(2)、(3)、(4)為先添加的4個Filter,(5)、(6)、(7)為Filter之間的連接線。

/

 

源代碼

/**
 * 最簡單的基於DirectShow的視頻播放器(Custom)
 * Simplest DirectShow Player (Custom)
 *
 * 雷霄骅 Lei Xiaohua
 * [email protected]
 * 中國傳媒大學/數字電視技術
 * Communication University of China / Digital TV Technology
 * http://blog.csdn.net/leixiaohua1020
 *
 * 本程序是一個簡單的基於DirectShow的視頻播放器。該播放器通過逐個添加
 * 濾鏡並連接這些濾鏡實現了視頻的播放。適合初學者學習DirectShow。
 * 
 * This software is a simple video player based on DirectShow.
 * It Add DirectShow Filter Manually and Link the Pins of these filters
 * to play videos.Suitable for the beginner of DirectShow.
 */

#include stdafx.h
#include 
//'1':Add filters manually
//'0':Add filters automatically
#define ADD_MANUAL 1

//Find unconnect pins
HRESULT get_unconnected_pin(
	IBaseFilter *pFilter, // Pointer to the filter.
	PIN_DIRECTION PinDir, // Direction of the pin to find.
	IPin **ppPin) // Receives a pointer to the pin.
{
	*ppPin = 0;
	IEnumPins *pEnum = 0;
	IPin *pPin = 0;
	HRESULT hr = pFilter->EnumPins(&pEnum);
	if (FAILED(hr))
	{
		return hr;
	}
	while (pEnum->Next(1, &pPin, NULL) == S_OK)
	{
		PIN_DIRECTION ThisPinDir;
		pPin->QueryDirection(&ThisPinDir);
		if (ThisPinDir == PinDir)
		{
			IPin *pTmp = 0;
			hr = pPin->ConnectedTo(&pTmp);
			if (SUCCEEDED(hr)) // Already connected, not the pin we want.
			{
				pTmp->Release();
			}
			else // Unconnected, the pin we want.
			{
				pEnum->Release();
				*ppPin = pPin;
				return S_OK;
			}
		}
		pPin->Release();
	}
	pEnum->Release();
	// Did not find a matching pin.
	return E_FAIL;
}

//Connect 2 filters
HRESULT connect_filters(
	IGraphBuilder *pGraph, 
	IBaseFilter *pSrc, 
	IBaseFilter *pDest)
{
	if ((pGraph == NULL) || (pSrc == NULL) || (pDest == NULL))
	{
		return E_POINTER;
	}
	//Find Output pin in source filter
	IPin *pOut = 0;
	HRESULT hr = NULL;
	hr=get_unconnected_pin(pSrc, PINDIR_OUTPUT, &pOut);
	if (FAILED(hr)){
		return hr;
	}
	//Find Input pin in destination filter
	IPin *pIn = 0;
	hr = get_unconnected_pin(pDest, PINDIR_INPUT, &pIn);
	if (FAILED(hr)){
		return hr;
	}
	//Connnect them
	hr = pGraph->Connect(pOut, pIn);
	pIn->Release();
	pOut->Release();
	return hr;
}

int _tmain(int argc, _TCHAR* argv[])
{
	IGraphBuilder *pGraph = NULL;
    IMediaControl *pControl = NULL;
    IMediaEvent   *pEvent = NULL; 
    // Init COM
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr)){
        printf(Error - Can't init COM.);
        return -1;
    }

	// Create FilterGraph
   hr=CoCreateInstance(CLSID_FilterGraph, NULL,CLSCTX_INPROC_SERVER,IID_IGraphBuilder, (void **)&pGraph);
    if (FAILED(hr)){
        printf(Error - Can't create Filter Graph.);
        return -1;
    }
   // Query Interface
    hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
    hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);

	//1. Add Filters=======================
	//Source
	IBaseFilter *pF_source = 0;
	hr = CoCreateInstance(CLSID_AsyncReader, 0, CLSCTX_INPROC_SERVER,IID_IBaseFilter, (void**)(&pF_source));
	if (FAILED(hr)){
		printf(Failed to create File Source.
);
		return -1;
	}
	hr = pGraph->AddFilter(pF_source, LLei's Source);
	if (FAILED(hr)){
		printf(Failed to add File Source to Filter Graph.
);
		return -1;
	}
	IFileSourceFilter* pFileSource;
	pF_source->QueryInterface(IID_IFileSourceFilter, (void**)&pFileSource);
	pFileSource->Load(Lcuc_ieschool.mpg, NULL);
	pFileSource->Release();

#if ADD_MANUAL
	//Demuxer
	IBaseFilter *pF_demuxer = 0;
	hr = CoCreateInstance(CLSID_MPEG1Splitter, 0, CLSCTX_INPROC_SERVER,IID_IBaseFilter, (void**)(&pF_demuxer));
	if (FAILED(hr)){
		printf(Failed to create Demuxer.
);
		return -1;
	}
	hr = pGraph->AddFilter(pF_demuxer, LLei's Demuxer);
	if (FAILED(hr)){
		printf(Failed to add Demuxer to Filter Graph.
);
		return -1;
	}
	//Decoder
	IBaseFilter *pF_decoder = 0;
	hr = CoCreateInstance(CLSID_CMpegVideoCodec, 0, CLSCTX_INPROC_SERVER,IID_IBaseFilter, (void**)(&pF_decoder));
	if (FAILED(hr)){
		printf(Failed to create Decoder.
);
		return -1;
	}
	hr = pGraph->AddFilter(pF_decoder, LLei's Decoder);
	if (FAILED(hr)){
		printf(Failed to add Decoder to Filter Graph.
);
		return -1;
	}
	//Render
	IBaseFilter *pF_render = 0;
	hr = CoCreateInstance(CLSID_VideoRenderer, 0, CLSCTX_INPROC_SERVER,IID_IBaseFilter, (void**)(&pF_render));
	if (FAILED(hr)){
		printf(Failed to create Video Render.
);
		return -1;
	}
	hr = pGraph->AddFilter(pF_render, LLei's Render);
	if (FAILED(hr)){
		printf(Failed to add Video Render to Filter Graph.
);
		return -1;
	}
	//2. Connect Filters=======================
	hr = connect_filters(pGraph, pF_source, pF_demuxer);
	if (FAILED(hr)){
		printf(Failed to link Source and Demuxer.
);
		return -1;
	}
	hr = connect_filters(pGraph, pF_demuxer, pF_decoder);
	if (FAILED(hr)){
		printf(Failed to link Demuxer and Decoder.
);
		return -1;
	}
	hr = connect_filters(pGraph, pF_decoder, pF_render);
	if (FAILED(hr)){
		printf(Failed to link Decoder and Render.
);
		return -1;
	}

	pF_source->Release();
	pF_demuxer->Release();
	pF_decoder->Release();
	pF_render->Release();
#else
	IPin*	 Pin;
	ULONG	 fetched;
	//	get output pin
	IEnumPins* pEnumPins;
	hr = pF_source->EnumPins(&pEnumPins);
	hr = pEnumPins->Reset();
	hr = pEnumPins->Next(1, &Pin, &fetched);
	pEnumPins->Release();
	//	render pin, graph builder automatically complete rest works
	hr = pGraph->Render(Pin);
#endif

    if (SUCCEEDED(hr)){
        // Run
        hr = pControl->Run();
        if (SUCCEEDED(hr)){
			long evCode=0;
			pEvent->WaitForCompletion(INFINITE, &evCode);
        }
    }
	//Release
    pControl->Release();
    pEvent->Release();
    pGraph->Release();
    CoUninitialize();
	return 0;
}

運行結果

程序的運行結果如下圖所示。運行後會播放“cuc_ieschool.mpg”文件。需要注意的是,本程序並沒有加入音頻解碼和播放的Filter,所以播放視頻的時候是沒有聲音的。

/

除了手動一個一個添加Filter之外,也可以在獲得“源”Filter的Pin之後,直接調用IFilterGraph的Render()方法“智能”自動構建Filter Graph。注意Render()方法和RenderFile()方法是不一樣的。RenderFile()是指定一個文件路徑後,自動構建整個Filter Graph,相對來說更加簡單些;而Render()方法則是首先要創建一個Source Filter之後,才可以自動構建整個Filter Graph。
可以通過修改源文件首部的宏定義ADD_MANUAL來設定是否手動添加Filter,如下所示。
//'1':Add filters manually
//'0':Add filters automatically
#define ADD_MANUAL 1

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved