PushSourceDesktop の動画速度が当倍にならない

解決


forty-five  2013-11-21 18:51:59  No: 73830  IP: [192.*.*.*]

開発環境 : 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;
}

動画速度を当倍にするにはどうしたらいいでしょうか?

編集 削除
forty-five  2013-11-24 10:12:35  No: 73831  IP: [192.*.*.*]

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;
    }

    ...
}

編集 削除
forty-five  2013-11-30 20:14:24  No: 73832  IP: [192.*.*.*]

上のコードでは結局うまくいきませんでした。
次のようにしたら当倍になったっぽいです。

    // 本当はメンバにしてコンストラクタで初期化する。
    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;

編集 削除
forty-five  2013-12-11 19:54:37  No: 73833  IP: [192.*.*.*]

度々すみません。
rtStart は m_prevFrameNumber から計算した方が良いようです。

REFERENCE_TIME rtStart = m_prevFrameNumber * UNITS / m_fps;

編集 削除