これまで動画を作成したり加工したりする記事は書いてきたが、そういえば再生する記事を書いていないことに気がついた。
今回は動画を再生する方法を紹介する。
 
まずは以下のようにStoryboardを設定する。
player_ss1
グレーの部分に動画を表示することになる(以後プレビューViewと呼ぶ)。
なぜ正方形にしてあるかはおいおいわかるだろう。

次にプレビューViewと再生開始ボタンをViewControllerに接続する。

それでは実装を開始しよう。
まず、今回使うプロパティを用意する。
先ほどのinterfaceのあたりを以下のようにする。

1.AVFoundationのインポート。
 @importを使用することで、手動でフレームワークを追加しなくても良くなる。

2.AVPlayerは動画の再生を制御するためのもの。
 AVPlayerLayerは動画を表示するためのものでCALayerのサブクラスだ。

これで、準備は完了だ。
動画再生部分を実装しよう。
以下のメソッドを追加する。

1.プレイヤーを初期化する。
 今回はバンドルにある動画ファイルを再生する。

2.動画を表示するレイヤー(以後プレイヤーレイヤーと呼ぶ。)を作成する。

3.プレイヤーレイヤーが正常に表示される状態になった場合に通知してもらえるようにする。
 これはKVOを利用している。
 これを行わずに[self.player play]を呼び出しても再生されるが、タイミング次第では正常に再生されないこともあるので、この処理は必ず行ったほうがいいだろう。

さて、2でvideoGravityというプロパティを設定しているが、これにはいくつかの種類がある。

AVLayerVideoGravityResize
プレイヤーレイヤーいっぱいに動画が表示される。
プレイヤーレイヤーのサイズと動画のサイズが異なる場合は、動画が拡大縮小される。
表示が崩れるので、ほとんど使うことは無いだろう。
player_ss2

AVLayerVideoGravityResizeAspect
アスペクト比を維持しつつ、動画全体がセンタリングされて表示される。
余白部分は何も表示されない。
プレビューViewを正方形にしたのは、これをわかりやすくするためだ。
player_ss3
AVLayerVideoGravityResizeAspectFill
アスペクト比を維持しつつ、プレイヤーレイヤーいっぱいに表示される。
縦幅、横幅の短い方に合わせられ、余った部分はカットされる。
player_ss4
次に、プレイヤーレイヤーの準備ができた際に受け取る通知の処理を記述する。

1.keyPathには該当する通知の文字列が入っているので、readyForDisplayの時を判定する。

2.この通知はもう必要なくなったので削除する。

3.再生開始する。

ここまでくればあと一歩だ。
ボタンのイベントハンドラ内部を実装する。

さあ、実行してみよう。
想定通り、動画が表示された!

2回以上再生する場合
動画が再生終了した後、または再生途中に再生開始ボタンを押した場合、現状の実装だと問題がある。
プレイヤーレイヤーがどんどん増えていってしまうのだ。
そこで、その問題を解決するためにsetupPlayerメソッドのプレイヤーレイヤーの処理を以下のようにする。

プレイヤーレイヤーがすでに存在する場合は、削除するようにした。
こうすればプレイヤーレイヤーが増殖し続けることは無い。

もう一つ、プレイヤーを使いまわす方法を考えてみよう。
setupLayerメソッドを以下のようにする。

playerが既に存在する場合は動画の頭にシークしてそこから再生する。
この場合、プレイヤーレイヤーは既に表示可能な状態なのでKVOを利用して通知を見張る必要は無い。 

動画の再生完了イベントを取得する
動画の再生が完了したタイミングを知りたいこともあるだろう。
例えば、動画をもう一度再生したり、画面遷移を行ったりする場合だ。
そのためのイベントを取得するにはNSNotificationCenterを使用する。
以下のメソッドを追加しよう。

viewWillAppearで通知を登録し、viewDidDisppearで通知を解除している。
今回のサンプルでは画面が一つしかなく、viewDidDisappearが呼ばれることは無いが、2つ以上の画面が存在するアプリ(殆どの場合はそうだろうが)では必ず通知が必要なくなったら削除するようにしよう。

次に通知で設定したメソッドを追加する。

ここではデバッグ出力をしているだけだが、必要に応じて処理を書くことでいろいろなことが出来るだろう。

さあ、実行してみよう。
動画が終了した時にデバッグ出力が表示された!