仮想カメラを勉強してみる

参考

Media Foundation の公式top

https://learn.microsoft.com/ja-jp/windows/win32/medfound/microsoft-media-foundation-sdk

仮想カメラ(VirtualCamera)のサンプルの場所(MSの公式サンプル)

https://github.com/microsoft/Windows-Camera/tree/master/Samples/VirtualCamera

Media Foundationのサンプル(あちこちにサンプルがある、、、こっちの方が、基礎的なことをしてるっぽい?すくなくとも、肝部分に絞ったコードにしてくれてるように見える。(↑のVirtual Cameraのサンプルは、余計な部分が多くて、どこを参考にしてよいか今の自分にはわからん))

https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/multimedia/mediafoundation

カスタムソースの簡単な方(wavsource)のサンプル
↑のMedia Foundationのサンプルの一部

https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/multimedia/mediafoundation/wavsource

MSIインストーラーPJ

vs2017,2019

https://marketplace.visualstudio.com/items?itemName=VisualStudioClient.MicrosoftVisualStudio2017InstallerProjects

vs2022

https://marketplace.visualstudio.com/items?itemName=VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects

メディアファンデーションのMFCreateVirtualCameraは、かなり新しいAPIらしい!(win11~)

https://github.com/roman380/tmhare.mvps.org-vcam/issues/5#issuecomment-1100854239

MediaFoundationでカメラ画像を取る方法の参考

http://codeit.blog.fc2.com/blog-entry-5.html

https://qiita.com/kenjiuno/items/fa8ff3483dfc2b48c466

もくじ

https://tera1707.com/entry/2022/02/06/144447

やりたいこと

仮想カメラ(Virtual Camera)というのを勉強したい。
とりあえず、Microsoftリポジトリに、Windowsでの仮想カメラのサンプルがあるので、それの動きを見てみて、コードも追いかけてみる。

とりあえず、まだ何の知識もないので、勉強したときのメモをつらつらと書く。

動作見たメモ&コード見たメモ

仮想カメラ(VirtualCamera)のサンプルの場所(MSの公式サンプル)
https://github.com/microsoft/Windows-Camera/tree/master/Samples/VirtualCamera

■MSの仮想カメラのサンプルの動かし方

上記サンプルには「.vdproj」という、MSIインストーラーのためのプロジェクトが含まれる。
インストーラープロジェクトはVS2017からデフォルトでは含まれないようになったらしいので、
開くには、Marketplaceから拡張をインストールする必要あり。
vs2017,2019
https://marketplace.visualstudio.com/items?itemName=VisualStudioClient.MicrosoftVisualStudio2017InstallerProjects
vs2022
https://marketplace.visualstudio.com/items?itemName=VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects



・v142(vs2019)プラットフォームツールセットをVSinstallerで入れる。

・sln一式を解凍。

~~・slnのフォルダのプロパティを開いて、「読み取り専用」を全解除~~

・VSでslnを開く。

・「Release」を選択

・構成マネージャーを開いて、「Release」の「_MSI_arm64」と「Test」のpj以外の「ビルド」にチェックを入れる。

→これでビルドしたら、ビル℃成功する。

ここで、即「VirtualCameraManager_App」のPJを実行しても、予期しないエラーみたいなんで落ちる。

・VirtualCamera_MSI_x64 をビルドして、右クリック→インストール、を選ぶ。
 選ぶと、なにかのインストールが始まる。
 終わると、デフォで「C:\Program Files\Contoso\VirtualCamera_MSI」にVirtualCamera_Installer.exe と VirtualCameraMediaSource.dll が配置されてる。

・VirtualCamera_Installer.exe を !!管理者権限で!! 実行すると、COMのdll「C:\Program Files\Contoso\VirtualCamera_MSI\VirtualCameraMediaSource.dll」が、COMサーバーとしてレジストリに登録されるっポイ。
(MSIで「インストール」すると上のフォルダに配置はされるが、レジストリに登録はされないっぽい)

・上記で、「開発PC」で動かす場合は、「VirtualCameraManager_App」を実行すると、仮想カメラを動かせるようになる。ただし、開発PCではないPC(VisualStudioやVCランタイム、.netなどが入っていない、素のPC)だと、おそらくVirtualCameraManager_Appで「+」を押して仮想カメラを追加した時点で「Unexpected error」みたいなのでエラーになる。

これは、VirtualCamera_Installer.exe や  VirtualCameraMediaSource.dll が依存しているvcruntime140d.dllなどがないため。

なので、素のPCで動かす場合は、ビルド成果物の出力フォルダの「Windows-Camera-master\Samples\VirtualCamera\x64\Debug」や「Windows-Camera-master\Samples\VirtualCamera\x64\Release」の中に、一緒に出力されてできている「vcruntime140d_app.dll」などのファイル群を一緒に、素のPC側にMSIでインストールした「C:\Program Files\Contoso\VirtualCamera_MSI」のフォルダの中にコピーしてやれば、動くようになる。

※最初は、真面目に、リリースビルドした「VirtualCamera_MSI」のPJで作成したインストーラーでインストールして、_Installerを実行して登録してもいいが、一度インストール&登録した後は、VirtualCamera_Installer、VirtualCameraMediaSourceのPJのビルド出力ふぉるだ(DebugでもOK)から、直接「C:\Program Files\Contoso\VirtualCamera_MSI」にコピーしてもよさそう。(vcruntime等々が必要な場合はそれも一緒に)



■「VirtualCameraMediaSource.dll」のコードをデバッグ実行する方法

`VirtualCameraMediaSource.dll`は、「VirtualCameraManager_App」でも、「VirtualCamera_Installer」でも、仮想カメラを作ったり、登録したりするために使われる。

が、(詳しい原理はまだわかってないのだが)仮想カメラを登録した後、実際に画像を出したりする際には、`FrameServer`というサービスが`VirtualCameraMediaSource.dll`をロードして使っている様子。(FrameServerのサービスは、実態はsvchost.exe)

[f:id:tera1707:20240105230916p:plain]

で、デバッグするときは、下記のようにすると、正攻法かはわからないが、とにかくデバッグはできた。

・タスクマネージャーのサービスタブで、「FrameServer」のサービスを探す。
・仮想カメラが動いていると、このサービスの「PID」に、値が入っている。(動いてないと、入っていない)
・VisualStudioで仮想カメラサンプルのソリューションを開き、このPIDを持ったsvchost.exeにアタッチする。
→これで、通常「VirtualCameraManager_App」や「VirtualCameraManager_Installer」からはよばれない部分(例えば「HRESULT SimpleFrameGenerator::CreateFrame()など」)のデバッグができる。

ただのメモだが、

・`VirtualCameraMediaSource.dll`を誰がロードしているかは、`tasklist /m VirtualCameraMediaSource.dll`でわかる。
・ただし、今回の場合、サービスがロードしているので、管理者で起動したコマンドプロンプトでtasklistを実行する必要あり。

[f:id:tera1707:20240105231802p:plain]

試した限り、「FrameServer」のsvchost.exeは、

・VirtualCameraManager_App のアプリが生きている間はsvchostも生きていて、FrameServerのサービスもRunninng状態になっている。
・VirtualCameraManager_Appが終了しても、仮想カメラを使用している奴(Windows標準のカメラアプリなど)がいる間は、FrameServerもRunningになっている。
・Appもカメラも終了すると、しばらくして、FrameServerも終了する(stopになる)っぽい。
・追記 改めてみると、仮想カメラ関係なく、Windowsの標準カメラアプリを起動すると、FrameServerのサービスも起動してるっぽい。画像を出す状況になったら、起動するサービスなのかもしれない。


■コード解析

・UIAddNewVirtualCameraDialog_PrimaryButtonClick()で、仮想カメラを作ってるっぽい。

・m_frameSourceが、画像の元っぽい。
 でも、画像を出すところは、ただ単に、「カメラ」の画像を出そうとしているだけ?
 この時点ですでに「仮想カメラ」が動いているから、ただそのカメラを出そうとしているだけかも?
 (VirtualCameraControl.xaml.csのInitializeAsync()の中。)

※OBSのコード、COMでオーディオやカメラのデバイスを取ってきてたりして、参考になりそう。
https://github.com/obsproject/obs-studio/blob/0e5007794836c31bebd46fb492776f38f84f0441/libobs/audio-monitoring/win32/wasapi-enum-devices.c#L27

・メディアファンデーションのMFCreateVirtualCameraは、かなり新しいAPIらしい!(win11~)
https://github.com/roman380/tmhare.mvps.org-vcam/issues/5#issuecomment-1100854239
ということは、DirectShowとかよりも断然こっちを使うべきか。


上のサンプルで、仮想カメラを作ってるのはこの辺かも。この辺から見てみる。
https://github.com/microsoft/Windows-Camera/blob/d35557a065826ab8e5219a52773b64cf2d108291/Samples/VirtualCamera/VirtualCameraManager_WinRT/VirtualCameraRegistrar.cpp#L32

「VirtualCameraMediaSource.dll」とはなんだ?COMサーバーとして登録されている??
このサンプルの、VirtualCamera_MSIで「インストール」をすると登録されるのか?UWPアプリを実行すると登録されるのか?

UIMediaPlayerElement.SetMediaPlayer(m_mediaPlayer); が、MediaPlayserで画像の再生を始めるところ。
これの画像の元ネタはどこなのか?
m_mediaSource = MediaSource.CreateFromMediaFrameSource(m_frameSource);で、画像のネタをセットしてるっぽい。


EnableVirtualCamera()したら、シンボリックリンクが入ってくる。これが
\\?\SWD#VCAMDEVAPI#514EF9837B32C7ACDD04A109E1B1A48F7DFF50D71F983C70E4EA575575C4F201#{e5323777-f976-4f5b-9b55-b94699c46e44}\{FCEBBA03-9D13-4C13-9940-CC84FCD132D1}
?このシンボリックリンクが、仮想カメラで、これを元にして作成したm_mediaCapture が、画像の元になってるっぽい。

ということは、
vcam.EnableVirtualCamera();のなかに、画像の元をとってきて、シンボリックリンクを取得してる奴がいる?

 void VirtualCameraProxy::EnableVirtualCamera() の中で、メディアファンデーションの「MFCreateVirtualCamera()」を使って、IMFVirtualCamera インターフェースを取ってきてる。
 で、それを使って、EnableVirtualCamera()の中で、
 ・m_spVirtualCamera->Start(nullptr)
 ・m_spVirtualCamera->GetAllocatedString() → これは、サンプルの自作関数pっぽい。これを使って、カメラ(今回は仮想カメラ)のfriendlyNameやシンボリックリンクの値を取ってる。
 などをしてる奴がいる。
https://learn.microsoft.com/ja-jp/windows/win32/api/mfvirtualcamera/nf-mfvirtualcamera-mfcreatevirtualcamera

今回のサンプルでは、
MFCreateVirtualCamera()で使うsourceId(メディアソースID?)は、
{7B89B92E-FE71-42D0-8A41-E137D06EA184} で固定にしてるっぽい。 → これが、「VirtualCameraMediaSource」が提供しているカメラのIDなのか??

ざっと見た限り、クライアント?側は、、、

・MFCreateVirtualCamera()に、
 ・FriendlyNameと
 ・MediaSourceのGuid
 を渡して仮想カメラをつくる。
 で、今回のSampleだと、MediaSourceのGUIDは、固定で{7B89B92E-FE71-42D0-8A41-E137D06EA184}を使っている。
・おそらく、VirtualCameraMediaSource 側も同じGUIDを使って、なにかサーバー的なことをしているような気がする。
※処理には関係ないが、winRT部分がstep実行等のデバッグができない。なぜか?不便なので調べる。
 →「デバッガーの種類」のところの、「アプリケーションプロセス」を「混合」にしたら、dll側もデバッグできるようになった!


→i明日は、
VirtualCameraMediaSource がどういう作りになってるか見る。
もういっかい、readmeをよみなおしてもよいかも。


はっきりしないが、
・一回再起動すると、VirtualCamera_Appのあぷりが動かなくなる?
・その場合、CiatualCamera_Installer.exeで、「systme」でVirtualCaemraをインストールすると動くようになる?
・そんなことしなくても、VisualStudioでデバッグ実行せずに普通にVirtualCamera_Appを起動して、+を押せば、起動するようになる?

VirtualCamera_Installer のPJの成果物VirtualCamera_Installer.exeが、VirtuakCameraを作成してる?一度見てみる。

・VirtualCamera_Installer.exeの処理の流れメモ
 ・MFStartup()でメディアファンデーションを初期化する
 ・やることをUserから入力させる(今回は1(register)を追いかけてみる)
 ・オプションを入力させる(今回は1 SimpleMediaSourceでやってみる)
 ・らいふたいむを入力させる(今回は1 system でやってみる)→MFVirtualCameraLifetime_System になる」
 ・ユーザーアクセスをにゅうりょくさせる(今回は1:CurrentUserにする)→MFVirtualCameraAccess_CurrentUser になる
 ・SimpleMediaSourceUT::CreateVirtualCamera()
  ・VCamUtils::RegisterVirtualCamera()
   ・MFCreateVirtualCamera()で仮想カメラを作る    →VirtualCameraManager_Appから、このVirtualCamera_Installer.exeを起動している・????違うかも???
   ・※管理者でVS起動して、デバッグをしていないと、エラーで終わるようになってる!
   ・MFCreateVirtualCameraで作った仮想カメラへのポインタで、
    ・DEVPKEY_DeviceInterface_VCamCreate_SourceId(値は{6AC1FBF7-45F7-4E06-BDA7-F817EBFA04D1})として、「0x00000232706fa55c L"{7B89B92E-FE71-42D0-8A41-E137D06EA184}"」を spVirtualCamera->AddProperty()する
     →この{7B89B92E-FE71-42D0-8A41-E137D06EA184}というのが、指定の場所においてある「VirtualCameraMediaSource.dll」のことをさすっぽい!!!!!!!
    ・DEVPKEY_DeviceInterface_VCamCreate_FriendlyName(値は{fmtid={6AC1FBF7-45F7-4E06-BDA7-F817EBFA04D1} pid=5 })として、「0x00000232706f1b6c L"SWVCamMediaSource"」を spVirtualCamera->AddPropertyする
    ・DEVPKEY_DeviceInterface_VCamCreate_Lifetimeとして、1(systemライフサイクルを。
    ・spVirtualCamera->Start(nullptr)をする。
     (具体的には「VirtualCameraMediaSourceActivate::ActivateObject(REFIID riid, void** ppv)」に飛んだ!)
     →ってことは、・・・・?なんだ
     ・?SimpleMediaSource::Initialize() ここで、MediaSourceを作ってるのか?
      ・SimpleMediaSource::_CreateSourceAttributes(_In_opt_ IMFAttributes* pActivateAttributes)
       ・MFCreateAttributes(&m_spAttributes, 1)を呼ぶ
       ・MFCreateSensorProfileCollection()を呼ぶ
       ・MFCreateSensorProfile(KSCAMERAPROFILE_Legacy, 0, nullptr, &profile)を呼ぶ
       ・profile->AddProfileFilter(STREAM_ID, L"((RES==;FRT<=30,1;SUT==))") を呼ぶ
       ・MFCreateSensorProfile(KSCAMERAPROFILE_HighFrameRate, 0 /*ProfileIndex*/, nullptr, &profile) を呼ぶ
       ・profile->AddProfileFilter(STREAM_ID, L"((RES==;FRT>=60,1;SUT==))")を呼ぶ
       ・profileCollection->AddProfile(profile.get())
       ・m_spAttributes->SetUnknown( MF_DEVICEMFT_SENSORPROFILE_COLLECTION, profileCollection.get())
       ・m_spAttributes->SetString(MF_VIRTUALCAMERA_CONFIGURATION_APP_PACKAGE_FAMILY_NAME, appInfo.PackageFamilyName().data()) で現在のUWPアプリを紐づけるらしい、、が必須ではないのか?
        (UWPアプリが紐づけられなくても、S_OKが変えるようにしている。。。)
      ・MFCreateEventQueue()でイベントキュー?を作る?
      ・m_streamList = wilEx::make_unique_cotaskmem_array<wil::com_ptr_nothrow<SimpleMediaStream>>(NUM_STREAMS); でストリームリストを作成、
      ・m_streamList.get()でgetする
      ・auto ptr = winrt::make_self<SimpleMediaStream>();でストリームを作成
      ・m_streamList[i] = ptr.detach();でストリームをでタッチ?
      ・m_streamList[i]を(m_streamList[i]->Initialize(this, i, MFSampleAllocatorUsage_UsesProvidedAllocator) でイニシャライズ
      ・イニシャライズしたストリームの情報を取得? m_streamList[i]->GetStreamDescriptor(&streamDescriptorList[i])
     ・m_spSimpleMediaSrc->QueryInterface(riid, ppv);する。
    ・ここで、もとのアプリのコード?のほうに戻ってくる。
    ・ spVirtualCamera.detach() する。


・VirtualCameraMediaSource.dllは、
 ・VirtualCamera_MSI_x64 が、(VirtualCamera_MSI_x64をビルドしてできたインストーラーを使ってインストールしたときに)
  ・"C:\Program Files\Contoso\VirtualCamera_MSI"に配置する。
  ・同時に、COMサーバーとしてレジストリにも登録する。
  ・その辺の配置や登録は、VirtualCamera_MSI_x64.vdprojの中身を見たら、登録するための記述(カスタムアクション?デプロイ?呼び名はわからないが)が書いてある。
 ・VirtualCamera_Installerからも呼ばれる。
 ・VirtualCameraManager_Appからも呼ばれる。
 よばれると、VirtualCameraMediaSourceActivate::ActivateObject(REFIID riid, void** ppv)がまずよばれるっぽい。
 ※もしくは、もしかしたら「m_spVirtualCamera->Start(nullptr)」が呼ばれると、ActivateObject()にくるのかも。

・VirtualCamera_MSI_x64がインストールするdllとして使うものは、MSのリポジトリのそのままの状態だとReleaseビルドのVirtualCameraMediaSource.dllなどを使うようになっている。
 (VirtualCamera_Installer.exeも。)
 それを変えるには、vdprojの中にある配置するものそれぞれのパスを手で書き換えた。(VSのUIから設定する方法が見当たらなかった)
 またその際、パス以外にも、なにかいじった気がする。(たしか、なにかのGUIDをどこかにコピーした気がする。で、なにをコピーしたらいいかを調べるために、リポジトリのvdprojと、自分で手でいじったvdprojをdifした気がする。詳しくは忘れた)

・VirtualCamera_Installer も VirtualCameraManager_App も、肝になる、COMサーバーとして登録された「VirtualCameraMediaSource.dll」を呼んでるだけっぽい。
 (カメラアプリから仮想カメラを選んだ時に、どんな画像が表示されるかは、やっぱりVirtualCameraMediaSource.dllをみないとわからんっぽい)

------------------------------------------

・dllの入り口は「HRESULT __stdcall DllGetClassObject(GUID const& clsid, GUID const& iid, void** result)」ここっぽい。

------------------------

・あの画像(青いグラデーションが、ちょっとずつ上に上がっていく画像)を作り出しているのは、SimpleFrameGeneratorクラスの「CreateFrame()」だった!
 ※ただ、そこの部分、なぜかbreakで止まってくれない。ちょっとだけ改造して、動きが変わってるのを見た、という確認方法で見た。

-------------------

MSの仮想カメラのサンプルは、「仮想カメラ」に必要なコード以外に、MediaFoundationでカメラ映像を再生する、とかの、仮想カメラ特融ではない、MediaFoundation関連のコードもたくさん含まれている。
で、MediaFoundationをあんまりしらない今の自分の知識だと、仮想カメラ特有部分なのか、MediaFoundationの基礎部分なのかが見分けられない。
→いったん急がば回れで、MediaFoundationを使ってカメラえいぞうを表示する方法をまず勉強するのが近道だと感じた。(その後、仮想カメラ特有部分を洗い出す)
 ※仮想カメラのサンプルコードの一部が、メディアファンデーションの(仮想カメラではない)サンプルコードの中にも出てくる。
 https://learn.microsoft.com/ja-jp/windows-hardware/drivers/stream/frame-server-custom-media-source#media-buffer
 MFで画像を作り出す部分、を勉強したい。

⇒まずMediaFoundationの基礎を勉強する!
→勉強した。もう一度仮想カメラに戻る。

■調査ToDo

・UWPアプリを終わらすと、なんでWindowsカメラの下図のマークが消えるのか?(仮想カメラがいないことになってる?)

[f:id:tera1707:20240106000223p:plain]

※windows11のカメラの設定画面からも、UWPアプリを落とすと仮想カメラの表示が消える。なんで?

[f:id:tera1707:20240106001122p:plain]

・上に関連して、
 UWPアプリを×で閉じると即windowsの設定画面から仮想カメラが消えて、windowsのカメラアプリのカメラ切り替えボタンも消えるが、VirtualCamera_Installer.exeの1.registerでカメラを登録すると、installer.exeを閉じても設定画面に仮想カメラが残り続ける。なぜ?
→違った、、、UWPのほうは、「Contoso VirtualCmaera・・・」という名前で、Instraller.exeで登録した仮想カメラは「SVWCCamMediaSource・・・」だった。
→ということは、単に、UWPのほうは、アプリ終了時に仮想カメラ削除してて、installer.exeのほうはそれをしてないだけ、ということかも>???

[f:id:tera1707:20240106011820p:plain]

→違った。Install.exeとUWPアプリの違いは、

・MFCreateVirtualCamera()するときに、
 ・Install.exeの方は、ライフタイムをMFVirtualCameraLifetime_Systemにしていた。
 ・UWPアプリの方は、ライフタイムをMFVirtualCameraLifetime_Sessionにしていた。
  (していた、というか、UWPアプリで仮想カメラつくるときに、自分でそういう設定を操作でしていた)

 で、Sessionのほうだと、「カメラは、 IMFVirtualCamera オブジェクトが破棄されるか、 IMFVirtualCamera::Shutdown が呼び出されるまで保持されます。 」とのことなので、アプリを落とすとオブジェクトが破棄されて、カメラも消えていたのだと思われる。
https://learn.microsoft.com/ja-jp/windows/win32/api/mfvirtualcamera/ne-mfvirtualcamera-mfvirtualcameralifetime

※ライフライムをSystemにすると、PCを再起動しても、仮想カメラは生きていた。

・仮想カメラ周りの構成図を書きたい。
 SimpleFrameGenerator→SimpleMediaSource→SimpleMediaStreamer→使う側、であってるか?

・なんとなく、UWPアプリのほうはもう見ずに、仮想カメラ作成と登録は、Installer.exeのほうの、`RegisterVirtualCamera()`だけ見て、削除は`UnInstallVirtualCamera()`だけ見ればよい気がする。 
(UWPのほうは、余計な処理をいろいろしすぎてて、初学者にはなにがしたいか、仮想カメラの処理の要点がどこかが全然わからない)

■調査してみた現状の感触 24/01/05

上でいろいろ調べてみた感触としては、

・MSの仮想カメラサンプルアプリのように、仮想カメラを作るうえで、UWPアプリを使う必要はなさそう。
 (現に、MSのサンプル内の非UWPのVirtualCamera_Installer.exeでも仮想カメラ作れてるし)
・サンプルアプリが使ってるWinRTも、必須ではなさそう。
 (たぶん、他のMediaFoundationのサンプルコードから、そのままVirtualCameraのサンプルコードを作ったときの名残じゃないか?)
・ライフタイムを「System」にしてやって、仮想カメラを作れば、仮想カメラを維持するために、ずっと何かのアプリを常駐させとくとかも必要なさそう。
・あと調べないといけないのは、MediaSource側で、好きな画像を出すにはどうしたらよいか?というところ。
※なんか、スパイ映画や警察ドラマとかでよく出てくる、複数の監視カメラ(まぁ今回の場合はPCにつないだwebカメラか?)の映像を、数秒おきに自動で切り替える、みたいなのをやってみたい。

■あとやること

・SimpleMediaSource の作り方
・HWMediaSource の作り方
・AugmentedMediaSource の作り方

■使う側を調べる

・7B89B92E・・・・は、メディアソースのGuid
 ≒レジストリに登録した、COMサーバとしてのdllのGuid
 ≒VirtualCameraMediaSourceActivateクラスのGuid?
 ≒IMFActivateを継承して作ったクラス
→このGuidを、MFCれあてVirtualCamera()のAPIの第五引数に入れている。(第四引数はfriendlyname)
★VCamUtil.cppのVCamUtil::RegisterVirtualCamera()
 →ここが、仮想カメラを使う側の最終到達地点のような気がする。(自信ないが)

■SimpkeMediaSourceをしらべる(処理の流れを追う)

?レジストリに7B89B92E・・・のGuidを登録しているのはどこ?
?winrt::make_self<>()ってなんだ?


・VCamがStart()されると、COMサーバーで登録したメディアソースのdllのほうに飛ぶっぽい。
 (今回のサンプルだと`C:\Program Files\Contoso\VirtualCamera_MSI`にある`VirtualCameraMediaSource.dll`)
・dllmain.cppのDllGetClassObject()
 このdllmainの第一引数に、例のメディアソースのGuid(7B89B92E・・)が載ってる。
 ・VirtualCameraMediaSourceActivateFactoryクラスのCreateInstance()にくる。
  ・ VirtualCameraMediaSourceActivate::Initialize()をよぶ
   ・MFCreateAttributes()でm_spActivateAttributesに値を入れる ★
    (MF_VIRTUALCAMERA_PROVIDE_ASSOCIATED_CAMERA_SOURCES にwinnverにお作法があるっぽい)
 ・VirtualCameraMediaSourceActivateクラスに行く
  ・ VirtualCameraMediaSourceActivate::GetUINT32()などを数回呼ぶっぽい?
  ・VirtualCameraMediaSourceActivate::ActivateObject()に行く
  ※SimpleMediaSourceの場合だと、VCまTypeは「vCamKind == (UINT32)VirtualCameraKind::Syntheti」になる??
  ★ここからSimpleMediaSource★
  ・ここで、SimpleMediaSource へのポインタを作成し、
   ・Initialize()を呼ぶ
    ・MFCreateAttributes()を呼び、m_spAttributesをつくる ★
    ・MFCreateSensorProfileCollection()を呼んで、profileCollectionをつくる
    ・MFCreateSensorProfile()を呼んで、profileをつくる。 ★
    ・このprofileに、好きなprofile(仮想カメラの設定、ということか?)をする。
    ・セットしたいprofileの数だけ、profileCollection->AddProfile(profile.get()));を行う。
    ※ここで出てくる「」とかがなにを示しているのかよくわからなかったが、下記に、参考になりそうなことが書いてあった。使うカメラ(の性能?)を指定する、ということだろうか、、、
https://learn.microsoft.com/ja-jp/windows-hardware/drivers/stream/camera-profile-v2-sensor-group-generation
    ※もしかしたら、Profileの追加は必須ではないのかも?指定したい人だけ指定したらよいのかも?(未確認)
    ・m_spAttributes->SetUnknown(MF_DEVICEMFT_SENSORPROFILE_COLLECTION, profileCollection.get()) で、Attributeにprofileをセットする ★

    ・MFCreateEventQueue()で、イベントキューを作る ★
    
    ・ここで、「SimpleMediaStream」の配列をnewしてる!!!★★★★
     (streamのArrayをしていしてるが、このサンプルの場合ははArray数は1だけっぽい)
    ・IMFStreamDescriptorを作成する★

    ★ここからSimpleMediaStream!★
    ・「IMFMediaType」をnewし、いろんな設定を行う。 ★
     ・MF_MT_MAJOR_TYPE
     ・MF_MT_SUBTYPE
     ・MF_MT_INTERLACE_MODE
     ・MF_MT_ALL_SAMPLES_INDEPENDENT
    ・MFSetAttributeSize()で、フレームサイズを指定 ★
    ・MFSetAttributeRatio()で、ふれーむれーと?を指定 ★
     ・MF_MT_FRAME_RATE
     ・MF_MT_PIXEL_ASPECT_RATIO
    ※このサンプルだと、何種類かメディアタイプ指定してる?(2つ)
    
    ・MFCreateAttributes()で、Attributeをセット? ★
     ・(pAttributeStore->SetGUID(MF_DEVICESTREAM_STREAM_CATEGORY, PINNAME_VIDEO_CAPTURE));
     ・(pAttributeStore->SetUINT32(MF_DEVICESTREAM_STREAM_ID, m_dwStreamId));
     ・(pAttributeStore->SetUINT32(MF_DEVICESTREAM_FRAMESERVER_SHARED, 1));
     ・(pAttributeStore->SetUINT32(MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES, MFFrameSourceTypes::MFFrameSourceTypes_Color));

    ・MFCreateEventQueue()で、イベントキュー作る ★
     ※あれ?上でも作ってたけど?streamとSourceで別のキューが必要?

    ・MFCreateStreamDescriptor()で、でスクリプタをセット ★
     ・メディアタイプの数
     ・上で作ったメディアタイプ
    
    ・作ったでスクリプタで、m_spStreamDesc->GetMediaTypeHandler(&spTypeHandler) をして、
     IMFMediaTypeHandlerタイプハンドラーを取得 ★
    ・タイプハンドラーに、spTypeHandler->SetCurrentMediaType(mediaTypeList[0]) をして
     現在のタイプをセット(このサンプルだと、2個作ってるので、1個目をセットしてる) ★

    ・StreamDescripterに下記を入れる ★
     ・pAttributeStore->SetGUID(MF_DEVICESTREAM_STREAM_CATEGORY,PINNAME_VIDEO_CAPTURE));
     ・pAttributeStore->SetUINT32(MF_DEVICESTREAM_STREAM_ID, m_dwStreamId));
     ・pAttributeStore->SetUINT32(MF_DEVICESTREAM_FRAMESERVER_SHARED, 1));
     ・pAttributeStore->SetUINT32(MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES,    MFFrameSourceTypes::MFFrameSourceTypes_Color));

    ・★streamのInitializeおわり★
    ・★ここから再びSimpleMediaSource★

   ・作ったStreamDescriptorを、「MFCreatePresentationDescriptor()」に渡して、 ★
    「IMFPresentationDescriptor」の取得する ★

  ・なんか、あとは、F10おしても、どこからよばれたかよくわからんような、(たぶん、継承したinterfaceのお作法的に呼ばれている)関数に飛んでた。

 ・で、`spVirtualCamera->Start(nullptr)`に戻ってきた。


■つぎ やること

・使うクラスを洗い出し
・どのAPIがどのクラスやIFを必要としてるかを洗い出し
・必要なクラスが継承しているinterfaceの持っている関数のmsdocsを読む。
 (コードだけ見ても、流れは分かってもなにをしているのか、各関数、in/outがなにを示すのかよくわからないので)
 (たぶん、メディアソースdllの、interfaceのメソッドを実装したやつらは、FrameServer(svchost)側のタイミングで呼ばれてる気がするので、基本実装はサンプルのままにして、自分のやりたいことだけできるよう実装したらいい気がする)

・下記を調べる
 ★メディアソースのdllと、それを使う側の紐づけの仕方
  ・メディアソースとして作成したdllをCOMサーバーとして
   ・GUIDと
   ・dllのパスを
   レジストリに登録する。
   たぶん、自分でやるときは、regsvrで登録する?(未確認)
  ・登録したGUIDをもつxxxxxActivateクラスを作成する
 それだけ、か?

 ★メディアソースとstreamを紐づける方法

・動画のサイズとか、fpsとかは、MediaStreamクラス(SimpleMediaStream)が握ってる感じか。
 (Initialize()でその辺設定してそう)

Media Foundationの勉強

Media FoundationのMS公式サンプル。

上のVirtualCameraよりこっちのほうが、百倍シンプルで読みやすいサンプル。

https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/multimedia/mediafoundation

SimplePlay

→動画ファイルを開いて再生するサンプル

MFPlayer2

→動画ファイルを開いて再生、早送り、巻き戻しなどの操作をするサンプル

SimpleCapture

→カメラを選択して映像を表示するサンプル

MFCaptureToFile

→カメラ映像をファイルに保存するサンプル(キャプチャ中の画面表示はなし。あくまで保存だけ)

ざっと、MediaFoundationでカメラ映像をウインドウ上に移すコードを書いてみた。

流れは下記のような感じ。

必要なもの(変数)

  • UINT32 m_cDevices;
  • IMFActivate** m_ppDevices;
  • WCHAR* m_pwszSymbolicLink;
  • IMFAttributes* pAttributes = NULL;
  • IMFPMediaPlayer* m_pPlayer;
  • UINT32 m_cchSymbolicLink;
  • IMFMediaSource* m_pSource;

必要なもの(クラス作成)

  • メディアプレイヤーのイベントを受けるためのクラスを作成
    • IMFPMediaPlayerCallback を継承したCOMクラスを作成

やること

  • 初期化する
    • CoInitialize(NULL)する
    • MFCreateAttributes(&pAttributes, 1);
  • カメラデバイスの列挙をする
    • pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
    • MFEnumDeviceSources(pAttributes, &m_ppDevices, &m_cDevices);
  • 列挙したデバイスの情報を取得
    • バイス名を取得
      • m_ppDevices[i]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &buffer, &length);
  • メディアプレイヤーを作成する
    • MFPCreateMediaPlayer(NULL, FALSE,0, cp, g_hWnd, &m_pPlayer);
  • 列挙したデバイスをActivateして、メディアソースを作成
    • シンボリックリンクを取得
    • m_ppDevices[i]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &m_pwszSymbolicLink, &m_cchSymbolicLink );
  • メディアソースからメディアアイテムを作成
    • m_pPlayer->CreateMediaItemFromObject( pSource, FALSE, 0, NULL);
  • メディアプレイヤーのメディアアイテムが作成されたイベント(MFP_EVENT_TYPE_MEDIAITEM_CREATED)で、メディアアイテムのセットを行う
    • m_pPlayer->SetMediaItem(pEvent->pMediaItem);
  • メディアプレイヤーのメディアアイテムがセットされたイベント(MFP_EVENT_TYPE_MEDIAITEM_SET)で、再生の準備と再生を行う
    • ビデオサイズの取得
      • m_pPlayer->GetNativeVideoSize(&szVideo, NULL);
      • ウインドウサイズを合わせるなどの調整が必要ならやる
    • メディアプレイヤーの再生
      • m_pPlayer->Play();

やってみたコード

このダイアログに、USBカメラの映像を出してみた。

https://github.com/tera1707/CameraJikken/tree/main/WindowsProject1

仮想カメラのサンプルのほうに出てきた用語がいくつも出てきた。

上の手順の中の「列挙したデバイスをActivateして、メディアソースを作成」のところでやった「メディアソース」を、仮想カメラのメディアソースに置き換えると、仮想カメラの映像が流せる、みたいな感じのような気がする。

ということは、仮想カメラの方のリポジトリの中の「メディアソース」を作成する部分以外は、そのメディアソースを「再生する」ためのコードだから仮想カメラ特有ではないということ??

「メディアソース」作成する部分に絞ってみれば仮想カメラ特有部分を抜き出せるのかも?

となると、やはり下記か。。。。(仮想カメラのUWPのUIとかは、もうあまり見なくてよいかも)

24/01/16

仮想カメラサンプルのVirtualCameraMediaSource.dllは、どうも「カスタムmediaSource」というものっぽい。

https://learn.microsoft.com/ja-jp/windows/win32/medfound/writing-a-custom-media-source

で、そのカスタムメディアソースのサンプルがいくつかある。

mpeg1ソース

https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/multimedia/mediafoundation/mpeg1source

wavソース

https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/multimedia/mediafoundation/wavsource

とりあえず2つありそうだが、wavソースの方がだいぶ簡単なカスタムメディアソースだとあったので、まずはそっちを見てみる。

→wavsourceのほうを、なにも考えずにvs2022でslnを開いてビルドしたら、なにもせずにビルド成功した。wavsourceのreadmeに、ビルドしてregsvr32.exeで登録して、とかの動かし方の作法がのってたので、明日はそれに従って、まずは動かしてみる。そこからコードおう。

WavSourceのサンプルを動かすためには、「BasicPlayback Sample」というサンプルのプレーヤーがいるらしい。

https://learn.microsoft.com/ja-jp/previous-versions//bb970475(v=vs.85) MSDocsのここの欄に出てる、見たいサンプルと同じ名前の項目を見た方がよいかも。

repoのreadmeにはなかった情報が、ここの中にはありそう。
(今回のWavSourceの場合は、WavSourceを試すために必要なPlayerの場所が、こっちのページに書かれてた)

※ただ、ここにかいてた「BasicPlayback」は、今のリポジトリからは無くなってる??

代わりっぽいのが下記の「protectedplayback」っぽい。(vs2022で、下記の2行だけコメントあうとしたら、ビルドできた。)

https://github.com/microsoft/Windows-classic-samples/tree/44d192fd7ec6f2422b7d023891c5f805ada2c811/Samples/Win7Samples/multimedia/mediafoundation/protectedplayback

HCURSOR hCursor = LoadCursor(NULL, MAKEINTRESOURCE(bWaiting ? IDC_WAIT : IDC_ARROW));
SetCursor(hCursor);

うーん、これを使って、WavSource.dllのreadme通りにやっても、エラーになる。。。

※wavファイルを選んだら、普通にMFPlayBackアプリで、音声が再生された。

regsvr32.exeには32bitと64bit用があるらしい。

regsvrをすると、 コンピューター\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Media Foundation\ByteStreamHandlers に、.xyzのキーが登録されるっぽい?のだが、 それが登録されてくれない。

今や64bitなのに、32bitでやっていたから??

推測:WavSurce.dllをregsvr32で登録すると、WavSurce.dllの中に書いているDllRegisterServer()が呼ばれる??→正解っぽい。

どうしても64bitのほうに登録されてくれない。別PCで、同じことやってみる。★いまここ

regsvr32の32bit/64bitについて↓

https://unemoto.com/wp/?p=130

うまくいった。うまくいかなかったのは、下記2点だった。

①アクティブなプラットフォームが「x64」なのに、x64時に使うプラットフォーム(下の赤枠)がWin32になってて、32bitになってた。

②32bit用のregsvr32.exe(C:¥Windows¥system32¥regsvr32.exe)を使ってしまってた。

→結果、「コンピューター\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Media Foundation\ByteStreamHandlers」の下に、「.xyz」というキーが追加されてほしいのに、されなかった。(代わりに、コンピューター\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows Media Foundation\ByteStreamHandlers の下に.xyzができてた)

なので、下記のようにしたらうまくいった。

  • WavSourceのサンプルのソリューション構成画面で、x64時の使用プラットフォームを「x64」にしてビルドする
  • C:¥Windows¥SysWOW64¥ にある regsvr32.exe で、「regsvr32.exe <WavSource.dllのパス>」をする。

そうすると、必要なレジストリキーが、WavSource.dllの「DllRegisterServer()」に書いてある場所に登録され、「protectedplayback」のアプリで、.wavを.xyzに拡張子だけ変えたファイルを再生できた。(失敗時は、エラーMsgBoxが出てた)

こんな感じで登録されてた↓

※メモ→別記事にしたい

32bitでビルドしたdllを、

  • regsvr32の32bit版で登録すると、32bit用の「コンピューター\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows Media Foundation\ByteStreamHandlers」に登録される
  • regsvr32の64bit版で登録しても、32bitと同じところに登録される(64bitにいってくれない)

64bitでビルドしたdllを、

  • regsvr32の32bit版で登録すると、→見てない
  • regsvr32の64bit版で登録すると、64bit用の「コンピューター\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Media Foundation\ByteStreamHandlers」に登録される

.xyzのファイルを再生してみた。

再生中に、メディアソースの「WavSource.dll」を握ってる奴がいるかを見たら、そのまんま、プレーヤーが握っていた。(動画のときは、FrameServerサービス=svchost.exeだった気がするが、wavは別なのかも?)

WavSourceのPJをVSで開いて、「protectedplayback」にアタッチ(プロセス名はMF_ProtectedPlayback.exe)でデバッグできた。

試しにbreakを「HRESULT WavStream::RequestSample(IUnknown* pToken)」の中に貼って止めてみると、音声も途中で止まった。(→メディアソースのWavSource.dllが、音声をなにか制御してるっぽい。)


メディアソースのトポロジの組み方を目で見えるようにする「TopoEdit」というのがメディアファンデーションのSDKに入ってるらしい。

これを使ってみようと思い、見てみたら、下記に入っていた

C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\topoedit.exe

これを実行して、 "File - Render File" からメディアソース?のファイルを開くと、トポロジが見える?らしい。

が、上のほうで作ったWavSource.dllを指定しても、エラーがでて開けなかった。

もしや、TopoEditが64bitで、WavSource.dllが32bitとか、その逆とかか?組み合わせでやってみる。

もしくは、TopoEditはメディアソースのdllを開くもんじゃない?別のものを開くべきなのか? 参考:
https://zenn.dev/tan_y/articles/16748129f4cce7

ちがったかも。ためしにTopoEditで、普通のwavファイルを開くと、トポロジ?が見えた。下記。

で、再生ボタンを押すと、再生できた。

.xyzが開けた!
上で、WavSource.dllをregsvr32で登録して、レジストリに.xyzのキーがある状態で、TopoEditから "File - Render File" で.wavをリネームして.xyzにしたファイルを開けばよかった。そうすると、下記が見えた。

明日やること 1/23

WavSource.dllのコードの中の、class WavSource : public IMFMediaSourceが実装している 「// IMFMediaSource」系のメソッドが、wav(xyz)を再生開始したり停止したりするときのメソッドで、 「// IMFMediaEventGenerator」系のメソッドが、今まさに再生中に繰り返し呼ばれる奴では?? (完全に想像)

一度、下記をやりたい。

  • WavSourceの、DLLロードされたときに呼ばれそうなところ(dllmain)に、デバッグログを入れる
  • WavSourceの、「// IMFMediaSource」系と「// IMFMediaEventGenerator」系のメソッドの中にデバッグ用ログを入れて、通ったことをわかるようにする
  • ProtectedPalybackの、StartとかStopしてそうなところに、デバッグログを入れる
  • というふうにしてから、それぞれのログファイルをtailして、どう流れるか見る ※ログは、スレッドセーフ風味にすること!

1/30

下記で作ったC++用ログを、WavSource.dllの各所に貼り付けると、こんな感じになった。(.xyzに拡張子を変えた.wavを、3秒くらい再生した)

https://tera1707.com/entry/2024/01/25/221222

2024/01/30 22:49:34 19852 23364 DllMain
2024/01/30 22:49:34 19852 23364 DllGetClassObject
2024/01/30 22:49:34 19852 23364 CClassFactory
2024/01/30 22:49:34 19852 23364 DllAddRef
2024/01/30 22:49:34 19852 23364 CClassFactory QueryInterface
2024/01/30 22:49:34 19852 23364 CClassFactory AddRef
2024/01/30 22:49:34 19852 23364 CClassFactory Release
2024/01/30 22:49:34 19852 23364 CClassFactory CreateInstance
2024/01/30 22:49:34 19852 23364 CWavByteStreamHandler::CreateInstance
2024/01/30 22:49:34 19852 23364 CWavByteStreamHandler::CWavByteStreamHandler
2024/01/30 22:49:34 19852 23364 DllAddRef
2024/01/30 22:49:34 19852 23364 CWavByteStreamHandler::QueryInterface
2024/01/30 22:49:34 19852 23364 CWavByteStreamHandler::AddRef
2024/01/30 22:49:34 19852 23364 CWavByteStreamHandler::AddRef
2024/01/30 22:49:34 19852 23364 CWavByteStreamHandler::Release
2024/01/30 22:49:34 19852 23364 CClassFactory Release
2024/01/30 22:49:34 19852 23364 CClassFactory ~CClassFactory
2024/01/30 22:49:34 19852 23364 DllRelease
2024/01/30 22:49:34 19852 23364 CWavByteStreamHandler::QueryInterface
2024/01/30 22:49:34 19852 23364 CWavByteStreamHandler::AddRef
2024/01/30 22:49:34 19852 23364 CWavByteStreamHandler::Release
2024/01/30 22:49:34 19852 23364 CWavByteStreamHandler::QueryInterface
2024/01/30 22:49:34 19852 23364 CWavByteStreamHandler::QueryInterface
2024/01/30 22:49:34 19852 23364 CWavByteStreamHandler::BeginCreateObject
2024/01/30 22:49:34 19852 23364 WavSource::CreateInstance
2024/01/30 22:49:34 19852 23364 WavSource::WavSource(HRESULT& hr)
2024/01/30 22:49:34 19852 23364 DllAddRef
2024/01/30 22:49:34 19852 23364 WavSource::Open()
2024/01/30 22:49:34 19852 23364 AudioDurationFromBufferSize()
2024/01/30 22:49:34 19852 23364 ValidateWaveFormat()
2024/01/30 22:49:34 19852 23364 CWavByteStreamHandler::EndCreateObject
2024/01/30 22:49:34 19852 23364 CWavByteStreamHandler::Release
2024/01/30 22:49:34 19852 23364 CWavByteStreamHandler::~CWavByteStreamHandler
2024/01/30 22:49:34 19852 23364 DllRelease
2024/01/30 22:49:34 19852 21336 WavSource::CreatePresentationDescriptor()
2024/01/30 22:49:34 19852 21336 WavSource::CreatePresentationDescriptor()
2024/01/30 22:49:34 19852 21336 WavSource::WaveFormat()
2024/01/30 22:49:34 19852 21336 WavSource::WaveFormat()
2024/01/30 22:49:34 19852 17952 WavSource::CreatePresentationDescriptor()
2024/01/30 22:49:34 19852 17952 WavSource::CreatePresentationDescriptor()
2024/01/30 22:49:34 19852 17952 WavSource::GetCharacteristics()
2024/01/30 22:49:34 19852 17952 WavSource::GetCharacteristics()
2024/01/30 22:49:34 19852 12972 WavSource::BeginGetEven()
2024/01/30 22:49:34 19852 17952 WavSource::GetCharacteristics()
2024/01/30 22:49:34 19852 17952 WavSource::Start()
2024/01/30 22:49:34 19852 17952 WavSource::ValidatePresentationDescriptor()
2024/01/30 22:49:34 19852 17952 WavSource::WaveFormat()
2024/01/30 22:49:34 19852 17952 WavSource::WaveFormat()
2024/01/30 22:49:34 19852 17952 WavSource::QueueNewStreamEvent()
2024/01/30 22:49:34 19852 17952 WavSource::CreateWavStream()
2024/01/30 22:49:34 19852 17952 WavStream::WavStream()
2024/01/30 22:49:34 19852 17952 QueueEventWithIUnknown()
2024/01/30 22:49:34 19852 17952 WavStream::AddRef()
2024/01/30 22:49:34 19852 17952 WavSource::QueueEvent()
2024/01/30 22:49:34 19852 17952 WavStream::AddRef()
2024/01/30 22:49:34 19852 17952 WavStream::Release()
2024/01/30 22:49:34 19852 12972 WavSource::BeginGetEven()
2024/01/30 22:49:34 19852 17952 WavStream::SetPosition()
2024/01/30 22:49:34 19852 17952 AudioDurationFromBufferSize()
2024/01/30 22:49:34 19852 17952 WavStream::QueueEvent()
2024/01/30 22:49:34 19852 17952 WavStream::DeliverQueuedSamples()
2024/01/30 22:49:34 19852 12972 WavStream::AddRef()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::AddRef()
2024/01/30 22:49:34 19852 12972 WavStream::Release()
2024/01/30 22:49:34 19852 12972 WavStream::GetStreamDescriptor()
2024/01/30 22:49:34 19852 12972 WavStream::AddRef()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::AddRef()
2024/01/30 22:49:34 19852 12972 WavStream::Release()
2024/01/30 22:49:34 19852 12972 WavStream::AddRef()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::AddRef()
2024/01/30 22:49:34 19852 12972 WavStream::AddRef()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavStream::Release()
2024/01/30 22:49:34 19852 12972 WavStream::Release()
2024/01/30 22:49:34 19852 12972 WavStream::Release()
2024/01/30 22:49:34 19852 17952 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavSource::BeginGetEven()
2024/01/30 22:49:34 19852 17952 WavStream::AddRef()
2024/01/30 22:49:34 19852 12972 WavStream::Release()
2024/01/30 22:49:34 19852 17952 WavStream::QueryInterface()
2024/01/30 22:49:34 19852 12972 WavSource::BeginGetEven()
2024/01/30 22:49:34 19852 17952 WavStream::AddRef()
2024/01/30 22:49:34 19852 12972 WavStream::BeginGetEvent()
2024/01/30 22:49:34 19852 12972 WavSource::BeginGetEven()
2024/01/30 22:49:34 19852 12972 WavStream::EndGetEvent()
2024/01/30 22:49:34 19852 12972 WavStream::BeginGetEvent()
2024/01/30 22:49:34 19852 12972 WavStream::RequestSample()
2024/01/30 22:49:34 19852 12972 WavStream::CreateAudioSample()
2024/01/30 22:49:34 19852 12972 AudioDurationFromBufferSize()
2024/01/30 22:49:34 19852 12972 WavStream::DeliverSample()
2024/01/30 22:49:34 19852 12972 QueueEventWithIUnknown()
2024/01/30 22:49:34 19852 12972 WavStream::QueueEvent()
2024/01/30 22:49:34 19852 12972 WavStream::CheckEndOfStream()
2024/01/30 22:49:34 19852 12972 WavStream::EndGetEvent()
2024/01/30 22:49:34 19852 12972 WavStream::BeginGetEvent()
2024/01/30 22:49:34 19852 12972 WavStream::RequestSample()
2024/01/30 22:49:34 19852 12972 WavStream::CreateAudioSample()
2024/01/30 22:49:34 19852 12972 AudioDurationFromBufferSize()
2024/01/30 22:49:34 19852 12972 WavStream::DeliverSample()
2024/01/30 22:49:34 19852 12972 QueueEventWithIUnknown()
2024/01/30 22:49:34 19852 12972 WavStream::QueueEvent()
2024/01/30 22:49:34 19852 12972 WavStream::CheckEndOfStream()
2024/01/30 22:49:34 19852 12972 WavStream::EndGetEvent()
2024/01/30 22:49:34 19852 12972 WavStream::BeginGetEvent()
2024/01/30 22:49:35 19852 23364 WavStream::RequestSample()
2024/01/30 22:49:35 19852 23364 WavStream::CreateAudioSample()
2024/01/30 22:49:35 19852 23364 AudioDurationFromBufferSize()
2024/01/30 22:49:35 19852 23364 WavStream::DeliverSample()
2024/01/30 22:49:35 19852 23364 QueueEventWithIUnknown()
2024/01/30 22:49:35 19852 23364 WavStream::QueueEvent()
2024/01/30 22:49:35 19852 23364 WavStream::CheckEndOfStream()
2024/01/30 22:49:35 19852 23364 WavStream::EndGetEvent()
2024/01/30 22:49:35 19852 23364 WavStream::BeginGetEvent()
2024/01/30 22:49:36 19852 23364 WavStream::RequestSample()
2024/01/30 22:49:36 19852 23364 WavStream::CreateAudioSample()
2024/01/30 22:49:36 19852 23364 AudioDurationFromBufferSize()
2024/01/30 22:49:36 19852 23364 WavStream::DeliverSample()
2024/01/30 22:49:36 19852 23364 QueueEventWithIUnknown()
2024/01/30 22:49:36 19852 23364 WavStream::QueueEvent()
2024/01/30 22:49:36 19852 23364 WavStream::CheckEndOfStream()
2024/01/30 22:49:36 19852 23364 WavStream::EndGetEvent()
2024/01/30 22:49:36 19852 23364 WavStream::BeginGetEvent()
2024/01/30 22:49:36 19852 19812 WavSource::Stop()
2024/01/30 22:49:36 19852 19812 WavStream::Flush()
2024/01/30 22:49:36 19852 19812 WavStream::QueueEvent()
2024/01/30 22:49:36 19852 19812 WavSource::QueueEvent()
2024/01/30 22:49:36 19852 23364 WavStream::EndGetEvent()
2024/01/30 22:49:36 19852 23364 WavStream::BeginGetEvent()
2024/01/30 22:49:36 19852 23364 WavSource::BeginGetEven()
2024/01/30 22:49:36 19852 23364 WavSource::BeginGetEven()
2024/01/30 22:49:36 19852 21336 WavSource::Shutdown()
2024/01/30 22:49:36 19852 21336 WavStream::Shutdown()
2024/01/30 22:49:36 19852 21336 WavStream::Flush()
2024/01/30 22:49:36 19852 21336 WavStream::Release()
2024/01/30 22:49:36 19852 21336 WavSource::QueueEvent()
2024/01/30 22:49:36 19852 21336 WavStream::QueueEvent()
2024/01/30 22:49:36 19852 21336 WavStream::Release()
2024/01/30 22:49:37 19852 19812 WavStream::Release()
2024/01/30 22:49:37 19852 19812 WavStream::Release()
2024/01/30 22:49:37 19852 19812 WavStream::Release()
2024/01/30 22:49:37 19852 19812 WavStream::~WavStream()
2024/01/30 22:49:37 19852 21336 WavSource::~WavSource()
2024/01/30 22:49:37 19852 21336 DllRelease
2024/01/30 22:49:37 19852 21336 DllMain

1秒おきに、RequestSample()が呼ばれるっぽい。その他、どのメソッドがどのタイミングで呼ばれるのか、は、ぶっちゃけ全然わからん。

今一度、ビルドしなおすときの手順メモ

  • slnをリビルド(全PJをビルドしなおす)
  • VirtualCamera_MSI_x64 を右クリックして、「アンインストール」する
    (C:\Program Files\Contoso\VirtualCamera_MSI の中のファイルが全部?消える)
  • VirtualCamera_MSI_x64 を右クリックして、「インストール」する (C:\Program Files\Contoso\VirtualCamera_MSI の中にファイルが配置される)
  • ビルドは終わり。
  • ↑のフォルダの中にある「VirtualCamera_Installer.exe」を管理者で実行する
  • VirtualCameraを、SystemでRegistrationする。(これで、Windosのカメラアプリとかに、「VirtualCamera」が出てくるようになる)
  • Windowsのカメラアプリで、VirtualCmaeraを選択する

→この状態で、ログがでるかを見る。

明日やること 1/30

次、VirtualCameraの「VirtualCameraMediaSource.dll」のコードの各所に、ログを入れてみる。
また、その時の構成?を、topoeditで見てみる。(たぶん、VirtualCameraを活かした状態で、topoでそのカメラを選択したら、構成が見れるんだろうと思う)

別のことがしたくなった。

下記のぺ―ジをもとに、MediaSouceの理解を進めたい。

https://learn.microsoft.com/ja-jp/windows/win32/medfound/tutorial--writing-a-custom-media-source

このページによると、、、

COMのことがわからなくなったら下記を見る。(とくに、MediaSourceは、インプロセスの「COMサーバー」pっぽいので、下のComServerのほうを見るとよいかも)

https://eternalwindows.jp/com/combase/combase00.html

https://eternalwindows.jp/com/comserver/comserver00.html

こんな感じ?じしんない↓

2/4

これを見ながら、

https://learn.microsoft.com/ja-jp/windows/win32/medfound/tutorial--writing-a-custom-media-source

これを動かして、コード見てみる。

samples/tree/main/Samples/Win7Samples/multimedia/mediafoundation/mpeg1sourcehttps://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/multimedia/mediafoundation/mpeg1source

2/4

QueryInterfaceを実装するときは、

これを見本にしたらいいっぽい。
(QITAB 、QITABENT、QISearch関数/マクロを使って、実装してるIFを返すという意味っぽい)

https://learn.microsoft.com/ja-jp/windows/win32/api/shlwapi/nf-shlwapi-qisearch

※あるIFが継承?している親IFも、含めてあげないといけないらしい。面倒そう、、、

2/5

MSのVirtualCameraサンプルを登録した状態でTopoEditを起動し、

  • [Topology] > [VideoCaptureSource] を押して、登録したVirtualCameraを選ぶ
  • [Topology] > [Add EVR] を押す →画面上にVideo Rendererが追加される
  • その2つの間を、線で結ぶ

として、再生ボタンを押すと、VirtualCameraをTopoEditで見ることができた。

そのようにしてできた図とその中の値達、なにか解析の手掛かりになるか...???

次は、とりあえず

  • あせらずMPEG1Sourceのサンプルのコードを、最初から最後まで見てみる(ケーススタディのページを見ながら)(HRESULT MPEG1Source::OnByteStreamRead(IMFAsyncResult *pResult)がデータ呼んでるところ?)
  • その次に、上のtopoeditの図を手掛かりに、VirtualCameraに戻ってみる?

をしてみる。

2/7

Mpeg1Sourceのreadmeに、使うようにと書かれているBasicPlaybackというサンプルアプリが、github上にみあたらない。サンプルからは削除された?らしい。

こちらの記事に、ありかが書かれていた。

http://blog.sssoftware.main.jp/?eid=1975

置き場はこちら。

https://learn.microsoft.com/en-us/windows/win32/medfound/media-session-playback-example BasicPlayback

→コードを取り込んでBasicPlaybackを動かしてみたが、MF_ProtectedPlayback.exe と同じ動きをするだけだった。(それどころか、MF_ProtectedPlayback.exeで再生できた.mpgが再生できない。何が違うかはわからない。。。)

Mpeg1Source.dllのコードを呼んでみた。 →さっぱりわからない。

流れは、こういうイメージ???

こういう流れを一通り見た後に、下記のあたりを見たら、何となく書いてることがわかるのかも?

※今見てみたが、全然わからん