🍎

MacOSのSafariでAV1のHLSを再生できるようになるまで

に公開

経緯

MacにてAV1対応が進む中、なぜかLTSのFFmpeg 7.1系でエンコードしたAV1+OpusのHLSが再生できないという状態でしたが、なんとなく思い込みでSafari側の問題なのかなと思ってました。ただFFmpeg 8.0のニュースがでて色々最新のFFmpegで試していたところ、最新のFFmpegではなぜかMacOSでもiOSでも再生できてそこで初めてエンコードの問題だと認識したという感じでした。

検証

検証環境

  • エンコード
    • FFmpeg 8.0 Branch
      • SVT-AV1 2.3.0
      • SVT-AV1 3.1.0
    • FFmpeg 7.1.1 LTS
      • SVT-AV1 2.3.0
  • 再生
    • Macbook Air (M3) 15.6
      • Safari 18.6
    • iPhone 16 Pro Max 18.6
    • iPad mini 7 18.6

検証まとめ

SVT-AV1 3.1.0 SVT-AV1 2.3.0
FFmpeg 7.1.1 - ×
FFmpeg 8.0

AV1のバージョンに限らず、FFmpegの最新版では再生できる。ということはFFmpeg側の問題か。

調査

mp4dumpにて書き出したdumpのdiffを取る

Bento4のmp4dumpを使用して、それぞれのinit.mp4のdumpをテキストに出力し、それぞれのdiffを取ってみます。

$ cp /ffmpeg7/av1/v0/init_0.mp4 ./init_0_230.mp4
$ cp /ffmpeg8/av1/v0/init_0.mp4 ./init_0_310.mp4

$ ./mp4dump init_0_230.mp4 > 230.txt
$ ./mp4dump init_0_310.mp4 > 310.txt

$ diff 230.txt 310.txt 
8c8
< [moov] size=8+1267
> [moov] size=8+1255
<   [trak] size=8+534
>   [trak] size=8+522
<     [mdia] size=8+386
>     [mdia] size=8+374
<       [minf] size=8+301
>       [minf] size=8+289
<         [stbl] size=8+237
>         [stbl] size=8+225
<               compressor = Lavc61.19.101 libsvtav1
>               compressor = Lavc62.10.100 libsvtav1
<           [sdtp] size=8+4
<             value = Lavf61.7.100
>             value = Lavf62.2.100

諸々違いはあるが基本的には数値だけ。ただ明確に違うのは、FFmpeg7.1.1の方にだけmoov/trak/mdia/minf/stblsdtpが存在していました。これが原因かな。

sdtp (Sample Dependency Type Box)を知る

https://developer.apple.com/documentation/quicktime-file-format/sample_dependency_flags_atom
Appleの開発者向けサイトにあるように、簡単にいうと依存関係の情報を記述するもののようです。

関連コミットを調べる

https://git.ffmpeg.org/gitweb/ffmpeg.git/commitdiff/4b6f6af384a76b8ca1cbb4fdde734ffaf111a934?hp=dc8e753f32a391c329adab3db323f79219042b24
FFmpegのgitをsdtpという検索ワードで調べてみると、最近これに関する修正がマージされていました。

ffmpeg で生成された HEVC fmp4 HLS ビデオは、現在 Apple ソフトウェア (Safari、QuickTime、AVFoundation) では再生できません。
これは、空の sdtp アトムが fmp4 の init セグメントに誤って書き込まれることが原因で発生します。B フレームを含むトラックには has_disposable フラグを設定できますが、init セグメントには実際のフレームが含まれていません (track->entry == 0)。この場合、sdtp アトムを書き込むのは誤りであり、Apple のパーサーによってファイルが拒否されます。
このパッチは、track->entry が 0 以外の場合にのみ sdtp アトムが書き込まれるようにすることで、この問題を修正します。
同様のパッチが 2023 年 11 月に Jay Zhang によって提案されましたが、マージされませんでした。

コミットメッセージを翻訳してみると、AV1ではないですがHEVCに関連してfmp4の生成についてのメッセージですね。

コードとしては以下の修正です。

movenc.c
-    if (track->par->codec_type == AVMEDIA_TYPE_VIDEO && track->has_disposable)
+    if (track->par->codec_type == AVMEDIA_TYPE_VIDEO && track->has_disposable && track->entry)

これが肝のようですね。条件の判定でsdtp_tagを書き出すかどうかの判定がおかしくなっていたようです。

FFmpeg 7.1.1のコードを修正する

ここまでわかれば、FFmpeg 7.1.1のmovenc.cの該当箇所を修正してコンパイルしてみて、それで生成したAV1 HLSがMacで再生できれば確定ですね。てことでやってみます。

$ cd ffmpeg_build/ffmpeg
$ vi libavformat/movenc.c
# ファイルを修正
$ make clean
$ ./configure
$ make && sudo make install
$ sudo ldconfig

該当部分を修正し、コンパイルして再インストールします。このFFmpeg 7.1.1改で改めてAV1 HLSをエンコードしてみたところ、無事再生されました。

おわりに

長らく思い込みで、MacのSafariはまだAV1対応が完全じゃないんだと思考停止してしまっていましたが、むしろsdtpの扱いなどの厳密さでいえばSafariの方が進んでいたんですね。反省しなければ。

FFmpegはLGPLとはいえ、x264などGPLソフトウェアを組み込むとGPLになってしまいます。今回はあくまで技術的な検証でしたが、ソースを書き換えたものを特に商用などで使用する際には十分ライセンスにご注意ください。

うちの会社は、この修正がマージされた7.1.2などが出るのを待とうと思います。


https://voice.stream.co.jp/

Discussion