Skip to main content.

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

Monday, July 09, 2012

DirectShowをC#から利用する(その3)タイプライブラリの参照

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

前々回にも書きましたが、DirectShowの実行モジュール(DLL)はいくつかに分かれており、タイプライブラリが含まれている実行モジュールもあれば、そうでないモジュールもあります。今回の方法は、タイプライブラリが含まれている場合のみ可能な方法です。例として、QuartzTypeLib(ActiveMovie control type library)を使ってみました。他には、Dexter 1.0 Type Library(qedit.dll)がこの方法が使えるようですが、その他は未確認です。(このタイプライブラリを検索するには、OLE/COM Object Viewというツールが使えます。)

タイプライブラリの参照方法
QuartzTypeLib(ActiveMovie control type library)は、quartz.dllが実体で、Windows\system32のディレクトリ(Windows7の場合)に存在します。参照方法として、TlbImp.exe (タイプ ライブラリ インポータ) というコマンドラインツールが利用出来ますが、Visual Studioからは、直接参照が出来ます。メニュー → プロジェクト → 参照の追加 から、参照タブを選択して、直接quartz.dllを参照します。これで、ソリューションエクスプローラーの参照設定で、QuartzTypeLibが確認出来ます。TlbImp.exeについては、ページ最後の関連リンクを参照して下さい。


作成したプログラムについて
ここまでで、QuartzTypeLibが利用出来るようになりました。QuartzTypeLibは、動画再生系のインターフェースが実装されているようですので、簡単な動画再生ツールを作成しました。具体的な各インターフェースの仕様は、ページ最後の関連リンクを参照して下さい。

プロジェクト一式は以下です。(このプログラムのターゲットは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;

namespace DirectShowMovie1
{
    public partial class FormMain : Form
    {
        //定数の定義
        private const int WS_CHILD = 0x40000000;
        private const int WS_CLIPSIBLINGS = 0x04000000;
        private const int EC_COMPLETE = 0x01;
        private const int EC_USERABORT = 0x02;
        private const int WM_APP = 0x8000;
        private const int WM_GRAPHNOTIFY = WM_APP + 1;

        //フィルタグラフマネージャと各インターフェースの定義
        private FilgraphManager filterGraph = null;

        private IMediaControl mediaControl = null;
        private IVideoWindow videoWindow = null;
        private IMediaEventEx mediaEventEx = null;
        private IBasicVideo basicVideo = null;
        private IMediaPosition mediaPosition = null;

        public FormMain()
        {
            InitializeComponent();
        }

        /// <summary>
        /// メニューからファイル選択、再生までの処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuFileOpen_Click(object sender, EventArgs e)
        {
            //ファイルを選択
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "Media Files|*.avi;*.wmv|All Files|*.*";

            //ファイルの再生
            if (DialogResult.OK == openFileDialog.ShowDialog())
            {
                //初期化
                cleanUp();

                try
                {
                    //フィルタ グラフ マネージャを生成
                    filterGraph = new FilgraphManager();

                    //各インターフェースを取得
                    basicVideo = (IBasicVideo)filterGraph;
                    mediaControl = (IMediaControl)filterGraph;
                    mediaEventEx = (IMediaEventEx)filterGraph;
                    videoWindow = (IVideoWindow)filterGraph;
                    mediaPosition = (IMediaPosition)filterGraph;

                    //グラフを構築
                    filterGraph.RenderFile(openFileDialog.FileName);

                    //動画のサイズの取得
                    int height = 0;
                    int width = 0;

                    basicVideo.GetVideoSize(out width, out height);

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

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

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

                    //イベント通知を受け取るように所有者ウィンドウを設定
                    mediaEventEx.SetNotifyWindow((int)this.Handle, WM_GRAPHNOTIFY, 0);

                    // グラフを実行
                    mediaControl.Run();

                }
                catch (Exception ex)
                {
                    //エラーメッセージ
                    String msg = @"動画ファイル再生に失敗しました。 ";
                    MessageBox.Show(msg + ex.Message, @"エラー", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

                    //初期化
                    cleanUp();
                }
            }
        }

        /// <summary>
        /// メニューからアプリ終了の処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuFileExit_Click(object sender, EventArgs e)
        {
            cleanUp();
            this.Close();
        }

        /// <summary>
        /// メニューから再生開始
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuActionRun_Click(object sender, EventArgs e)
        {
            if (mediaControl != null)
            {
                mediaControl.Run();
            }
        }

        /// <summary>
        /// メニューから再生停止
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuActionStop_Click(object sender, EventArgs e)
        {
            if (mediaControl != null)
            {
                mediaControl.Stop();
                mediaPosition.CurrentPosition = 0;
            }
        }

        /// <summary>
        /// メニューから再生一時停止
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuActionPause_Click(object sender, EventArgs e)
        {
            if (mediaControl != null)
            {
                mediaControl.Pause();
            }
        }

        /// <summary>
        /// Windowsメッセージ処理のオーバーライド
        /// </summary>
        /// <param name="m"></param>
        protected override void WndProc(ref Message m)
        {
            int lEventCode;
            int lParam1;
            int lParam2;

            if (m.Msg == WM_GRAPHNOTIFY)
            {
                //イベントをキューから取得する
                //ここでは、一応、while文でキューから全てのイベントを取得する
                while (true)
                {
                    try
                    {
                        mediaEventEx.GetEvent(out lEventCode, out lParam1, out lParam2, 0);
                        mediaEventEx.FreeEventParams(lEventCode, lParam1, lParam2);

                        //ストリーム終了または中断
                        if ((lEventCode == EC_COMPLETE) || (lEventCode == EC_USERABORT))
                        {
                            mediaControl.Stop();
                            mediaPosition.CurrentPosition = 0;

                            //初期化
                            cleanUp();
                            this.Size = new Size(320, 240);
                        }
                    }
                    catch (Exception)
                    {
                        break;
                    }
                }
            }

            base.WndProc(ref m);
        }

        /// <summary>
        /// 各COMオブジェクトの初期化
        /// </summary>
        private void cleanUp()
        {
            //必要な場合は、動画の停止と画面の初期化
            if (mediaControl != null)
            {
                mediaControl.Stop();
            }

            if (mediaEventEx != null)
            {
                mediaEventEx.SetNotifyWindow(0, 0, 0);
            }

            if (videoWindow != null)
            {
                videoWindow.Visible = 0;
                videoWindow.Owner = 0;
            }

            //各COMオブジェクト(インターフェース)のRelease
            if (filterGraph != null)
            {
                Marshal.ReleaseComObject(filterGraph);
                filterGraph = null;
            }

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

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

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

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

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

            return;
        }

        /// <summary>
        /// フォームを閉じる際の処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            cleanUp();
        }
    }
}


次回は、DirectShowの実行モジュールにタイプライブラリが含まれていない場合についてです。

関連リンク
DirectShow.NETライブラリ
COM 相互運用性 - 第 1 部 : C# クライアント チュートリアル
方法: Tlbimp.exe を使用してプライマリ相互運用機能アセンブリを生成する
DirectShowドキュメント
COM プログラミング入門 - Web/DB プログラミング徹底解説

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


Amazon関連リンク