LoginSignup
2
5

More than 1 year has passed since last update.

DirectShowによる仮想カメラ開発のメモ

Last updated at Posted at 2021-09-09

1. はじめに

DirectShowによる仮想カメラ開発の機会がありましたので、参考にした記事等を記載いたします。
ここではDirectShowによる具体的な実装の説明は省かせていただきます。

2. 仮想カメラ開発の先駆者さま方による記事

これをみて勉強しました。

① 日本語で分かりやすい仮想カメラ実装の解説記事

② 海外でポピュラーな仮想カメラ実装の最新コード

Visual Studio 2019 に対応

DirectShowのコア部分たるBaseClassesは開発元のMicrosoftによる更新が滞っており、これまで開発環境を選ぶ状態でした。が!
たまたまBaseClassesをVisual Studio 2019にアップグレードしたものをみつけました。上記の仮想カメラ実装で使われております。

3. 仮想カメラのコードをみてみよう

手っ取り早く実装したい方は、2項 ② の仮想カメラのコードをベースに、以下の項目を弄るとよいでしょう。

① 映像を出力するモジュールの初期化処理

Filters/Filters.cpp
CVCamStream::CVCamStream(HRESULT *phr, CVCam *pParent, LPCWSTR pPinName) :
    CSourceStream(NAME("Virtual Cam"),phr, pParent, pPinName), m_pParent(pParent)
{
    ////////////////////

    // 映像出力モジュールの初期化処理を書く

    ////////////////////

    // Set the default media type as 320x240x24@15
    GetMediaType(4, &m_mt);
}

② 映像を出力するモジュールの更新処理

Filters/Filters.cpp
HRESULT CVCamStream::FillBuffer(IMediaSample *pms)
{
    REFERENCE_TIME rtNow;

    REFERENCE_TIME avgFrameTime = ((VIDEOINFOHEADER*)m_mt.pbFormat)->AvgTimePerFrame;

    rtNow = m_rtLastTime;
    m_rtLastTime += avgFrameTime;
    pms->SetTime(&rtNow, &m_rtLastTime);
    pms->SetSyncPoint(TRUE);

    BYTE *pData;
    long lDataLen;
    pms->GetPointer(&pData);

    ////////////////////

    // 出力映像の更新
    // output : RGB24

    // 例. ランダム画像を出力する
    lDataLen = pms->GetSize();
    for(int i = 0; i < lDataLen; ++i)
        pData[i] = rand();
    // 例. おわり

    ////////////////////

    return NOERROR;
} // FillBuffer

②仮想カメラの横幅、高さ、FPS

Filters/Filters.cpp
// See Directshow help topic for IAMStreamConfig for details on this method
HRESULT CVCamStream::GetMediaType(int iPosition, CMediaType *pmt)
{
    if(iPosition < 0) return E_INVALIDARG;
    if(iPosition > 8) return VFW_S_NO_MORE_ITEMS;

    if(iPosition == 0) 
    {
        *pmt = m_mt;
        return S_OK;
    }

    DECLARE_PTR(VIDEOINFOHEADER, pvi, pmt->AllocFormatBuffer(sizeof(VIDEOINFOHEADER)));
    ZeroMemory(pvi, sizeof(VIDEOINFOHEADER));

    pvi->bmiHeader.biCompression = BI_RGB;
    pvi->bmiHeader.biBitCount    = 24;
    pvi->bmiHeader.biSize       = sizeof(BITMAPINFOHEADER);

    ////////////////////
    // 第一引数 iPositionによって横幅、高さを設定する
    pvi->bmiHeader.biWidth      = 80 * iPosition;
    pvi->bmiHeader.biHeight     = 60 * iPosition;
    ////////////////////

    pvi->bmiHeader.biPlanes     = 1;
    pvi->bmiHeader.biSizeImage  = GetBitmapSize(&pvi->bmiHeader);
    pvi->bmiHeader.biClrImportant = 0;

    pvi->AvgTimePerFrame = 1000000;

    SetRectEmpty(&(pvi->rcSource)); // we want the whole image area rendered.
    SetRectEmpty(&(pvi->rcTarget)); // no particular destination rectangle

    pmt->SetType(&MEDIATYPE_Video);
    pmt->SetFormatType(&FORMAT_VideoInfo);
    pmt->SetTemporalCompression(FALSE);

    // Work out the GUID for the subtype from the header info.
    const GUID SubTypeGUID = GetBitmapSubtype(&pvi->bmiHeader);
    pmt->SetSubtype(&SubTypeGUID);
    pmt->SetSampleSize(pvi->bmiHeader.biSizeImage);

    return NOERROR;

} // GetMediaType

(追記予定)

③仮想カメラの名前、識別用ID
(追記予定)

(余談)配布されている汎用仮想カメラの最強は?

OBS Studioの仮想カメラでなんでもできます

OBS Studio v26以降にて標準搭載の仮想カメラ機能により、OBS画面をそのままフィルタに流せます。普通の仮想カメラの用途ならこれで十分。

OBSで仮想カメラ4個ほしい!

OBS Studio 25.08 + obs-virtualcam 2.0.5

obs-virtualcam のソースコードについても仮想カメラ実装例として参考になるかと思います。

以上

時間ができたら追記いたします。

2
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
5