開発環境 : 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;
| ツイート |   |