FC2ブログ

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

DirectShow でストリーム内の位置をシークする

動画に対して処理を加えたりデータを取り出すといった用途では,任意の位置のデータを取り扱いたいことがあります.動画を取り扱うには様々なレベルの API がありますが,Windows で利用できて,かつ知名度や互換性などを考えると DirectShow が最も有力な選択肢かと思います.そこで本記事では IMediaSeeking インターフェースを用いて,動画ファイル内の位置を変更する方法を説明します.

Keywords: DirectShow, IMediaSeeking, シーク


本記事ではメディアをシークする最低限の部分しか説明しません.実際にデータ処理等を行う場合には,フィルタ構造がもっと複雑になります.


作業環境
Windows7 Professional 64bit
VisualC++ 2010 Express

プログラムの流れは以下の通りです.
1.COM を初期化
2.フィルタグラフを作成
3.ソースフィルタをフィルタグラフに追加
4.レンダリングフィルタを作成し,フィルタグラフに追加
5.フィルタを接続
6.グラフ操作インターフェースを取得
7.シークに利用するタイムフォーマットをフレーム単位に設定
8.メディアの長さを取得し,ループして各フレーム位置にシーク
9.終了処理

コードの頭のほうで NullRenderer の CLSID を宣言しています.もともとは qedit.h の内部で宣言されていたのですが,VC 2010 Express では qedit.h が含まれていません.このようにコード内で宣言し,strmiids.lib をリンクすることで利用できます.

フィルタグラフはレンダリングフィルタまで接続しなくてはなりません.これを忘れると IMediaSeeking のメソッドはことごとく失敗するので注意して下さい.

#include <stdio.h>
#include <stdlib.h>

#include <windows.h>
#include <dshow.h>

extern "C" const CLSID CLSID_NullRenderer;

// Utility function
template<typename T>
static void safe_release(T **pointer)
{
T* p = *pointer;
if(p != NULL) {
p->Release();
p = NULL;
}
}


// DirectShow helper functions
static HRESULT GetUnconnectedPin(
IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin
)
{
*ppPin = NULL;
IEnumPins *pEnum = NULL;
IPin *pPin = NULL;

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

hr = pPin->ConnectedTo(&pTmp);
if(SUCCEEDED(hr)) { // This pin is connected yet.
pTmp->Release();
}
else { // This pin is not connected, return it.
pEnum->Release();
*ppPin = pPin;
return S_OK;
}
}
pPin->Release();
}

pEnum->Release();
// Pin not found
return E_FAIL;
}

static HRESULT ConnectFilters(
IGraphBuilder *pGraph,
IPin *pOut,
IBaseFilter *pDest
)
{
if((pGraph==NULL) || (pOut==NULL) || (pDest==NULL)) {
return E_POINTER;
}

// Find input pin of downstream filter
IPin *pIn = NULL;
HRESULT hr = GetUnconnectedPin(pDest, PINDIR_INPUT, &pIn);
if(FAILED(hr)) {
return hr;
}

// Try connection
hr = pGraph->Connect(pOut, pIn);

pIn->Release();
return hr;
}

static HRESULT ConnectFilters(
IGraphBuilder *pGraph,
IBaseFilter *pSrc,
IBaseFilter *pDest
)
{
if((pGraph==NULL) || (pSrc==NULL) || (pDest==NULL)) {
return E_POINTER;
}

// find output pin
IPin *pOut = NULL;
HRESULT hr = GetUnconnectedPin(pSrc, PINDIR_OUTPUT, &pOut);
if(FAILED(hr)) {
return hr;
}

hr = ConnectFilters(pGraph, pOut, pDest);

pOut->Release();
return hr;
}


int main(int argc, char **argv)
{
HRESULT hr;

IGraphBuilder *graph = NULL;
IMediaSeeking *ms = NULL;
IBaseFilter *sf = NULL; // sf: Source Filter
IBaseFilter *rf = NULL; // rf: Render Filter

LONGLONG i, imax;

// 1. Initialize COM system
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if(FAILED(hr)) {
printf("COM intialize failed\n");
goto shutdown;
}

// 2. Create filter graph
hr = CoCreateInstance(
CLSID_FilterGraph, NULL,
CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&graph
);
if(FAILED(hr)) {
printf("FilterGraph creation failed\n");
goto shutdown;
}

// 3. Open source file and add to filter graph
hr = graph->AddSourceFilter(L"sample.avi", L"Source", &sf);
if(FAILED(hr)) {
printf("File open failed\n");
goto shutdown;
}

// 4. Create null renderer filter and add to filter graph
hr = CoCreateInstance(
CLSID_NullRenderer, NULL,
CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&rf
);
if(FAILED(hr)) {
printf("NullRenderer creation failed\n");
goto shutdown;
}

hr = graph->AddFilter(rf, L"Null Renderer");
if(FAILED(hr)) {
printf("NullRenderer not be added to graph\n");
goto shutdown;
}

// 5. Connect filters
// Output pin of source filter and input pin of render filter
// are connected.
hr = ConnectFilters(graph, sf, rf);
if(FAILED(hr)) {
printf("Filter pin connection failed\n");
goto shutdown;
}

// 6. Get media controller interfaces
hr = graph->QueryInterface(IID_IMediaSeeking, (void**)&ms);

// 7. Set seeking time format
hr = ms->SetTimeFormat(&TIME_FORMAT_FRAME);
if(FAILED(hr)) {
printf("Time format error\n");
goto shutdown;
}

// 8. Get media length, and set position at each frame
// sequentially in for loop
hr = ms->GetDuration(&imax);
if(FAILED(hr)) {
printf("Can't getting media length\n");
goto shutdown;
}

for(i=0; i<imax; i++) {
// Set media position
hr = ms->SetPositions(
&i, AM_SEEKING_AbsolutePositioning,
NULL, AM_SEEKING_NoPositioning
);
if(FAILED(hr)) {
printf("Media seeking failed\n");
break;
}

// Carry out your data processing
}

shutdown:
// 9. Release COM objects and shutdown
safe_release(&ms);
safe_release(&rf);
safe_release(&sf);
safe_release(&graph);
CoUninitialize();
return FAILED(hr);
}

実際にはフィルタグラフに SampleGrabber などを追加し,処理を行うことになると思います.SampleGrabber の利用法については以下のサイトが参考になると思います.

SampleGrabberを使って静止画を取得する
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

プロフィール

Ishida Akihiko

Author:Ishida Akihiko
FC2ブログへようこそ!

免責事項
当サイトに掲載する記事内容は,必ずしも正確性,信頼性,妥当性,有用性,完成度などを保証しません.記事の利用はすべて自己責任でお願いします.当サイトに掲載された内容によって発生したいかなる損害に対しても,管理人は一切の責任を負いかねます.
最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
アクセスカウンター
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QR
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。