1. 程式人生 > >最簡單的基於DirectShow的示例:視訊播放器自定義版

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

=====================================================

最簡單的基於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(L"xxx.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 <dshow.h>
//'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.\n");
		return -1;
	}
	hr = pGraph->AddFilter(pF_source, L"Lei's Source");
	if (FAILED(hr)){
		printf("Failed to add File Source to Filter Graph.\n");
		return -1;
	}
	IFileSourceFilter* pFileSource;
	pF_source->QueryInterface(IID_IFileSourceFilter, (void**)&pFileSource);
	pFileSource->Load(L"cuc_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.\n");
		return -1;
	}
	hr = pGraph->AddFilter(pF_demuxer, L"Lei's Demuxer");
	if (FAILED(hr)){
		printf("Failed to add Demuxer to Filter Graph.\n");
		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.\n");
		return -1;
	}
	hr = pGraph->AddFilter(pF_decoder, L"Lei's Decoder");
	if (FAILED(hr)){
		printf("Failed to add Decoder to Filter Graph.\n");
		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.\n");
		return -1;
	}
	hr = pGraph->AddFilter(pF_render, L"Lei's Render");
	if (FAILED(hr)){
		printf("Failed to add Video Render to Filter Graph.\n");
		return -1;
	}
	//2. Connect Filters=======================
	hr = connect_filters(pGraph, pF_source, pF_demuxer);
	if (FAILED(hr)){
		printf("Failed to link Source and Demuxer.\n");
		return -1;
	}
	hr = connect_filters(pGraph, pF_demuxer, pF_decoder);
	if (FAILED(hr)){
		printf("Failed to link Demuxer and Decoder.\n");
		return -1;
	}
	hr = connect_filters(pGraph, pF_decoder, pF_render);
	if (FAILED(hr)){
		printf("Failed to link Decoder and Render.\n");
		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
 

下載


Simplest DirectShow Example
 

專案主頁


CDSN下載地址:http://download.csdn.net/detail/leixiaohua1020/8348163
 
本程式包含了DirectShow開發的示例程式。適合DirectShow初學者進行學習。
它包含了以下幾個子程式:
simplest_directshow_player: 最簡單的基於DirectShow的視訊播放器。
simplest_directshow_player_custom: 最簡單的基於DirectShow的視訊播放器(Custom)。
playerGUI: 最簡單的基於DirectShow的播放器-圖形介面版。
simplest_directshow_info: 最簡單的Directshow資訊顯示例子。
simplest_directshow_filter: 目前還未完成。
 

相關推薦

簡單基於DirectShow示例視訊播放定義

=====================================================最簡單的基於DirectShow的示例文章列表:=====================================================本文記錄一個簡單

簡單基於Flash的流媒體示例網頁播放(HTTP,RTMP,HLS)

                =====================================================Flash流媒體文章列表:=====================================================本文繼續上一篇文章,記錄一些基於Flas

簡單基於FFMPEG+SDL的視訊播放拆分-解碼播放

=====================================================最簡單的基於FFmpeg的視訊播放器系列文章列表:=====================================================本文補充記錄《

100行程式碼實現簡單基於FFMPEG+SDL的視訊播放(SDL1.x)

                =====================================================最簡單的基於FFmpeg的視訊播放器系列文章列表:=====================================================簡介FFMPEG

簡單基於FFMPEG+SDL的視訊播放 ver2 (採用SDL2.0

                =====================================================最簡單的基於FFmpeg的視訊播放器系列文章列表:=====================================================簡介之前做過一個

XCode【100行程式碼實現簡單基於FFMPEG+SDL的視訊播放

【來自】 1.新建XCode工程後,發現即使安裝了SDL和FFMPEG也編譯不成功,需要修改各種環境。經過我的不懈努力加百穀啥的...貼個能編譯通過的過程出來。謹記! 2.首先需要編譯好ffmpeg原始碼,然後還需要安裝SDL(ffmpeg直接編譯,SDL我是通過brew安

Android基礎學習總結(十六)——基於ijkplayer封裝支援簡單介面UI定製的視訊播放

前言 專案開發中遇到需要解析播放m3u8視訊流的情況,但是原生的PlayerView非常慢,使用起來複雜,不適合上手,這裡找到一款ijkplayer是Bilibili基於ffmpeg開發並開源的輕量級視訊播放器,支援播放本地網路視訊,也支援流媒體播放。支援An

基於ijkplayer封裝支援簡單介面UI定製的視訊播放

簡介 特性 基於ijkplayer封裝的視訊播放器介面,支援 RTMP , HLS (http & https) , MP4,M4A 等; 可根據需求去定製部分介面樣式; 常用的手勢操作左邊上下亮度,右邊上下聲音,左右滑動播放進度調整;

簡單易用的H5視訊播放解決方案

  Chimee是由奇舞團開源的一套H5視訊播放器解決方案,由奇舞團視訊雲前端團隊結合在業務和視訊編解碼方向的沉澱積累傾心打造。Chimee支援MP4、M3U8、FLV等多種媒體格式,同時它也幫我們解決了大部分的相容性、差異化問題,包括全屏、自動播放、內聯播放、直播解碼等常見媒體播放需求。   通過便捷的可

基於 FFmpeg + SDL 的視訊播放的製作》課程的視訊

這兩天開始帶廣播電視工程大二的暑假小學期的課程設計了。本次小學期課程內容為《基於 FFmpeg + SDL 的視訊播放器的製作》,其中主要講述了視音訊開發的入門知識。由於感覺本課程的內容不但適合本科生,而且也比較適合無視音訊基礎的開發者入門使用,所以在講課的同時也錄製了一部分

簡單的redis教程centos6.5下redis單機安裝《一》

安裝 命令如下: wget http://download.redis.io/releases/redis-3.0.0.tar.gz tar -zxvf redis-3.0.0.tar.gz cd

Android筆記視訊播放播放本地視訊和網路視訊

這篇博文主要是記錄一下VideoView的使用,這個demo使用VideoView來播放本地視訊和網路視訊。 先來看一下效果圖: 接下來說程式碼: 1。佈局檔案: <?xml ve

基於IJKPlayer的簡易視訊播放

寫在前面 PS:沒錯,這就是那篇躺在草稿箱裡好幾個月的殭屍部落格,直到現在(2017年1月中旬)才打算寫完,簡單總結一下知識點,以備不時之需。 現在的專案是一個電影預告的APP,必然得有個視訊播放器,之前是用VideoView寫的,並且所有功能寫在一個Activi

用C/C++開發基於VLC SDK的視訊播放

在windows系統如果開發萬能播放器,一般都是基本DirectShow來開發,開發也很簡單,但缺點也很多,一個檔案格式是否能夠播放完全取決於你是否安裝了正確的解析器和解碼器,即使現在有了萬能解器安裝包也會出現很多問題

XBMC原始碼分析 4視訊播放(dvdplayer)-解碼(以ffmpeg為例)

XBMC分析系列文章: 本文我們分析XBMC中視訊播放器(dvdplayer)中的解碼器部分。由於解碼器種類很多,不可能一一分析,因此以ffmpeg解碼器為例進行分析。 XBMC解碼器部分檔案目錄如下圖所示: 解碼器分為音訊解碼器和視訊解碼器。在這裡我們看一下視訊

XBMC原始碼分析 6視訊播放(dvdplayer)-檔案頭(以ffmpeg為例)

XBMC分析系列文章: XBMC原始碼簡析 5:視訊播放器(dvdplayer)-解複用器(以ffmpeg為例)本文我們分析XBMC中視訊播放器(dvdplayer)中的檔案頭部分。檔案頭部分裡包含的是封裝Dll用到的標頭檔案。由於檔案頭種類很多,不可能一一分析,

線上視訊播放 Mac免費

線上視訊播放器 mac版是mac上一款非常好用的線上視訊直播軟體,無廣告無外掛、支援觀看線上直播和視訊點播!線上視訊播放器 mac集中了所有的高、標清衛視,輪播劇,地方特色臺等等,而且軟體裡的“最新”欄目還增加了樂視,愛奇藝,優酷,風行,騰訊,芒果,PPTV等視訊客戶端上線的最新VIP會員電影,

簡單基於libVLC的例子簡單基於libVLC的視訊播放(圖形介面

=====================================================最簡單的基於libVLC的例子文章列表:=====================================================本文記錄使用libVLC

簡單基於libVLC的例子簡單基於libVLC的視訊播放

=====================================================最簡單的基於libVLC的例子文章列表:=====================================================本文記錄使用libVLC

簡單基於FFMPEG+SDL的音訊播放拆分-解碼播放

=====================================================最簡單的基於FFmpeg的音訊播放器系列文章列表:=====================================================本文補充記錄《