Skip to main content.

(2014/07/09)このサイトは更新を終了しました。今しばらく公開は続けたいと思いますが古くなっている情報もありますのでご注意下さい。新しいサイトはこちらです。引き続き株式会社インデペンデンスシステムズ横浜は鋭意営業中です。

Monday, July 09, 2012

DirectShowをC#から利用する(その4)IDLファイルからタイプライブラリの生成

前回の続きで、DirectShowをC#から利用する方法についてです。今回は、IDLファイルからタイプライブラリを生成して、そのタイプライブラリの参照を利用する方法です。前提として、DirectShowをC#で利用するには、公開されている DirectShow.NETライブラリを利用するのが最も簡単ですが、開発実務の制約等でこういったライブラリが利用出来ないという場合の方法です。

毎回のように書いていますが、DirectShowの実行モジュール(DLL)はいくつかに分かれており、タイプライブラリが含まれている実行モジュールもあれば、そうでないモジュールもあります。今回は、その実行モジュールにタイプライブラリが含まれていない場合についてです。この場合は、公開されているIDL(インターフェイス定義専用言語)ファイルからタイプライブラリを生成します。

DirectShowのIDLファイルからタイプライブラリを生成する方法
DirectShowのIDLファイルは、WindowsSDKの配下のIncludeフォルダに存在します。今回はこの中で、キャプチャに関係するインターフェースのタイプライブラリを作成します。手順としましては、作成するタイプライブラリの定義のIDLを作成します。そして、MIDLコンパイラでタイプライブラリを生成します。このあたりの概念については、ページ最後の関連リンクを参照して下さい。(私では解説は出来ません。)

キャプチャに関係するインターフェースのIDLファイルの作成
以下のようなファイルを作成します。これは、以下からの引用です。
DirectShow and C#

import "devenum.idl";
import "axcore.idl";
import "axextend.idl";

[
uuid(22995cc9-e37e-4b96-9326-b418935ac4be),
helpstring("DirectShow interfaces")
]
library DirectShow
{
    interface IFilterGraph;
    interface ICreateDevEnum;
    interface IGraphBuilder;
    interface ICaptureGraphBuilder2;
    interface IFileSinkFilter;
    interface IFileSinkFilter2;
    interface IAMAudioInputMixer;
};

まず、library DirectShowでタイプライブラリの名称を定義しています。そして、ここに含めるinterfaceを列挙しています。これは、例なので、実際には必要なインターフェースを列挙します。実際のインターフェースは上のimportのidlに定義されています。この例では、キャプチャに関係するインターフェースが列挙されているのでこのまま使いました。あと、uuidについては、これも例なので自分で作成します。Windows SDKに、uuidgenコマンドがありますので、これで生成出来ます。

次に、MIDLコンパイラを、以下のようにコマンドラインから実行します。
midl directshow.idl

以下は、オプションでターゲットを指定する場合です。
midl /win32 directshow.idl
midl /x64 directshow.idl

次に、参照可能なアセンブリをtlbimpで生成します。以下のように実行します。
tlbimp directshow.idl

これを実行すると、DirectShow.dllが生成されますが、警告が出るようです。とりあえず、警告が出たということを覚えておいて、先に進みます。生成されたこのDirectShow.dllをVisual Studioから参照すれば、C#からこれらのインターフェースが利用出来ます。参照の方法は前回と同様です。あと、前回のquartz.dllも必要となりますので、これも参照に追加します。

Webカメラ(ビデオ入力)の映像を表示するプログラムの作成
キャプチャ関連ということで、Webカメラ(ビデオ入力)の映像を表示するプログラムを作成しました。以下がフォームのプログラムです。プログラム全体は、お手数ですが、以下をダウンロードして見て下さい。(このプログラムのターゲットはx86になっています。)

フォームのソースは以下です。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Runtime.InteropServices;

using QuartzTypeLib;
using DirectShow;

namespace DirectShowVideoInput1
{
    public partial class Form1 : Form
    {
        //フィルタグラフマネージャ
        private FilgraphManager filterGraphManager = new FilgraphManager();

        //各インターフェース
        private IGraphBuilder graphBuilder = null;
        private ICaptureGraphBuilder2 captureGraphBuilder = null;
        private IMediaControl mediaControl = null;
        private IVideoWindow videoWindow = null;
        private IBasicVideo basicVideo = null;

        private ICreateDevEnum createDevEnum = null;

        /// <summary>
        /// フォームコンストラクタ
        /// </summary>
        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// フォーム初期化
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            //Comオブジェクトの初期化
            try
            {
                initComObject();
            }
            catch (Exception ex)
            {
                MessageBox.Show("COM初期化エラー:" + ex.Message, @"エラー", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            //Video入力の初期化
            try
            {
                setupVideoInput();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Video入力初期化エラー:" + ex.Message, @"エラー", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            //画面初期化
            try
            {
                setupVideoWindow();
            }
            catch (Exception ex)
            {
                MessageBox.Show("画面初期化エラー:" + ex.Message, @"エラー", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            //キャプチャ開始
            try
            {
                mediaControl.Run();
            }
            catch (Exception ex)
            {
                MessageBox.Show("キャプチャエラー:" + ex.Message, @"エラー", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }
        }

        /// <summary>
        /// 表示画面の設定
        /// </summary>
        private void setupVideoWindow()
        {
            //動画のサイズの取得
            int width = 0;
            int height = 0;

            basicVideo.GetVideoSize(out width, out height);

            ////m_BasicVideo.GetVideoSize(out width, out height);
            int WS_CHILD = 0x40000000;
            int WS_CLIPSIBLINGS = 0x04000000;

            //VideoWindowをメインWindowにアタッチして、子ウィンドウに設定
            videoWindow.Owner = (int)this.Handle;
            videoWindow.WindowStyle = WS_CHILD | WS_CLIPSIBLINGS;

            //親Windowをビデオのサイズに設定(+の部分は縦と横がうまく表示されるように適当に設定)
            this.Size = new Size(width + 25, height + 50);

            //VideWindowを動画のサイズに設定 
            videoWindow.SetWindowPosition(2, 2, width, height);
        }

        /// <summary>
        /// ビデオ入力を取得
        /// </summary>
        private void setupVideoInput()
        {
            //ビデオ入力を取得
            IEnumMoniker monikers = null;
            createDevEnum.CreateClassEnumerator(DirectShowGUID.VideoInputDeviceCategory(), out monikers, 0);
            if (monikers == null)
            {
                throw new Exception("ビデオ入力が取得出来ません。");
            }

            // ビデオ入力を列挙して最初のデバイスをグラフに追加する
            IMoniker moniker = null;
            uint fetched;

            monikers.RemoteNext(1, out moniker, out fetched);
            Guid iid = DirectShowGUID.BaseFilter();

            //バインドしてグラフに追加
            object source = null;
            moniker.RemoteBindToObject(null, null, ref iid, out source);
            graphBuilder.AddFilter((IBaseFilter)source, "Video Capture");

            //キャプチャグラフビルダに設定してプレビュー
            captureGraphBuilder.SetFiltergraph(graphBuilder);
            captureGraphBuilder.RenderStream(DirectShowGUID.PinCategoryPreviw(),
                    DirectShowGUID.MediaTypeVideo(), source, null, null);
        }

        /// <summary>
        /// 各オブジェクトの生成
        /// </summary>
        private void initComObject()
        {
            //キャプチャグラフビルダ
            captureGraphBuilder = CreateComObject<ICaptureGraphBuilder2>(DirectShowGUID.CaptureGraphBuilder2());

            //各インターフェース
            graphBuilder = (IGraphBuilder)filterGraphManager;
            mediaControl = (IMediaControl)filterGraphManager;
            videoWindow = (IVideoWindow)filterGraphManager;
            basicVideo = (IBasicVideo)filterGraphManager;

            //デバイス列挙
            createDevEnum = CreateComObject<ICreateDevEnum>(DirectShowGUID.CreateDevEnum());
        }

        /// <summary>
        /// 各オブジェクトの解放
        /// </summary>
        private void releaseComObject()
        {
            //各COMオブジェクト(インターフェース)のRelease
            if (captureGraphBuilder != null)
            {
                Marshal.ReleaseComObject(captureGraphBuilder);
                captureGraphBuilder = null;
            }

            if (filterGraphManager != null)
            {
                Marshal.ReleaseComObject(filterGraphManager);
                filterGraphManager = null;
            }

            if (graphBuilder != null)
            {
                Marshal.ReleaseComObject(graphBuilder);
                graphBuilder = null;
            }

            if (basicVideo != null)
            {
                Marshal.ReleaseComObject(basicVideo);
                basicVideo = null;
            }

            if (mediaControl != null)
            {
                Marshal.ReleaseComObject(mediaControl);
                mediaControl = null;
            }

            if (videoWindow != null)
            {
                Marshal.ReleaseComObject(videoWindow);
                videoWindow = null;
            }

            if (createDevEnum != null)
            {
                Marshal.ReleaseComObject(createDevEnum);
                createDevEnum = null;
            }

        }

        /// <summary>
        /// 指定したクラスのインスタンスの生成
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="guid"></param>
        /// <returns></returns>
        private T CreateComObject<T>(Guid guid) where T : class
        {
            Type comType = Type.GetTypeFromCLSID(guid);
            object o = Activator.CreateInstance(comType);
            if (o == null)
            {
                return null;
            }
            else
            {
                return (T)o;
            }
        }

        /// <summary>
        /// フォームクローズ時の処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            //COMオブジェクトの解放
            releaseComObject();
        }

    }
}


プロジェクト一式は以下です。(このプログラムのターゲットはx86になっています。)
プロジェクト一式

以上がこのような方法で、DirectShowをC#から利用することが出来ますという方法でした。

関連リンク、参考リンク
DirectShow.NETライブラリ

COM プログラミングの基本 (中)
COM プログラミング入門 - Web/DB プログラミング徹底解説
EternalWindows COMサーバー / IDLファイルの作成
COM 相互運用性 - 第 1 部 : C# クライアント チュートリアル
DirectShowドキュメント

Windowsプログラミング関連記事


Amazon関連リンク