Raspberry Pi 3とFFmpegでUSBカメラの映像と音声をストリーミングする

Wireless streaming of USB Camera on Raspberry Pi 3, Battery driven.

Raspberry Pi 3につないだUSBカメラの映像と音声をffmpegを使ってストリーミングしてみます。
映像エンコードはハードウエア支援のh264_omxを使います。
マルチメディアストリーミングの基本となるプロトコル udp, rtpとsapでやってみました。





前回、Raspberry Pi 3のハードウエアエンコーダh264_omxが使えるFFmpegをビルドしました。
Rapberry Pi 3 のハードウエアエンコーダh264_omxが使えるFFmpegをビルドする
そしてffmpegでUSBカメラの映像と音声を録音できることも確かめました。
Raspberry Pi 3につないだUSBカメラの映像と音声をFFmpegで録画する
今回はUSBカメラの映像と音声をffmpegでストリーミングしてみます。

ストリーミングといってもいろいろな方法があります。
USBカメラの映像をLAN内で見るだけならmjpg-streamerが良いと思います。USBカメラの映像をそのままネットに流す感じなので処理も軽く映像の遅れも少ないです。ですが音声を扱う事ができません。
映像と音声を一緒にとするとgstreamerも試してみたいです。WindowsのDirectShowのグラフのようにデータの流れをテキストで記述するので思った通りの処理をさせるには良いでしょう。ただし、知識がないと手も足も出ません。

私が今更Linux系のツールを使ったところで大した情報もありません。
そこで、ffmpegでの処理にこだわってみます。ffmpegの情報は多いように見えますが使える情報は意外と少ない感じです。
ストリーミングのプロトコルもいろいろあるようですがまずは基本的なプロトコルから試してみます。UDPとRTPを試します。LAN内でUSBカメラの映像をストリーミングすることを想定します。

準備

今回の環境を説明しておきます。今回はLAN内でのストリーミングです。ルータを超える事は考えません。
USBカメラはRaspberry Pi 3(以後RPi3)に接続されます。RPi3のOSはRASPBIAN JESSIEです。
FFmpegをビルドして使います。今回のバージョンは次の通り。
ffmpeg version N-81872-gbe1d324 Copyright (c) 2000-2016 the FFmpeg developers
built with gcc 4.9.2 (Raspbian 4.9.2-10)
configuration: --pkg-config-flags=--static --disable-debug --disable-shared --enable-static --enable-gpl --enable-version3 --enable-nonfree --enable-mmal --enable-omx-rpi --enable-omx --extra-cflags=-I/home/pi/FFmpeg-build/ffmpeg/include --extra-ldflags=-L/home/pi/FFmpeg-build/ffmpeg/lib --extra-libs=-ldl
libavutil      55. 32.100 / 55. 32.100
libavcodec     57. 60.101 / 57. 60.101
libavformat    57. 51.102 / 57. 51.102
libavdevice    57.  0.102 / 57.  0.102
libavfilter     6. 63.100 /  6. 63.100
libswscale      4.  1.100 /  4.  1.100
libswresample   2.  2.100 /  2.  2.100
libpostproc    54.  0.100 / 54.  0.100

映像を受ける方はWindowsパソコンで説明します。受信するプログラムはVLCかffplayで試します。他のOSでも同様に動かせるでしょう。このパソコンのIPアドレスを"192.168.11.10"とします。自分のアドレスに読み替えてください。

今回使ったUSBカメラはこちら
販売終了 新製品

iBuffaleの広角レンズが特徴のUSBカメラです。

ffmpegでストリーミング

ffmpegだけでストリーミングしてみます。基本的なプロトコルを使ってみましょう。

UDPでユニキャスト

udpではデータを垂れ流す感じで使えます。受信側が解釈できれば何でも流せますから単純で簡単です。

最初はユニキャストでやってみます。ユニキャストとは1対1の通信です。Raspberry Pi 3のカメラ映像を特定のIPアドレスに送ります。そのIPアドレスの端末でしか映像は見れません。

まずは映像を見るパソコン側の準備です。vlcを使います。
vlcのメニューから
メディア->ネットワークストリームを開く...
をクリックします。ネットワークURLに次のように入れます。
udp://@:1234
再生をクリックします。
vlcに入れるネットワークURLはパソコンのIPアドレスの1234ポートを表します。次のように入れても同じです。アドレスは読み替えてください。
udp://@192.168.11.10:1234

これでパソコン側は受信準備ができました。

RPi3で次のコマンドを実行します。
ffmpeg  -f alsa -thread_queue_size 8192 -i hw:1 \
  -f v4l2  -input_format yuyv422  -video_size 640x480 -i /dev/video0 \
  -c:v h264_omx -b:v 512k \
  -c:a aac -b:a 64k  -async 100 \
  -f mpegts udp://192.168.11.10:1234
udpのアドレスは映像を見るパソコンのIPアドレスです。読み替えてください。

これでパソコンのvlcで映像が見えれば成功です。RPi3が送る最初のキーフレームをうまく受信できないと映像が表示されないかもしれません。vlcとffmpegを止め、もう一度試してみましょう。
今回はポート番号は1234としました。

上のffmpegコマンドでは最初にalsa音声を、次にv4l2映像を読み込んでいます。この順番を逆にすると映像と音声の同期がとれず1秒ほどズレる事があります。タイムスタンプ絡みの問題と思うのですがすっきり解決できませんでした。

UDPでマルチキャスト

ユニキャストでは映像を見れるパソコンが固定されてしまいます。マルチキャストをしてLAN内の端末どこからでも見れるようにしてみます。これはIPマルチキャストグループアドレスへ配信すればできます。ユニキャストでのパソコンのIPアドレスの代わりにマルチキャスト用のIPアドレスを指定すれば良いだけです。今回は"224.1.1.1"としてみました。

まずは映像を見る側のパソコンの設定です。vlcで再生します。
ネットワークURLに次のように入れます。
udp://@224.1.1.1:1234
再生をクリックします。

RPi3で次のコマンドを実行します。
ffmpeg -f alsa -thread_queue_size 8192 -i hw:1 \
  -f v4l2  -input_format yuyv422 -video_size 640x480 -i /dev/video0 \
  -c:v h264_omx -b:v 512k \
  -c:a aac -b:a 64k -async 100 \
  -f mpegts udp://224.1.1.1:1234
アドレスがマルチキャスト用に変わっただけです。

これでLAN内のどのパソコンでもカメラ映像の再生ができるようになりました。

RTPでマルチキャスト

ちょっと上位のプロトコルのrtpでのストリーミングも試してみます。udpはなんでも流せちゃう素のデータ転送プロトコルです。rtpもudpでデータを送りますが、映像と音声をリアルタイムで転送するために必要な情報を少し追加してメディア用ストリームに仕立て上げてくれます。

rtpでは映像と音声を一緒に送る事はできません。一つのストリームは映像のみか音声のみになります。えっ、不便じゃん?
ですから複数のストリームを使います。そのため少し面倒なことがあります。どのストリームに映像があるか、音声のストリームはどれか、というのを受信側に教える必要があります。このひと手間が必要になります。

RTPで映像と音声を送って再生できる事はわかるのですが、具体的にffmpegでストリームを流す方法がさっぱりわかりませんでした。ググっても音声のみの例ばかりです。動かないけどどうして?というスレッドも良く見かけます。さて・・・どうしたら良いのでしょうか?

途方に暮れあきらめかけていましたが有力な情報が書いてあるブログにたどり着きました。
RTP streaming with ffmpegLUCA'S CHRONICLES
lucabeさん、ありがとう。
ffmpegのオプション指定の意味を再認識できました。ffmpegのオプションはすごいなぁ。オプション指定の順番が重要です。それがわかれば後は簡単です。
newaudioはffmpegの古いバージョンのオプションです。今は使いません。mapで指定できる事です。ffmpegが勝手に解釈してくれるのでmapの出番もあまりないです。

まずは送信側のRPi3での設定です。ffmpegのコマンドが少しややこしくなります。
ffmpeg -f alsa -thread_queue_size 4096 -i hw:1 \
  -f v4l2 -thread_queue_size 4096 -input_format yuyv422 -video_size 640x480 -i /dev/video0 \
  -c:v h264_omx -b:v 512k -an -f rtp  rtp://224.1.1.1:5004 \
  -c:a aac -b:a 64k -vn -f rtp rtp://224.1.1.1:5006 \
  -sdp_file stream.sdp

入力の設定まではudpと同じです。マルチキャストアドレスも同じです。
出力側に少し細工します。
映像のみのストリームをポート5004番で出力します。
音声のみのストリームをポート5006番で出力します。
最後の"-sdp_file stream.sdp"というのが、映像と音声の組み合わせを表すファイルを作る命令です。stream.sdpというファイルが作られます。

一度RPi3で上のffmpegコマンドを実行してstream.sdpファイルを作ってください。このファイルは再生側で使います。ファイルができたらffmpegは終了させます。

今回、再生側のパソコンではffplayを使います。ffplayはFFmpegプログラムの一つです。stream.sdpファイルをRPi3から取り出してパソコンのffplayのあるフォルダへ置いてください。
そして次のコマンドでffplayを実行します。
ffplay -protocol_whitelist "file,udp,rtp" stream.sdp
パソコン側はストリームが来るのを待ちます。
この後にRPi3上で先ほどのffmpegコマンドを実行してください。
【2019/2/27追記 この時、最後の"-sdp_file stream.sdp"はいりません。】
しばらくするとパソコン側で映像と音声が再生されるでしょう。

ffplayのオプション"-protocol_whitelist"が無いと
[rtp @ 05852620] Protocol not on whitelist 'file,crypto'!0B f=0/0
stream.sdp: Invalid data found when processing input
こんなエラーが出てしまいます。

ストリームを送ってからffpalyを動かすと次のエラーが出ました。
[h264 @ 0578bb20] decode_slice_header error
[h264 @ 0578bb20] no frame!
[h264 @ 0578bb20] non-existing PPS 0 referenced
RPi3のffmpegを終了させ、再生側のffplayを先に実行してからRPi3でffmpegを実行してみてください。
キーフレームが見つからないとこんなエラーが出るようです。でもGOP長指定してもダメでした。ffplayを先に動かしていないと再生できません。

ちなみにstream.sdpファイルはvlcでも使えるはずです。vlcでffmpegが作ったsdpファイルを使う時は、sdpファイルをテキストエディタで開いて先頭行の"SDP:"を削除してください。
ですが私が試した限りでは映像がうまく表示されませんでした。パソコン起動後、最初の1回は映るのですがその後ダメになります。音声しか聞こえないんですよねぇ。なんでだろう。

SAPでマルチキャスト

rtpを使うと一つモヤモヤとした疑問がわきませんでしたか?
sdpファイルをなんで手動でコピーしたんだろう・・・ネットで送れよ!

これをやってくれるのがSAP:Session Announcement Protocol (RFC 2974)です。
また、rtpでは受信側が先に待機しないとうまく再生できませんでした。sapを使うと受信側を後から動かして好きな時に受信できます。こうでなくては使い物になりません。

sapを使うにはまた一つややこしくなります。映像と音声を送るのはあくまでrtpです。sapはsdpの内容をアナウンスするプロトコルです。sdp用のストリームを一つ作ります。

ffmpegではどうやって使えばいいのでしょうか。1週間くらい悩んでました。
ややこしいのはそれぞれのプロトコルのアドレスとポートがごちゃ混ぜになってしまうからです。ffmpegで使うにはsapのストリームは意識しなくていいという事に気づくまで動かせませんでした。
そしてrtpでは自分で音声用、映像用とストリームを作っていましたが、sapを使うとffmpegがrtpを自動で作ってくれます。。自分で作る必要はなくとても簡単になります。理解できれば・・・ですが。

それではやってみましょう。
sdpの情報を送るブロードキャストアドレスとポート番号はデフォルトを使います。デフォルトのブロードキャストアドレスは"224.2.127.254 (sap.mcast.net)"の"9875"です。送信側は意識しなくていいです。受信側で使うので憶えておきます。

RPi3側で次のコマンドを実行すればストリーミングが開始されます。
ffmpeg -f alsa -thread_queue_size 4096 -i hw:1 \
  -f v4l2 -thread_queue_size 4096 -input_format yuyv422 -video_size 640x480 -i /dev/video0 \
  -c:v h264_omx -b:v 512k -c:a aac -b:a 64k \
  -f sap sap://224.1.1.1:5004
rtpの時のように音声と映像のストリームを別々に作る必要はありません。rtpのストリームは勝手に作られます。これを理解するのに丸一日かかりました。
さらに勘違いしたのが"sap://"で指定するアドレスとポート番号です。このアドレスとポート番号はrtpストリームのものです。ですからrtpの時と同じになります。sdpのではありません。
複数のrtpストリームが必要な場合は+2されたポート番号に作られます。今回は音声と映像なので2つのストリームが作られるはずです。

それでは受信側です。
受信側はsdpのアナウンスストリームを得さえすれば良いのです。ですからrtpのアドレスとポート番号は必要ありません。sdpのアナウンス情報から自動的につないでくれます。これも間違えやすいポイントですね。
sap自体のアドレスとポート番号はデフォルト値を使っていますので、
ffplayで再生する場合は
ffplay sap://
これだけです。あるいは明示的に
ffplay sap://224.2.127.254:9875
と入れましょう。white listは必要ないんですね。

vlcで再生する場合は・・・うまくできてません。
RPi3側のsapの設定を少し変えます。
"-f sap sap://224.1.1.1:5004?same_port=1"
としてください。複数ストリームのポート番号は+1した値になるようです。VLC/Live555とかいう方式に合わせます。
パソコンでvlcを動かします。表示->プレイリストかCtlr+Lでプレイリストを表示します。
右側のメディア一覧から"ネットワークストリーム(SAP)"をクリックします。
しばらくするとタイトルが表示されます。"No Name"だと思います。名前を付けたければffmpegのmetadataでtitleを追加すればできます。
選択して再生します。
これでいいはず・・・と思ったのですが全く再生しません。

ffplayを使う方が簡単でいいですかね。ただし、ffplayで画質評価はしないでね、とどこかに書いてあったと記憶してます。デコード処理をリアルタイム最優先で行ってるのでしょうか。

まとめ

これでUSBカメラの映像と音声をストリーミングする事ができました。
Raspberry Pi 3のハードウエアエンコーダh264_omxを使って映像をエンコードしています。
モバイルバッテリーでも動かせばワイヤレスでも動きます。

家の中の赤ん坊モニタなどには十分でしょう。留守中のペットのモニタとなるともう少し工夫が必要です。
より本格的な監視カメラなら専用のプログラムもありますのでそちらを使った方が良いでしょうね。
今回はffmpegを無理やり使う事が目的みたいなものです。

rtpの方がudpより映像フレームレートが高くなります。なんで?どうもv4l2の動作がわからない。
RPi3だとmux処理が遅いのかな?

sapを使うと面倒なrtpのストリームを意識しないで済みます。sapとrtpの関係が理解できるとストリーミングの仕組みがより理解できると思います。足りない情報を他のプロトコルで追加していると判れば他のプロトコルも理解しやすいでしょう。より複雑なプロトコルはffserverを使うことになるのかな。
sapのデフォルトのブロードキャストアドレスやポート番号は都合が悪いという場合はもちろん変更できます。FFmpegのドキュメント(sap)を見てください。



コメント

  1. コメント失礼します.
    ffmpeg関連の投稿,とても参考になっておりありがたい限りです.
    一点,わからないところがあり答えていただけるとありがたいです.
    この記事のsapでマルチキャストでのRasPi3側のコマンド
    ”ffmpeg -f alsa -thread_queue_size 4096 -i hw:1 \
    -f v4l2 -thread_queue_size 4096 -input_format yuyv422 -video_size 640x480 -i /dev/video0 \
    -c:v h264_omx -b:v 512k -c:a aac -b:a 64k \
    -f sap sap://224.1.1.1:5004”
    なのですがオプションについて説明いただけるとありがたいです.

    私はRasPi3-RasPi3間ではこのコマンドでの実行が確認できたのですが
    Windos-RasPi3間ではストリーミング情報を受け取ることができませんでした.
    そこでコーデックが違う可能性を考えているのですがいかんせんオプションに詳しくないためどこを変更すればよいかがわからなくて困っております.
    ご返信いただけるとありがたいです.

    返信削除
    返信
    1. コメントありがとうございます。反応があるとうれしいです。
      少し時間が経って忘れている所もありますが、thread queue size 以外は普通のエンコード命令です。thread queue sizeを指定しないとエラーで動かなかったような。
      コーデックは -c:vで指定します。h264_omxはラズパイのハードウエアエンコーダを使う指定なのでWindowsでは動きません。h264にするなどお使いのffmpegに合わせて指定します。
      後はファイヤーウォールで遮断されてる可能性がありますね。

      削除
    2. 返信ありがとうございます。
      「-vcodec」だと思っていたのですがコメントを拝見後調べると「-c:v」でも可能だったんですね,理解することができました.
      私もできない原因をコーデックだと思いh264でも試してみたのですができませんでした.(オプションの-formatsでh264が対応していることも確認しました)
      ファイアウォールの可能性もあるんですね,そちらもあまり詳しくはないのですが頑張ってみようと思います.ありがとうございます.

      コメントについてまた一つ質問があるのですが本記事の「sapでマルチキャスト」はRasPi-Windows間ではないのでしょうか?

      削除
    3. ffmpegのオプション指定はできるだけ最新の表現に合わせています。

      RasPiのffmpegから送りWindowsのffplayで再生しました。

      削除
  2. コメントのおかげで理解することができ,ストリーミング再生もRasPi-Windows間で行うことができました.
    ありがとうございます.

    返信削除
    返信
    1. 問題を解決できると楽しいですよね🎵

      削除
    2. このコメントは投稿者によって削除されました。

      削除
  3. はじめまして、tkと言います。
    だいぶ前の記事ですが、教えていただけると助かります。
    やりたいことは、ラズパイからスマホに映像音声をストリーミング配信することです。



     本体:ラズパイ3b+
     OS:raspbian
     カメラ:logicool c270
     ffmpeg:n4.1
    alsa:1.1.7
     受信用端末:andoroidスマホ

    ①udpユニキャスト
     ・以下のエラーが何度か出ます。解決方法をご存じでしょうか。
      似たような質問が他でも出ていますが、解決方法は記載されていません。
     エラー↓
    Non-monotonous DTS in output stream 0:1; previous: 38400, current: 1350; changing to 38401. This may result in incorrect timestamps
    in the output file.
     ・音ずれがやはり1~2秒ありました。
      改善策はあるでしょうか。

    ②UDPマルチキャスト
     マルチキャストだと、ほぼ常時ブロックノイズが沢山出てしまいました。
     レートを512kから100k程度にすると、少し安定しますが、
     レートを下げずに安定させる方法(オプションなど)があるでしょうか。
     
    ③rtpマルチキャスト
     ffplay.exeのディレクトリに置く「sdpファイル」の参考リンクがありましたが、
     この場合、具体的なファイルの中身を教えていただけないでしょうか。

    ④sapを使用した場合のvlcプレイヤー動作
     再生されない、とのことでしたが、
     その後も再生できないでしょうか。
     解決方法が見つかったら教えてもらえると大変助かります。
     

    返信削除
    返信
    1. こんにちは!
      ① よくあるメッセージですよね。原因は特定し難いです。
      -fflags +igndts タイムスタンプを付け直す。-iの前に。genptsも考慮。
      ffmpegのバージョンを変えてみるなども。
      ② ネットワーク環境によるものかもしれません。
      ③ ffmpegで生成してみましょう。
      ④ 試していません。
      ここで紹介したプロトコルは基本的なものばかりです。勉強に使うくらいで実用性は低いです。最近はもっと高機能なプロトコルがあります。
      こちらも参考にしてください。
      https://signal-flag-z.blogspot.com/2018/03/ffmpeg-hls-streaming.html

      削除
    2. 返信ありがとうございます!

      ①色々とあるのに解決が難しいようで・・・。ffmpegのバージョン変更も試してみます。
      ②ローカルで行っているので、あと怪しいとすれば無線lan親機くらいかと思います。変えて試してみます。

      hlsでのストリーミングの紹介、ありがとうございます。
      こちらもさっそく試してみます!

      削除
  4. 大変・大変参考になりました。ありがとうございます!

    返信削除
    返信
    1. コメントありがとうございます!

      削除

コメントを投稿

最近のコメント

Threaded Recent Comments will be here.