【Unity】【C#】VideoPlayer で動画の終了判定をする 
2017/06/09 Fri [edit]

Unity5.6 からの新機能のせいか、まだ色々バグがあるらしいね。対応フォーマット(エンコード)等の問題もあるとは思うが、終了判定で真っ先に思いついた「VideoPlayer.isPlaying」を監視する方法は動画によっては上手くいかないこともあるようなので、他の方法をやってみた。とりあえずのやり方でそのうちバグフィックスされるかもしれないが、一応上手くいったのでメモしておく。
ちなみに VideoPlayer.isPlaying が false になるのは以下のときなど。
・アプリなどが停止したとき
・ワンショット(Loop がオフ)で動画が終了したとき(※ただし動画によっては true のままになることがある)
・そもそも再生してないとき(PlayOnAwake オフで起動したとき)
(※) Unity 5.6.1p2 / Windows10(x64) で確認
■動画の現在のフレームカウントを監視して終了判定する(ループなしのとき)
using UnityEngine;
using UnityEngine.Video;
public class Example : MonoBehaviour {
public VideoPlayer videoPlayer; //アタッチした VideoPlayer をインスペクタでセットする
// Update is called once per frame
void Update () {
if ((ulong)videoPlayer.frame == videoPlayer.frameCount)
{
//※ここに終了したときの処理など
return;
}
}
}
注意点は動画の全フレーム数のプロパティ「VideoPlayer.frameCount」は Start() で取得したときと再生時ではなぜか値が異なる点だ。Start() での値は使わないほうが良いかもしれない。あと、現在のフレーム「VideoPlayer.frame」は long 型で、動画の全フレーム数「VideoPlayer.frameCount」は ulong 型なので、負の値を使うことがあるのなら(?)(負の値で逆方向から指定とかはできないようだが)、等号などは気を付けよう(普通は超えることはないが、シーク機能を使うとかなら「>=」でも良い)。
なお、簡略のため Update() で終了判定しているが、毎フレーム単位のきっちりとした時間でなくて良いなら、コルーチンで 0.5~1.0 秒おきの判定で十分だ(その方が負荷が軽い)。
■ループ終端到達のコールバックを利用して終了判定する(ループ再生のとき)
using UnityEngine;
using UnityEngine.Video;
public class Example : MonoBehaviour {
public VideoPlayer videoPlayer; //アタッチした VideoPlayer をインスペクタでセットする
void OnEnable() {
videoPlayer.loopPointReached += EndReached;
}
void OnDisable() {
videoPlayer.loopPointReached -= EndReached;
}
//ループ終端の到達コールバック
void EndReached(VideoPlayer vp) {
//※ここに終了したときの処理など
}
}
このコールバック(VideoPlayer.loopPointReached)はループ再生オン(VideoPlayer.isLooping = true)のとき、動画の終端に来たとき呼び出される。ワンショットで再生(ループなし:isLooping = false)のときは呼び出されないようだ。
ちなみに以下のように終端にきたとき無理矢理停止する方法でワンショットの終了判定も考えたが(起動時には Loop をオンにする)、なぜかエディタ上では上手くいくが、Android などの実機でやるとアプリが不正終了してしまうことがあるようだ。念のためやらない方が良いだろう。
void EndReached(VideoPlayer vp) {
//※ここに終了したときの処理など
//強制的に停止する
videoPlayer.isLooping = false;
videoPlayer.Stop(); //※なぜか Android 上では落ちることがある
}
■簡単な再生終了イベントコールバックをするスクリプト
以上の2つをまとめた簡単なイベントコールバックをするスクリプトを書いておこう。フレーム数の判定もコルーチンに移してある。簡易ライブラリとして自由に使ってもらって構わない。
using System.Collections;
using UnityEngine;
using UnityEngine.Video;
using UnityEngine.Events;
/// <summary>
/// 動画が終了したときコールバックイベントを発生させる
/// 2017/6/9 Fantom (Unity 5.6.1p2)
/// http://fantom1x.blog130.fc2.com/blog-entry-244.html
///(使い方)
///・VideoPlayer を含む GameObject にアタッチして、インスペクタから OnVideoFinished にコールバックしたいメソッド(引数なしメソッド)を登録する。
///(仕様説明)
///・再生開始時に VideoPlayer.isLooping が true(ループ再生)のとき、VideoPlayer.loopPointReached(ループポイント到達のコールバック)で終了判定をする。
///・再生開始時に VideoPlayer.isLooping が false(ループなし)のとき、CheckFrame() でフレーム数による終了判定をする。
///・再生途中で VideoPlayer.isLooping や frameCheckInterval を変更することには未対応(停止後、再開すれば正しく動作する)。
/// </summary>
public class VideoFinishedCallback : MonoBehaviour
{
public VideoPlayer videoPlayer; //アタッチした VideoPlayer をインスペクタでセットする
public float frameCheckInterval = 0.5f; //ループなしのときに現在のフレーム番号で終端を判別する(間隔を空ければ負荷は軽くなる)
//インスペクタ用コールバック
public UnityEvent OnVideoFinished; //コールバックするメソッドをインスペクタから登録する
void Awake() {
if (videoPlayer == null)
videoPlayer = GetComponentInChildren<VideoPlayer>();
}
void OnEnable() {
videoPlayer.loopPointReached += EndReached; //ループポイント到達のコールバック
videoPlayer.started += Restart; //再生開始のコールバック
}
void OnDisable() {
videoPlayer.loopPointReached -= EndReached;
videoPlayer.started -= Restart;
}
//ループ終端の到達コールバック
void EndReached(VideoPlayer vp) {
Finish();
}
//フレーム数のチェックを開始する(コルーチンの再開)
void Restart(VideoPlayer vp) {
if (!videoPlayer.isLooping) //true のときフレーム数はチェックしない
StartCoroutine(CheckFrame());
}
// Use this for initialization
void Start() {
if (!videoPlayer.isLooping && videoPlayer.isPlaying) //PlayOnAwake ※念のため
StartCoroutine(CheckFrame());
}
bool checking = false; //コルーチン2重防止
//現在のフレームと全フレーム数を比較して終了判定をする(ループ再生時は上手く機能しない)
IEnumerator CheckFrame() {
if (checking)
yield break;
checking = true;
WaitForSeconds wfs = new WaitForSeconds(frameCheckInterval);
while (!videoPlayer.isLooping)
{
if ((ulong)videoPlayer.frame >= videoPlayer.frameCount)
{
checking = false;
Finish();
break;
}
yield return wfs;
}
}
//動画が終了したときの処理
void Finish() {
if (OnVideoFinished != null)
OnVideoFinished.Invoke(); //イベントコールバック
}
}
使い方は VideoPlayer を含む GameObject にアタッチして、インスペクタから OnVideoFinished にコールバックしたいメソッド(引数なしメソッド)を登録するだけだ。その他のコールバック実装方法は以下のページにまとめてあるので必要あれば参照して欲しい。
・【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録
簡単に仕様を説明しておくと、起動時または再生開始時(VideoPlayer.started)に、Loop がオフのときは、CheckFrame() コルーチンでフレーム数でのチェック、Loop がオンのときは、VideoPlayer.loopPointReached コールバックでチェックするようになる。再生途中で変更することには対応してないので注意しよう。最初から最後まで単純な再生する分には問題ないと思うが、途中で VideoPlayer 自体の設定を変更するなら、Update() での監視と終了処理のイベント発行(Finish() 内での OnVideoFinished)の整合性などを考えて書き直す必要が出てくるかもしれない。最低限の機能しか入っていないので、好きに改造して使って欲しい。
(関連記事)
【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録
【Unity】【C#】インスペクタでの UnityEvent のコールバック登録の有無を調べる
- 関連記事
トラックバック
トラックバックURL
→http://fantom1x.blog130.fc2.com/tb.php/244-0227c8af
この記事にトラックバックする(FC2ブログユーザー)
| h o m e |