ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
AVAssetWriterとAuthor fragmented MPEG-4コンテンツ
より早くよりスムーズなHLSストリーミング体験のため、音声や動画コンテンツをFragmented MPEG-4に変換しましょう。Fragmented MPEG-4フォーマットの扱い方、動画からfragmentedコンテンツを生成する方法、AVAssetWriterを設定しHLSアウトプット用のフラグメントを作る方法を見ていきます。
リソース
-
ダウンロード
こんにちは WWDCへようこそ
“AVAssetWriterと Author Fragmented MPEG-4コンテンツ” ミズノ・タカギです 私はAppleのCoreMediaエンジニアです このセッションではHLSの断片化された MP4ファイルを書き込むための AVFoundationの新機能についてお話します これはHLSの一般的なワークフローを示す図です ソースマテリアルがあります ビデオオンデマンド素材 またはライブ素材の場合があります メディアデータをエンコードする部分があります 例えば ビデオサンプルは HEVCにエンコードされ オーディオサンプルは AACにエンコードされます
次に ここにセグメンターがあります
メディアデータを 特定の形式でセグメント化します
セグメンターは セグメントを同時にリストする― プレイリストも作成します
最後に コンテンツをホストする ウェブサーバがあります AVFoundationは主に セグメンターの行いに 役立つ新機能を提供します
AVFoundationにはAVAssetWriterがあり メディアオーサリングアプリケーションが MP4などの指定されたコンテナータイプの 新しいファイルにメディアデータを書き込めます メディアデータをHLSの断片化されたMP4形式で 出力するように拡張しました
Apple HLSは MPEG-2トランスポートストリームや ADTS MPEGオーディオなどの 他の形式をサポートしています ただし この新機能は 断片化されたMP4固有のものです これはユースケースの事例です
私の事例はソースが ビデオオンデマンド素材です
AVFoundationにはメディアファイルから サンプルデータを読み取るための オブジェクトのAVAssetReaderがあります
ソースムービーからサンプルデータを読み取り サンプルをAVAssetWriterにプッシュして 断片化されたMP4セグメントファイルを 書き込みます
もう1つの事例は ソースがライブマテリアルです
AVFoundationには AVCaptureVideoDataOutputと AVCaptureAudioDataOutputがあります これらは接続されたデバイスから キャプチャされたサンプルデータを 提供するオブジェクトです
デバイスからサンプルデータを受け取り サンプルをAVAssetWriterにプッシュして MP4セグメントファイルを書き込みます 断片化されたMP4はISOベースの メディアファイル形式に基づく― ストリーミングメディア形式です Apple HTTP Live Streamingは2016年以降 断片化されたMP4をサポートしています これが通常のMP4ファイルの基本的な図です まずこのファイルが どのファイル形式に準拠しているかを示す― ファイルタイプボックスから始まります
そのすべてのサンプルデータに関する 情報を整理するムービーボックスがあり― そのすべてのサンプルデータを含む メディアデータボックスがあります ムービーボックスには オーディオコーデックやビデオコーデックなど サンプル全体に関連する情報が 含まれています
またサンプルデータの場所など サンプルデータへの参照も含まれています ほとんどのボックスの順序は任意です
ファイルタイプボックスが 最初に来る必要がありますが 他のボックスは どのような順序でもかまいません AVAssetWriterを使用したことがある場合は movieFragmentIntervalプロパティが あることをご存知かもしれません これはムービーフラグメントが 書き込まれる頻度を指定します
結果のムービーは いわゆる断片化されたムービーです 以下は断片化されたムービーファイルの 基本的な図です
このムービーには メディアデータボックスにある― すべてのサンプルデータを参照する ムービーフラグメントボックスがあります
この形式の構造は ライブキャプチャに役立ちます 例えば クラッシュなどによって予期せずに 書き込みが中断された場合でも 部分的に書き込まれたデータは― 引き続きアクセスおよび再生できます
この断片化されたムービーのケースでは 書き込みが正常に終了した場合 AVAssetWriterが最後のステップとして ファイルをデフラグすることに注意してください したがって これは通常の ムービーファイルを作成することになります
以下は断片化されたMP4ファイルの 基本的な図です ファイルタイプボックスが最初に来て ムービーボックスは ファイルタイプボックスの後に来ます 次にメディアデータボックスが後に続く ムービーフラグメントボックスがあります その後ムービーフラグメントボックスと メディアデータボックスのペアが続きます これらのボックスの順序は次のようになります 断片化されたムービーと 断片化されたMP4の主な違いは 断片化されたMP4のムービーボックスには サンプルデータへの参照が 含まれていないことです サンプル全体に関連する情報のみが含まれ ムービーフラグメントボックスと メディアデータボックスの順序が逆になります
次にAVAssetWriterの 使用方法についてご説明します これはAVAssetWriterのインスタンスを 作成する方法を示しています AVAssetWriterはファイルを書き込まず データを出力するため出力URLは指定しません 出力コンテンツタイプを指定するだけです 断片化されたMP4は MP4ファミリーに準拠しているため 常にAVFileType.mp4でUTTypeを 指定する必要があります AVAssetWriterInputを作成します この事例ではメディアサンプルを エンコードするために 辞書であるcompressionSettingsが 提供されています またはパススルーモードと呼ばれる outputSettingsに nilを設定できます パススルーモードではAVAssetWriterは 指定されたとおりにサンプルを書き込み inputをAVAssetWriterに追加するだけです AVAssetWriterの設定方法は次のとおりです outputFileTypeProfileを 指定する必要があります
メディアデータを断片化されたMP4形式で 出力するには Apple HLSまたはCMAF準拠のプロファイルを 指定する必要があります
preferredOutputSegmentIntervalを 指定します
AVAssetWriterはその間隔で メディアデータを出力します この時間間隔は 合理的な時間にすることができますが HLSのターゲットセグメント期間は秒単位の 整数でなければならず これがHLSの正しい選択であるため ここではgreater precisionを 使用していません またinitialSegmentStartTimeも指定します
これは間隔の開始点です
次に指定されたプロトコルを実装する デリゲートオブジェクトを指定します
プロトコルについては後で話します この後 通常のファイル書き込みと 同じプロセスが続行されます
書き込みを開始しAVAssetWriterInputを 使用してサンプルを追加します
このセッションではこれ以上説明しませんが 2011年のWWDCセッション "Working with Media in AV Foundation"で これらのプロセスの 実行方法をご覧いただけます セッションビデオはAppleの デベロッパサイトにあります デリゲートメソッドのプロトコルは 次のとおりです 2つのメソッドがあります どちらのデリゲートメソッドも 断片化されたメディアデータの NSDataを配信します
また断片化された メディアデータのタイプを示す―
AVAssetSegmentTypeも配信します
その内の1つを実装する必要があります 違いは2つ目が AVAssetSegmentReportオブジェクトを 配信することです そのオブジェクトには断片化された メディアデータに関する情報が含まれています そのような情報が必要ない場合は1つ目の方を 実装する必要があります
AVAssetSegmentTypeには 2つのタイプがあります 初期化と分離です 初期化タイプの断片化された メディアデータには ファイルタイプボックスと ムービーボックスが含まれます 分離可能タイプの断片化された メディアデータには 1つのムービーフラグメントボックスと 1つのメディアデータボックスが含まれます
分離可能なタイプのデータを 正常に受け取ります
受け取ったメディアデータを セグメントと呼ばれるものにパッケージ化し セグメントをファイルに書き込みます
ファイルタイプボックスと ムービーボックスのペアは
初期化セグメントである必要があります
またムービーフラグメントボックスと メディアデータボックスのペアは
1つのセグメントにすることができます
HLSの場合これらのセグメントを すべて含む単一のファイルを ストリーミングに使用できます
または各セグメントを複数の セグメントファイルに分割することもできます
さらにムービーフラグメントボックスと メディアデータボックスの 複数のペアを1つのセグメントにして 1つのセグメントファイルに保存できます HLSはプレイリストを使用します クライアントは主にプレイリストで セグメントについて理解をします
こちらがプレイリストの事例です URLや期間など セグメントに関する情報を示す― タグが いくつかあります AVAssetWriterは プレイリストを書き込みません プレイリストを書いてください そのためデリゲートメソッドの1つが AVAssetSegmentReportを配信します AVAssetSegmentReportが提供する 情報に基づいて プレイリストと Iフレームプレイリストを作成できます
fmp4Writerというサンプルコードを見ると プレイリストの作成方法が分かります またプレイリストに関するドキュメントは こちらのデベロッパ ストリーミングページにあります サンプルがエンコードされないモードである パススルーモードでは AVAssetWriterは優先出力セグメント間隔に 達したか超えた後 次の同期サンプルの直前に すべてのサンプルを含む メディアデータを出力します
ここでの同期サンプルは別のサンプルに 依存しないサンプルです これはCMAFでは すべてのセグメントが 同期サンプルで始まる必要があるためです Apple HLSもこれを優先します このルールはビデオだけでなく USACオーディオなどのサンプルの依存関係がある オーディオにも適用されます したがってパススルーの場合追加できる AVAssetWriterInputは1つだけです 同期サンプルが間隔の近くに 配置されていない場合 メディアデータは指定された時間よりも かなり長い時間で出力される可能性があります その結果HLSには適しません 解決策の1つはビデオサンプルを 同時にエンコードすることです 前述のとおりAVAssetWriterInputで 圧縮の出力設定を指定すると サンプルがエンコードされます エンコードモードでは優先出力セグメント間隔に ちょうど到達するか それを超えるビデオサンプルは 強制的に同期サンプルとして エンコードされます
つまり同期サンプルは 自動的に生成されるため 断片化されたメディアデータは 指定された間隔で または それに非常に近い間隔で 出力されます オーディオとビデオの 両方をエンコードする場合 メディアごとに 1つのAVAssetWriterInputを追加できます 以前 私はパススルーの場合は 追加できる― AVAssetWriterInputは 1つだけと言いました ただしマスタープレイリストを使用して ビデオとオーディオを 別々のストリームとして配信できます 一対のオーディオストリームと ビデオストリームだけでなく 様々なビットレートまたは異なる言語で ストリームを配信することもできます
複数のストリームを作成するには AVAssetWriterの 複数のインスタンスを 作成する必要があります 繰り返しになりますが マスタープレイリストに関するドキュメントと HLSのビデオとオーディオの エンコードに関する推奨事項は こちらのデベロッパストリーミングページに 載っています
これは高度なユースケースですが preferredOutputSegmentIntervalとは 異なるセグメンテーションが必要な場合は 自分で行うことができます 例えば間隔に達した後ではなく 間隔に達する前に 同期サンプルではないメディアデータを 出力したい場合があります AVAssetWriterのプロパティを設定する前に お見せしたコードは次のとおりです カスタムセグメンテーションを通知するには preferredOutputSegmentIntervalを CMTime indefiniteに設定します initialSegmentStartTimeを 設定する必要はありません インターバルがないためです その他の設定は preferredOutputSegmentIntervalに 使用される設定と同じです これはパススルー専用です カスタムセグメンテーションでは エンコードできません したがってAVAssetWriterInputを 作成するときは
nilを出力設定に設定する必要があります
メディアデータを出力するには flushSegmentメソッドを呼び出します
flushSegmentは前回の呼び出し以降に 追加されたすべてのサンプルを含む― メディアデータを出力します
次の断片化されたメディアデータが 同期サンプルで開始できるように 同期サンプルの前にflushSegmentを 呼び出す必要があります
そうしないと非同期サンプルで開始された― 断片化されたメディアデータを示す エラーが発生します
このモードではオーディオと ビデオを多重化できます ただしオーディオとビデオの両方に サンプルの依存関係がある場合 両方のメディアを均等に揃えることが 難しくなります その場合ビデオとオーディオを 別々のストリームとして パッケージ化することを 検討する必要があります
AVAssetTrackにオーディオトラックに サンプルの依存関係があるかどうかを示す― 新しいプロパティが追加されました
このプロパティを使用して トラックにサンプルの依存関係があるかを 事前に確認できます メディアデータを断片化された MP4形式で出力するには Apple HLSまたはCMAF準拠のプロファイルを 指定する必要があることを先に述べました
CMAFは断片化されたMP4セグメントを 構築する方法に関する一連の制約です
ストリーミング用に断片化されたMP4を 使用するための規格です
Apple HLSを含む複数のプレーヤーで サポートされています Apple HLSのみを対象とする場合 一部の制約は不要です ただしメディアライブラリの より幅広い視聴者を獲得したい場合は CMAFを検討してください
次にHLSがオーディオプライミングを どのように処理するかについて説明します この図はAACオーディオの オーディオ波形を表しています
AACオーディオコーデックでは エンコーディングアルゴリズムの性質上 オーディオサンプルを正しくエンコード およびデコードするために ソースPCMオーディオサンプルを 超えるデータが必要です このためエンコーダは最初のtrueオーディオ サンプルの前に無音部分を追加します
これはプライミングと呼ばれます AACの最も一般的なプライミングは 2112オーディオサンプルです これは約48ミリ秒であり そのサンプルレートは44100であると 仮定しています
オーディオとビデオが別々のストリームとして 別々のファイルに書き込まれる場合 メディアデータは 次のようにレイアウトされます
両方のメディアが 時間0から始まると仮定します お気づきかもしれませんが オーディオとビデオが同時に開始した場合
オーディオがプライミングによって遅延し オーディオとビデオがわずかにずれます CMAFは問題を補正するために オーディオトラックに エディットリストボックスを採用しています プライミングは編集で削除され
オーディオとビデオの開始時間が 一致するようになります
従来Apple HLSはエディットリストを 使用していないため Apple HLSプロファイルを指定した場合 古いプレーヤーとの互換性のために エディットリストボックスは使用されません 代わりにオーディオメディアの baseMediaDecodeTimeは プライミングの量を後方にシフトします
しかしながらbaseMediaDecodeTimeは 符号なし整数として定義されているため ゼロより前にすることはできません
1つの解決策は オーディオとビデオの両方を 同じタイムオフセット分だけ 前方にシフトすることです このようにしてオーディオメディアの baseMediaDecodeTimeは プライミングの量を後方にシフトできます
開始時間がゼロでない場合でも HLSではビデオサンプルの最も早い― プレゼンテーションタイムから 再生が開始されます
したがってタイムオフセットを待たずに すぐに再生が開始されます
これはビデオとオーディオの 正確な同期だけでなく プライミング時間が異なる オーディオバリアント間の タイムスタンプの 正確な同期にとっても重要です したがってApple HLSプロファイルを 指定する場合は すべてのサンプルに一定の時間を追加して メディア時間をシフトすることを お勧めします
initialSegmentStartTimeも 同じタイムオフセットだけ シフトする必要があります 先に述べたように最も一般的な プライミングは1秒未満なので タイムオフセットには数秒で十分です ただしAppleのセグメンターツールである mediafilesegmenterは10秒のオフセットがあるため 同じ10秒を選択できます サンプルコードはすべてのサンプルに タイムオフセットを追加する方法を示しています このデモではサンプルのコマンドライン アプリケーションを使用して 断片化されたMP4セグメントファイルと プレイリストを作成します
次にSafariを使用して 結果のコンテンツを再生します
このデモシーンはコンテンツをホストする ローカルサーバーとして 事前に設定されています
端末アプリケーションにコマンドライン アプリケーションを入力しました
このコマンドラインアプリケーションは 既製のソースムービーから メディアデータを 読み取りビデオとオーディオをエンコードします これがソースムービーです 長さはわずか30秒です このコマンドラインアプリケーションは 優先出力セグメント間隔に6秒を使います セグメントファイルとプレイリストは このディレクトリに作成されます では始めましょう ソースムービーのビデオフレームレートは 30フレーム/秒です 5つのセグメントファイルが表示され プレイリストが作成されました プレイリストを開きます
各セグメントの継続時間は6秒です
ローカルホストをURLとして入力し Safariを使用して再生します
再生してみましょう
HLSにはいくつかの要件があります またユーザーのエクスペリエンスを 改善するための推奨事項もいくつかあります AVAssetWriterを使用して書き込んだ セグメントファイルとプレイリストを検証し それらのファイルが要件と推奨事項を 満たしていることを 確認することをお勧めします 2016年のWWDCセッション “Validating HTTP Live Streams”は この目的のための優れた情報源です セッションビデオは デベロッパ向けのサイトにあります
AVAssetWriterはHLSの断片化されたMP4形式で メディアデータを出力するようになりました ご清聴 ありがとうございました
-
-
5:36 - Instantiate AVAssetWriter and input
// Instantiate asset writer let assetWriter = AVAssetWriter(contentType: UTType(AVFileType.mp4.rawValue)!) // Add inputs let videoInput = AVAssetWriterInput(mediaType: .video, outputSettings: compressionSettings) assetWriter.add(videoInput)
-
6:28 - Configure AVAssetWriter
assetWriter.outputFileTypeProfile = .mpeg4AppleHLS assetWriter.preferredOutputSegmentInterval = CMTime(seconds: 6.0, preferredTimescale: 1) assetWriter.initialSegmentStartTime = myInitialSegmentStartTime assetWriter.delegate = myDelegateObject
-
8:00 - Delegate methods
optional func assetWriter(_ writer: AVAssetWriter, didOutputSegmentData segmentData: Data, segmentType: AVAssetSegmentType) optional func assetWriter(_ writer: AVAssetWriter, didOutputSegmentData segmentData: Data, segmentType: AVAssetSegmentType, segmentReport: AVAssetSegmentReport?)
-
8:37 - AVAssetSegmentType
public enum AVAssetSegmentType : Int { case initialization = 1 case separable = 2 }
-
13:45 - Custom segmentation
// Set properties assetWriter.outputFileTypeProfile = .mpeg4AppleHLS assetWriter.preferredOutputSegmentInterval = .indefinite assetWriter.delegate = myDelegateObject // Passthrough let videoInput = AVAssetWriterInput(mediaType: .video, outputSettings: nil)
-
15:17 - Audio has dependencies
extension AVAssetTrack { /* indicates whether this audio track has dependencies (e.g. kAudioFormatMPEGD_USAC) */ open var hasAudioSampleDependencies: Bool { get } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。