Date

Universal Windows Platform (UWP) で動作する動画再生アプリを作ってみます。

Visual Studio 2015 を起動しプロジェクトを新規作成します。メニューから File - New - Project 、テンプレートは Visual C# - Windows - Universal の Blank App (Universal Windows) を選択します。

Creating Project

XAML に要素を追加する

MainPage.xaml に MediaElement と制御用のコントロールを配置していきます。

Gridで縦に2分割し、上側に MediaElement, 下側に再生、停止、SeekBar (独自に実装した簡易シークバー、後述) を置きます。

xaml ※ 見やすいように 1280 x 720 の横画面に変更しています

再生ボタンをタップしたときの処理

再生ボタンをタップすると Assets フォルダにコピーした fishes.wmv を再生することにします。 (fishes.wmv は Windows-universal-samples にあるサンプルメディアです。)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// MainPageクラス

  private async void playBtn_Click(object sender, RoutedEventArgs e)
  {
      var uri = new Uri("ms-appx:///Assets/fishes.wmv");
      var file = await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(uri);
      if (file != null)
      {
          this.mediaElement.SetPlaybackSource(MediaSource.CreateFromStorageFile(file));
          this.mediaElement.Play();
      }
  }

Assets フォルダ内のファイルを指定するには ms-appx:// で始まる Uri から Windows.Storage.StorageFile を取得します。それを MediaElement.SetPlaybackSource に渡し、Play を呼びます。すると XAML の MediaElement 内に再生された動画が表示されます。

停止ボタンをタップしたときの処理

停止ボタンがタップされたイベントで Stop を呼ぶだけです。

これで、再生と停止ができるようになりました。

シーク(再生位置の変更)

シークは、ちょっと工夫が必要です。UWP では Slider を使いたくなりますが、シークバーとして使う場合

  • ドラッグ完了したときにだけイベントを取得したい。
  • ドラッグ中はイベントを発生させたくない(内部で保持している値は変化してほしくない)
  • 動画再生中、値を随時プログラム側から更新するが、イベントを発生させたくない

という要件があります。ところが Slider を使うと、ドラッグ中でも ValueChanged イベントが発生してしまいます。要件を満たさないので独自に作る必要があります。今回は SeekBar というクラスを用意し UserControl を実装しました。Rectangle で横線、Thumb でツマミを表現しています。Slider とは異なり、横向き専用、値の範囲は0.0〜1.0という簡易な作りにしています。

まずメディアがオープンされたときに、メディアの長さを取得します。それから SeekBar を再生位置に合わせて変化させるために、0.1 秒ごとにイベントが発生するように作成したタイマをスタートします。メディアがオープンされたイベントを取得するには XAML の MediaElement 要素に MediaOpened="mediaElement_Opened" を記述します。

1
2
3
MainPage.xaml に以下を追加

<MediaElement x:Name="mediaElement" Grid.Row="0" MediaOpened="mediaElement_Opened" />
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// MainPageクラス

  public MainPage()
  {
      this.InitializeComponent();

      timer = new DispatcherTimer();
      timer.Interval = TimeSpan.FromSeconds(0.1); // 0.1 秒ごとのタイマ
      timer.Tick += timer_Tick;

      this.seekBar.IsEnabled = false;
      playerProps = new PlayerProps();
      this.DataContext = playerProps;
  }

  private void mediaElement_Opened(object sender, RoutedEventArgs e)
  {
      this.playerProps.Position = 0;
      this.playerProps.Duration = this.mediaElement.NaturalDuration.TimeSpan.Ticks;
      this.timer.Start();
      this.SeekBar.IsEnabled = true;
  }

playerProps はコードを参照してください。

タイマイベントが来るたびに、現在の再生位置 MediaElement.Position を取得し SeekBar に反映させます。

1
2
3
4
5
6
7
8
// MainPageクラス

  void timer_Tick(object sender, object e)
  {
      this.playerProps.Position = this.mediaElement.Position.Ticks;
      this.playerProps.MoviePos =
       (this.playerProps.Position * 1000 / this.playerProps.Duration) / 1000.0;
  }

シークバー上の Thumb のドラッグ完了するイベントで再生位置を更新します。ドラッグが「完了」したときだけなので、ドラッグ中はイベントは発生しません。

1
2
3
4
5
6
7
8
// MainPageクラス

  private void SeekBar_ValueChanged(object sender, SeekBarValueChangedEventArgs e)
  {
      System.Diagnostics.Debug.WriteLine("ValueChanged : " + e.Value);
      long ticks = (long)(e.Value * playerProps.Duration);
      this.mediaElement.Position = TimeSpan.FromTicks(ticks);
  }

実行結果

Result

Play ボタンを押すと再生が開始されます。Stop ボタンを押すと停止します。シークバーの Thumb をドラッグすると、シークできます。


ソースコードはGitHubからどうぞ


Comments

comments powered by Disqus