YouTube 上で、HLS でストリーム配信される動画を見かけるようになりました。

HLS (Http Live Streaming) は、Apple社が提唱する、ストリーミング配信用のプロトコルです。配信に用いるファイルを複数の断片(フラグメント)に分割して、これを HTTPプロトコルに乗せて次から次へと配信していくものです。

FFmpeg というツールを用いて、YouTube 上の HLS動画を保存する方法の覚え書きです。

用意するもの
  • FFmpeg
    FFmpeg は、動画・音声データ変換ソフトです。オープンソース(LGPL)です。

    FFmpeg の公式サイト
    http://ffmpeg.mplayerhq.hu/

    Windows 向けビルドは、たとえば、
    Zeranoe FFmpeg Builds
    http://ffmpeg.zeranoe.com/builds/
    などで入手可能です。
    通常は Static Build でよいと思います。32bit/64bit は、お使いの環境に合わせてください。

    解凍して、中身をどこか適当なフォルダに置いてください。
  • youtube.rsite.js
    こちらに置いてあります。右クリックから保存してください。
    (このスクリプトは、Craving Explorer の site-script の形式を借りて作成していますが、サイトスクリプトとしては使えませんので、Craving Explorer にはインストールしないでください。)
    (Windows の WSH上で動作します。URLの入力ボックスの表示に Script Control を使っています。Script Control は 32bit環境のWSHでしか動作しないので、64bit環境でお使いになる場合は、32bit用の WScript から起動してください。あるいは、ご自分で使いやすいように書き換えてください。)

準備
  • FFmpeg のフォルダ内、binフォルダに ffmpeg.exe があります。コマンドラインツールです。今回はこの ffmpeg.exe を使います。
  • 以下の手順の説明では、作業フォルダ(ダウンロードしたファイルが保存されるフォルダ)を、ffmpeg.exe のある binフォルダにしたものとして記述します。binフォルダ以外の場所で作業したい、という場合は、事前に、ffmpeg.exe のある binフォルダへパスを通す、などしてください。パスの通し方は、検索などして調べてください。

以下、

YouTube のチャンネル
Official Olympic Channel by the IOC
http://www.youtube.com/user/olympic

内にある、

Volleyball - Women Bronze Final JPN-KOR - London 2012 Olympic Games
http://www.youtube.com/watch?v=RQph1b7Mloo

を例にして記していきます。

手順1
HLS manifest file URL の取得。
  1. youtube.rsite.js を起動します。
    • Windows の WSH上で動作します。
    • ファイル上でダブルクリック、あるいは、
    • ファイル上で右クリック、開く、あるいは、
    • コマンドプロンプト(Dos窓)から、wscript の引数に youtube.rsite.js を指定して開く、など。
    • 64bit環境の場合は、32bit用の wscript を使ってください。
  2. インプットボックスが開くので、入力ボックスに
    http://www.youtube.com/watch?v=RQph1b7Mloo
    を入力。
  3. インプットボックスの OKボタンをクリック。
  4. メッセージウィンドウが表示され、hls manifest url というのが表示されれば成功です。
    • 失敗の場合は、エラーメッセージなどが表示されるかもしれません。
    • あるいは、その動画が HLSによる配信ではない場合は、いろいろ別 の表示が出るかもしれません。これは無視してください。hls manifest url と間違わないようにしてください。
  5. hls manifest url を後で使いますので、メモしておいてください。
    • メッセージウィンドウがアクティブの状態で、Ctrl + C で、メッセージ内容がクリップボードへコピーされます。(XP以降)
    • あるいは、メッセージウィンドウ内の文字列を取得できるアプリケーションなどご利用ください。

manifest file の URL が取得できたら、manifest file を、いったん、ローカルへ保存して、中身を確認します。manifest file 自体は、テキストファイルですので、メモ帳など、テキストエディタで開くことができます。この手順(manifest file を保存して中身を確認する操作)は、必須ではありません。手順1で取得した manifest file の URLを、後述する、ffmpeg の -i パラメータへ、直接渡してもかまいません。上記、スクリプトで取得した manifest file URL は、いわば、親manifest file の URL で、この親manifest file内に、複数の子manifest file URL が指定されています。
(2012.08.31 追記)
FFmpeg の -map を用いると、手順1で取得した manifest file から、直接、画質・音質を指定して、ファイルに出力することができるようでした。この場合、次の手順2は必要ありません。この記事の最後にある「補記1」を参照してください。

手順2
manifest file の中身を確認する。
  1. 手順1で取得した親manifest file URL を使って、manifest fileをローカル環境へ保存してください。
    • 保存は、ダウンロードマネージャなどをお使いになると簡単だと思います。
    • あるいは、ブラウザのURL欄に直接、manifest file の URL を入力して、開くと、保存されるかもしれません(ブラウザの挙動は、環境設定によります)。
  2. 保存した manifest file をテキストエディタで開いてください。
    • #EXT-X-STREAM-INF: 以下に画質などの説明があって、その次の行にある URL が、その画質の動画の manifest file URLになります。複数の #EXT-X-STREAM-INF: があって、画質の異なる複数の manifest file URL が用意されているのがわかると思います。ご自分の希望にあった画質の manifest file URL をコピーしてください。
    • 最初のほうにある、#EXT-X-MEDIA: 以下の manifest file URL は、音声の manifest file URL になります。スペイン語実況など、元の動画とは異なる音声をかぶせたい場合などに使います。

手順3
FFmpeg を使って保存
  1. ffmpeg.exe のあるフォルダ( binフォルダ )をカレントにしてコマンドプロンプトウィンドウを開きます。
    • ffmpeg.exe のあるフォルダを作業フォルダにした場合として説明しています。
    • この説明では、ダウンロードしたファイルが作業フォルダ内に保存されます。
    • 作業フォルダを別の場所にしたい、保存場所を別フォルダにしたい、などの場合は、binフォルダへパスを通す、などして、説明を読み替えてください。
  2. コマンドプロンプトウィンドウへ
    ffmpeg -i "manifest file URL" -vcodec copy -acodec copy "outfilename.ts"
    
    と入力します。
    • 緑字部分 manifest file URL には、手順2で取得した manifest file の URL を記します。(ここで、手順1で取得した manifest file の URLを使ってもかまいません。その場合、画質は自動的にひとつ選定されるようです。この件、後述)。((2012.08.31 追記)「補記1」を参照してください)
    • 赤字部分 outfilename.ts には、出力するファイル名を記します。拡張子は、ts にしてください。
    • あらかじめ、テキストエディタ上で、コマンドライン文字列を作成してこれをコピー、コマンドラインウィンドウへと貼り付けすると楽かもしれません。
    • FFmpeg のパラメータは、他にもたくさんあります。ご自分でいろいろ工夫してみてください。
  3. よければ、コマンドライン文字列末尾で、Enter キーを押下します。
  4. コマンドプロンプト画面上でダウンロードが始まれば、成功です。失敗の場合は、エラーメッセージなど表示されるかもしれません。

Apple HLS は、MPEG-2 TS 型式が用いられることが多いようですが、そうしなければならない、ということでもないようです。上記、ffmpeg のパラメータは、ご自身でいろいろ試行して、最も良いものを探ってください。

上記、YouTube の例では、親manifest の中に、複数の子manifest URL が用意されていて、そこで、画質などが指定できるようになっていました。いつもこのような、親子関係があるとは限らないので、ケースに応じて、ご自身で調べてください。めんどくさければ、親manifest を直接、ffmpeg の -i パラメータに指定してもかまいません。例えば、上記のYouTube の例で、親manifest(手順1で、スクリプトが取得する manifest file URL)を、ffmpeg -i に指定してやると、ffmpeg は、自動的に、最も高画質のものを採ろうとするような感じです(このあたりの挙動、きちんと確認していません)。自動的ではなく、明示的に画質を指定したい場合は、手順2のように、いったん、親manifest file を開いて、自分が保存したい画質の子manifest URL を指定してやるのが安全だと思います。
(2012.08.31 追記)
上記記述に関しては、「補記1」を参照してください。 親manifest を指定したうえで、map オプションを用いて、映像トラック、音声トラックを指定することができます。上記の方法と、「補記1」の方法と、好みの方法を選んでください。

高画質のものは、保存ファイルのサイズが、そうとうな容量に達することがありますので、注意してください。

上記、YouTube の例で、#EXT-X-MEDIA: 以下の音声manifest URL は、ffmpeg -i に指定して、単独の音声ファイルとして保存可能です。(ローカルに保存後、ffmpeg を用いて、音声を入れ替える、などの操作が可能です)。あるいは、-i を複数指定して、直接、音声をかぶせることもできるようでした(うまくいかない場合があるみたいです)((2012.08.31 追記)「補記1」を参照してください)

VLC を使ってストリーム再生してみる
上記例の YouTube の HLS 配信動画を、 VLC Media Playerを使ってストリーム再生する方法です。 VLC Media Player については、ここでは説明しません。

コマンドプロンプトから
ffmpeg -i "manifest file URL" -vcodec copy -acodec copy -f mpegts pipe: | "vlc.exeへのフルパス" - 
とします。末尾の - を忘れないようにしてください。
スペイン語実況でストリーム再生したい場合は、
ffmpeg -i "manifest file URL" -i "スペイン語音声のmanifest file URL" -vcodec copy -acodec copy -f mpegts pipe: | "vlc.exeへのフルパス" - 
のようにすると、できるようです。(うまくいかない場合もあるみたいです。よくわかりません。ご自分でいろいろ試行してみてください。回線状態によるのかもしれません。)((2012.08.31 追記)「補記1」を参照してください)

その他
YouTube の manifest file URL には、問い合わせクライアントの ipアドレスが含まれているみたいで、もしかしたら、問い合わせの際の ipアドレスと、動画を要求する際の ipアドレスが異なると、弾かれるのかもしれません。このあたり、未確認です。

この記事の内容を実行するに際しては、すべて、ご自身の判断と責任において行うようにしてください。



(補記1)
FFmpeg の -map オプションを用いた方法を記しておきます。このオプションを使うと、上記、手順2のように、わざわざ、マニフェストファイルを開いて中身を確認する、という作業が必要なくなります。
  1. コマンドプロンプトウィンドウへ、
    ffmpeg -i "manifest file url"
    
    と入力して Enter キーを押します。
    • ffmpeg.exe のあるフォルダへパスが通っている、ないし、ffmpeg.exe のあるフォルダ( binフォルダ )がカレントになっているものとして記しています
    • 緑字部分manifest file url には、手順1で取得した manifest file url(親マニフェストファイルURL)を記します。(手順2で取得した子manifest file url を指定してもかまいませんが、おそらく、画像、音声、それぞれ1つずつの Stream (トラック)しか表示されないでしょう)。
  2. コマンドプロンプトウィンドウ内に次々と表示が流れていくと思います。この表示ののうち、たとえば、
      Duration: 01:36:11.00, start: 18805.513533, bitrate: 0 kb/s
        Stream #0:0: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p, 426x240 [SAR 1:1 DAR 71:40], 25 fps, 25 tbr, 90k tbn, 50 tbc
        Metadata:
          variant_bitrate : 390000
        Stream #0:1: Audio: aac ([15][0][0][0] / 0x000F), 22050 Hz, mono, s16, 31 kb/s
        Metadata:
          variant_bitrate : 390000
        Stream #0:2: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p, 640x360 [SAR 1:1 DAR 16:9], 25 fps, 25 tbr, 90k tbn, 50 tbc
        Metadata:
          variant_bitrate : 1337000
        Stream #0:3: Audio: aac ([15][0][0][0] / 0x000F), 48000 Hz, stereo, s16, 130 kb/s
        Metadata:
          variant_bitrate : 1337000
        Stream #0:4: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p, 854x480 [SAR 1:1 DAR 427:240], 25 fps, 25 tbr, 90k tbn, 50 tbc
        Metadata:
          variant_bitrate : 1390000
        Stream #0:5: Audio: aac ([15][0][0][0] / 0x000F), 48000 Hz, stereo, s16, 120 kb/s
        Metadata:
          variant_bitrate : 1390000
        Stream #0:6: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p, 1280x720[SAR 1:1 DAR 16:9], 25 fps, 25 tbr, 90k tbn, 50 tbc
        Metadata:
          variant_bitrate : 3405200
        Stream #0:7: Audio: aac ([15][0][0][0] / 0x000F), 48000 Hz, stereo, s16, 156 kb/s
        Metadata:
          variant_bitrate : 3405200
        Stream #0:8: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 25 fps, 25 tbr, 90k tbn, 50 tbc
        Metadata:
          variant_bitrate : 6466000
        Stream #0:9: Audio: aac ([15][0][0][0] / 0x000F), 48000 Hz, stereo, s16, 156 kb/s
        Metadata:
          variant_bitrate : 6466000
        Stream #0:10: Video: h264 (Constrained Baseline) ([27][0][0][0] / 0x001B), yuv420p, 426x240 [SAR 1:1 DAR 71:40], 12.50 fps, 12.50 tbr, 90k tbn, 25 tbc
        Metadata:
          variant_bitrate : 260520
        Stream #0:11: Audio: aac ([15][0][0][0] / 0x000F), 22050 Hz, mono, s16, 30 kb/s
        Metadata:
          variant_bitrate : 260520
        Stream #0:12: Video: h264 (Constrained Baseline) ([27][0][0][0] / 0x001B), yuv420p, 128x72 [SAR 1:1 DAR 16:9], 6 fps, 6 tbr, 90k tbn, 12 tbc
        Metadata:
          variant_bitrate : 64000
        Stream #0:13: Audio: aac ([15][0][0][0] / 0x000F), 22050 Hz, mono, s16, 23 kb/s
        Metadata:
          variant_bitrate : 64000
    
    の、青字部分から始まるデータが、このファイルに含まれる Stream (トラック) です。その Stream のデータなども記されているのがわかると思います。
    • Stream # に続くカンマで区切られた2つの数字が、この Stream を指定する識別子になります。たとえば、Stream #0:1 だったら、識別子は、0:1 です。
  3. たとえば、自分が使いたい映像の Steam が 0:0 、音声の Stream が 0:1 ならば、
    ffmpeg -i "manifest file url" -vcodec copy -acodec copy -map 0:0 -map 0:1 "outfilename.ts"
    
    のようにします。

スペイン語音声等を指定する場合は、たとえば、次のようにします。
ffmpeg -i "manifest file url" -i "スペイン語音声のmanifest file"  -vcodec copy -acodec copy -map 0:0 -map 1:0 "outfilename.ts"
0:0 のカンマ前の 0 は、-i で指定したひとつめのもの( 0 番目のもの = "manifest file url" )を指し、これに含まれる Stream 0 番目の映像Stream を指定しています。1:0 のカンマ前の 1 は、-i で指定したふたつめのもの( 1 番目のもの = "スペイン語音声のmanifest file" )を指し、その Stream 0 番目の音声Steam を指定しています。Stream は、上記手順に従って、あらかじめ調べておいてください。
(補記1、終わり)

(補記2)
映像Stream、音声Stream が複数ある場合(例えば上記例だと、「親」マニフェストファイルを指定した場合)で、map を指定しないと、FFmpeg は、自動的に、最高画質、最高音質の Stream をひとつずつ選び、これを使うようでした。例えば、上記例で map指定をしない場合、
Stream mapping:
  Stream #0:8 -> #0:0 (copy)
  Stream #0:3 -> #0:1 (copy)
となっていて、映像Stream に 0:8 が、音声Stream に 0:3 が、それぞれ使われています。おそらく、0:8 は、5つめの子マニフェストファイルに由来し、0:3 は、2つめの子マニフェストファイルに由来するものと思われます。これは、映像のためのフラグメントファイルと音声のためのフラグメントファイルが異なるとこを意味し、それぞれ別々にフラグメントファイルを取得することになるのだろうと思います。
(補記2、おわり)

(補記3)
本文中では、保存するファイルの拡張子を .ts にしています。保存するファイルの拡張子を .mkv にしてやると、保存ファイルのサイズが多少小さくなるかもしれません。
(補記3、おわり)

(補記4)
本文中で例として使った YouTube の HLS動画では、英語の実況音声の他、スペイン語の実況音声、自然な音の音声(会場の音声)、が選択できるようです。英語実況は、おそらく、動画と共に記録されている音声Streamで、補記1で見たStreamに当たると思われます。スペイン語実況音声、および、自然な音の音声、は、本文、手順2で取得したマニフェストファイルの #EXT-X-MEDIA: 以下に記された子マニフェストファイルURLで指定されているようです。LANGUAGE="es" のものが、スペイン語の実況音声、LANGUAGE="x-na" のものが、自然な音の音声、だろうと思います。補記1で記した方法で保存可能だろうと思います(あるいは、本文中に記したように、音声だけ別に保存して、あとから、ffmpeg を使って、音声を付加したり、いれかえたり、ということも可能です。)あるいは、副音声トラックを作って、そこに別の音声を入れておく、ということも可能です。たとえば、
ffmpeg -i "manifest file URL" -i "自然な音声のmanifest file URL"
で、保存したいStream が、映像Stream 0:0、英語実況の音声Stream 0:1、自然な音声Stream 1:0 だとすると、
ffmpeg -i "manifest file URL" -i "自然な音声のmanifest file URL" -vcodec copy -acodec copy -map 0:0 -map 0:1 -map 1:0  "outfilename.ts"
のようにすると、副音声に自然な音の音声を保存することができるようです。保存するファイルの拡張子は、補記3のように、.mkv でもうまくいくようでした。(検索などすると、上記の例のように、新しく音声トラックを追加するような場合、-newaudio パラメータを用いる例がよく紹介されているのですが、比較的新しい ffmpeg の場合、このパラメータそのものが見当たらず、ffmpeg のバージョンによって、パラメータに違いがあるのかもしれません。このあたりよくわかりません。ご自分でお使いの ffmpeg に合わせて、適宜、読み替えてください。)
(補記4、おわり)