Androidでマルチメディアを再生する方法はいろいろあります。本稿では、Androidのマルチメディアアーキテクチャに基づいてオーディオとビデオ再生をすべて担当する基本APIである、MediaPlayerを使ってAndroidでビデオを再生する方法を紹介します。
紹介
Androidのメディア再生に関する主要クラスはMediaPlayer、AudioManager、VideoViewです。特にMediaPlayerはオーディオとビデオ再生を担当する基本APIでファイルとストリームをすべてサポートします。本稿ではMediaPlayerを用いてSurfaceViewに動画を表示する方法について、段階別に説明します。
Androidマルチメディアアーキテクチャ
まずはじめにAndroidメディアプレイヤーと関連するマルチメディアアーキテクチャを確認してみます。Androidアプリケーションとメディアサーバは異なるプロセスで実行されますが、メディアサーバーは端末の起動と共に開始されメディア再生を管理します。ネイティブミドルウェア階層のSurface Flingerはビデオハードウェアを抽象化したもので、複数のアプリケーションの映像データを画面に表示する役割をします。Audio Flingerも同じくオーディオハードウェアを抽象化したもので、複数のアプリケーションのオーディオデータを再生します。
ストリームやファイルなどがメディアサーバに届けられると、カーネルでのハードウェアコーデックを経て内部のネイティブプレイヤーがAudio FlingerやVideo Flingerに伝達されます。最終的にはカーネル内のドライバーに送られて画面表示や音声再生が行われます。
MediaPlayerを生成する
MediaPlayerを生成する方法は二種類あります。一つはコンストラクタを使用する方法、もう一つはファクトリメソッドを使用する方法です。
最初の方法を使用するにはコンストラクタでMediaPlayerを生成した後、setDataSource()メソッドでデータソースを設定してsetDisplay()メソッドでサーフェイスホルダーを渡します。その次に同期的な準備メソッドのprepare()や非同期的な準備メソッドのprepareAsync()を呼び出します。
一方、ファクトリメソッドを使用すればこのようなメソッド呼び出しの段階を減らすことができます。staticなcreateメソッドでMediaPlayerを生成し、引数でContextとデータソース、SurfaceHolderなどを渡します。SurfaceHolderを指定しなければオーディオだけ再生されます。このSurfaceHolderについては後ほど説明します。 ファクトリメソッドを使用する方が楽に見えますが、注意すべき点はMediaPlayerが自動的に同期メソッドのprepare()を呼び出してしまうので大容量メディアには非効率という点です。
MediaPlayerを生成する例を見てみましょう。さきほど説明したコンストラクタとファクトリメソッドそれぞれを使用してMediaPlayerを生成するコードです。いずれかの方法で再生準備を終えた後start()メソッドで動画再生を開始します。
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(path);
mediaPlayer.setDisplay(surfaceHolder);
mediaPlayer.prepare();
// mediaPlayer.prepareAsync();
mediaPlayer.start();
MediaPlayer mediaPlayer
= MediaPlayer.create(context, R.raw.file1);
mediaPlayer.setDisplay(surfaceHolder);
mediaPlayer.start();
MediaPlayerの状態遷移図
メディアプレイヤーには多くの状態がありますが、Android開発者のサイトでこれを表した状態遷移図を見ることができます。様々な状態がある上、特定の状態でのみ使うことができるメソッドもあるのでメディアプレイヤーを初めて使用する方なら目を通してみることをおすすめします。もし状態に合わないメソッドを呼び出された場合にはIllegalStateExceptionが投げられます。
MediaPlayerのコールバック
MediaPlayerは容量が比較的大きなソースを扱うことが多いため、たくさんのコールバックを提供しています。バッファ関連のコールバックや準備関連のコールバック、SeekBarというプログレスバーを動かして再生位置を変更した際該当位置への移動完了を通知するOnSeekCompleteListener等もあります。詳しい内容は公式ドキュメントをご覧ください。
メディア再生のための権限設定
MediaPlayerを使用する際に外部URLからストリーミングを受ける場合はインターネット権限が必要です。また、映像再生中に画面が暗くなったりスリープになるのを防止する方法の一つとしてwake lockを獲得する方法がありますが、そのためにはWAKE_LOCK権限が必要になります。それぞれ下記のようにManifest.xmlファイルに設定します(画面が暗くならないようにする方法にはWAKE_LOCK権限を必要としない方法もあります。まずは権限を必要としない方法から検討することをお勧めします。)。
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
SurfaceView
オーディオと違ってビデオ再生のためにはビューの階層構造内に描画するために画面が必要ですが、これを提供するのがまさにSurfaceViewです。SurfaceViewはメインスレッドで直接制御しなければならないキャンバスと違って、バックグラウンドスレッドで画面をアップデートすることができ、ANR(Application Not responding)を防ぐことができる上、ユーザーの入力をブロックすることもなくすぐに反応することができます。 SurfaceHolderはこのようなSurfaceオブジェクトを管理します。MediaPlayerでビデオを再生するにはSurfaceViewを指定しなければなりませんが、SurfaceHolderを生成した後にsetDisplayメソッドの引数に渡せばよいです。
メディア再生Surfaceの準備
メディアが再生されるSurfaceViewをXMLに宣言する方法は下記になります。
<SurfaceView
android:id=“@+id/surface”
android:layout_width=“400dp”
android:layout_height=“240dp” />
上のコードのようにxmlにSurfaceViewを指定するとアクティビティで該当SurfaceViewからSurfaceHolder取得することができます。このとき、アクティビティはSurfaceHolderのコールバックを実装しなければなりません。下の例ではホルダーのコールバックをActivityが直接処理するようにaddCallback(this)を呼び出しています。
public class MediaPlayerActivity extends Activity implements SurfaceHolder.Callback {
SurfaceView surfaceView;
SurfaceHolder surfaceHolder;
MediaPlayer mediaPlayer;
@Override
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_media_player);
surfaceView = (SurfaceView) findViewById(R.id.surface);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
// 以下略
}
}
MediaPlayer準備および制御
SurfaceViewの準備をしたあとは、先に説明したとおりコンストラクタかファクトリメソッドのいずれかでMediaPlayerを準備します。 先ほどのアクティビティに次のようにSurfaceHolderのCallbackメソッドを実装します。
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mediaPlayer.setDataSource( /* 省略 */ );
mediaPlayer.setDisplay(holder);
//
} catch (Exception e) { /* 省略 */ }
}
}
メディアプレイヤーを制御するメソッドは先ほどお見せした状態遷移図に記述されているように、状態によって呼べるメソッドが異なります。よく使うメソッドはstart()、pause()、stop()です。それぞれに対応するボタンを作りクリックリスナーをセットします。stop()を呼んだあと、次の映像再生を準備するためにあらかじめprepare()を呼ぶことを推奨するのですが、これはtry catch文で囲う必要があります。また、メディアプレイヤーが使用するリソースを解放するため、アクティビティのonDistory()メソッドなどでMediaPlayerをreleaseしてください。
追加的な機能
本稿ではMediaPlayerの最も基本的な機能のみを紹介するためにSurfaceViewの幅と高さをハードコードされた値で設定しました。したがって、5:3でない映像ソースなら横と縦の割合が合わないことがあります。もし映像の比率を維持したまま全体画面で再生したい場合はStack Overflowを参考にしてください。
参考サイト
Media Playback、MediaPlayer、SurfaceViewなど、Android開発者サイトには、Androidでマルチメディアを扱う方法をより正確に理解するためのページが用意されています。メディアフレームワークに関してはこれらサイトを参考にしました。
- Android Developers-Media Playback
- Android Developers-MediaPlayer
- Android Developers-SurfaceView
- Android Developers-SurfaceHolder
- Android Media Framework Overview
- Android Multimedia Framework Overview
About the content
This content has been published here with the express permission of the author.