MAGAZINE
ルーターマガジン
webmって何だ?youtubeの配信の仕組みを調査してみる
youtube-dlというCLIツールがあります。Pythonで記述されたプログラムで、pip経由でもパッケージマネージャー経由でもインストールできます。名前はYouTubeとついていますが、YouTube以外の動画配信サービスにも対応しています。
youtube-dlを使用していると、時々困ることがあります。それはダウンロードされる動画の形式です。webmだったりmkvだったり、少なくとも私には見慣れないファイル形式でダウンロードされることがあります。もちろんyoutube-dl -f mp4 hoge
のように書けばフォーマットは指定できますが、webm形式の謎は残ります。ということで今回はファイル形式に着目し、youtube-dlがどのような仕組みでyoutubeにアクセスしているかを調査したいと思います。
準備
macユーザーの場合はbrewを使うのが簡単です。# インストール
$ brew install youtube-dl
# 念のためアップデート
$ youtube-dl -U
# バージョン確認
$ youtube-dl --version
執筆時点(2021年1月9日)での最新版は2021.01.08です。頻繁にメンテナンスされていることが分かります。
次に動画、音声ファイルの変換のために ffmpegをインストールします。
# インストール
$ brew install ffmpeg
$ ffmpeg -version
実行方法
バージョンが確認できたらまずは動画をダウンロードしてみます。コマンドは簡単で、
youtube-dl "ダウンロードしたい動画のURL"
でダウンロードできます。今回はYoutube最古の動画「Me at the zoo」を指定して実行してみます。
$ youtube-dl "https://www.youtube.com/watch?v=jNQXAC9IVRw"
ダウンロードできたらファイル形式を見てみます。
Me at the zoo-jNQXAC9IVRw.webm
のようになっていると思います。ん?webm?見慣れない形式ですね。webmについて調べると、こんな記事を見つけました。YouTube、全動画をWebM形式に変換すると発表
どうやらYoutubeでは10年前から動画の保存形式としてwebmを採用しているようです。
他にはどのような形式でダウンロードできるのかを--list-format
オプションで調べてみます。
$ youtube-dl --list-format "https://www.youtube.com/watch?v=jNQXAC9IVRw"
出力は以下のようになりました。
format code extension resolution note 249 webm audio only tiny 51k , opus @ 50k (48000Hz), 118.67KiB 250 webm audio only tiny 67k , opus @ 70k (48000Hz), 153.83KiB 140 m4a audio only tiny 95k , m4a_dash container, mp4a.40.2@128k (44100Hz), 221.02KiB 251 webm audio only tiny 98k , opus @160k (48000Hz), 225.23KiB 394 mp4 192x144 144p 62k , av01.0.00M.08, 15fps, video only, 137.63KiB 160 mp4 192x144 144p 66k , avc1.4d400b, 15fps, video only, 141.17KiB 278 webm 192x144 144p 70k , webm container, vp9, 15fps, video only, 158.42KiB 133 mp4 320x240 240p 124k , avc1.4d400c, 15fps, video only, 265.85KiB 395 mp4 320x240 240p 138k , av01.0.00M.08, 15fps, video only, 316.81KiB 242 webm 320x240 240p 165k , vp9, 15fps, video only, 378.64KiB 18 mp4 320x240 240p 334k , avc1.42001E, 15fps, mp4a.40.2@ 96k (44100Hz), 772.00KiB (best)
webmの他にもmp4、m4aの形式でダウンロードできることが分かります。youtube-dlでは特にオプションを指定しないと、最高音質、最高画質の動画をダウンロードするようになっています。しかし、今回は一番下にある(best)と表示されたmp4形式ではなく、webm形式の動画がダウンロードされました。これは何故でしょうか。もう一度先ほどの動画をダウンロードするコマンドを叩き、今度はターミナルの出力に注目してみます。
$ youtube-dl "https://www.youtube.com/watch?v=jNQXAC9IVRw" [youtube] jNQXAC9IVRw: Downloading webpage Destination: Me at the zoo-jNQXAC9IVRw.f242.webm 100% of 378.64KiB in 00:00 Destination: Me at the zoo-jNQXAC9IVRw.f251.webm 100% of 225.23KiB in 00:00 [ffmpeg] Merging formats into "Me at the zoo-jNQXAC9IVRw.webm" Deleting original file Me at the zoo-jNQXAC9IVRw.f242.webm (pass -k to keep) Deleting original file Me at the zoo-jNQXAC9IVRw.f251.webm (pass -k to keep)
ログを読むと、どうやら
Me at the zoo-jNQXAC9IVRw.f242.webmと
Me at the zoo-jNQXAC9IVRw.f251.webmの2つのファイルをダウンロードしてそれらをffmpegで合成して一つのファイルにしていることが分かります。.webmの前のf241などの番号は--list-formatで表示されたformat codeに対応しています。
242 webm 320x240 240p 165k , vp9, 15fps, video only, 378.64KiB 251 webm audio only tiny 98k , opus @160k (48000Hz), 225.23KiB
今回選ばれた二つのファイルはそれぞれvideo only、audio onlyの出力があります。youtube-dlは(best)のmp4を選択するよりも、webm形式のこれら二つの動画、音声ファイルを結合した方が最高音質、最高画質の動画になると判断したようです。実際に(best)と表示されているmp4形式と比べて、音質は48100Hzと44100Hz、画質も高画質なvp9、低画質なavc1とそれぞれの項目で最善な形式が選択されていることが分かります。
youtube-dlが裏で何をやっているのか
動画のダウンロードの仕組みが分かったところで、youtube-dlがこれらの形式をどのように取得しているかを調査します。まずは--list-formatで表示されたファイルをどのように取得しているかです。Youtubeの動画URLを取得する方法を探していると、以下の記事を発見しました。Youtubeの動画URLを取得するSwiftコードを書いた
http://www.youtube.com/get_video_info?video_id=のようなURLを叩くと、動画の情報を取得でき、そこからその動画に対応しているフォーマットの一覧が取得できるようです。youtube_dlのGithubリポジトリで「get_video_info」を検索してみると、https://github.com/ytdl-org/youtube-dl/blob/4759543f6e5d532795eb1d5434692bb6d5e1f0ec/youtube_dl/extractor/youtube.py#L1705 でこのURLが使われていることがわかります。どうやらyoutube_dlでもこの方法で動画のURLを取得しているようです。動画URLの取得方法が分かったところで、先ほどの動画に対して叩いてみます。
http://www.youtube.com/get_video_info?video_id=jNQXAC9IVRw
すると、「get_video_info」という名前のテキストファイルがダウンロードされました。このファイルはパーセントエンコードされているため、デコードします。デコードしたファイルの中を見ると、どうやらitagというキーが--list-formatオプションのformat codeに対応し、複数の動画URLが入っているようです。ファイル内検索で「itag」を検索し、出てきた部分を一部抜粋します。
"adaptiveFormats":[
{"itag":133,"url":"https://r2---sn-ogul7n76.googlevideo.com/videoplayback?expire=1610521134\u0026ei=zkX-X9aRGsGWgQOCnpDQCQ\u0026ip=2404%3A7a80%3A97c0%3A1c00%3Ac1d1%3A7380%3A26bc%3Ad37c\u0026id=o-ABmGDNYqh6Zt1aNYpMRyvIlNdbpSHqQicgz1ATTcAPVy\u0026itag=133\u0026aitags=133%2C160%2C242%2C278%2C394%2C395\u0026source=youtube\u0026requiressl=yes\u0026mh=VD\u0026mm=31%2C29\u0026mn=sn-ogul7n76%2Csn-ogueln7r\u0026ms=au%2Crdu\u0026mv=m\u0026mvi=2\u0026pl=29\u0026initcwndbps=1533750\u0026vprv=1\u0026mime=video%2Fmp4\u0026ns=EEDqAGwOdl_KBuYDSUzYdVcF\u0026gir=yes\u0026clen=272229\u0026dur=18.933\u0026lmt=1524502649984547\u0026mt=1610499396\u0026fvip=2\u0026keepalive=yes\u0026c=WEB\u0026n=3xPqXwoziDQ0lQ\u0026sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt\u0026sig=AOq0QJ8wRQIhAKaJEQAIx9GOUGn6NqJZhuvsF51V_DrFANVAhv0ikSF0AiACNqb0k6K2rI_vBNVhNK9u0PJ3tGG_sLgchxa5Wv8giw%3D%3D\u0026lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps\u0026lsig=AG3C_xAwRAIgSWwYQsjOFqLGRDUr_vxElCDEhKZTdA-N3waawslUH1oCIGGLtcvYenjTBr3C-5Fk3idMuHV8qMYMTCcPZce2-Xm0","mimeType":"video/mp4; codecs=\"avc1.4d400c\"","bitrate":124891,"width":320,"height":240,"initRange":{"start":"0","end":"712"},"indexRange":{"start":"713","end":"792"},"lastModified":"1524502649984547","contentLength":"272229","quality":"small","fps":15,"qualityLabel":"240p","projectionType":"RECTANGULAR","averageBitrate":115028,"approxDurationMs":"18933"},
{"itag":242,"url":"https://r2---sn-ogul7n76.googlevideo.com/videoplayback?expire=1610521134\u0026ei=zkX-X9aRGsGWgQOCnpDQCQ\u0026ip=2404%3A7a80%3A97c0%3A1c00%3Ac1d1%3A7380%3A26bc%3Ad37c\u0026id=o-ABmGDNYqh6Zt1aNYpMRyvIlNdbpSHqQicgz1ATTcAPVy\u0026itag=242\u0026aitags=133%2C160%2C242%2C278%2C394%2C395\u0026source=youtube\u0026requiressl=yes\u0026mh=VD\u0026mm=31%2C29\u0026mn=sn-ogul7n76%2Csn-ogueln7r\u0026ms=au%2Crdu\u0026mv=m\u0026mvi=2\u0026pl=29\u0026initcwndbps=1533750\u0026vprv=1\u0026mime=video%2Fwebm\u0026ns=EEDqAGwOdl_KBuYDSUzYdVcF\u0026gir=yes\u0026clen=387725\u0026dur=18.933\u0026lmt=1524503121231492\u0026mt=1610499396\u0026fvip=2\u0026keepalive=yes\u0026c=WEB\u0026n=3xPqXwoziDQ0lQ\u0026sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt\u0026sig=AOq0QJ8wRAIgcxhQ_wt5n7PudDeYhpxuMSO24HhmgNz2BdUq6PGQeFoCIBQhWAdDIGUu5SkY0OubQSrWAPHM_m83FqFisDd65eAB\u0026lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps\u0026lsig=AG3C_xAwRAIgSWwYQsjOFqLGRDUr_vxElCDEhKZTdA-N3waawslUH1oCIGGLtcvYenjTBr3C-5Fk3idMuHV8qMYMTCcPZce2-Xm0","mimeType":"video/webm; codecs=\"vp9\"","bitrate":165017,"width":320,"height":240,"initRange":{"start":"0","end":"198"},"indexRange":{"start":"199","end":"264"},"lastModified":"1524503121231492","contentLength":"387725","quality":"small","fps":15,"qualityLabel":"240p","projectionType":"RECTANGULAR","averageBitrate":163830,"approxDurationMs":"18933"},
(以下略)...
JSON形式でadaptive_formatsに配列で格納されているデータがYoutube動画のURLになっています。これでダウンロードする動画のURL取得方法が分かりました!ちなみに、youtube-dlには--write-info-json
オプションがあり、ダウンロードした動画の情報を別途jsonファイルにしてダウンロードしてくれます。このjsonファイルを見ると、youtube-dlがget_video_infoで取得したデータをどのようにパースしてコーデックや解像度の情報を表示しているかが分かります。
まとめ
youtube-dlが実行されるとき
- get_video_infoのURLを叩いて動画の情報が入ったファイルを取得する。
- 取得したファイルをデコード、パースする。
- ユーザーの指定するオプションに応じて最適なファイルを決定してダウンロードする。
という手順を踏んでいるようです。
参考
- YouTube、全動画をWebM形式に変換すると発表https://www.itmedia.co.jp/enterprise/articles/1104/20/news065.html
- Youtubeの動画URLを取得するSwiftコードを書いたhttps://muunyblue.github.io/0a7d83f084ec258aefd128569dda03d7.html
CONTACT
お問い合わせ・ご依頼はこちらから