2019年5月23日より、従来の getplayerstatus からの rtmp は受信できなくなり、WebSocket 経由の rtmpならまだ受信できたが、2019年5月30日より公式の生放送以外 WebSocket 経由の rtmp は受信できなくなった。

ニコ生の録画と配信方法の歴史にあるように2013年11月14日から2019年5月23日までの仕様である、ユーザー生放送の生放送とタイムシフト、チャンネル生放送の生放送には-Nオプションが必要で、ユーザー生放送のタイムシフトの保存には放送時間分だけ時間がかかっている。この仕様に対応した-Nオプションではなく新しいオプションも備えて機能も拡大した rtmpdump のテストバージョンを公開している。テストバージョンは少し前から公開しているが、使い方を整理したり、新しいオプションのバグ取りになどに時間を取られ今日の記事公開となった。

主な特徴

  • 既存の-Nオプションにも対応
  • タイムシフトのオフセット対応。これによりユーザー生放送のタイムシフトが時間指定で保存開始時間を決められる。同時に分割保存も可能になり分割数に応じて保存も早く済むようになる
  • 分割保存したファイルの連結に ffmpeg を使うが rtmpdump で保存した場合、動画時間のメタデータが修正されないので、rtmpdump から標準出力して ffmpeg に渡すか、ffmpeg 単体で保存する
  • 時間ぴったりに分割保存すると前後の重複部分または欠損部分が多少発生し、この問題の解決方法はあるが手間がかかる(後述)
  • librtmp に対応しているので ffmpeg で直接読み込んで遅延のないミラー放送したり、ffplay でニコ生を見たりできる
  • 現時点では使いやすくするフロントエンド(kakorokuR のようなもの)やコメントビューワのプラグインはないので直接コマンドを叩く
  • 差分ファイルも添付されているのでクロスプラットフォームに対応。公式のソースコードはPublic Git Hosting – rtmpdump.git/logで、2015-12-15 compn update copyright year のソースコードを使う
  • 現在配布している rtmpdump でオフセットと-Bを併用するとエラーが出るのでパッチを適用する

配布コミュニティ

2018年12月12日の昼以降は getplayerstatus の一部が欠けているので getedgestatus で補う。
https://ow.live.nicovideo.jp/api/getedgestatus?v=lv[id]

getplayerstatus, getedgestatus を1つにする。

コマンド例

-Nオプションを使う従来の方法は使わない方法も含めてニコ生の配信データを保存する rtmpdump のコマンド内容のまとめを参照する。以下のコマンドは-Nを必要としていた放送の新しいオプションでのコマンド例である。

rtmpdump コマンド一覧と使い方
ニコ生の新配信と録画方法について

以前のようにタグのすべてが引数に割り当てらず “,” で区切って指定しているオプションがあるので、サンプルコマンド例を参考にする。

オフセットを使う場合や、出力時間を最後までではなく指定時間で終えるには rtmpdump では-B、ffmpeg では-tで時間指定する。

ユーザー生放送ライブ

旧配信が廃止して受信できなくなっている。
rtmpdump -vr "rtmp://nleu13.live.nicovideo.jp:1935/liveedge/live_161013_22_7/lv278843100" -C S:151356:lv278843100:0:1476365625:8b0a9a56ed1c6b5d -E "nlPlayNotice,S:rtmp://nlpoca123.live.nicovideo.jp:1935/publicorigin/161013_22_1/|S:lv278843100?1476365625:30:c60d7f424e749fe5|S:lv278843100" -o output.flv
rtmpdump -vr "rtmp->url/lv[id]" -C S:rtmp->ticket -E "nlPlayNotice,S:stream->contents_list->contents[0]|S:stream->contents_list->contents[1]" -o output.flv

ffmpeg -f live_flv -i "rtmp://nleu13.live.nicovideo.jp:1935/liveedge/live_161013_22_7/lv278843100 live=1 nofcsub=1 conn=S:151356:lv278843100:0:1476365625:8b0a9a56ed1c6b5d cmdinv=nlPlayNotice cmdinvamf=S:rtmp://nlpoca123.live.nicovideo.jp:1935/publicorigin/161013_22_1/ cmdinvamf=S:lv278843100?1476365625:30:c60d7f424e749fe5 cmdinvamf=S:lv278843100 cmdinvamf=N:-2" -sn -c copy output.flv
ffmpeg -f live_flv -i "rtmp->url/lv[id] live=1 nofcsub=1 conn=S:rtmp->ticket cmdinv=nlPlayNotice cmdinvamf=S:stream->contents_list->contents[0] cmdinvamf=S:stream->contents_list->contents[1] cmdinvamf=S:lv[id] cmdinvamf=N:-2" -sn -c copy output.flv

以下同様に ffmpeg を ffplay に変えることで再生できるffplay -i "rtmp://nleu13.live.nicovideo.jp:1935/liveedge/live_161013_22_7/lv278843100 live=1 nofcsub=1 conn=S:151356:lv278843100:0:1476365625:8b0a9a56ed1c6b5d cmdinv=nlPlayNotice cmdinvamf=S:rtmp://nlpoca123.live.nicovideo.jp:1935/publicorigin/161013_22_1/ cmdinvamf=S:lv278843100?1476365625:30:c60d7f424e749fe5 cmdinvamf=S:lv278843100 cmdinvamf=N:-2"

ユーザー生放送TS

[offset] がオフセット秒指定。3カ所、同じ時間を指定する。コマンド例は10秒から。最初からは 0 を指定する。rtmpdump だとデータを直ぐに受信しなくなっているので ffmpeg を使った方が受信しやすい。
rtmpdump -vr "rtmp://nleu22.live.nicovideo.jp:1935/liveedge/ts_161013_21_0/lv278802681.f4v_10" -C S:151356:lv278802681:0:1476361488:d1b030e28474edb6 -E "nlPlayNotice,S:rtmp://nlpoca132.live.nicovideo.jp:1935/fileorigin/ts_00|S:mp4:/content/20161013/lv278802681_133040579000_3_52e7d7.f4v?1476361488:30:483e59a2edb36984|S:lv278802681.f4v_10|N:10" -o output.flv
rtmpdump -vr "rtmp->url/lv[id].f4v_[offset]" -C S:rtmp->ticket -E "nlPlayNotice,S:stream->quesheet->que[0]|S:mp4:stream->quesheet->que[1]|S:lv[id].f4v_[offset]|N:[offset]" -o output.flv

ffmpeg -analyzeduration 30M -probesize 30M -f live_flv -i "rtmp://nleu22.live.nicovideo.jp:1935/liveedge/ts_161013_21_0/lv278802681_133040579000_3_52e7d7.f4v_10 live=1 nofcsub=1 conn=S:151356:lv278802681:0:1476361488:d1b030e28474edb6 cmdinv=nlPlayNotice cmdinvamf=S:rtmp://nlpoca132.live.nicovideo.jp:1935/fileorigin/ts_00 cmdinvamf=S:mp4:/content/20161013/lv278802681_133040579000_3_52e7d7.f4v?1476361488:30:483e59a2edb36984 cmdinvamf=S:lv278802681_133040579000_3_52e7d7.f4v_10 cmdinvamf=N:10" -sn -c copy output.flv
ffmpeg -analyzeduration 30M -probesize 30M -f live_flv -i "rtmp->url/stream->quesheet->que[1]_[offset] live=1 nofcsub=1 conn=S:rtmp->ticket cmdinv=nlPlayNotice cmdinvamf=S:stream->quesheet->que[0] cmdinvamf=S:mp4:stream->quesheet->que[1] cmdinvamf=S:stream->quesheet->que[1]_[offset] cmdinvamf=N:[offset]" -sn -c copy output.flv

チャンネル生放送ライブ

rtmpdump -vr rtmp://nlech02.live.nicovideo.jp:1935/onairliveedge/live_161013_21_2/lv278499331 -C S:151356:lv278499331:0:1476361786:cd197d8ed814643f -E "nlPlayNotice,S:rtmp://chnl02.ep.live.nicovideo.jp:1935/publicorigin/161013_08_3/|S:lv278499331?1476361786:30:c0771c76d1093d35|S:lv278499331|N:-2" -o output.flv
rtmpdump -vr "rtmp->url/lv[id]" -C S:rtmp->ticket -E "nlPlayNotice,S:stream->contents_list->contents[0]|S:stream->contents_list->contents[1]|S:lv[id]|N:-2" -o output.flv

ffmpeg -f live_flv -i "rtmp://nlech02.live.nicovideo.jp:1935/onairliveedge/live_161013_21_2/lv278499331 live=1 nofcsub=1 conn=S:151356:lv278499331:0:1476361786:cd197d8ed814643f cmdinv=nlPlayNotice cmdinvamf=S:rtmp://chnl02.ep.live.nicovideo.jp:1935/publicorigin/161013_08_3/ cmdinvamf=S:lv278499331?1476361786:30:c0771c76d1093d35 cmdinvamf=S:lv278499331 cmdinvamf=N:-2" -sn -c copy output.flv
ffmpeg -f live_flv -i "rtmp->url/lv[id] live=1 nofcsub=1 conn=S:rtmp->ticket cmdinv=nlPlayNotice cmdinvamf=S:stream->contents_list->contents[0] cmdinvamf=S:stream->contents_list->contents[1] cmdinvamf=S:lv[id] cmdinvamf=N:-2" -sn -c copy output.flv

分割ファイルの連結方法

ffmpeg にはファイル同士を連結するconcatがあるが、無劣化でコピーして連結するには demuxer のconcatを使う。

concat を使い分ける
【ffmpeg】動画・音声を連結する concat の使い方 其の3

fileプロトコルで入力順にファイル名を記述したテキストファイルを読み込ませる方法をとる。
ffmpeg -f concat -safe 0 -analyzeduration 30M -probesize 30M -i concat.txt -c copy output.flv

concat.txt。# 行は実行されないコメント扱いになる。

file D:/video/test1.flv
file D:/video/test2.flv
# ffmpeg -f concat -safe 0 -analyzeduration 30M -probesize 30M -i concat.txt -c copy output.flv
# test1.flv, test2.flv の順番に連結

連結部分の重複を回避する

重複部分を調べる

ffmpeg にはキーフレームや I, P, B などのフレームタイプを調べたり、フレームサイズを調べたり、フレームの同一性を調べるハッシュを調べたりできるのでこれを利用して重複フレームを調べる。他のアプリケーションでも利用できるフォーマットでログを出力するには ffmpeg の標準出力ではなく ffprobe で出力形式を指定する。今回は JSON を利用する。

ffprobe Documentationb : Main options

ffprobe -of json -show_packets -show_data_hash adler32 -analyzeduration 30M -probesize 30M -i input.flv -select_streams v:0 > show_data_hash.json
ログ形式

        {
            "codec_type": "video",
            "stream_index": 0,
            "pts": 0,
            "pts_time": "0.000000",
            "dts": 0,
            "dts_time": "0.000000",
            "size": "11127",
            "pos": "574",
            "flags": "K_",
            "data_hash": "adler32:6696b483"
        },

フレーム毎に各配列にデータが収められているのでこのデータを利用する。ここで重要なのが pts, flags, data_hash で pts でカットする時間を決め、flags でキーフレームかどうかを調べ、data_hash でフレームの同一性を調べる。スクリプトを書いて自動で処理できるようになればこれで楽に処理できるが、目視ではフレーム数が多くわかりにくいので CSV で出力すると一覧性が増す。

目視なら ffprobe よりも見やすい
ffmpeg -analyzeduration 30M -probesize 30M -i input.flv -c copy -an -f framehash -hash adler32 input.flv.csv

ではどのキーフレームを基準にカットする時間を決めるかだが、現在の仕様ではオフセット使用時での保存開始数秒は dts が正しく振られないこと(再生すると早送りになる)があるのでその数秒をカットしてその前の動画に含める必要がある。このカットする時間はコーデックや映像の GOP 間隔に依存するので静止画配信の場合では多く見積もって 20秒あればほぼ確実である。まとめると30分の配信(実際にはユーザー生放送で外部ツールを使うと29分で一度途切れる)を3分割して保存するには

  1. 0秒から620秒まで
  2. 600秒から1220秒まで
  3. 1200秒から最後まで

の3つのコマンドを実行して保存することになる。多少の重複時間は気にしない、見たら消すような放送ならこのような手間は自動化できていない現時点では不要である。

時間指定で連結する

FFmpeg Formats Documentation : concatを参照すると、オフセットや時間指定もできるのでこれを指定することで動画をカットしながら連結することができる。最初から最後まで連結するだけならfileにファイル名とパスを指定するだけで良い。開始時点を決めるinpointや、終了時間を決めるoutpointを使う場合は、動画時間のdurationを併用する。

キーフレームをinpointoutpointで指定すると過不足なく任意の区間をコピーできる。キーフレームの場所はffprobeを使って調べる。
ffprobe -v error -i input -select_streams v:0 -skip_frame nokey -show_entries frame=pkt_pts_time -of csv=print_section=0

ffprobe の使い方

# cocnat.txt
file 000.flv
duration 34.95
outpoint 34.95
file 001.flv
inpoint 4.348
duration 29.95 # 34.298-4.348
outpoint 34.298
file 002.flv
inpoint 5

重複無く漏れなく連結するには余分を持ってタイムシフトを保存して上の方法で調べたハッシュとキーフレームのPTSを参考にする。

  1. 1入力のファイルの最後のキーフレームと、2入力のファイルの2番目(以降)の同じキーフレームをハッシュを元に探す
  2. 1入力のファイルのキーフレームのPTSをdurationoutpointに指定するが、このままだと連結するとキーフレームが重複するので1フレーム時間減らす
  3. 2入力のファイルはinpointを2番目(以降)のキーフレームのPTSを指定
  4. 先ほどと同様に最後のキーフレームの手前までのPTSと 最初のPTSを引いたのをdurationで指定
  5. 以下同様

まとめると1入力のファイルの最後のキーフレームを調べて、2入力のファイルに同じキーフレームがあるのを確認。確認できたら1入力のファイルの最後のキーフレーム前までのPTSを durationoutpointで指定し、2入力のファイルはそのキーフレームのPTSから読み込めるようにinpointを指定し、以下連結数に応じて複数回繰り返す。

8 thoughts on “N を使わない新しいニコ生用の rtmpdump と ffmpeg(librtmp)

  • アオ

    すいません、教えてください。
    rtmpdumpやffmpegでユーザーチャンネルのTSを録画したいのですが、
    “rtmp://*******” から始まる stream->quesheet->que[0] が存在しないので録画できません。

    “/content/*****” から始まる stream->quesheet->que[1] はあるのですが…

    rtmpdumpやffmpegでユーザーチャンネルのTSのオフセット録画は対応していないのでしょうか?
    よろしくお願いします。

    • admin

      ユーザーチャンネルの TS は普通の rtmpdump で保存できるのでこのコマンドは必要ありません
      http://nico-lab.net/nicolive_rtmpdump_commands/#i-6 を参照
      オフセットを使う場合は rtmpdump で行うとタイムスタンプがおかしくなるので
      ffmpeg で保存の時にするか、すべてを保存してから動画編集ツールなどでカットします
      ffmpeg でオフセットを使う場合。以下のコマンドでも分からなければ再度質問してください
      ffmpeg -ss 開始時間 -i 入力ファイルパス -t 出力時間 -c copy 出力ファイルパス

  • 質問です。この新しいバージョンはkakorokuに入っているrtmpdumpと差し替えても大丈夫でしょうか? またzipファイルに同梱されていましたrtmpdumpwは、何に使うものでしょうか。

    • admin

      差し替えても使えるはずです。
      rtmpgw はストリームをリレーして他のツールで再生できるようにするツールです
      https://srad.jp/~witch/journal/503587/ を参照

      rtmpgw -r rtmp://radiko.smartstream.ne.jp:1935/ -y simul-stream -D 127.0.0.1 -g 10080 -v
      mplayer “http://127.0.0.1:10080/?a=TBS%2f_defInst_”

  • びん

    質問があります。
    最新バージョン(20161201版)でもパッチを適用する必要があるのでしょうか?
    この記事よりファイルの方が新しいので既に適用済みなのかも、とちょっと気になりました。

  • pinpon

    ご享受ください。
    RTMPDUMPにて「-E」オプションがないようですが、これはないものが正しいのでしょうか?
    それとも「-e」で行うのが正しいのでしょうか?

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)