訳あって、動画再生中の画像をキャプチャしたかったので、簡単な動画再生アプリをC#で作ってみました。
C#で動画再生(メディアプレーヤー)機能を搭載したプログラムを開発する場合、マイクロソフト標準の MediaElement というコントロールを使います。
今回紹介するサンプルプログラムは MediaElement の使い方を紹介することが目的なので、本来メディアプレーヤーが備えておくべき機能や制御は省略しており、かつ System.Windows.Form などの参照設定や、NuGetでインストールが必要なライブラリは使っていません。
それゆえ、ソースをコピーしてお使いのVisual Studio 2022 に張り付けて頂ければ、そのまま動作すると思います。
ちなみに、C#で動画再生を行う方法については多くの方が解説されていますが、本記事では基本的な操作である「再生」、「停止」、「再生位置の変更」だけでなく、他ではあまり触れられていない「再生スピード」、「ボリューム」、「画像キャプチャ」についても解説しています。
また、本記事で紹介するサンプルプログラムについては、Visual Studio 2022 のソリューションファイルとして一式をダウンロードできるようにしています。
是非参考にしてみてください。
サンプルプログラムの画面
本記事では、下記のプログラムを前提に説明を行っています。

MediaElement の設定方法
C# で動画ファイルを再生する場合、マイクロソフト標準の MediaElement というコントロールを使用します。
このコントロールは、MP4、AVI、WMV、MKVなど多くのフォーマットに対応し、4K、8KのVR動画さえ再生することが可能です。
また、任意の位置からの再生、ボリュームや再生スピードの制御など、メディアプレーヤーとして必要な機能が揃っています。
利用するのは簡単で、Visual Studio のレイアウトエディタからコントロールをドラッグ&ドロップするか、XAMLに直接 <MediaElement ~> と記述するだけで使えるようになります。

この時、MediaElementの LoadedBehavior を “Manual” に設定しておかないとプログラムから制御が出来ませんので、XAMLに必ず記述して下さい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<Window x:Class="MediaPlayer.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:MediaPlayer" mc:Ignorable="d" Title="MediaPlayer" Height="460" Width="900"> <Grid > <MediaElement x:Name="uxVideoPlayer" LoadedBehavior="Manual" Stretch="Uniform" /> </Grid> <Window> |
MediaElement の使い方
詳しくは下記のマイクロソフトの公式ページに記載されている内容を元に分かりやすく説明しています。

基本機能
MediaElement でよく使うメソッドとプロパティは次の通りです。
機能 | 種類 | 名前 | 説明 |
---|---|---|---|
動画ファイルの指定 | プロパティ | Source | 再生したい動画を以下の様に指定します。 Source = new Uri(動画ファイルのパス) |
動画の再生 | メソッド | Start() | 動画を再生します。 |
動画の停止 | メソッド | Stop() | 動画を停止します。Stop()の後にStart()を呼ぶと、 動画の先頭から再生されます。 |
動画の一時停止 | メソッド | Pause() | 一時停止します。Pause()の後にStart()を呼ぶと、 動画の続きを再生します。 |
再生位置の移動 | プロパティ | Position | 再生位置をミリ秒で指定します。 Position=new TimeSpan(0, 0, 0, 0, 再生位置) 動画の途中に移動したい場合 → Position = new TimeSpan(0, 0, 0, 0, 0) 8分後に指定したい場合(8*60*1000=3600000) →Position = new TimeSpan(0, 0, 0, 0, 3600000) 現在の再生位置を取得する場合、 →Position.ToString() ちなみに、hh:mm:ss.xxxxxx の形式で返ってくるため 秒単位に丸めたい場合は Substring(0,8)を付けます。 →Position.ToString().Substring(0,8) |
音量の設定 | プロパティ | Volume | 音量を0~1までの実数を設定します。(例 0.225412) 0~100 で指定したい場合、Volume から受け取る時は 100を掛け、Volumeに設定する場合は100で割ります。 |
再生速度の設定 | プロパティ | SpeedRatio | 再生速度を0~10までの実数で設定します。(例 1.228123) |
今回は MediaElement に uxVideoPlayer という名前を付けています。
1 |
<MediaElement x:Name="uxVideoPlayer" LoadedBehavior="Manual" Stretch="Uniform" /> |
下記のサンプルは、 uxVideoPlayerに対してプロパティの設定とメソッドの呼び出しを行う具体例です。本格的なメディアプレーヤーアプリを作るには、考慮すべき点が多々ありますが、非常に簡単な操作で動画再生を制御できることが、お分かりいただけたかと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//音量の設定 uxVideoPlayer.Volume = 0.5; //再生速度の設定 uxVideoPlayer.SpeedRatio = 1.0; //動画ファイルの指定 uxVideoPlayer.Source = new Uri(@"D:\Movies\StarGateVol1.MP4"); //動画の再生 uxVideoPlayer.Play(); //再生位置の移動(0時8分から再生) uxVideoPlayer.Position = new TimeSpan(0, 0, 0, 0, 8*60*1000); //一時停止 uxVideoPlayer.Pause(); //停止 uxVideoPlayer.Stop(); |
動画のキャプチャについて
再生中の画面をファイルに保存するのは少々煩雑なので、下記の通り関数化しました。このままコピペしてお使いください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
/// <summary> /// 動画のキャプチャを取る /// </summary> /// <param name="mediaElement"></param> /// <param name="fileName"></param> private void SaveFrame(MediaElement mediaElement, string fileName) { //動画が再生されていないと例外が発生するので、try~catchで例外を無効化 try { // キャプチャ対象のフレームをRenderTargetBitmapにレンダリングする RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap( (int)mediaElement.ActualWidth, (int)mediaElement.ActualHeight, 96, 96, PixelFormats.Pbgra32); renderTargetBitmap.Render(mediaElement); // ファイルに保存する using (FileStream fileStream = new FileStream(fileName, FileMode.Create)) { JpegBitmapEncoder encoder = new JpegBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap)); encoder.Save(fileStream); } } catch { } } |
応用例(簡易メディアプレーヤー)
今回作成したサンプルプログラムの画面を説明しておきます。
再生位置、ボリューム、再生速度はスライダーで指定できるようになっています。再生位置は指定してそこまで移動することはできますが、再生の進行に合わせてスライダーが自動で移動したり、現在の再生位置の時刻が表示されるわけではありません。
これらを実現しようとするとタイマー割込みによる監視が必要になり、ソースが複雑化するため今回は割愛しました。
また、画面キャプチャの出力先も、MyPicture フォルダ固定になっています。フォルダ選択ダイアログを使う場合、System.Window.Forms の参照設定が必要になるため、これも省略しています。
この辺は、必要に応じて修正して頂ければと思います。

プロジェクト一式のダウンロード
Visual Studio 2022でビルドできるソリューションファイル一式は以下からダウンロードできます。
ソースコード
まず、XAMLのソースコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<Window x:Class="MediaPlayer.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:MediaPlayer" mc:Ignorable="d" Title="MediaPlayer" Height="460" Width="900"> <Grid > <Grid.RowDefinitions> <RowDefinition Height="409*"/> <RowDefinition Height="30"/> </Grid.RowDefinitions> <MediaElement x:Name="uxVideoPlayer" Grid.Row="0" LoadedBehavior="Manual" UnloadedBehavior="Stop" Stretch="Uniform" MediaOpened="uxVideoPlayer_MediaOpened"/> <StackPanel Orientation="Horizontal" Grid.Row="1"> <Button Content="📁" Click="OpenFileButton_Click" Margin="5" Background="{x:Null}" BorderThickness="0" /> <Button Content="▶" Click="Start_Click" Margin="5" Background="{x:Null}" BorderThickness="0" /> <Button Content="■" Click="Stop_Click" Margin="5" Background="{x:Null}" BorderThickness="0"/> <Button Content="⏸" Click="Pause_Click" Margin="5" Background="{x:Null}" BorderThickness="0"/> <Slider x:Name="uxTimeSlider" Value="0" Width="300" Height="20" Margin="5" ValueChanged="uxTimeSlider_ValueChanged" ></Slider> <TextBlock x:Name="uxTime" Width="50" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBlock> <Slider x:Name="uxVolumeSlider" Minimum="0" Maximum="1" Value="0.5" Width="100" Height="20" Margin="5" ValueChanged="uxVolumeSlider_ValueChanged" ></Slider> <TextBlock x:Name="uxVolume" Width="40" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBlock> <Slider x:Name="uxSpeedSlider" Value="1" Width="80" Height="20" Margin="5" ValueChanged="uxSpeedSlider_ValueChanged" ></Slider> <TextBlock x:Name="uxSpeed" Width="40" VerticalAlignment="Center" HorizontalAlignment="Center" ></TextBlock> <Button Content="📷" Click="Capture_Click" Margin="5" Background="{x:Null}" BorderThickness="0" /> </StackPanel> </Grid> </Window> |
次に、C#側のソースコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
using Microsoft.Win32; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace MediaPlayer { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { private DateTime _lastSliderTime = DateTime.MinValue; private TimeSpan _timeSliderInterval = TimeSpan.FromSeconds(0.1); public MainWindow() { InitializeComponent(); //ボリュームと再生速度のスライダー初期値をセット uxVideoPlayer.Volume = (double)uxVolumeSlider.Value; uxVideoPlayer.SpeedRatio = (double)uxSpeedSlider.Value; uxTime.Text = "00:00:00"; uxVolume.Text = ((int)(uxVolumeSlider.Value * 100)).ToString(); uxSpeed.Text = Math.Round(uxSpeedSlider.Value,1).ToString(); } /// <summary> /// 再生ボタンクリック時のイベントハンドラ /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Start_Click(object sender, RoutedEventArgs e) { uxVideoPlayer.Play(); } /// <summary> /// 一時停止ボタンクリック時のイベントハンドラ /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Pause_Click(object sender, RoutedEventArgs e) { uxVideoPlayer.Pause(); } /// <summary> /// ストップボタンクリック時のイベントハンドラ /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Stop_Click(object sender, RoutedEventArgs e) { uxVideoPlayer.Stop(); uxVideoPlayer.Position = new TimeSpan(0, 0, 0, 0, 0); uxTimeSlider.Value = 0; uxTime.Text = "00:00:00"; } /// <summary> /// 動画ファイルのオープン /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OpenFileButton_Click(object sender, RoutedEventArgs e) { var dialog = new OpenFileDialog(); dialog.Filter = "Video Files|*.mp4;*.mkv;*.avi;*.wmv|All Files|*.*"; if (dialog.ShowDialog() == true) { uxVideoPlayer.Source = new Uri(dialog.FileName); uxVideoPlayer.Play(); } } /// <summary> /// タイムラインスライダーのイベントハンドラ /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void uxTimeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { DateTime now = DateTime.Now; TimeSpan timeSinceLastUpdate = now - _lastSliderTime; if (timeSinceLastUpdate >= _timeSliderInterval) { uxVideoPlayer.Position = new TimeSpan(0, 0, 0, 0, (int)uxTimeSlider.Value); _lastSliderTime = now; uxTime.Text = uxVideoPlayer.Position.ToString().Substring(0,8); } } /// <summary> /// 動画ファイル再生開始のイベントハンドラ /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void uxVideoPlayer_MediaOpened(object sender, RoutedEventArgs e) { uxTimeSlider.Maximum = uxVideoPlayer.NaturalDuration.TimeSpan.TotalMilliseconds; } /// <summary> /// ボリュームスライダーのイベントハンドラ /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void uxVolumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { uxVideoPlayer.Volume = (double)uxVolumeSlider.Value; if (uxVolume != null) { uxVolume.Text = ((int)(uxVideoPlayer.Volume * 100)).ToString(); } } /// <summary> /// 再生スピードスライダーのイベントハンドラ /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void uxSpeedSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { uxVideoPlayer.SpeedRatio = (double)uxSpeedSlider.Value; if (uxSpeed != null) { uxSpeed.Text = Math.Round(uxVideoPlayer.SpeedRatio,1).ToString(); } } /// <summary> /// キャプチャボタンのイベントハンドラ /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Capture_Click(object sender, RoutedEventArgs e) { var folder = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures); var filename = "Capture_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".jpg"; SaveFrame(uxVideoPlayer, System.IO.Path.Combine(folder, filename)); } /// <summary> /// 動画のキャプチャを取る /// </summary> /// <param name="mediaElement"></param> /// <param name="fileName"></param> private void SaveFrame(MediaElement mediaElement, string fileName) { //動画が再生されていないと例外が発生するので、try~catchで例外を無効化 try { // キャプチャ対象のフレームをRenderTargetBitmapにレンダリングする RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap( (int)mediaElement.ActualWidth, (int)mediaElement.ActualHeight, 96, 96, PixelFormats.Pbgra32); renderTargetBitmap.Render(mediaElement); // ファイルに保存する using (FileStream fileStream = new FileStream(fileName, FileMode.Create)) { JpegBitmapEncoder encoder = new JpegBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap)); encoder.Save(fileStream); } } catch { } } } } |
補足説明
全体的にそれほど難しくはないと思いますが、1点補足しておきたいのが、再生位置を指定するスライダーの挙動です。
再生位置をスライダーで動かすとValueChangedイベントが発生するので、そこで Position プロパティにスライダーの Value を設定してあげれば良いのですが、スライダーを少し動かすだけで、数十回のイベントが発生してしまいます。
このため、MediaElement の再生がイベントに追い付かず、結果的にスライダーを動かして数秒間は早送りのような状態が続き、なかなか指定した位置から再生してくれません。
そこで、now という変数に現在時刻を設定し、最後にスライダーを動かした時刻(_lastSliderTime)との差が_timeSliderIntervalの値(0.1秒に設定) 以内だったら Position Valueを設定しないようにしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/// <summary> /// タイムラインスライダーのイベントハンドラ /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void uxTimeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { DateTime now = DateTime.Now; TimeSpan timeSinceLastUpdate = now - _lastSliderTime; if (timeSinceLastUpdate >= _timeSliderInterval) { uxVideoPlayer.Position = new TimeSpan(0, 0, 0, 0, (int)uxTimeSlider.Value); _lastSliderTime = now; uxTime.Text = uxVideoPlayer.Position.ToString().Substring(0,8); } } |
まとめ
今回はC#とWPFによるWindowsアプリにおいて、MediaElement コントロールを使った動画再生の方法と、再生中の画像のキャプチャについて解説しました。
MediaElement はマイクロソフト標準のコントロールであり、様々な動画フォーマットに対応しており、8K動画やVR動画もちゃんと再生してくれる優れものです。
動画の再生、停止、一時停止の他、音量、再生スピード、再生位置の指定など、メディアプレーヤーのアプリを自作するために必要な機能が揃っています。
本格的なメディアプレーヤーを作成するには、ドラッグ&ドロップによるファイル指定やプレイリストの編集機能など、様々な機能が必要になりますが、簡易的に動画をちょこっと再生させたい場合などは重宝します。
今回紹介したサンプルプログラムは中途半端になってはいますが、必要な機能を盛り込みやすくなっていると思いますので、是非自分用のオリジナルメディアプレーヤー向けに改造して、役立てていただければと思います。