2020-09-23

AVAudioEngine / Apple Low-Lantecy HLSのお話

iOSDC Japan 2020で講演を行ったセッションについてご紹介します。

本記事に関しては、必要となったタイミングで、更新を行います。

今日からわかるAVAudioEngineの全て

AVAudioEngineと書いておきながら、一番最初に低レイヤーのお話から始まった本セッションですが、お楽しみいただけたでしょうか?

CoreAudioはとても複雑になっており、私自身も全て把握はしきれていませんが、その中でも必要となる知識について解説をしました。

特にお話したかったのがAudioUnitです。AudioUnitは、後半に出てくるManual Renderingでも登場しますが、基本的にRemote I/OとVoice Processing I/Oを理解しておくと、結構便利です。

このAudioUnitですが、入力と出力を別のバスにすることが可能で、以前登壇したセッションでは、入力をRemote I/Oにして、出力をVoice Processing I/Oにすることで、入力はエコキャンをせずに、出力側でエコキャンする実装をやっていました。Voice Processing I/Oにしてしまうと、iPhoneでは通話スピーカーから出力するように優先ルーティングされてしますし、基本的に音量ゲインが下がってしまうので、音が小さくなってしまいます。そういった際にこのように工夫することで回避することもできます。

https://speakerdeck.com/yaminoma/androidfalsertmppureyawogai-shan-sitahua-plus-a?slide=6

また、オーディオを語る上で外すことのできないAVAudioSessionについても合わせてお話させていただきました。

AVAudioSessionについては別記事で解説していますので、そちらも合わせてご覧ください。AVAudioSessionはとても複雑で、SDKがAudioSessionを奪うみたいなケースがTwitterの反応を見ていてもチラホラ聞こえてきましたが、AudioSessionは後からActiveにした方が優先されるため、SDKがActiveにしてしまうと音楽が止まるなどのケースがあったりします。なので、弊社のSDKなどでは、AVAudioSessionをアプリ側で任せるか、SDK側で任せるかを選ぶような形にしています。

また、途中AVAudioSessionを開放する話をしましたが、何が言いたかったかというと、例えばApple Musicで音楽を聞いている際に、Twitterで動画を再生した場合、Apple Musicで再生している曲が一時停止します。Twitterで再生した動画が停止した場合に、Apple Musicで再生している曲が復帰し、再び流れます。この実装はTwitterが持っているAVAudioSessionが開放された際にApple Musicに通知が飛び、再生を再開する実装になっているのです。TwitterがもしAVAudioSessionを開放しない場合は、Apple Musicはいつ再生するべきかを把握することができないため、再生が復帰できない、というケースがよくあります。なので、使わないAVAudioSessionは解除しておきましょう!という形でお話させていただきました。

また、アプリを起動した瞬間(didFinishLaunchingWithOptionsなど)にActiveするケースも多いですが、そうするとアプリが起動した瞬間に他のアプリで流していた場合に一時停止したりする場合が多いです。基本的にActiveにするタイミングは再生する直前にすると、ユーザー体験がとてもよくなります。

後半に関しては、実際のユースケースを元にお話しました。

実際にはAVAudioEngineにはもっと使い方があったのですが、今回は時間の関係上、最小限のご紹介となりました。詳しくはAppleのドキュメントをご覧ください。

また、installTapの解説も行いました。ミュートの話はイメージが伝わりにくいと思いましたが、実際のケースを話すと録音アプリでファイルに書き込む際に自分の声はスピーカーから出さずにファイルに書き込む!みたいなケースの時にお役に立てると思います。

また、外部のライブラリに繋ぎ込んで、RTMPなどで配信する際の注意ポイントとして、多くのライブラリではAVAudioPCMBufferではなくCMSampleBufferを前提にインターフェースが作られていることが多いです。そのため、AVAudioPCMBufferをCMSampleBufferに変換する必要がありますのでご注意ください。参考例として、Objective-Cで書いた変換する実装を掲載しておきます。Swiftでも書けると思いますが、結構大変かもしれません。。。

- (CMSampleBufferRef)createAudioSampleBufferFrom:(AVAudioPCMBuffer *) pcmBuffer {
    AudioBufferList *audioBufferList = [pcmBuffer mutableAudioBufferList];
    AudioStreamBasicDescription asbd = *pcmBuffer.format.streamDescription;

    CMSampleBufferRef sampleBuffer = NULL;
    CMFormatDescriptionRef format = NULL;

    OSStatus error = CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &asbd, 0, NULL, 0, NULL, NULL, &format);
    if (error != noErr) {
        return nil;
    }

    CMSampleTimingInfo timing = { CMTimeMake(1, asbd.mSampleRate), kCMTimeZero, kCMTimeInvalid };
    error = CMSampleBufferCreate(kCFAllocatorDefault,
                                 NULL, false, NULL, NULL, format,
                                 pcmBuffer.frameLength,
                                 1, &timing, 0, NULL, &sampleBuffer);
    if (error != noErr) {
        CFRelease(format);
        NSLog(@"CMSampleBufferCreate returned error: %d", (int)error);
        return nil;
    }

    error = CMSampleBufferSetDataBufferFromAudioBufferList(sampleBuffer, kCFAllocatorDefault, kCFAllocatorDefault, 0, audioBufferList);
    if (error != noErr) {
        CFRelease(format);
        NSLog(@"CMSampleBufferSetDataBufferFromAudioBufferList returned error: %d", (int)error);
        return nil;
    }

    CFRelease(format);
    return sampleBuffer;
}

今回、iOS 14の話を資料には盛り込んでいないのですが、iOS 14からは立体音響が追加されているため、バイノーラル録音がiPhoneで出来るようになりました。詳しい話は省きますが、とても楽しくなりそうですね。

本セッションは、ポッドキャストアプリなどを作る方が多いと思い、そういった方をターゲットにお話しました。

皆さんのお役に立てれば幸いです。

Apple Low-Latency HLSを使った超低遅延配信について

Apple Low-Latency HLS(AL-HLS)に関しては、Tokyo Video Tech #7の発表スライドでは、主にネットワーク部分をテーマに他の方がお話されていましたが、今回はiOS開発者目線でご説明しています。

AL-HLSを知るにはまずスタンダートなHLSを知る必要があります。これに関しては以前書いた記事がありますので、こちらを参考ください。

さて、今回ですが、AL-HLSには多くのタグが出てきます。これらのタグに関して、セッションでは時間の都合上全てお話できなかったのですが、注目ポイントとしてHLSのプレイリストをサーバに対して、細かく要求することができるようになった点です。

AL-HLSではGETパラメータを使い、URLのクエリに特定のキーを入れることで、サーバ側に様々な要求をすることができます。

また、レンディションレポートもとても魅力的な機能となっており、これまで他のレンディションに移る際にバッファリングが必要だったものがすぐ切り替えられるようになりました。

このスライドで、注釈忘れた点が1つあるのですが、今までAVPlayerではChunked-Transfer Encodingは非サポートでした。しかし、AL-HLSはそれと同じようなことができるため、CMAF-ULLのようなことが実現できるようになったのです。

AL-HLSはまだ出始めで、情報もほとんどない中、頑張って調べながらチャレンジしていましたが、今後普及することはおそらく間違いないと思うので、この資料が皆さんのお役に立てることを祈っております。

まとめ

iOSDC Japan 2020の前夜祭セッションは事前録画なのですが、事前録画だとどうしても皆さんの反応が見えないため、ちゃんと理解しているのか?もっと優しく話すべきか?という部分が非常に課題でした。

前夜祭セッションはニッチな枠ということで、多くの方が普段関わりがない分野をお話するのにあたり、あまりにも分からなさすぎて寝てしまう!というのを防ぐため、なるべくトークでカバーをするようにしました。

ただ、そういった工夫もあり、二番目にコメントが盛り上がったということで賞もいただきました。ありがとうございます。

また、業務などが重なり、あまり皆さんとお話できておらず、申し訳ありません。ただ、基本的に質問頂ければ答えますので、SNS等でお聞き頂ければお答え致します。また、本記事も必要に応じて更新していきます。

そういうわけで、運営の皆さん、そしてご視聴いただいた皆さん、本当にありがとうございました!