お久しぶりです。streampackのfadoです。花粉の季節到来に最近猛威を振るっている新型コロナウィルスに加えて、外出する気力が奪われていきますよね。オリンピックの年ですし早く収束してくれることを祈ります!さて、今回はFFmpegというツールを使ってストリームを画像に変換する流れを紹介したいと思います。本記事で少しでもみなさんの参考になれたら嬉しいです!

注意事項

  • 検証はAWS環境で行ったものとなります。
  • 検証環境のOSはAmazon Linuxにしました。
  • 入力ファイルはrtspストリーミングと想定しています。
  • Wowza Media Systems社が無料で提供するrtspストリームのサンプルを使用しています。
    • rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov

作業の目的

  • rtspストリームをFFmpegを用いて画像(jpgファイル)に変換
  • 出力されたjpgファイルをほぼオンザフライでS3に同期させます

AWSリソース

  • EC2
    • インスタンスタイプ: t2.small
    • OS: Amazon Linux
    • HDD: 30GiB
  • S3

EC2にアタッチするIAMロールやセキュリティグループの設定等は省略させていただきます。

検証環境の準備

1.jpg画像用フォルダーを作成

$ mkdir -p ~/images/test1

2.FFmpegのインストールとセットアップ。

$ wget https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-amd64-static.tar.xz
$ tar xvfJ ffmpeg-git-amd64-static.tar.xz
$ sudo cp -av ffmpeg-git-20200211-amd64-static/ffmpeg /usr/local/bin/
$ sudo cp -av ffmpeg-git-20200211-amd64-static/ffprobe /usr/local/bin/
$ which {ffmpeg,ffprobe}
/usr/local/bin/ffmpeg
/usr/local/bin/ffprobe
$ ffmpeg -version
ffmpeg version N-51730-gf15007afa9-static https://johnvansickle.com/ffmpeg/  Copyright (c) 2000-2020 the FFmpeg developers
built with gcc 8 (Debian 8.3.0-6)
configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gmp --enable-libgme --enable-gray --enable-libaom --enable-libfribidi --enable-libass --enable-libvmaf --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libdav1d --enable-libxvid --enable-libzvbi --enable-libzimg
libavutil      56. 39.100 / 56. 39.100
libavcodec     58. 68.102 / 58. 68.102
libavformat    58. 38.100 / 58. 38.100
libavdevice    58.  9.103 / 58.  9.103
libavfilter     7. 75.100 /  7. 75.100
libswscale      5.  6.100 /  5.  6.100
libswresample   3.  6.100 /  3.  6.100
libpostproc    55.  6.100 / 55.  6.100

3.incronのインストール

  • incronはEPELレポジトリからインストールしますのでまずEPELリポジトリを有効にする必要があります。
$ sudo yum-config-manager --enable epel
$ sudo yum -y install incron
  • バージョンを確認
$ incrond -V
incrond 0.5.9
  • 自動起動を設定し、サービスを起動
$ sudo chkconfig incrond on
$ sudo /etc/init.d/incrond start

作業の手順

1.S3に同期させるためのスクリプトの作成

  • ソースのjpgファイルが削除されたらターゲットのjpgファイルも削除されるように --delete オプションを付けます。
  • BUCKET名は適切なものに書き換えてください。

aws-synctoS3.sh

#!/bin/bash
/usr/bin/aws s3 sync /home/ec2-user/images/test1 s3://BUCKET名/images/test1 --delete
  • 実行権限を付与
$ chmod +x ~/aws-synctoS3.sh

2.incrontabの設定

  • incrontab -eで設定
  • 設定後、incrontab -dでテーブルをreload
  • どんなタイプが設定可能かincrontab -tで確認可能
  • incronのログは /var/log/cronに出力されます
$ incrontab -l
/home/ec2-user/images/test1 IN_MODIFY,IN_CREATE,IN_DELETE,IN_ONESHOT,IN_NO_LOOP /home/ec2-user/aws-synctoS3.sh

IN_MODIFY ファイルが上書きされた場合でもトリガーされる
IN_ONESHOT イベントを一回のみ監視する
IN_NO_LOOP イベントが完了するまで監視をしない

  • 問題ないかログを確認しましょう。
$ sudo tail -f /var/log/cron
Feb 21 01:23:09 ip-10-16-10-61 incrond[2727]: ready to process filesystem events
Feb 21 01:49:01 ip-10-16-10-61 CROND[2776]: (root) CMD (/sbin/start --quiet update-motd)
Feb 21 01:53:52 ip-10-16-10-61 incrond[2727]: table for user ec2-user created, loading
Feb 21 01:55:31 ip-10-16-10-61 incrond[2727]: table for user ec2-user changed, reloading

3.バックグラウンドでFFmpegコマンドの実行

$ ffmpeg -rtsp_transport tcp -i rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov -f image2 -s 1920x1080 -r 15 /home/ec2-user/images/test1/image-%08d.jpg  < /dev/null > /dev/null 2>&1 &
  • パラメーターの説明
-rtsp_transport 
    rtspのトランスポートプロトコルを指定。udpでパケロスが生じた場合の回避策としてtcpに指定
-i
    入力ファイルもしくはストリームを指定
-f
    フォーマットを指定。FFmpegは賢いのでこのパラメーターは省略しても問題ない
-s
    解像度を指定 (wxh)
-r
    出力ファイルのフレームレートを指定
%08d
    出力のファイル名に8桁の数字で順番をつける

< /dev/null > /dev/null 2>&1 &
    FFmpegをバックグラウンドで起動させ、FFmpegの標準出力と標準出力エラーを/dev/nullにリダイレクトする
  • jobsコマンドでバックグラウンドで動いているプロセスを確認できます。
$ jobs
[1]+  実行中               ffmpeg -rtsp_transport tcp -i rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov -f image2 -s 1920x1080 -r 15 /home/ec2-user/images/test1/image-%08d.jpg < /dev/null > /dev/null 2>&1 &

4.指定したフォルダーにjpgファイルができたことを確認

$ ls ~/images/test1/image-0000* | head
/home/ec2-user/images/test1/image-00000001.jpg
/home/ec2-user/images/test1/image-00000002.jpg
/home/ec2-user/images/test1/image-00000003.jpg
/home/ec2-user/images/test1/image-00000004.jpg

5.incronがトリガーされたことを確認

$ sudo tail -f /var/log/cron
Feb 21 03:14:48 ip-10-16-10-61 incrond[2727]: (ec2-user) CMD (/home/ec2-user/aws-synctoS3.sh)
Feb 21 03:14:49 ip-10-16-10-61 incrond[2727]: (ec2-user) CMD (/home/ec2-user/aws-synctoS3.sh)
Feb 21 03:14:51 ip-10-16-10-61 incrond[2727]: (ec2-user) CMD (/home/ec2-user/aws-synctoS3.sh)

6.出力されたjpgファイルがS3に問題なく同期されたことを確認

結果

FFmpegを使ってrtspストリームを15fpsでjpg画像に変換することができました。出力されたjpgファイルをincronを用いてS3に同期させることもできましたので本記事の目的は達成できたかと思います。今回は15fpsを指定しましたので仕組み上、1秒に15回S3に同期されることになります。注意すべき点はFFmpegの処理よりもincronプロセスの方が高負荷であることです。30fps60fpsに変換したい場合や2つ以上のストリームの画像変換を試したい場合、jpgではなくpngで出力させたい場合などインスタンスタイプを変更しながら検証する必要が出てきます。

私が検証した環境ではt2.smallFFmpegincronと同時に走らせて15fpsならCPU使用率が約80%、ロードアベレージが1.5を超えた結果となりました。ギリギリですね。

感想

FFmpegはとても万能で動画や音声処理をするには欠かせないツールです。仕事でも動画変換をしたり動画配信したりでFFmpegを使う機会がたくさんあります。使いこなせていない機能もまだまだあります。FFmpegとはもっともっと仲良くなりたいなと考えている今日この頃の私です。実はAmazon Linux 2での検証もしてみたのですがAmazon Linux 2のEPELで採用されているincronのバージョンが新しくなり、IN_NO_LOOPがデフォルトとなったり、IN_ONESHOTは今回と違う動きを見せたりで少し苦戦していましたが再チャレンジし、いずれか報告できたらいいなと思っています。

参考文献

https://www.ffmpeg.org/
https://ffmpeg.org/ffmpeg-formats.html#image2-2
https://en.wikibooks.org/wiki/FFMPEG_An_Intermediate_Guide/image_sequence
https://linux.die.net/man/5/incrontab

元記事はこちら

FFmpegでストリームから画像に出力する