青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

牽著老婆滿街逛

嚴以律己,寬以待人. 三思而后行.
GMail/GTalk: yanglinbo#google.com;
MSN/Email: tx7do#yahoo.com.cn;
QQ: 3 0 3 3 9 6 9 2 0 .

Directshow開發的基本技巧

http://blog.csdn.net/aoosang/archive/2005/05/26/381134.aspx

要:本篇文檔主要講述了Directshow開發的一些基本概念和技巧

?

1視頻播放(Video Rendering

dshow的視頻提交過濾器可以在窗口模式和無窗口模式下工作。在窗口模式下,過濾器創建一個自己的窗口,在里面播放視頻。在無窗口模式下,過濾器直接將視頻在應用程序提供的窗口上顯示,過濾器本身不創建窗口。

窗口模式

在窗口模式下,視頻提交過濾器創建一個窗口,然后將視頻禎帖到窗口上,你可以將這個窗口帖到你的應用程序的窗口。

?Video Renderer只支持窗口模式,VMR-7 and VMR-9缺省的是窗口模式,也支持無窗口模式。

為了在你的應用程序中顯示視頻,你可以將視頻窗口設置成應用程序的子窗口。你可以通過

IVideoWindow *pVidWin = NULL;

pGraph->QueryInterface(IID_IVideoWindow, (void **)&g_pVidWin);

pVidWin->put_Owner((OAHWND)hwnd);

pVidWin->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS);

RECT grc;

GetClientRect(hwnd, &grc);

pVidWin->SetWindowPosition(0, 0, grc.right, grc.bottom);

結束時一定要清理現場

pControl->Stop(); 

pVidWin->put_Visible(OAFALSE);

pVidWin->put_Owner(NULL);? 

?

無窗口模式

當采用無窗口的模式時,就沒有必要暴露IVideoWindow接口了。

為了能夠使用VMR的缺省行為,在構建Graph圖之前必須要調整VMR

1 創建一個過慮器圖表管理器,

2創建一個VMR,加入到graph中,

3 調用VMRIVMRFilterConfig::SetRenderingMode方法設置VMRMode_Windowless標志。

4調用IVMRWindowlessControl::SetVideoClippingWindow給視頻指定一個顯示窗口。

然后調用IGraphBuilder::RenderFile或者其他的方法來創建其他的Graph

下面的代碼顯示了如何創建一個VMR,將其添加到Graph,如何設置無窗口模式

HRESULT InitWindowlessVMR(

??? HWND hwndApp,????????????????? // Window to hold the video.

??? IGraphBuilder* pGraph,???????? // Pointer to the Filter Graph Manager.

??? IVMRWindowlessControl** ppWc,? // Receives a pointer to the VMR.?? )

{

??? if (!pGraph || !ppWc) return E_POINTER;

??? IBaseFilter* pVmr = NULL;

??? IVMRWindowlessControl* pWc = NULL;

??? // Create the VMR.

??? HRESULT hr = CoCreateInstance(CLSID_VideoMixingRenderer, NULL,

??????? CLSCTX_INPROC, IID_IBaseFilter, (void**)&pVmr);

??? if (FAILED(hr))

??? {

??????? return hr;

??? }

???

??? // Add the VMR to the filter graph.

??? hr = pGraph->AddFilter(pVmr, L"Video Mixing Renderer");

??? if (FAILED(hr))

??? {

??????? pVmr->Release();

??????? return hr;

??? }

??? // Set the rendering mode.?

??? IVMRFilterConfig* pConfig;

??? hr = pVmr->QueryInterface(IID_IVMRFilterConfig, (void**)&pConfig);

??? if (SUCCEEDED(hr))

??? {

??????? hr = pConfig->SetRenderingMode(VMRMode_Windowless);

??????? pConfig->Release();

??? }

??? if (SUCCEEDED(hr))

??? {

??????? // Set the window.

??????? hr = pVmr->QueryInterface(IID_IVMRWindowlessControl, (void**)&pWc);

??????? if( SUCCEEDED(hr))

??????? {

??????????? hr = pWc->SetVideoClippingWindow(hwndApp);

??????????? if (SUCCEEDED(hr))

??????????? {

??????????????? *ppWc = pWc; // Return this as an AddRef'd pointer.

??????????? }

??????????? else

??????????? {

??????????????? // An error occurred, so release the interface.

??????????????? pWc->Release();

??????????? }

??????? }

??? }

??? pVmr->Release();

??? return hr;

}

你也可以調用下面的函數

IVMRWindowlessControl *pWc = NULL;

hr = InitWindowlessVMR(hwnd, pGraph, &g_pWc);

if (SUCCEEDED(hr))

{

??? // Build the graph. For example:

??? pGraph->RenderFile(wszMyFileName, 0);

??? // Release the VMR interface when you are done.

??? pWc->Release();

}

下面看看如何設置視頻的位置

有兩個矩形需要考慮,一個是源矩形,一個是目的矩形。源矩形決定開始播放視頻的位置,目的矩形決定在窗口顯示視頻的區域。VMR將源矩形按照目的矩形的大小進行擴展。

IVMRWindowlessControl::SetVideoPosition可以設置兩個矩形的大小,源矩形必須小于等于本地視頻大小。你可以通過IVMRWindowlessControl::GetNativeVideoSize獲取本地的視頻區域大小。

// Find the native video size.

long lWidth, lHeight;

HRESULT hr = g_pWc->GetNativeVideoSize(&lWidth, &lHeight, NULL, NULL);

if (SUCCEEDED(hr))

{

??? RECT rcSrc, rcDest;

??? // Set the source rectangle.

??? SetRect(&rcSrc, 0, 0, lWidth/2, lHeight/2);

???

??? // Get the window client area.

??? GetClientRect(hwnd, &rcDest);

??? // Set the destination rectangle.

??? SetRect(&rcDest, 0, 0, rcDest.right/2, rcDest.bottom/2);

???

??? // Set the video position.

??? hr = g_pWc->SetVideoPosition(&rcSrc, &rcDest);

}

處理窗口消息

因為VMR沒有自己的窗口,所以當視頻需要重畫或者改變的時候你要通知它。

1 當你接到一個WM_PAINT消息,你就要調用IVMRWindowlessControl::RepaintVideo來重畫視頻

2 當你接到一個WM_DISPLAYCHANGE消息,你就要調用IVMRWindowlessControl::DisplayModeChanged.

3 當你接到一個WM_SIZE消息時,重新計算視頻的位置,然后調用SetVideoPostion

下面的代碼演示了WM_PAINT消息的處理

void OnPaint(HWND hwnd)

{

??? PAINTSTRUCT ps;

??? HDC???????? hdc;

??? RECT??????? rcClient;

??? GetClientRect(hwnd, &rcClient);

??? hdc = BeginPaint(hwnd, &ps);

??? if (g_pWc != NULL)

??? {

??????? // Find the region where the application can paint by subtracting

??????? // the video destination rectangle from the client area.

??????? // (Assume that g_rcDest was calculated previously.)

??????? HRGN rgnClient = CreateRectRgnIndirect(&rcClient);

??????? HRGN rgnVideo? = CreateRectRgnIndirect(&g_rcDest);?

??????? CombineRgn(rgnClient, rgnClient, rgnVideo, RGN_DIFF);?

???????

??????? // Paint on window.

??????? HBRUSH hbr = GetSysColorBrush(COLOR_BTNFACE);

??????? FillRgn(hdc, rgnClient, hbr);

?

??????? // Clean up.

???? ???DeleteObject(hbr);

??????? DeleteObject(rgnClient);

??????? DeleteObject(rgnVideo);

?

??????? // Request the VMR to paint the video.

??????? HRESULT hr = g_pWc->RepaintVideo(hwnd, hdc);?

??? }

??? else? // There is no video, so paint the whole client area.

??? {

??????? FillRect(hdc, &rc2, (HBRUSH)(COLOR_BTNFACE + 1));

??? }

??? EndPaint(hwnd, &ps);

}

盡管我們要自己處理onpaint消息,但是已經非常簡單了。

2 如何處理事件通知(Event Notification

?? 當一個Directshow的應用程序運行的時候,在 filter Graph內部就會發生各種各樣的事件,例如,一個filter也許發生數據流錯誤。Filter通過給graph mangaer發送事件通知來和graph通信,這個事件通知包括一個事件碼和兩個事件參數。事件碼表示發生事件的類型,兩個參數用來傳遞信息。

Filter發送的這些事件,其中的一部分可以被Manager直接處理,不通知應用程序,但有一部分事件,Manager將事件放入到一個隊列中,等待應用程序處理。這里我們主要討論在應用程序中經常遇到的三種事件

EC_COMPLETE表明回放已經結束

EC_USERABORT表明用戶中斷了回放。用戶關閉視頻播放窗口時,視頻Render會發生這個事件

EC_ERRORABORT表明出現了一個錯誤。

應用程序可以通知filter graph manager,在某個指定的事件發生時,向指定的窗口發生一個指定的消息。這樣應用程序就可以在消息循環中對發生的事件產生反應。

? 首先定義消息,

#define WM_GRAPHNOTIFY? WM_APP + 1

? 然后向filter graph manager請求IMediaEventEx接口,然后調用IMediaEventEx::SetNotifyWindow方法來設置消息通知窗口

IMediaEventEx *g_pEvent = NULL;

g_pGraph->QueryInterface(IID_IMediaEventEx, (void **)&g_pEvent);

g_pEvent->SetNotifyWindow((OAHWND)g_hwnd, WM_GRAPHNOTIFY, 0);

然后在WindowProc函數增加一個處理WM_GRAPHNOTIFY消息的函數

case WM_GRAPHNOTIFY:

??? HandleGraphEvent();

??? break;

HandleGraphEvent()函數具體定義如下

void HandleGraphEvent()

{

??? // Disregard if we don't have an IMediaEventEx pointer.

??? if (g_pEvent == NULL)

??? {

??????? return;

??? }

??? // Get all the events

??? long evCode;

??? LONG_PTR param1, param2;

??? HRESULT hr;

?? ?while (SUCCEEDED(g_pEvent->GetEvent(&evCode, &param1, &param2, 0)))

??? {

??????? g_pEvent->FreeEventParams(evCode, param1, param2);

??????? switch (evCode)

??????? {

??????? case EC_COMPLETE:? // Fall through.

??????? case EC_USERABORT: // Fall through.

??????? case EC_ERRORABORT:

??????????? CleanUp();

??????????? PostQuitMessage(0);

??????????? return;

??????? }

??? }

}

在釋放IMediaEventEx指針前,要取消事件通知消息,代碼如下

// Disable event notification before releasing the graph.

g_pEvent->SetNotifyWindow(NULL, 0, 0);

g_pEvent->Release();

g_pEvent = NULL;

?

?

3如何枚舉系統的設備和過慮器

? 有時,應用程序需要查看系統中所有的filter。例如,視頻應用程序需要列出系統中可用的捕捉設備。因為dshow基于com結構的,你在設計程序的時候是沒法知道系統中正在使用的過濾器。Directshow提供了兩種方法來枚舉系統中注冊的過慮器。

1 系統設備枚舉器

系統設備枚舉器提供了一個很好的方法根據種類來枚舉系統中注冊的過慮器。也許枚一種不同的硬件都會有自己的過慮器,或許所有的硬件設備共用同一個filter。這個對于采用WDM驅動程序的硬件很有用。

系統設備枚舉器根據不同的種類創建了一個枚舉器,例如,音頻壓縮,視頻捕捉。不同種類的枚舉器對于每一種設備返回一個獨立的名稱(moniker)。種類枚舉器自動將相關的即插即用,演播設備包括進來。

按照下面的步驟使用設備枚舉器

1 創建枚舉器組件,CLSIDCLSID_SystemDeviceEnum

2 指定某一種類型設備,參數CLSID,通過ICreateDevEnum::CreateClassEnumerator獲取某一種類的枚舉器,這個函數返回一個IEnumMoniker接口指針,如果該種類的空或者不存在,這個方法就返回S_FALSE。因此,當你調用這個函數時一定要檢查返回值是否為S_OK,而不要用SUCCEEDED宏。

3 然后IEnumMoniker::Next枚舉每一個moniker。這個方法返回一個IMoniker接口指針。

4 要想知道設備的名稱,可以通過下面的函數IMoniker::BindToStorage

5 然后利用IMoniker::BindToObject生成綁定道設備上的filter。調用IFilterGraph::AddFilterfilter添加到Graph圖中。

?????? 1

// Create the System Device Enumerator.

HRESULT hr;

ICreateDevEnum *pSysDevEnum = NULL;

hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,

??? IID_ICreateDevEnum, (void **)&pSysDevEnum);

if (FAILED(hr))

{

??? return hr;

}

?

// Obtain a class enumerator for the video compressor category.

IEnumMoniker *pEnumCat = NULL;

hr=pSysDevEnum->CreateClassEnumerator(CLSID_VideoCompressorCategory, &pEnumCat, 0);

?

if (hr == S_OK)

{

??? // Enumerate the monikers.

??? IMoniker *pMoniker = NULL;

??? ULONG cFetched;

??? while(pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK)

??? {

???? ???IPropertyBag *pPropBag;

??????? hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag,

??????????? (void **)&pPropBag);//知道設備的名稱

??????? if (SUCCEEDED(hr))

??????? {

??????????? // To retrieve the filter's friendly name, do the following:

??????????? VARIANT varName;

??????????? VariantInit(&varName);

??????????? hr = pPropBag->Read(L"FriendlyName", &varName, 0);

??????????? if (SUCCEEDED(hr))

??????????? {

??????????????? // Display the name in your UI somehow.

??????????? }

??????????? VariantClear(&varName);

?

??????????? // To create an instance of the filter, do the following:

??????????? IBaseFilter *pFilter;

??????????? hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter,

??????????????? (void**)&pFilter); //生成一個filter綁定到設備上。

??????????? // Now add the filter to the graph.

??????????? //Remember to release pFilter later.

??????????? pPropBag->Release();

??????? }

??????? pMoniker->Release();

??? }

??? pEnumCat->Release();

}

pSysDevEnum->Release();

在上面我們IMoniker::BindToObject生成綁定道設備上的filter,當然我們還可以用另外的一種方法來生成綁定到設備上的filter

利用IMoniker::GetDisplayName得到moniker的名字。然后你把moniker的名字做參數傳遞給IFilterGraph2::AddSourceFilterForMoniker,就可以創建一個綁定到設備的filter了。在上面我們是調用IMoniker::BindToObject生成filter的,還是上面的簡單些。看看代碼吧。

LPOLESTR strName = NULL;

IBaseFilter pSrc = NULL;

hr = pMoniker->GetDisplayName(NULL, NULL, &strName);

if (SUCCEEDED(hr))

{

?? ?// Query the Filter Graph Manager for IFilterGraph2.

??? IFilterGraph2 *pFG2 = NULL;

??? hr = pGraph->QueryInterface(IID_IFilterGraph2, (void**)&pFG2);

??? if (SUCCEEDED(hr))

??? {

??????? hr = pFG2->AddSourceFilterForMoniker(pMoniker, 0, L"Source", &pSrc);

??????? pFG2->Release();

??? }

??? CoTaskMemFree(strName);

}

// If successful, remember to release pSrc.

2 Filter Mapper

? 搜索系統中的filter的另一個方法就是采用Filer MapperFilter mapper是一個com對象,它按照一定的條件來搜索系統的filer,它比系統設備枚舉器(System Device Enumerator)的效率要低一些。所以當你要枚舉某特定種類的filter時,你應該使用系統設備枚舉器,但是當你搜索支持某種媒體類型的filter時,同時也找不到清晰的filter,你應該使用filter mapper

Filter Mapper 暴露一個IFilerMapper2接口,要想搜索一個接口,你可以調用該接口的IFilterMapper2::EnumMatchingFilters方法,這個方法需要傳遞一些參數來定義搜索條件,同時該方法返回一個適合條件的filter的枚舉器,這個枚舉器提供一個IEnumMoniker接口,并且對于每個適合的filter都提供一個單獨的moniker

下面的例子演示了,枚舉所有的支持DV,并且至少有一個輸出pinfilter,這個filter支持任何媒體類型。

IFilterMapper2 *pMapper = NULL;

IEnumMoniker *pEnum = NULL;

?

hr =CoCreateInstance( CLSID_FilterMapper2,NULL, CLSCTX_INPROC, IID_IFilterMapper2,

??? ????????????????(void **) &pMapper);

if (FAILED(hr))

{

??? // Error handling omitted for clarity.

}

?

GUID arrayInTypes[2];

arrayInTypes[0] = MEDIATYPE_Video;

arrayInTypes[1] = MEDIASUBTYPE_dvsd;

?

hr = pMapper->EnumMatchingFilters(

??????? &pEnum,

??????? 0,????????????????? // Reserved.

??????? TRUE,?????????????? // Use exact match?

??????? MERIT_DO_NOT_USE+1, // Minimum merit.

??????? TRUE,?????????????? // At least one input pin?

??????? 1,????????????????? // Number of major type/subtype pairs for input.

??????? arrayInTypes,?????? // Array of major type/subtype pairs for input.

??????? NULL,?????????????? // Input medium.

??????? NULL,?????????????? // Input pin category.

??????? FALSE,????????????? // Must be a renderer?

??????? TRUE,?????????????? // At least one output pin?

??????? 0,????????????????? // Number of major type/subtype pairs for output.

??????? NULL,?????????????? // Array of major type/subtype pairs for output.

??????? NULL,?????????????? // Output medium.

??????? NULL);????????????? // Output pin category.

?

// Enumerate the monikers.

IMoniker *pMoniker;

ULONG cFetched;?

//////////下面就是枚舉filter了,就是系統枚舉設備filter

while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK)

{

??? IPropertyBag *pPropBag = NULL;

??? hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag,

?????? (void **)&pPropBag);

?

??? if (SUCCEEDED(hr))

??? {

??????? // To retrieve the friendly name of the filter, do the following:

??????? VARIANT varName;

??????? VariantInit(&varName);

??????? hr = pPropBag->Read(L"FriendlyName", &varName, 0);

??????? if (SUCCEEDED(hr))

??????? {

??????????? // Display the name in your UI somehow.

??????? }

??????? VariantClear(&varName);

?

??????? // To create an instance of the filter, do the following:

??????? IBaseFilter *pFilter;

??????? hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pFilter);

??????? // Now add the filter to the graph. Remember to release pFilter later.

???

??????? // Clean up.

??????? pPropBag->Release();

??? }

??? pMoniker->Release();

}

// Clean up.

pMapper->Release();

pEnum->Release();

4如何枚舉Graph圖中的對象(filterpin

有些時候,應用程序需要枚舉graph中的filter或者是枚舉filter所支持的pin。因此directshow提供了枚舉graph filter中的com組件方法。

1 枚舉filter

Filter圖表管理器支持IFilterGraph::EnumFilters方法,來枚舉graph圖中的所有的filter。他返回一個IEnumFilters接口,利用這個接口就可以遍歷graph中的所有的filter

下面的代碼演示了,如何遍歷graph中的filter,并且顯示filter的名字。

HRESULT EnumFilters (IFilterGraph *pGraph)

{

??? IEnumFilters *pEnum = NULL;

??? IBaseFilter *pFilter;

??? ULONG cFetched;

??? HRESULT hr = pGraph->EnumFilters(&pEnum);

??? if (FAILED(hr)) return hr;

??? while(pEnum->Next(1, &pFilter, &cFetched) == S_OK)

??? {

??????? FILTER_INFO FilterInfo;

??????? hr = pFilter->QueryFilterInfo(&FilterInfo);

??????? if (FAILED(hr))

??????? {

??????????? MessageBox(NULL, TEXT("Could not get the filter info"),

??????????????? TEXT("Error"), MB_OK | MB_ICONERROR);

??????????? continue;? // Maybe the next one will work.

??????? }

?

#ifdef UNICODE

??????? MessageBox(NULL, FilterInfo.achName, TEXT("Filter Name"), MB_OK);

#else

??????? char szName[MAX_FILTER_NAME];

??????? int cch = WideCharToMultiByte(CP_ACP, 0, FilterInfo.achName,

??????????? MAX_FILTER_NAME, szName, MAX_FILTER_NAME, 0, 0);

??????? if (chh > 0)

??????????? MessageBox(NULL, szName, TEXT("Filter Name"), MB_OK);

#endif

??????? // The FILTER_INFO structure holds a pointer to the Filter Graph

??????? // Manager, with a reference count that must be released.

??????? if (FilterInfo.pGraph != NULL)

??????? {

??????????? FilterInfo.pGraph->Release();

??????? }

??????? pFilter->Release();

??? }

??? pEnum->Release();

??? return S_OK;

}

2 枚舉pin

Filter支持IBaseFilter::EnumPins方法,這個方法可以可以枚舉filter所有的pin。它返回一個IEnumPins接口,IEnumPins::Next可以遍歷pin的接口。

下面的代碼演示了如何如何查找一個輸出和輸入pin。利用PIN_DIRECTION參數來制定pin的類型(輸入還是輸出)。

HRESULT GetPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin)

{

??? IEnumPins? *pEnum = NULL;

??? IPin?????? *pPin = NULL;

??? HRESULT??? hr;

?

??? if (ppPin == NULL)

??? {

??????? return E_POINTER;

??? }

?

??? hr = pFilter->EnumPins(&pEnum);

??? if (FAILED(hr))

??? {

??????? return hr;

??? }

??? while(pEnum->Next(1, &pPin, 0) == S_OK)

??? {

??????? PIN_DIRECTION PinDirThis;

??????? hr = pPin->QueryDirection(&PinDirThis);

??????? if (FAILED(hr))

??????? {

??????????? pPin->Release();

??????????? pEnum->Release();

??????????? return hr;

??????? }

??????? if (PinDir == PinDirThis) //如果類型符合

??????? {

??????????? // Found a match. Return the IPin pointer to the caller.

??????????? **ppPin = pPin;

??????????? pEnum->Release();

??????????? return S_OK;

??? ????}

??????? // Release the pin for the next time through the loop.

??????? pPin->Release();

??? }

??? // No more pins. We did not find a match.

??? pEnum->Release();

??? return E_FAIL;?

}

利用這個方法可以很容易的就查找一個pin,然后調用IPin::ConnectedTo方法確定這個pin是否被連接,可以查找一個空閑的pin

3 查找媒體類型

每個pin都支持一個IPin::EnumMediaTypes方法,可以來枚舉pin支持的媒體類型。它返回一個IEnumMediaTypes接口,這個接口的方法IEnumMediaTypes::Next返回一個指向AM_MEDIA_TYPE類型的指針。可以參考上面的代碼來遍歷pin所支持的媒體類型。

5 Seeking Filter graph

主要講述了如何在一個媒體數據流中定位,任意指定開始播放的位置。

1 檢查是否支持seek

Directshow通過IMediaSeeking接口支持seekingFilter graph管理器支持這個接口,但是實際seeking的功能是有graph中的filter來實現的。

有一些數據是不能seek的,例如,你不可能seek從照相機中采集的活動的視頻流。如果一個數據流可以被seek,但是,seek的類型還分以下幾種類型,可以給你的數據流選擇一種

1 定位到數據流中的一個絕對位置

2 返回數據流的持續時間

3返回數據流中的當前播放位置

4回放。

IMediaSeeking接口定義了一套標志AM_SEEKING_SEEKING_CAPABILITIES,用來描述可能支持的seek功能。

typedef enum AM_SEEKING_SeekingCapabilities {

??? AM_SEEKING_CanSeekAbsolute??????? = 0x1,

??? AM_SEEKING_CanSeekForwards??????? = 0x2,

??? AM_SEEKING_CanSeekBackwards?????? = 0x4,

??? AM_SEEKING_CanGetCurrentPos?????? = 0x8,

??? AM_SEEKING_CanGetStopPos????????? = 0x10,

??? AM_SEEKING_CanGetDuration???????? = 0x20,

??? AM_SEEKING_CanPlayBackwards?????? = 0x40,

??? AM_SEEKING_CanDoSegments????????? = 0x80,

??? AM_SEEKING_Source???????????????? = 0x100

}?? AM_SEEKING_SEEKING_CAPABILITIES;

?

可以通過IMediaSeeking::GetCapabilities查看數據流支持的seek能力都有哪些。應用程序可以采取 &測試每一項。例如,下面的代碼檢查了graph是否可以seek 一個任意的位置

?DWORD dwCap = 0;

HRESULT hr = pSeek->GetCapabilities(&dwCap);

if (AM_SEEKING_CanSeekAbsolute & dwCap)

{

??? // Graph can seek to absolute positions.

}

2Setting and Retrieving the Position

?Filter graph包含兩個位置,當前位置和停止位置,定義如下:

?1當前位置,當一個graph正處于運行的時候,當前位置就是當前的回放位置,相對于開始的位置而言。如果graph處于停止或者暫停狀態的時候,當前位置就是數據流下次開始播放的位置點。

?2 停止位置,停止位置就是數據流將要停止的位置,當一個graph到達一個停止位置時,將沒有數據流,filter graph管理器將會發送一個EC_COMPLETE事件。

?可以通過IMediaSeeking::GetPositions方法可以獲取這些位置值。返回值都是相對于原始的開始位置。

?通過IMediaSeeking::SetPositions方法可以seek一個新的位置,見下面:

#define ONE_SECOND 10000000

REFERENCE_TIME rtNow? = 2 * ONE_SECOND,

?????????????? rtStop = 5 * ONE_SECOND;

?

hr = pSeek->SetPositions(

??? &rtNow,? AM_SEEKING_AbsolutePositioning,

??? &rtStop, AM_SEEKING_AbsolutePositioning

??? );

注:1秒是10,000,000參考時間單位。為了方便,這個例子將這個值定義為ONE_SECOND,如果你使用的dshow的基類,常量CUITS的值和這個值相等。

? RtNow參數指定新的當前位置,第二個參數用來標示如何來定位rtNow參數。在這個例子中,AM_SEEKING_AbsolutePositioning 標志表示rtNow指定的位置是一個絕對的位置。RtStop參數指定了停止時間,最后一個參數也指定了絕對位置。

? 如果想指定一個相對的位置,可以指定一個AM_SEEKING_RelativePositioning參數,

為了設置這個位置不能改變,可以指定一個AM_SEEKING_NoPositioning參數。此時,參考時間應該設置為NULL。下面的例子將位置向前seek 10秒,然后停止位置不變。

REFERENCE_TIME rtNow = 10 * ONE_SECOND;

hr = pSeek->SetPositions(

??? &rtNow, AM_SEEKING_RelativePositioning,

??? NULL, AM_SEEKING_NoPositioning

??? );

?

3Setting the Playback Rate

調用IMediaSeeking::SetRate方法可以改變回放的速率。通過將新的速率設置成原來速率的倍數就可以設置新的速率,例如,pSeek->SetRate(2.0)

將新的速率設置為原來速率的兩倍。比率大于1說明回放的速度比原來的大,如果介于01之間,就比正常的速度慢。

?如果我們不考慮回放速率,當前位置和停止位置相對于開始位置都是不變的。舉個例子,如果我們有一個可以播放20秒的文件,將當前時間設置為10秒就會將播放位置設置到中間,如果播放的速率提高要原來的2倍,如果停止時間是20秒,你將播放位置設置到原來的10秒處,結果現在只能播放5秒了,因為速度提高了兩倍。

?

4Time Formats For Seek Commands

? IMediaSeeking接口中的許多函數的參數都要求指定一個位置值,比如當前位置,或者停止位置,缺省的情況下這些參數是以of 100 nanoseconds為時間單位的,稱為參考時間,任何支持seekfilter必須支持按參考時間來進行定位。一些filter也支持采取其他時間單位進行定位。例如,根據指定的楨的數量,或在數據流偏移的字節數進行定位。

? 這種用來定位的時間單位稱為時間格式,采用一個GUID來標示。Directshow定義了一系列的時間格式,詳細地可以參考SDK。第三方也可以定義自己的時間格式。

? 為了確定graph中的當前的filter是否支持特定的時間格式,可以調用

IMediaSeeking::IsFormatSupported方法,如果filter支持該時間格式,該函數返回ok否則返回false或者一個錯誤碼。如果filter支持某種指定的時間格式,可以調用IMediaSeeking::SetTimeFormat方法切換到其他的時間格式。如果SetTimeFormat方法成功,下面的seek命令就要使用新的時間格式。

? 下面的代碼檢查graph是否支持用楨的數量進行定位,如果支持,定位到第20楨。

hr = pSeek->IsFormatSupported(&TIME_FORMAT_FRAME);

if (hr == S_OK)

{

??? hr = pSeek->SetTimeFormat(&TIME_FORMAT_FRAME);

??? if (SUCCEEDED(hr))

??? {

??????? // Seek to frame number 20.

??????? LONGLONG rtNow = 20;

??????? hr = pSeek->SetPositions(

??????????? &rtNow, AM_SEEKING_AbsolutePositioning,

??????????? 0, AM_SEEKING_NoPositioning);

??? }

}

?

6 如何設置Graph時鐘(Setting Graph Clock

當你構建了一個graph后,graph管理器會自動地給你的graph選擇一個參考時鐘的。Graph中的所有filter都同步于時鐘。特別的,Renderer filter還要根據參考時鐘的時間來決定每一個samplePresentation 時間。

? 通常的情況下,應用程序是沒有必要重新設置graph管理器選擇好的參考時鐘的。但是,如果你想修改參考時鐘,你可以通過graph管理器提供的IMediaFilter::SetSyncSource方法來重新設置參考時鐘。這個方法的參數是一個時鐘的IReferenceClock接口指針。可以在graph停止的時候調用這個函數,下面的例子演示了如何指定一個時鐘

IGraphBuilder *pGraph = 0;

IReferenceClock *pClock = 0;

?

CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,

??? IID_IGraphBuilder, (void **)&pGraph);

?

// Build the graph.

pGraph->RenderFile(L"C:\\Example.avi", 0);

?

// Create your clock.

hr = CreateMyPrivateClock(&pClock);

if (SUCCEEDED(hr))

{

??? // Set the graph clock.

??? IMediaFilter *pMediaFilter = 0;

??? pGraph->QueryInterface(IID_IMediaFilter, (void**)&pMediaFilter);

??? pMediaFilter->SetSyncSource(pClock);

??? pClock->Release();

??? pMediaFilter->Release();

}

?這段代碼假定CreateMyPrivateClock 是應用程序定義的一個函數,用來創建一個時鐘,然后返回一個IReferenceClock接口。

? 你也可以在graph沒有設置時鐘的情況下運行graph。當SetSyncSource 函數的參數為NULL的時候就給graph設置了一個空的參考時鐘。如果graph沒有時鐘,graph將運行的快許多。因為renderer 不用再按照samplepresentation 時間了,只要sample到達了renderer filter,就可以立即被提交。所以,當你想處理數據盡可能快,而不是還要考慮預覽的實際時間,你就可以給graph設置一個空的時間。

?



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=381134

posted on 2007-01-29 17:57 楊粼波 閱讀(1149) 評論(0)  編輯 收藏 引用

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            美国三级日本三级久久99| 久久久亚洲午夜电影| 国产精品久久久久影院色老大 | 在线播放中文一区| 精品成人久久| 亚洲黄色天堂| 一区二区三区视频在线播放| 亚洲亚洲精品三区日韩精品在线视频| 一本大道久久a久久综合婷婷 | 亚洲精品乱码| 亚洲一二三区视频在线观看| 欧美在线二区| 欧美肥婆在线| 中文日韩在线视频| 久久久91精品国产一区二区精品| 老鸭窝毛片一区二区三区| 欧美日韩视频在线一区二区| 国产一区二区三区自拍| 亚洲日本激情| 久久国产手机看片| 亚洲国产天堂久久国产91| 欧美黑人多人双交| 亚洲图片欧美一区| 欧美不卡视频| 国产一区二区三区丝袜| 野花国产精品入口| 久久香蕉国产线看观看av| 日韩午夜在线观看视频| 久久精品视频99| 国产精品日韩久久久久| 亚洲精品国精品久久99热一| 久久激情婷婷| 亚洲伊人色欲综合网| 欧美 日韩 国产精品免费观看| 国产乱子伦一区二区三区国色天香| 最近中文字幕日韩精品 | 久久综合久久综合久久| 欧美手机在线| 亚洲精品视频一区| 欧美成年视频| 久久精品国产免费| 国产日韩欧美日韩| 亚洲自拍高清| 一本一本a久久| 欧美日韩1080p| 亚洲欧洲精品一区二区| 久热国产精品视频| 欧美一区二区三区喷汁尤物| 国产精品久久久久久久久久免费| 精东粉嫩av免费一区二区三区| 亚洲私人黄色宅男| 亚洲精品在线观看免费| 欧美二区不卡| 亚洲精品国产精品国自产在线| 久久人人爽人人| 久久成人资源| 国自产拍偷拍福利精品免费一| 欧美亚洲网站| 欧美伊人久久| 精品电影在线观看| 美女精品在线观看| 久久综合色播五月| 亚洲国产一区二区三区在线播| 老司机精品久久| 久久综合国产精品| 亚洲乱码国产乱码精品精| 最新69国产成人精品视频免费| 久久一区欧美| 亚洲美女黄网| 一本大道av伊人久久综合| 国产精品成人免费| 午夜精品一区二区在线观看| 亚洲午夜精品| 极品少妇一区二区三区| 欧美国产一区二区在线观看 | 男人的天堂成人在线| 乱中年女人伦av一区二区| 亚洲人成人一区二区三区| 亚洲精品欧美一区二区三区| 国产精品久久久久久久久久久久久久 | 久久久久久久久久久久久久一区| 午夜久久久久| 亚洲国产精品女人久久久| 亚洲毛片一区二区| 国产香蕉久久精品综合网| 欧美黄色精品| 国产精品国产福利国产秒拍| 久久久噜噜噜久久人人看| 欧美国产日韩一二三区| 午夜一区在线| 免费国产一区二区| 性欧美超级视频| 免费成人黄色| 销魂美女一区二区三区视频在线| 久久精品毛片| 亚洲综合欧美| 免费高清在线视频一区·| 亚洲香蕉网站| 蜜桃av噜噜一区二区三区| 亚洲在线免费| 免费中文日韩| 久久精品理论片| 欧美日韩精品一区二区| 国产日韩av一区二区| 亚洲国产日韩欧美综合久久| 亚洲最新合集| 在线观看一区欧美| 亚洲一级在线观看| 亚洲乱码精品一二三四区日韩在线 | 中日韩美女免费视频网址在线观看 | 中文无字幕一区二区三区| 狠狠88综合久久久久综合网| 日韩网站免费观看| 亚洲国产老妈| 久久精品国产精品亚洲精品| 亚洲免费在线观看视频| 欧美aa在线视频| 美女脱光内衣内裤视频久久影院| 国产精品视屏| 亚洲一区二区三区四区视频| 99精品福利视频| 蜜桃av一区| 欧美xxx成人| 好吊色欧美一区二区三区四区| 亚洲视频二区| 亚洲欧美电影在线观看| 欧美日韩国产在线| 日韩午夜在线播放| 在线一区观看| 欧美日韩在线三区| av成人国产| 亚洲欧美国产精品桃花| 欧美色视频在线| 亚洲美女av电影| 一区二区三区四区五区精品| 欧美精品二区| 日韩视频不卡中文| 亚洲专区欧美专区| 国产欧美日韩在线播放| 亚洲欧美日韩在线| 久久久www成人免费精品| 国产在线视频欧美一区二区三区| 亚洲综合欧美日韩| 久久久久成人精品免费播放动漫| 国产日产亚洲精品系列| 久久er精品视频| 免费观看日韩| 99re6热在线精品视频播放速度 | 欧美电影免费观看| 日韩午夜精品视频| 亚洲欧美日韩天堂一区二区| 国产精品视频yy9299一区| 亚洲欧美日本精品| 久久另类ts人妖一区二区| 激情成人综合| 欧美大片免费久久精品三p | 免费视频最近日韩| 亚洲美女av电影| 欧美在线一区二区三区| 国内精品写真在线观看| 卡一卡二国产精品| 牛牛精品成人免费视频| 亚洲国产精品一区二区www在线 | 国产精品亚洲成人| 久久精品91久久久久久再现| 欧美激情欧美狂野欧美精品 | 亚洲第一网站免费视频| 欧美高清不卡| 亚洲欧美三级在线| 欧美激情无毛| 香蕉尹人综合在线观看| 亚洲激情在线观看| 国产精品夜夜夜一区二区三区尤| 久久国产精品一区二区| 91久久久久久| 久久精品女人天堂| 99精品国产在热久久下载| 国产精品综合| 欧美国产综合视频| 欧美一区日本一区韩国一区| 最新国产の精品合集bt伙计| 久久久999成人| 亚洲欧美成人一区二区在线电影| 亚洲国产精品精华液2区45| 国产精品麻豆成人av电影艾秋| 久久色在线播放| 亚洲欧美一区二区三区极速播放| 亚洲激情欧美| 欧美+日本+国产+在线a∨观看| 亚洲综合视频网| 99日韩精品| 91久久在线| 精品成人a区在线观看| 国产精品日韩久久久久| 欧美日韩国产色站一区二区三区| 久久国产精品一区二区三区四区| 亚洲午夜av| 一本色道久久88综合亚洲精品ⅰ| 亚洲成人自拍视频| 毛片基地黄久久久久久天堂|