AVAudioPlayer と AVPlayer の初期化時の注意点
AVAudioPlayer
には NSURL
で初期化を行う以下のメソッドがある。
- (instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError;
AVPlayer
にも NSURL
で初期化を行う以下のメソッドがある。
+ (id)playerWithURL:(NSURL *)URL; - (instancetype)initWithURL:(NSURL *)URL;
一見この2つのメソッドの使い方に違いは無いようだが実はちょっとした落とし穴(といっても特定の状況下での)がある。
以下のように NSURL
から各プレイヤーのインスタンスを生成してオーディオ再生を行うコードを書いてみる。
問題の現象
引数として渡す NSURL
を以下のようアプリに組み込んだオーディオファイルから生成してみる。
NSURL *URL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"aif"];
この NSURL
を使ってオーディオ再生を行う場合どちらのプレイヤーも正常に再生される。
問題は無い。
次に以下のようにテンポラリのディレクトリに保存されているオーディオファイルから NSURL
を生成してみる。
NSString *URLString = [NSTemporaryDirectory() stringByAppendingPathComponent:@"temp.aif"];
NSURL *URL = [NSURL URLWithString:URLString];
この NSURL
を使ってオーディオ再生を行おうとすると AVAudioPlayer
は再生されるが AVPlayer
の方は再生されないという現象が起こる。
原因
原因は前者の NSURL
と後者の NSURL
との違いにある。
生成された NSURL
のログ出力を確認すると前者は
file:///private/var/mobile/Containers/Bundle/Application/D4829216-D6F9-4670-B20E-0ECF7D74766B/Test.app/test.aif
後者は
/private/var/mobile/Containers/Data/Application/6B0B08BE-80E4-4901-B455-053719CA2326/tmp/temp.aif
となっていて、前者は fileURL で後者は fileURL ではないという違いがある。
この2つの違いは以下のメソッドで確認する事ができる。
@property (readonly, getter=isFileURL) BOOL fileURL; // Whether the scheme is file:; if [myURL isFileURL] is YES, then [myURL path] is suitable for input into NSFileManager or NSPathUtilities.
前者は URL.fileURL == YES
で後者は URL.fileURL == NO
となる。
もし AVAudioPlayer
が AVPlayer
同様に fileURL でないと再生不可であれば問題に気付きやすいが実際には以下の表のようになっていて、なぜか AVPlayer
の方だけ再生できないという事で嵌ってしまった。
Playable for | fileURL | NOT fileURL |
---|---|---|
AVAudioPlayer | YES | YES |
AVPlayer | YES | NO |
解決策
AVAudioPlayer
の方は内部で以下のようなコードで fileURL に変換してやっているのかも知れない。
ネットワーク上のオーディオファイルを直接読み込んで再生するケースもあるので、fileURL かどうかではなく scheme
の有無で fileURL に変換するかどうかを決める。
if (!URL.scheme) {
URL = [NSURL fileURLWithPath:URL.path];
}
AVPlayer
に渡すURLをこの様に前処理してやるのも安全度は高まるが、それよりまず NSURL
を生成する際のコードに気を付ける。
今回の場合
NSString *URLString = [NSTemporaryDirectory() stringByAppendingPathComponent:@"temp.aif"];
NSURL *URL = [NSURL URLWithString:URLString];
これを以下のように書き換え、元から正しく fileURL を生成して AVAudioPlayer
に渡すようにする。
NSString *URLString = [NSTemporaryDirectory() stringByAppendingPathComponent:@"temp.aif"];
NSURL *URL = [NSURL fileURLWithPath:URLString];
まとめ
AVPlayer
は fileURL でないパスのみのローカルファイルの NSURL
を再生できない。
ローカルのファイルから NSURL
を生成する時は fileURLWithPath:
メソッドなどを用いて正しく fileURL を生成するようにする。