前回コントロールとしてMediaElementを使いました。今回は一味違うMediaPlayerを使ってみます。
実はこのMediaPlayer、(WPFのことを何も知らない)以前にWinFormで貼り付けたことがあります。この時の結果は、
”追記:アップしてから、「↑のコードでどうなったの?」という結論を書き忘れましたぁ!実は↑のコードは「正常にコンパイルされ、動くことは動き」ます。ただ、動画ファイルを読み込んで再生しても「音しか出ない」だけです。まぁ、画像処理部分が解説した通りなので、再生されるわけがないのですが...”
となりました。今回はきちんとWPFで書きますので、赤字部分がどうなるか解説します。(なお、以下の色分けは青がコメントと解説、赤がMediaPlayer、橙が動画を表示するUIElement、紫がビデオ再生(VideoDrawingクラス)、ピンクが描画ブラシとなります。)
【Test_MediaPlayer.cs】
///////////////////////////////////////////
//Test_MediaPlayer.cs - MediaPlayer Sample
///////////////////////////////////////////
using System;
using System.Windows; //PresentationFramework.dllにある
using System.Windows.Controls; //PresentationFramework.dllにある
using System.Windows.Media; //PresentationCore.dllにある(MediaPlayer, Brushes構造体使用の為)
/* 参照DLL
RefFile=C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll
RefFile01=C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationCore.dll
RefFile02=C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\WindowsBase.dll
RefFile03=C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Xaml.dll
*/
namespace Test_MediaPlayer
{
///////////////////////////
//エントリーポイントクラス
///////////////////////////
class MainApp
{
[STAThread]
public static void Main()
{
DlgWindow dlg = new DlgWindow();
Application ap = new Application(); //明示的にインスタンスを作らなければならない
ap.Run(dlg);
}
}
public partial class DlgWindow : Window //派生元が"Form"から"Window"となる
{
//コントロール
//private MediaElement me; //解説:今回はMediaElementを使いません。
private MediaPlayer mp = null; //解説:これが噂のMediaPlayerクラスインスタンスです。
private Canvas canvas = null; //解説:今回は動画再生画面をUIElement配置用のCanvasコントロールにしてみます。(詳細は後記)
//private Button canvas = null; //解説:Canvasで実験したら、Buttonクラスに入れ替えてみてください。
private VideoDrawing vd = null; //解説:VideoDrawingはビデオ再生機能の為のクラスです。(詳細は後記)
private Button Btn1, Btn2, Btn3, Btn4, extBtn;
//ファイル名
string fName = "";
//MediaPlayerコントロール用変数
bool toggle = true;
public DlgWindow()
{
this.Title = "MediaPlayer サンプル";
this.Width = 640;
this.Height = 480;
this.WindowStyle = WindowStyle.ThreeDBorderWindow;
this.ResizeMode = ResizeMode.NoResize;
this.WindowState = WindowState.Normal;
this.WindowStartupLocation = WindowStartupLocation.CenterScreen; //Manual, CenterOwnerもある(Manyualの場合のTopとLeftで設定)
this.Background = SystemColors.ControlBrush;
InitControls();
}
public void InitControls()
{
//グリッド(Grid)と縦二列を作成
Grid grid = new Grid();
ColumnDefinition colL = new ColumnDefinition();
ColumnDefinition colR = new ColumnDefinition();
//列を定義
//colL.Width = GridLength.Auto; //解説:何も入れていないと幅0となる。
colL.Width = new GridLength(540, GridUnitType.Pixel);
colR.Width = GridLength.Auto;
//colR.Width = new GridLength(80, GridUnitType.Pixel); //解説:右列を固定幅にする場合
grid.ColumnDefinitions.Add(colL);
grid.ColumnDefinitions.Add(colR);
//スタックパネル(StackPanel)を作成
StackPanel stackPanel = new StackPanel();
stackPanel.Width = 80;
stackPanel.Height = 420;
stackPanel.HorizontalAlignment = HorizontalAlignment.Right;
//Canvas //解説:Canvasコントロールを単にGridの左列に貼り付けるだけです。
canvas = new Canvas();
//canvas = new Button(); //解説:Canvasで実験したら、Buttonクラスに入れ替えてみてください。
//canvas.Content = "I'm a mere button.";
canvas.Margin = new Thickness(10, 10, 10, 10); //構造体 Thickness(左, 上, 右, 下)
//グリッド左列に動画再生用Canvasコントロールを登録
Grid.SetColumn(canvas, 0);
grid.Children.Add(canvas);
//ボタン1
Btn1 = new Button();
Btn1.Width = 60;
Btn1.Height = 24;
Btn1.HorizontalAlignment = HorizontalAlignment.Right;
Btn1.VerticalAlignment = VerticalAlignment.Top;
Btn1.Margin = new Thickness(10, 10, 10, 0); //構造体 Thickness(左, 上, 右, 下)
Btn1.Content = "Open";
Btn1.Click += Button1_Click;
stackPanel.Children.Add(Btn1);
//ボタン2ボタン
Btn2 = new Button();
Btn2.Width = 60;
Btn2.Height = 24;
Btn2.HorizontalAlignment = HorizontalAlignment.Right;
Btn2.VerticalAlignment = VerticalAlignment.Top;
Btn2.Margin = new Thickness(10, 10, 10, 0); //構造体 Thickness(左, 上, 右, 下)
Btn2.Content = "Play";
Btn2.Click += Button2_Click;
stackPanel.Children.Add(Btn2);
//ボタン3ボタン
Btn3 = new Button();
Btn3.Width = 60;
Btn3.Height = 24;
Btn3.HorizontalAlignment = HorizontalAlignment.Right;
Btn3.VerticalAlignment = VerticalAlignment.Top;
Btn3.Margin = new Thickness(10, 10, 10, 0); //構造体 Thickness(左, 上, 右, 下)
Btn3.Content = "Pause";
Btn3.Click += Button3_Click;
stackPanel.Children.Add(Btn3);
//ボタン4ボタン
Btn4 = new Button();
Btn4.Width = 60;
Btn4.Height = 24;
Btn4.HorizontalAlignment = HorizontalAlignment.Right;
Btn4.VerticalAlignment = VerticalAlignment.Top;
Btn4.Margin = new Thickness(10, 10, 10, 0); //構造体 Thickness(左, 上, 右, 下)
Btn4.Content = "Stop";
Btn4.Click += Button4_Click;
stackPanel.Children.Add(Btn4);
//終了ボタン
extBtn = new Button();
extBtn.Width = 60;
extBtn.Height = 24;
extBtn.HorizontalAlignment = HorizontalAlignment.Right;
extBtn.VerticalAlignment = VerticalAlignment.Bottom;
extBtn.Margin = new Thickness(10, 10, 10, 10); //構造体 Thickness(左, 上, 右, 下)
extBtn.Content = "終了";
extBtn.Click += extBtn_Click;
stackPanel.Children.Add(extBtn);
//グリッド右列にスタックパネルを登録
Grid.SetColumn(stackPanel, 1);
grid.Children.Add(stackPanel);
//ウィンドウにグリッドを登録
this.Content = grid;
//MediaPlayerとVideoDrawing
mp = new MediaPlayer(); //解説:メディアプレーヤーのインスタンス実体を生成します。
vd = new VideoDrawing(); //解説:ビデオ再生のインスタンス実体を生成します。
}
void Button1_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.OpenFileDialog ofd = new Microsoft.Win32.OpenFileDialog();
ofd.AddExtension = true;
ofd.DefaultExt = "*.*";
ofd.Filter = "Media(*.*)|*.*";
ofd.ShowDialog();
fName = ofd.FileName;
//ofd.Dispose(); //WPFでは不要(https://learn.microsoft.com/ja-jp/dotnet/desktop/wpf/windows/how-to-open-common-system-dialog-box?view=netdesktop-8.0)
}
void Button2_Click(object sender, RoutedEventArgs e)
{
if(fName == "") //解説:メディアファイルが指定されていない場合
MessageBox.Show("メディアファイルを選択してください。", "エラー", MessageBoxButton.OK, MessageBoxImage.Exclamation);
else //解説:メディアファイルが指定されている場合、再生を開始します。
{
//解説:先ずメディアプレーヤーにソースファイルを喰わせます。
mp.Open(new System.Uri(fName, UriKind.Absolute)); //Uri:URI(Uniform Resource Identifier) のオブジェクト表現を可能にし、URI の一部へ簡単にアクセスできるようにします。
//解説:ビデオ再生サイズを指定します。
vd.Rect = new System.Windows.Rect(0, 0, 200, 200);
//解説:ビデオ再生のソースをメディアプレーヤーに指定します。
vd.Player = mp;
//解説:ビデオ再生を描画ブラシとして生成します。
DrawingBrush br = new DrawingBrush(vd);
canvas.Background = br; //解説:Canvasコントロールの背景にビデオ再生描画ブラシを登録します。
mp.Play(); //解説:再生を開始します。
Btn3.Content = "Pause"; //解説:ボタン3の「一時停止」の表示(MediaElementの場合と同じ)
toggle = true; //解説:トグルスイッチを真にします。(MediaElementの場合と同じ。)
}
}
void Button3_Click(object sender, RoutedEventArgs e)
{
if(toggle)
{
//解説:MediaPlayerになった以外は、MediaElementの場合と同じです。
mp.Pause();
Btn3.Content = "Resume";
}
else
{
//解説:MediaPlayerになった以外は、MediaElementの場合と同じです。
mp.Play();
Btn3.Content = "Pause";
}
toggle = !toggle;
}
void Button4_Click(object sender, RoutedEventArgs e)
{
//解説:MediaPlayerになった以外は、MediaElementの場合と同じです。
mp.Stop();
Btn3.Content = "Pause";
toggle = true;
}
void extBtn_Click(object sender, RoutedEventArgs e)
{
Close();
}
}
}
いかがでしょうか?
MediaElementクラスは、インスタンスを生成するだけでそれ自体がコントロールとなって動画再生が出来ましたが、MediaPlayerの場合は、
(1)動画ソースファイルの読み込みと管制→MediaPlayer
↓(登録)
(2)動画データの再生→VideoDrawing
↓(登録)
(3)描画ブラシ→DrawingBrush
↓(登録)
(4)(コントロール等)UIElementの背景(Backgroundメンバー)
という手続きを経て動画を再生します。(注)
注:と言っても、Microsoft Learningのサンプルには(1)→(2)までしか載っておらず、私はwebをググって英文のサイトに辿り着き、(3)→(4)を行うことで動画がUIElementに表示されることを知りました。
なーんだ、MediaElementの方が簡単じゃん!又は、
MediaPlayerは面倒くさい。又は、
結局動画の再生、一時停止、中止はMediaElementとおんなじじゃん!
と思ったあなた、正解ですっ!
ただしっ
MediaPlayerは、
(背景がある)どんなUIElementでも動画再生できる
というところがMediaElementと違うのです。↑のサンプルでCanvasの代わりにButton等他のコントロールを使ってテスト(注)してみたり、そのコントロールのContentメンバーに何か登録したらどうなるかテストしてみてください。
注:↑のコードの「//解説:Canvasで実験したら、Buttonクラスに入れ替えてみてください。」というコメントが付いている行の行頭のコメント記号(//)を切り取り、Canvasクラスにしている相対する行の頭に貼り付けて、コメントを入れ替えてコンパイルしてみてください。又、ボタンにはContentメンバーに文字列を設定しています。それがどう見えるかも確認してください。(インスタンス名canvasは、Buttonクラスにしてもそのまま使っているのでいじらなくて結構です。)
これこそがWEBデザイン時代の動画再生なんだなぁ
と思いました。
(未再生)
(Canvasクラスで再生する場合)
(Buttonクラスで再生する場合)
まぁ、いずれにしてもWin32ではDirectShowのラッパーを作る必要があった(昔の)C++やWinFormとは異なり、WPF+C#では「MediaElementというコントロールを使う」か「どんなコントロールでも背景をMediaPlayerで描画する」ことで動画再生はとても簡単になりました。
と、いうことが分かったので、動画再生は一件落着です。
今度はもう一つ引っ掛かっていたSAPIがWPFでどう扱われているか確かめたのでご紹介しましょう。