Top > Programming > .NetFramework > WPF > MediaElementBehavior
Last-modified: Tue, 25 Sep 2012 09:41:01 JST
Counter:6250 Today:1 Yesterday:5 Online:2
このエントリーをはてなブックマークに追加

MediaElementの挙動について

About

 C#とWPFって良いですよね。だって簡単に作れるんですもの。「動画から画像を取得するなんて面倒そうな実装もWPFのMediaElementを使えば何だか簡単に出来そう!」なんてことを考えていた時期が私にもありました(ここでは"それ"について解説しませんが)。実際に動画を再生する程度は簡単なのですが、利用用途によってはその挙動が非常に面倒な仕様なのでまとめておきます。

HasVideo/HasAudioプロパティ等は読み込んだ直後に取得できない

 次の様なサンプルプログラムを書いてみました。ボタンを押して任意の動画ファイルを読み込んだ直後に、MediaElementから情報を取得してみます(サンプルとなる動画はサンプルプログラムに含まれます)。

    <Grid>
        <MediaElement x:Name="mediaElement" />
        <Button Content="OFD" Height="44" Margin="396,10,0,0"
         VerticalAlignment="Top" Width="111" Click="Button_Click_1"/>
    </Grid>
        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            if ((bool)ofd.ShowDialog())
            {
                this.mediaElement.Source = new Uri(ofd.FileName);
                //直後に情報を取得してみる
                Console.WriteLine(mediaElement.HasVideo);
                Console.WriteLine(mediaElement.HasAudio);
                Console.WriteLine(mediaElement.NaturalVideoHeight);
            }
        }

 結果は次のテキストの通り。実は2回ファイルを開く操作をしています。見て分かるように、1回目の実行では「HasVideo」「HasAudio」「NaturalVideoHeight(Width)」などのプロパティ値が取得できません。

False
False
0
True
True
720

 こうなると嫌な予感がしてくるわけです。実験すると分かりますが、1回目に動画ファイル、2回目にことなる画像ファイルを取得・設定してみます。するとどうなるか。

//[1]動画を開く
False
False
0
//[2]画像を開く
True
True
0

 御覧の通りの挙動にございます。つまり前のファイル、"現在MediaElementで再生されている(た)ファイルの情報"が取得されていることが分かります。しかも動画像サイズに至っては取得に失敗しています。これはもう良く分からない仕様だなぁと1人嘆いていました。

 さらに検証を続けます。

プロパティ値の取得は再生状態に依存しない?

 今度はMediaElementをXAMLから取り除き、コード側で宣言・初期化する仕様に変更しました。サンプルのプロジェクト2になります。

        MediaElement mediaElement;

        public MainWindow()
        {
            InitializeComponent();
            this.mediaElement = new MediaElement();
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            if ((bool)ofd.ShowDialog())
            {
                this.mediaElement.Source = new Uri(ofd.FileName);
                //直後に情報を取得してみる
                Console.WriteLine(mediaElement.HasVideo);
                Console.WriteLine(mediaElement.HasAudio);
                Console.WriteLine(mediaElement.NaturalVideoHeight);
            }
        }

 先と同じように2回動画ファイルを開いてみます。結果は次の通り。いっそダメ押しにと、自分の眼がおかしくなったんじゃないかと思い3回実行しましたが、MediaElementを再生せずに各プロパティ値を取得することができませんでした。動画であっても画像であっても取得できませんでした。どうやらそういう仕様のようです。

False
False
0
False
False
0
False
False
0

 そこで少し仕様を変更しました。"再生していないから取得できないんじゃないか"、とそう思ったわけです。そこで下の様にサンプルプログラムを書き換えてみました。コンストラクタで初期化する段階で「LoadedBehavior = Manual」としています。LoadedBehaviorプロパティがManualになっていない状況では、読み込んだファイルを任意に再生・停止することができません。

                this.mediaElement.Source = new Uri(ofd.FileName);
                //再生してみる
                this.mediaElement.Play();
                //直後に情報を取得してみる
                Console.WriteLine(mediaElement.HasVideo);
                Console.WriteLine(mediaElement.HasAudio);
                Console.WriteLine(mediaElement.NaturalVideoHeight);

 結果はご覧の通り。惨敗。つまりメディアの再生状態には依存しないコンポーネントとして扱われなければならないということです。

False
False
0
False
False
0

コンポーネントとして扱われるかに依存する

 結論から言うと、MediaElementからメディアの情報を取得するために必要な条件とは、GUIコンポーネントとして登録されているか否かということが分かりました。サンプルの3つ目のプロジェクトになります。コード側でMediaElementを定義し、コードからGUIコンポーネントとして登録しています。試しに(不)可視化設定も追加してみました。

        public MainWindow()
        {
            InitializeComponent();
            this.mediaElement = new MediaElement();
            //GUIコンポーネントに追加
            this.subGrid.Children.Add(this.mediaElement);
            //不可視設定
            this.mediaElement.Visibility = System.Windows.Visibility.Hidden;
        }

 無事?元の挙動に戻ったことが確認されます。先のプロジェクト2では何度やっても取得することができなかったプロパティですが、GUIコンポーネントとしてコード側から設定してやることで取得することが確認できています。またコンポーネントのVisibilityには依存しないようで、不可視にしていてもプロパティは取得することができました。

False
False
0
True
True
720