やりたいこと
サーバ側でWebM形式で保存されている動画をダウンロードしてAndroidとiOSのアプリで再生したい。
Androidは標準のVideoViewでWebM形式の動画を再生することができるが、
iOSの動画プレーヤーはWebM形式には対応していないため、WebMを再生できるライブラリが必要...
再生中はシークバーと経過時間などを表示したい。
使用したライブラリ
多くのコーデックに対応しているVLCが提供しているライブラリLibVLCSharpを使えば、iOSでもWebMを再生できそう。
ただ、ライブラリを見てみるとVideoViewはあくまで動画再生部分のコントロールであって、シークバーなどは用意されていないため、自分で実装する必要がある。
使用するにあたって
- LibVLCSharpはPCLには対応していないので.NETStandardにしなければならない。
- Xamarin.Formsのバージョンを3.2.0以上にする。
実装方法(.NET Standardのライブラリ側)
-
NuGetで「LibVLCSharp.Forms(0.5.0)」を追加。
-
MainPage.xaml.cs(動画プレーヤーページ)のOnAppearingメソッドで初期化処理を記述。
xaml側のソースは割愛します。詳しくは下記のGitHubを参照してください。MainPage.xaml.csprotected override void OnAppearing() { base.OnAppearing(); // VLC Initialize Core.Initialize(); var libVLC = new LibVLC(); _media = new Media(libVLC, "http://www.quirksmode.org/html5/videos/big_buck_bunny.webm", FromType.FromLocation); VideoView.MediaPlayer = new MediaPlayer(libVLC) { Media = _media }; VideoView.MediaPlayer.TimeChanged += OnMediaPlayerTimeChanged; VideoView.MediaPlayer.LengthChanged += OnMediaPlayerLengthChanged; VideoView.MediaPlayer.EndReached += OnMediaPlayerEndReached; PlaybackSlider.TouchUp += OnPlaybackSliderTouchUp; ... }
-
再生・一時停止ボタン押下処理
MainPage.xaml.csprivate void OnPlayImageButtonClicked(object sender, EventArgs e) { if (VideoView.MediaPlayer.State == VLCState.Playing) { // Pause VideoView.MediaPlayer.Pause(); PlayImageButton.Source = _playImage; } else { if (VideoView.MediaPlayer.State == VLCState.Ended) { VideoView.MediaPlayer.Media = _media; } // Play VideoView.MediaPlayer.Play(); PlayImageButton.Source = _pauseImage; Tool.IsVisible = true; } }
-
LengthChangedイベントで動画の長さを保持
MainPage.xaml.csprivate void OnMediaPlayerLengthChanged(object sender, MediaPlayerLengthChangedEventArgs e) { _length = e.Length; }
-
TimeChangedイベントで動画再生時の時間処理を記述
MainPage.xaml.csprivate void OnMediaPlayerTimeChanged(object sender, MediaPlayerTimeChangedEventArgs e) { var elapsedTimeSpan = TimeSpan.FromMilliseconds((double)e.Time); // ElapsedTime var remainingTimeSpan = TimeSpan.FromMilliseconds((double)(_length - e.Time)); // RemainingTime var elapsedTotalSeconds = Math.Floor(elapsedTimeSpan.TotalSeconds); // ElapsedTime(TotalSeconds) // Draw Device.BeginInvokeOnMainThread(() => { PlaybackSlider.Value = VideoView.MediaPlayer.Position; }); if (Math.Abs(_elapsedTotalSeconds - elapsedTotalSeconds) < 1) { return; } // Backup TotalSeconds _elapsedTotalSeconds = elapsedTotalSeconds; // Draw(1Seconds) Device.BeginInvokeOnMainThread(() => { ElapsedTime.Text = string.Format("{0:D2}:{1:D02}", elapsedTimeSpan.Minutes, elapsedTimeSpan.Seconds); RemainingTime.Text = string.Format("{0:D2}:{1:D02}", remainingTimeSpan.Minutes, remainingTimeSpan.Seconds); }); }
-
EndReachedイベントで動画終了処理を記述
MainPage.xaml.csprivate void OnMediaPlayerEndReached(object sender, EventArgs e) { _elapsedTotalSeconds = 0; // Draw Device.BeginInvokeOnMainThread(() => { PlaybackSlider.Value = 0; PlayImageButton.Source = _playImage; Tool.IsVisible = false; ElapsedTime.Text = "00:00"; RemainingTime.Text = "00:00"; }); }
-
シークバーのタップイベントで動画のポジションを変更
シークバーの詳しい実装については下記のGitHubを参照してください。MainPage.xaml.csprivate void OnPlaybackSliderTouchUp(object sender, EventArgs e) { // Draw Device.BeginInvokeOnMainThread(() => { VideoView.MediaPlayer.Position = (float)sender; }); }
実装方法(iOS側)
- NuGetで「VideoLAN.LibVLC.iOS(3.1.5-alpha)」を追加。
- シークバーのRendererを実装。(GitHubを参照)
実装方法(Android側)
- NuGetで「VideoLAN.LibVLC.Android(3.0.0)」を追加。
- シークバーのRendererを実装。(GitHubを参照)
注意点
Androidで動画ページから別ページに遷移したり、アプリをバックグラウンドに移行すると、
「[Adreno] DequeueBuffer: dequeueBuffer failed」のエラーが出続けてしまうので、
ページのOnDisappearingでVideoView.MediaPlayerを明示的にDisposeする必要がある。
App.xaml.csのOnSleepでもDisposeする。
実行
さすがVLCだけあって綺麗に動画が再生されました。
iOS
Android
ソース
今後
動画の回転表示や、再生ボタンをオーバレイ化したい。