Flashビデオ/オーディオ配信サイトからrtmpdumpでダウンロードするために解析する

おことわり

  • Wiresharkで通信ログを見たときに RTMP ストリームがあるもの限定です*1
  • 個人利用に限ります。
  • 当方は責任を負いません。

必要なもの

基本編:「先読み!夕方ニュース」

手順1(キャプチャ)
  1. Wireshark を起動しておきます。
  2. 目的のサイトをいちおう開いておきます。
  3. Wireshark でパケットキャプチャを開始します。
  4. 目的サイトをリロードします。
  5. しばらく目的サイトの音声を流しておきます。再生/停止を繰り返すのも良いでしょう。
  6. 音声を止めます。
  7. キャプチャを止めます。
手順2(解析)
  • キャプチャしたデータから RTMP パケットを見付けて右クリック、「Follow TCP Stream」を選択します*2。どこでもいいです。ちゃんと RTMP ストリームの冒頭から表示してくれます。

  • 「Follow TCP Stream」ウィンドウを確認します。


このウィンドウをスクロールしていき、「connect.?」ってな文字があるところから「play」に続いて「mp3」とかフォーマットを示す文字列に続くあたりまでを右クリックでコピーし、適当なエディタなどへペーストします。

connect.?..........app...flv9..flashVer...MAC 19,0,0,245..swfUrl..-https://www.nhk.or.jp/hitokoto/player2012.swf..tcUrl...rtm.p://flv9.nhk.or.jp/flv9..fpad....capabilities.@m........audioCodecs.@.........videoCodecs.@o.......
videoFunction.?.........page.Url..1https://www.nhk.or.jp/hitokoto/sound15111202.html..objectEncoding.@...........).u.m8....S."(5.....;.3.F.....".6v|...V-........@D.W.......=..Qc..c{]9_.>..@.MSc../..[.....:Cw.u...Z;Z.>...s... 4.[......n.P.J/
Q.u...N
wH (X..(......8".=...V.F2)..W..G.;j....c.............&%..............&%................................._result.?..........vKey.....fmsVer...FMS/4,5,5,4013..capabilities.@o........mode.@.............level...status..code...NetConnection.Connect.Success..description...Connection succeeded...objectEncoding.@.........data.......version..
4,5,5,4013.....................onBWDone.............J.........&%.C...........createStream.@......................._result.@.........?.............*.........play.............mp3:hitokoto/15111202H...........seek...................H...........pause.....................
  • パラメータになるところを切り取っていきます。
app...flv9
flashVer...MAC 19,0,0,245
swfUrl..-https://www.nhk.or.jp/hitokoto/player2012.swf
tcUrl...rtm.p://flv9.nhk.or.jp/flv9
page.Url..1https://www.nhk.or.jp/hitokoto/sound15111202.html
play.............mp3:hitokoto/15111202
  • rtmpdumpに渡すパラメータをリストアップしていきます。

上記以外にアウトプットするファイル名 -o を指定しました。
rtmpdump のコマンドは以下のようになりました。

rtmpdump \
--app flv9 \
--flashVer 'MAC 19,0,0,245' \
--swfUrl https://www.nhk.or.jp/hitokoto/player2012.swf \
--rtmp rtmp://flv9.nhk.or.jp/flv9 \
--pageUrl https://www.nhk.or.jp/hitokoto/sound15111202.html \
--playpath mp3:hitokoto/15111202 \
-o 15111202_orig.mp3

NHK「先読み!夕方ニュース」の番組アーカイブスのダウンロード - 別館 子子子子子子(ねこのここねこ)ではオプション指定をショート表記にしていましたが、今回は Wireshark でのデータとの照合がやりやすいようにロング表記にしています。
tcUrl のところは --rtmp に、また play のところは --playpath に対応します。
-o はテキトーな名前で良いのですが、いちおう --playpath の部分にある元のファイル名に拡張子 .mp3 を付けています。念のため orig とか書いてます。

手順3(変換)

ダウンロードしたあとのファイルは、残念ながらうまく出来ないことが多いです。ので、ffmpeg を通します。

$ ffmpeg -i 15111202_orig.mp3 15111202.mp3
ffmpeg version 2.7.2 Copyright (c) 2000-2015 the FFmpeg developers
  built with Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
  configuration: --prefix=/usr/local/Cellar/ffmpeg/2.7.2_1 --enable-shared --enable-pthreads --enable-gpl --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-opencl --enable-libx264 --enable-libmp3lame --enable-libvo-aacenc --enable-libxvid --enable-libvorbis --enable-librtmp --enable-openssl --enable-nonfree --enable-vda
  libavutil      54. 27.100 / 54. 27.100
  libavcodec     56. 41.100 / 56. 41.100
  libavformat    56. 36.100 / 56. 36.100
  libavdevice    56.  4.100 / 56.  4.100
  libavfilter     5. 16.101 /  5. 16.101
  libavresample   2.  1.  0 /  2.  1.  0
  libswscale      3.  1.101 /  3.  1.101
  libswresample   1.  2.100 /  1.  2.100
  libpostproc    53.  3.100 / 53.  3.100
Input #0, flv, from '15111202_orig.mp3':
  Duration: 00:23:22.25, start: 0.000000, bitrate: 97 kb/s
    Stream #0:0: Audio: mp3, 48000 Hz, stereo, s16p, 96 kb/s
Output #0, mp3, to '15111202.mp3':
  Metadata:
    TSSE            : Lavf56.36.100
    Stream #0:0: Audio: mp3 (libmp3lame), 48000 Hz, stereo, s16p
    Metadata:
      encoder         : Lavc56.41.100 libmp3lame
Stream mapping:
  Stream #0:0 -> #0:0 (mp3 (native) -> mp3 (libmp3lame))
Press [q] to stop, [?] for help
[libmp3lame @ 0x7ff549801c00] Queue input is backward in time
[mp3 @ 0x7ff54a800000] Application provided invalid, non monotonically increasing dts to muxer in stream 0: 3503 >= 3503
 :
[mp3 @ 0x7ff549801000] overread, skip -7 enddists: -6 -6  
[libmp3lame @ 0x7ff549801c00] Queue input is backward in time
[mp3 @ 0x7ff54a800000] Application provided invalid, non monotonically increasing dts to muxer in stream 0: 9448751 >= 9448751
[mp3 @ 0x7ff549801000] overread, skip -6 enddists: -4 -4  
    Last message repeated 1 times
 :
(中略)
 :
size=   21912kB time=00:23:22.32 bitrate= 128.0kbits/s    
video:0kB audio:21912kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.001056%

たくさんエラーが出ましたが、気にしないことにしますw
これで目的のオーディオファイルが取得出来ました。
個人でご利用下さい。

応用編:動画サイト

まずターゲットを決めます。今回は

にしました*3

手順1(キャプチャ)
  1. Wireshark を起動しておきます。
  2. 目的のサイトをいちおう開いておきます。
  3. Wireshark でパケットキャプチャを開始します。
  4. 目的サイトをリロードします。
  5. しばらく目的サイトの動画を流しておきます。
  6. 動画を止めます。
  7. キャプチャを止めます。
手順2(解析)
  • キャプチャしたデータから RTMP パケットを見付けて右クリック、「Follow TCP Stream」を選択します。どこでもいいです。ちゃんと RTMP ストリームの冒頭から表示してくれます。

  • 「Follow TCP Stream」ウィンドウを確認します。


このウィンドウをスクロールしていき、「connect.?」ってな文字があるところから「play」に続いて「mp4」とかフォーマットっぽい文字列に続くあたりまでを右クリックでコピーし、適当なエディタなどへペーストします。

connect.?..........app..`ondemand?videoId=4599166283001&lineUpId=&pubId=3971130137001&playerId=4132654436001&affiliateId=..fl.ashVer...MAC 19,0,0,245..swfUrl..jhttp://admin.brightcove.com/viewer/us20150903.1327/federatedVideoUI/BrightcovePlayer.swf?uid=1.447510930363..tcUrl...rtmp://cp313263.edgefcs.net:1935/ondemand?videoId=4599166283001&lineUpId=&pubId=3971130137001&playerId=413.2654436001&affiliateId=..fpad....capabilities.@m........audioCodecs.@.........videoCodecs.@o.......
videoFunction.?.........page.Url..%http://video.tv-tokyo.co.jp/sorakara/..objectEncoding.............TF.............B..d.........B<;(.........S.A..........._result.?..........fmsVer...FMS/5,0,7,7055..capabilities.@_........mode.?.............level...status..code...NetConnection.Connect.Success..description...Connection succeeded...objectEncoding...........data.......version..
5,0,7,7055.....................onBWDone............u.............................createStream.@........C.........._result.@.........?.......B.....
...........C.....q....getStreamLength.@..........Rmp4:videos/3971130137001/201511/2357/3971130137001_4603862784001_4599166283001.mp4...............play.............mp4:videos/3971130137001/201511/2357/3971130137001_4603862784001_4599166283001.mp4?videoId=4599166283001&lin............eUpId=&pubId=3971130137001&playerId=4132654436001&affiliateId=..........
  • パラメータになるところを切り取っていきます。
app..`ondemand?videoId=4599166283001&lineUpId=&pubId=3971130137001&playerId=4132654436001&affiliateId=
fl.ashVer...MAC 19,0,0,245
swfUrl..jhttp://admin.brightcove.com/viewer/us20150903.1327/federatedVideoUI/BrightcovePlayer.swf?uid=1.447510930363
tcUrl...rtmp://cp313263.edgefcs.net:1935/ondemand?videoId=4599166283001&lineUpId=&pubId=3971130137001&playerId=413.2654436001&affiliateId=
page.Url..%http://video.tv-tokyo.co.jp/sorakara/
play.............mp4:videos/3971130137001/201511/2357/3971130137001_4603862784001_4599166283001.mp4?videoId=4599166283001&lin............eUpId=&pubId=3971130137001&playerId=4132654436001&affiliateId=
  • rtmpdumpに渡すパラメータをリストアップしていきます。

上記以外に開始秒 --start、終了秒 --stop、そしてアウトプットするファイル名 -o を指定しました。キャプチャ時間が長くてもあとで面倒なので、冒頭30秒のみを取得するようにしています。
rtmpdump のコマンドはこうなりました*4

rtmpdump \
--app 'ondemand?videoId=4599166283001&lineUpId=&pubId=3971130137001&playerId=4132654436001&affiliateId=' \
--flashVer 'MAC 19,0,0,245' \
--swfUrl 'http://admin.brightcove.com/viewer/us20150903.1327/federatedVideoUI/BrightcovePlayer.swf?uid=1.447510930363' \
--rtmp 'rtmp://cp313263.edgefcs.net:1935/ondemand?videoId=4599166283001&lineUpId=&pubId=3971130137001&playerId=413.2654436001&affiliateId=' \
--pageUrl http://video.tv-tokyo.co.jp/sorakara/ \
--playpath 'mp4:videos/3971130137001/201511/2357/3971130137001_4603862784001_4599166283001.mp4?videoId=4599166283001&lineUpId=&pubId=3971130137001&playerId=4132654436001&affiliateId=' \
--start 0 \
--stop 30 \
-o 3971130137001_4603862784001_4599166283001.mp4

tcUrl のところは --rtmp に、また play のところは --playpath と書き換えます。
-o はテキトーな名前で良いのですが、いちおう --playpath の部分にある元のファイル名を使っています。

手順3(変換)

ダウンロードしたあとのファイルは、残念なことにそのままでは再生出来ないことが多いです。ので、ffmpeg を通します。

$ ffmpeg -i 3971130137001_4603862784001_4599166283001.mp4 sorakara20151110.mp4
ffmpeg version 2.7.2 Copyright (c) 2000-2015 the FFmpeg developers
  built with Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
  configuration: --prefix=/usr/local/Cellar/ffmpeg/2.7.2_1 --enable-shared --enable-pthreads --enable-gpl --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-opencl --enable-libx264 --enable-libmp3lame --enable-libvo-aacenc --enable-libxvid --enable-libvorbis --enable-librtmp --enable-openssl --enable-nonfree --enable-vda
  libavutil      54. 27.100 / 54. 27.100
  libavcodec     56. 41.100 / 56. 41.100
  libavformat    56. 36.100 / 56. 36.100
  libavdevice    56.  4.100 / 56.  4.100
  libavfilter     5. 16.101 /  5. 16.101
  libavresample   2.  1.  0 /  2.  1.  0
  libswscale      3.  1.101 /  3.  1.101
  libswresample   1.  2.100 /  1.  2.100
  libpostproc    53.  3.100 / 53.  3.100
Input #0, flv, from '3971130137001_4603862784001_4599166283001.mp4':
  Metadata:
    moovPosition    : 36
    avcprofile      : 100
    avclevel        : 31
    aacaot          : 2
    videoframerate  : 30
    audiochannels   : 2
    length          : 131904512
    timescale       : 48000
    sampletype      : mp4a
  Duration: 00:45:48.01, start: 0.000000, bitrate: 27 kb/s
    Stream #0:0: Video: h264 (High), yuv420p, 1024x576 [SAR 1:1 DAR 16:9], 30.30 fps, 29.97 tbr, 1k tbn, 59.94 tbc
    Stream #0:1: Audio: aac (LC), 48000 Hz, stereo, fltp
[libx264 @ 0x7fbceb86ac00] using SAR=1/1
[libx264 @ 0x7fbceb86ac00] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 AVX2 LZCNT BMI2
[libx264 @ 0x7fbceb86ac00] profile High, level 3.1
[libx264 @ 0x7fbceb86ac00] 264 - core 146 r2555 0c21480 - H.264/MPEG-4 AVC codec - Copyleft 2003-2015 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=6 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
Output #0, mp4, to 'sorakara20151110.mp4':
  Metadata:
    moovPosition    : 36
    avcprofile      : 100
    avclevel        : 31
    aacaot          : 2
    videoframerate  : 30
    audiochannels   : 2
    length          : 131904512
    timescale       : 48000
    sampletype      : mp4a
    encoder         : Lavf56.36.100
    Stream #0:0: Video: h264 (libx264) ([33][0][0][0] / 0x0021), yuv420p, 1024x576 [SAR 1:1 DAR 16:9], q=-1--1, 29.97 fps, 30k tbn, 29.97 tbc
    Metadata:
      encoder         : Lavc56.41.100 libx264
    Stream #0:1: Audio: aac (libvo_aacenc) ([64][0][0][0] / 0x0040), 48000 Hz, stereo, s16, 128 kb/s
    Metadata:
      encoder         : Lavc56.41.100 libvo_aacenc
Stream mapping:
  Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
  Stream #0:1 -> #0:1 (aac (native) -> aac (libvo_aacenc))
Press [q] to stop, [?] for help
frame=  903 fps= 38 q=-1.0 Lsize=    7879kB time=00:00:30.06 bitrate=2147.1kbits/s dup=3 drop=0    
video:7376kB audio:470kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.418613%
[libx264 @ 0x7fbceb86ac00] frame I:5     Avg QP:20.85  size: 53390
[libx264 @ 0x7fbceb86ac00] frame P:428   Avg QP:23.98  size: 14026
[libx264 @ 0x7fbceb86ac00] frame B:470   Avg QP:28.22  size:  2728
[libx264 @ 0x7fbceb86ac00] consecutive B-frames: 12.8% 46.7% 19.6% 20.8%
[libx264 @ 0x7fbceb86ac00] mb I  I16..4: 11.6% 55.5% 32.9%
[libx264 @ 0x7fbceb86ac00] mb P  I16..4:  5.5% 13.0%  1.0%  P16..4: 38.3% 17.9%  9.9%  0.0%  0.0%    skip:14.4%
[libx264 @ 0x7fbceb86ac00] mb B  I16..4:  0.3%  0.4%  0.1%  B16..8: 42.4%  4.0%  0.6%  direct: 1.3%  skip:50.9%  L0:40.0% L1:54.8% BI: 5.2%
[libx264 @ 0x7fbceb86ac00] 8x8 transform intra:65.4% inter:61.2%
[libx264 @ 0x7fbceb86ac00] coded y,uvDC,uvAC intra: 37.8% 43.2% 6.8% inter: 18.8% 17.4% 0.4%
[libx264 @ 0x7fbceb86ac00] i16 v,h,dc,p: 28% 33% 13% 25%
[libx264 @ 0x7fbceb86ac00] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 24% 24% 25%  4%  5%  5%  6%  4%  5%
[libx264 @ 0x7fbceb86ac00] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 24% 19% 17%  6%  8%  8%  7%  5%  6%
[libx264 @ 0x7fbceb86ac00] i8c dc,h,v,p: 63% 19% 15%  3%
[libx264 @ 0x7fbceb86ac00] Weighted P-Frames: Y:18.5% UV:13.1%
[libx264 @ 0x7fbceb86ac00] ref P L0: 67.8% 18.4% 10.5%  3.2%  0.2%
[libx264 @ 0x7fbceb86ac00] ref B L0: 92.7%  6.9%  0.3%
[libx264 @ 0x7fbceb86ac00] ref B L1: 98.0%  2.0%
[libx264 @ 0x7fbceb86ac00] kb/s:2005.33

これで Mac で見れる sorakara20151110.mp4 ファイルになりました*5

もうちょっと自動化(ダメっぽい

上記パラメータと動画タグのパラメータを比較してみましょう。

[1] pry(main)> require 'watir-webdriver'
=> true
[2] pry(main)> require 'open-uri'
=> true
[3] pry(main)> browser = Watir::Browser.new(:chrome)
=> #<Watir::Browser:0x..fd3003741a01aba16 url="data:," title="data:,">
[4] pry(main)> browser.goto 'http://video.tv-tokyo.co.jp/sorakara/'
=> "http://video.tv-tokyo.co.jp/sorakara/"
[5] pry(main)> url =browser.element(:xpath, '//*[@id="myExperience"]').attribute_value('data')
=> "http://c.brightcove.com/services/viewer/federated_f9?&width=960&height=540&flashID=myExperience&bgcolor=%23FFFFFF&playerID=4132654436001&playerKey=AQ~~%2CAAADnJnNGak~%2CDZjCima01WloIgH8areDN1xf8t0sk_UI&isVid=true&isUI=true&autoStart=true&dynamicStreaming=true&%40videoPlayer=4599166283001&includeAPI=true&templateLoadHandler=onTemplateLoad&templateReadyHandler=brightcove%5B%22templateReadyHandlermyExperience%22%5D&debuggerID=&originalTemplateReadyHandler=onTemplateReady&startTime=1447516662948"
[6] pry(main)> url[/videoPlayer=\d+/]
=> "videoPlayer=4599166283001"
[7] pry(main)> url[/playerID=\d+/]
=> "playerID=4132654436001"

videoIdplayerId に対応する部分は取得出来るけど、pubId 相当の部分は取得出来ないですな…。

参考リンク

*1:無い場合には HLS (HTTP Live Streaming) とかで通信して Flash はビュワーとしてしか使ってないと思われるので、Webサイトのソースやら通信ログからm3u8やらmp4の拡張子を探しましょう

*2:日本語版だと「追跡…」>「TCPストリーム」

*3:個人的趣味です

*4:なお、このコマンドのパラメータは必要十分条件ではなく、十分条件のみ満たしているのではないかと推測されます。以下のパラメータのうち「?」以降は無くてもダウンロードできました。

*5:「意外と知られていない善光寺のヒミツ…坊主の私服姿連発でくもじい激怒!?」の回でした。