開発環境 : WinXP Home Edition SP3 + VC6 + Microsoft DirectX 9.0 SDK (Summer 2004)
テスト環境 : WinXP Home Edition SP3、Win7 Home Premium
動画再生 : Y901、Windows Media Player
DirectX のサンプル PushSource の PushSourceDesktop を使って
デスクトップをビデオキャプチャしているのですが、動画速度の問題で困っています。
デフォルトの
m_rtFrameLength(FPS_5), // Capture and display desktop 5 times per second
だとスロー再生のようになります。5 秒の動画が 10 秒かけて再生されるような感じです。
他の値だと、
m_rtFrameLength(FPS_30), // 早送り。
m_rtFrameLength(FPS_20), // 早送り。
m_rtFrameLength(FPS_10), // スロー。
このように FPS を変えると動画速度が変わってしまうようです。
動画ファイルのプロパティでは指定したフレームレートになっています。
PushSource のその他の部分はいじっていません。
PushSourceDesktop の利用側のコードは以下のようにしています。
#define WIN32_LEAN_AND_MEAN
#define WINVER 0x0500
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <comdef.h>
#include <dshow.h>
#pragma comment(lib, "strmiids.lib")
_COM_SMARTPTR_TYPEDEF(IGraphBuilder, __uuidof(IGraphBuilder));
_COM_SMARTPTR_TYPEDEF(IBaseFilter, __uuidof(IBaseFilter));
_COM_SMARTPTR_TYPEDEF(ICaptureGraphBuilder2, __uuidof(ICaptureGraphBuilder2));
_COM_SMARTPTR_TYPEDEF(IMediaControl, __uuidof(IMediaControl));
_COM_SMARTPTR_TYPEDEF(ICreateDevEnum, __uuidof(ICreateDevEnum));
EXTERN_C const GUID DECLSPEC_SELECTANY CLSID_PushSourceDesktop
= { 0x4ea6930a, 0x2c8a, 0x4ae6, { 0xa5, 0x61, 0x56, 0xe4, 0xb5, 0x4, 0x44, 0x37 } };
int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, int cmdShow)
{
::CoInitializeEx(0, COINIT_DISABLE_OLE1DDE);
try
{
// グラフビルダを作成する。
IGraphBuilderPtr graphBuilder(CLSID_FilterGraph);
// ビデオフィルタを取得する。
IBaseFilterPtr video(CLSID_PushSourceDesktop);
graphBuilder->AddFilter(video, L"Video Capture");
// キャプチャグラフビルダを取得する。
ICaptureGraphBuilder2Ptr captureGraphBuilder2(CLSID_CaptureGraphBuilder2);
// フィルタグラフを設定する。
captureGraphBuilder2->SetFiltergraph(graphBuilder);
// Mux を取得する。
IBaseFilterPtr mux;
captureGraphBuilder2->SetOutputFileName(&MEDIASUBTYPE_Avi, L"C:\\test.avi", &mux, 0);
// レンダストリームを設定する。
captureGraphBuilder2->RenderStream(0, 0, video, 0, mux);
// メディアコントロールを取得する。
IMediaControlPtr mediaControl(graphBuilder);
// 録音開始。
mediaControl->Run();
// 録音中。
::MessageBox(0, TEXT("録音中"), TEXT("Test"), MB_OK);
// 録音中止。
mediaControl->Stop();
}
catch (...)
{
}
::CoUninitialize();
return 0;
}
動画速度を当倍にするにはどうしたらいいでしょうか?
CPushPinDesktop::FillBuffer() を以下のようにすれば一応当倍になりました。
ただ、何故 FPS の半分待てばよいのか理由は分からないままです。
理由が分かる方いましたら、説明お願いできないでしょうか?
HRESULT CPushPinDesktop::FillBuffer(IMediaSample *pSample)
{
{
static int g_time = ::timeGetTime();
int time = ::timeGetTime();
int span = time - g_time;
int sleep = 1000 / (30 / 2) - span; // 30 FPS
if (sleep > 0)
::Sleep(sleep);
g_time = time;
}
...
}
上のコードでは結局うまくいきませんでした。
次のようにしたら当倍になったっぽいです。
// 本当はメンバにしてコンストラクタで初期化する。
static int m_startTime = ::timeGetTime();
static int m_prevFrameNumber = 0;
// フレーム番号を算出する。
int frameNumber = (::timeGetTime() - m_startTime) / (1000 / m_fps);
if (frameNumber == m_prevFrameNumber)
{
// サンプリングが早すぎて
// フレーム番号が増えていないときは
// フレーム番号が増えるまでスリープする。
::Sleep(1000 / m_fps); // 1 フレーム分スリープ。
frameNumber++;
}
REFERENCE_TIME rtFrameLength = UNITS / m_fps;
REFERENCE_TIME rtStart = frameNumber * rtFrameLength;
REFERENCE_TIME rtStop = rtStart + rtFrameLength;
mediaSample->SetTime(&rtStart, &rtStop);
m_prevFrameNumber = frameNumber;
度々すみません。
rtStart は m_prevFrameNumber から計算した方が良いようです。
REFERENCE_TIME rtStart = m_prevFrameNumber * UNITS / m_fps;
ツイート | ![]() |