#えむけーろぐ

間違った事を書いていたらやさしく教えてください

Cloudflare経由でMP3ファイルを配信すると、一部のPodcastクライアントでバグる

3行で

  • Cloudflare経由でMP3ファイルを配信すると、一部のPodcastクライアントでバグることがある。
  • どうも、ID3タグの情報が欠損してしまっているのが原因なようだ。(未確定)
  • そもそもCloudflare CDNで音声ファイル配信は規約違反っぽいのでやめよう。

はじめに

友人たちと趣味でやっている「ゆるふわPodcast」では、WebサイトやRSSフィード、そして音声ファイルの配信をすべて自前でやっている。自作のRuby on RailsアプリケーションにWebサイトとフィードの配信をやらせて、フィード記載の音声配信用URLを踏むとキャッシュの効いたURLにリダイレクトされ、MP3ファイルがダウンロードされる、という具合だ。

もともと音声ファイルの配信だけはSoundCloudにやらせていた(フィードにSoundCloudの音声ファイルのURLを載せていた)のだが、コスト削減やロギング等の理由により自前での音声配信に切り替えた。*1

f:id:mktakuyax:20220309010324p:plain

Cloudflare経由での音声配信 概略図

Cloudflare経由配信での不具合

自前配信に切り替えた当初は、CDNにCloudflareを利用していた。

ところが、Cloudflare経由での音声配信に切り替えてから、下記のような事象がリスナーから報告されるようになった。

  • 2〜3分再生したと思ったら突然最初に戻ってしまう。(iOS Podcast App、Google Podcast、Podcast Addict)
  • シークバーが無くなってしまう。(Google Podcast)

上記に加え、下記の事象も自分の手元で確認した。(すべてiOS Podcast App)

  • そもそもエラーが出て再生できない。
  • 倍速再生が出来ない。
  • チャプター情報を閲覧出来ない。
f:id:mktakuyax:20220308233046p:plain
f:id:mktakuyax:20220308233042p:plain
左側:エラー、右側:本来はエピソードメモの下部にチャプターがあるはずなのに無い

厄介なのが、すべてのPodcastクライアントで起こるわけではないということだ。僕は普段Overcastを使っていたのだがそちらでは再現せず、この問題に気づくことが出来なかった。また、同じApple製Podcastクライアントでも、iOS版では起こるがmacOS版では起こっていないように見えた。

ゆるふわPodcastのリスナーが使っているPodcastクライアントアプリは、今回問題となっているもの(AppleCoreMedia(たぶんiOS / macOSのPodcast App), Podcast, PodcastAddict, GooglePodcasts)等で4割を占めている。つまり半数近くのリスナーが正しくエピソードを再生できていない可能性がある、ということになる。

正直言ってこれは大問題だ。

f:id:mktakuyax:20220308000407p:plain

2022年3月1日〜3月8日にエピソードをダウンロードしたPodcastクライアント内訳(User-Agentベース)

調査

ファイルのチェックサム比較

原因調査の第一歩として、手元の編集後のファイルと、CDN経由でダウンロードしたファイルの同一性をチェックすることにした。

手元にあったEP126のファイル(編集後のファイル)を「local.mp3」、CDN経由でダウンロードしたファイルを「from_server.mp3」とし、SHA-256ハッシュを比較した。

もちろん、両者のSHA-256ハッシュは一致している。

f:id:mktakuyax:20220308233620p:plain

手元のファイル・CDN経由でダウンロードしたファイルそれぞれのSHA-256チェックサム

ffprobeコマンドを使ってID3タグ(タイトルやチャプターなどのメタデータ)を確認してみても、特に相違はない。チェックサム同じなのだから当然だが。

f:id:mktakuyax:20220308235607p:plain

ffprobeコマンドで表示したMP3ファイルのメタデータ

Cast Feed Validator

次に、フィードがおかしい可能性を考え、何らかのvalidatorにかけてみることを考えた。

当初は一般的なRSSフィードのvalidatorにでもかけてみようと思ったが、Podcastのフィードに特化した「Cast Feed Validator」というものを見つけたのでそちらを利用してみることに。

www.castfeedvalidator.com

結果、2点ほど気になる結果が出てきた。

まず、Server Testsの結果。どうも、CloudflareはPodcastの配信において問題を引き起こす可能性があるらしい。

f:id:mktakuyax:20220308234538p:plain

次に、Media Testsの結果。実際にMP3ファイルをダウンロードして調べてくれるようだが、ID3タグを読み込めないというエラーが出ていた。

f:id:mktakuyax:20220308234645p:plain

Podcastの配信にCloudflareを使っていると問題が起こる可能性があり、実際にID3タグが読み込めないという問題が発生している、ということになる。

実際にCloudflare経由でダウンロードしても手元のファイルとまったく同じであることが確認出来ているのに、一体なぜ……。

検証

エッジキャッシュをOFFにする → 解決せず

音声ファイルの配信はRailsのActive Storageを利用して実現している。

https://yuru28.com/rails/active_storage/blobs/proxy/hogehoge-slug/podcast-epXXX.mp3

上記のようなURLにアクセスがあると、Railsが裏でS3にアクセスしてファイルをダウンロードし、Railsがユーザーにファイルを配信している。Cloudflare側では、このURLをエッジ側でキャッシュするようにルールを設定している。

このルール側でのキャッシュをOFFにして、毎回BYPASSさせる(= オリジンからファイルが配信される)ようにしてみた。

f:id:mktakuyax:20220309000808p:plain

エッジ側でのキャッシュをOFFにしてみる

結果、改善せず。つまり、エッジに保存されているキャッシュが腐っている(何らかの理由でID3タグが欠損してしまっている)などではなく、Cloudflareを経由することそのものが良くないようだ。

CloudFront経由で配信してみる → 解決!

Cast Feed Validatorの言うことを信じることにして、別のCDN経由での配信を試してみることにした。

Cloudflare以外でお手軽に使えるCDNといえば、Amazon CloudFrontだろう。Webサイト・フィードと音声ファイル配信を同じドメインでやっている都合上、Herokuの前段にいきなりCloudFrontを置くというのは出来なかったので、音声ファイルが保存されているS3をオリジンとしたCloudFrontディストリビューションを作成し、フィード記載のURL(yuru28.com)から別ドメイン(cdn-ls.yuru28.com)で提供しているCloudFrontディストリビューションのURLにリダイレクトするようにしてみた。

f:id:mktakuyax:20220309002812j:plain

CloudFront経由での音声配信 概略図

結果、問題が解決した。

問題が報告されていたPodcastクライアントで問題が起こらなくなったし、Cast Feed ValidatorでもID3タグ関連のエラーが出なくなった。

f:id:mktakuyax:20220309003409p:plain

CloudFront経由での音声配信にすると、ID3タグ関連のテストが通るようになった。

フィードの配信はCloudflare経由なので、依然Server TestsではCloudflareを使っている旨の警告が出ているが、音声配信そのものがCloudflareでなければ問題ないようだ。

f:id:mktakuyax:20220309003531p:plain

フィードをCloudflareで配信しているのでServer Testsで警告が出ているが、問題なし

結論

以上より、下記のような結論に至った。

  • Cloudflare経由でMP3ファイルを配信すると、一部PodcastアプリでID3タグが読み込めず、音声の再生に問題が発生する。
  • curlコマンド等で直接ダウンロードしても問題がないように見える。

無料で使えるので嬉しいCDNなのだが、今回のような問題を踏んでしまうため少なくとも音声配信はCloudFrontを使っていこうと思う。

そもそも

同じような問題を踏んだ人がいないかとググっている中で、そもそもCloudflareの利用規約では非HTMLコンテンツの提供に関して制限があることを知った。

2.8 Limitation on Serving Non-HTML Content

(前略)Use of the Services for serving video or a disproportionate percentage of pictures, audio files, or other non-HTML content is prohibited, unless purchased separately as part of a Paid Service or expressly allowed under our Supplemental Terms for a specific Service. (後略)

Self-Serve Subscription Agreement | Cloudflare

ここでいう「a specific Service」は、Cloudflare Workersなどのサービスを指すようで、少なくとも通常のキャッシュサーバとしてのCDNには含まれていない。

2022年現在、Webサイトを作れば多少の画像や動画、音声は登場すると思う。が、それらが主体のWebサイトに使ってはいけないようだ。もちろん、Podcastの配信システムの主体は音声ファイルだ。

ここまで、技術的な問題によってPodcastの配信にCloudflareを使わない方が良いという話をしてきたが、そもそも利用規約的にも良くないということが判明した。

ProやBusiness、Enterpriseなど有料プランなら別途可能になるかもしれないが、少なくともFreeプランではやめたほうが良さそうだ。

 

*1:詳細は先日の吉祥寺.pmで発表した「Podcast配信システムを自作したら捗った話」を参照。