2022年5月11日水曜日

ffmpeg : -c:v copy で 小さくなる?

motionで外部パイプの外出しffmpegで動画を作成させるのは色々と無駄な感じがするものの、motionのビルドや実行環境の整合性などを考えると安定感はあるのかなとちょいちょい使ってみてはいるのですが、ffmpegの癖なのかドライバの癖なのか意外と一筋縄ではいかなかったり。

そんな右往左往しているときに少しづつ調整しても圧縮されすぎて出来上がった動画がブロックノイズと言うよりもモザイク状態になってしまい仕方なしに固定ビットレートで結構大きめに指定することでようやくまともな動画に。

動作を見ていると出来上がったmp4ファイルは2M程度の大きさだったはずが、音声とマージして出来上がったのが800kとかやたらと小さくなっている現象を目の当たりにしました。

早速VLCのメディア情報の統計を見比べてみるとビットレート自体は変わってない感じただ、ブロック数とフレーム数の数が一致していなかった。ただ音声を結合させているのでこの辺の数字は何ともな感じ。なので、処理途中のファイルを抜き出して同様の処理を通してみることに。

pi@rasp3bprs:~ $ ffmpeg -i /home/motion/tmp/OrbitAF_2/20220511102817D-03.mp4 -c:v copy ./out.mp4

[mov,mp4,m4a,3gp,3g2,mj2 @ 0x1b3ecd0] st: 0 edit list: 1 Missing key frame while searching for timestamp: 0
[h264 @ 0x1b40120] no frame!
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/home/motion/tmp/OrbitAF_2/20220511102817D-03.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf58.45.100
  Duration: 00:00:32.40, start: 0.000000, bitrate: 633 kb/s
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 640x480, 165 kb/s, 5.03 fps, 5 tbr, 10240 tbn, 60 tbc (default)
    Metadata:
      handler_name    : VideoHandler
Output #0, mp4, to './out.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf58.45.100
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 640x480, q=2-31, 165 kb/s, 5.03 fps, 5 tbr, 10240 tbn, 10240 tbc (default)
    Metadata:
      handler_name    : VideoHandler
Stream mapping:
  Stream #0:0 -> #0:0 (copy)
Press [q] to stop, [?] for help
frame=  162 fps=0.0 q=-1.0 Lsize=     657kB time=00:00:32.20 bitrate= 167.2kbits/s speed=3.52e+03x
video:656kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.218411%


もとのファイル

2,565,577 バイト
325ブロック
463フレーム

出来上がったファイル

673,137 バイト
325ブロック
463フレーム

ブロック数とフレーム数は一致。ただ、出来上がったファイルサイズはかなり違う。

32秒の動画で、motionから作成しているときのffmpegのパラメータは-b:v 1000kとしている。
bpsなので1秒当たりのビット数。細かい数字は置いておいて(差が2割ぐらい出そうですが)、ざっくりした数字は 8で割った数が1秒間のバイト数 125k程度と言うことかな?

それの32倍 4000kバイト…なんか出来上がったファイルも想定より全然小さいのだが…。

fpsと総フレーム数が合ってなさそうなのでmotionのログを見てみると、

[1:ml1:D] [NTC] [ALL] [ 5月 11 10:28:21] motion_detected: モーションが検出されました. - イベント3を開始中
[1:ml1:D] [INF] [EVT] [ 5月 11 10:28:21] event_ffmpeg_newfile: ソースFPS 5
[1:ml1:D] [NTC] [ENC] [ 5月 11 10:28:21] ffmpeg_set_codec: fpsが低いため, 5フレームを10フレームコンテナに変換しています
[1:ml1:D] [INF] [ENC] [ 5月 11 10:28:21] ffmpeg_set_quality: libx264 コーデック vbr/crf /ビットレート: 30
[1:ml1:D] [NTC] [EVT] [ 5月 11 10:28:21] event_newfile: 動画ファイルを出力中です: /home/motion/tmp/OrbitAF_2/20220511102817D-03.mp4

コンテナ的には10フレームにしているように見えるが、libx264とか出てる時点で動作検知動画の方を指しているような気もしないでもなく。

フレーム数から考えても実際の動画の記録時間と一致しない。

思考停止…

動画の中身を見てみると、表示されている時間は 10:28:17で始まって10:29:00で終わっている。なので、時間軸は43秒ほど。よくよく見ていると間に何度か飛んでいる状態になっているので、再生時間と一致はしてないけど、処理としては全てのフレームは出力している形になっているのかな?なんか考えるだけおかしくなりそうな動画。

そもそもraspberry piで動いているmotionは無理してる感が強いので気にしてはいなかったのですが、なかなか難儀な現状なのだと実感。

まぁこの辺のことは一旦保留。

現状、omxとかv4l2m2mとかのハードウェアでエンコードしようとするとどうも思い通りにならず、予想よりはるかに劣化した動画になっているのが大問題。

パイプで外部プログラムでエンコードするとビルトインのエンコードと違って遅延もそれなりに発生しているのでイベント処理で結構面倒なことになってたり。

エンコードで固定ビットレートを理論値よりかなり大きくしないと思った通りの動画にならず、その上出来上がった動画をcopyしているだけのはずが、ファイルサイズが小さくなるという。

ま、あれか?ファイルサイズが小さくなるというのは単純に処理上領域確保してデータを書き込んで、閉じて、追記でまたファイルを開いて領域を確保してデータを書き込んで…を繰り返した結果、余計なパディングが発生しているってことでいいのかな? -c:v copyの場合はそのパディングが無くなっただけなのかな?

バイナリチェックしないと何とも言えんな…

なんかあまりバイナリ比較がまともに行えなかったものの、テキストとして強制的に比較した結果、所々の違いはあったものの、元のファイルは末尾にゴミが付いていただけの様子。

予想ではフレームとフレームやブロックとブロックの間にパディングされているものだと思ったのだが。

0 件のコメント:

コメントを投稿