前回Ubuntu22.04にSRTが使えるGStreamerをセットアップしました。今回はPythonでUSBカメラの映像送信を行うサービスを作ります。まず以下のpythonをセットアップするのですが、ubutnu22.04にはほとんどインストールされていると思います。

PyGObjectを利用することで、PythonからC言語で書かれたライブラリ(GObjectベースのライブラリ)を直接使うためのバインディングを提供します。これにより、Pythonプログラム内でGStreamerのようなマルチメディアフレームワークを操作することが可能になります。とても便利な仕組みです。

sudo apt-get update
sudo apt install python3
sudo apt-get install python3-gi python3-gi-cairo gir1.2-gtk-3.0

python3-giは PyGObject のPython 3向けのパッケージで、PythonプログラムからGObjectベースのライブラリを利用するためのインターフェースを提供します。

python3-gi-cairoは、PyGObjectを使用しているPythonアプリケーション内で、Cairo グラフィックスライブラリを利用するためのバインディングを提供します。Cairoはベクターベースのグラフィックスを扱うための強力な2Dグラフィックスライブラリです。

gir1.2-gtk-3.0はGTK+ 3.0のGObject Introspection Repository(GIR)ファイルです。GObject Introspectionは、言語バインディングを生成するためのライブラリのAPIを記述するメタデータを提供します。このファイルをインストールすることで、そのライブラリの関数やクラスをPyGObjectを通じてPythonから直接アクセスできるようになります。

今回はPythonでGStreamerを使うことが目的なので、以下のような機能を使います。

import giは、Pythonで特定のプログラム(ここではGTKやGStreamerなど)を使えるようにするためのものです。gi.require_version(‘Gst’, ‘1.0’)は、GStreamer(ビデオやオーディオを扱うためのプログラム)のどのバージョンを使うかを明確に指定しています。from gi.repository import Gst, GLibは、実際にビデオやオーディオの処理をするGStreamer(Gst)と、プログラムの基本的な動作を管理するGLib(GLib)を使えるようになります。

今回USBカメラの映像を取得してSRTストリームに流すプログラムを作成します。自分の環境としてsrt://192.168.10.17:10000?mode=callerとしてますが、受信側のIPとポートは適宜作成してください。GStreamerのパイプラインも利用環境によって適宜設定が必要でしょう。

sudo nano cam_stream.py
import gi
import sys
import signal

gi.require_version('Gst', '1.0')
gi.require_version('GLib', '2.0')
from gi.repository import Gst, GLib

def handle_signal(sig, frame, pipeline):
"""
シグナルハンドラ:プログラムの終了処理を行う
"""
print(f"Received signal: {sig}. Exiting...")
pipeline.set_state(Gst.State.NULL)
sys.exit(0)

def on_bus_message(bus, message, pipeline):
"""
メッセージハンドラ:GStreamerバスからのメッセージを処理する
"""
msg_type = message.type
if msg_type == Gst.MessageType.ERROR:
err, debug_info = message.parse_error()
print(f"Error: {err}, {debug_info}")
pipeline.set_state(Gst.State.NULL)
sys.exit(1) # エラー発生時には終了コード1で終了

def main():
# GStreamerの初期化
Gst.init(None)

# ストリーミングパイプラインの定義
streaming_pipeline_description = """
v4l2src device=/dev/video0 ! image/jpeg,width=1920,height=1080,framerate=30/1 !
jpegdec ! videoconvert ! videoflip method=rotate-180 !
clockoverlay halignment=left valignment=top font-desc="Sans, 12" time-format="%Y-%m-%d %H:%M:%S" !
x264enc bitrate=10000 speed-preset=ultrafast tune=zerolatency ! mpegtsmux !
srtsink uri="srt://192.168.10.17:10000?mode=caller"
"""

pipeline = Gst.parse_launch(streaming_pipeline_description)
pipeline.set_state(Gst.State.PLAYING)

main_loop = GLib.MainLoop()
signal.signal(signal.SIGINT, lambda sig, frame: handle_signal(sig, frame, pipeline))
signal.signal(signal.SIGTERM, lambda sig, frame: handle_signal(sig, frame, pipeline))

bus = pipeline.get_bus()
bus.add_signal_watch()
bus.connect("message", lambda bus, message: on_bus_message(bus, message, pipeline))

main_loop.run()
pipeline.set_state(Gst.State.NULL)

if __name__ == "__main__":
main()

streaming_pipeline_descriptionは、GStreamerのコマンドほぼそのままです。ビデオキャプチャデバイスからビデオを取得し、さまざまな変換を施した後、インターネットを介して特定のアドレスに送信する一連の指示です。ビデオは180度回転され、時刻がオーバーレイされ、エンコードされてから送信されます。

pipeline = Gst.parse_launch(streaming_pipeline_description)でパイプラインを作成し、pipeline.set_state(Gst.State.PLAYING)でビデオのストリーミングを開始します。

handle_signal関数であるシグナルハンドラを設定することで、プログラムを停止することができます。またプログラムが終了する際に適切にリソースを解放するために設定されています。on_bus_message関数は、ビデオストリーミング中に発生するエラーを検出して適切に処理するために使われます。エラーが検出された場合、適切なメッセージを表示し、プログラムを終了します。

main_loop = GLib.MainLoop()でメインループを作成し、main_loop.run()でループを開始します。これにより、プログラムはイベントを継続的に監視し続けます。

エラー発生時には終了コード1で終了することで、、systemdでサービスとして管理する際に非常に役立ちます。これは、プログラムの終了が正常だったのか、あるいは何か問題があって終了したのかをsystemdが識別できるようにするためです。実行権限を付けます。

sudo chmod +x cam_stream.py

以下サービスを作ります。/yoururl/にはcam_stream.py/を保存した場所を入れます。カメラのストリームは継続的に動いてほしいのでRestart=alwaysに設定します。

nano /etc/systemd/system/cam-stream.service
[Unit]
Description=Start Camera Stream
Wants=network-online.target
After=network-online.target

[Service]
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
Type=simple
ExecStart=/usr/bin/python3 /yoururl/cam_stream.py
Restart=always
RestartSec=5s

[Install]
WantedBy=multi-user.target
sudo chmod +x /etc/systemd/system/cam-stream.service

systemdを使用すると、Pythonスクリプト(.pyファイル)などををUbuntu 22.04のシステムサービスとして設定し、管理することができます。便利ですよね。systemdは、Linuxシステムの起動時に、必要なサービスやプロセスを適切な順序で開始します。これにより、システムが速やかに起動し、安定して動作するようになります。cam-stream.serviceを例にsystemdのコマンドをまとめるとこんな感じ。

sudo systemctl daemon-reload
systemdの設定を更新するための重要なコマンドであり、サービスファイルを追加、変更、または削除するたびに実行する必要があります。

sudo systemctl enable cam-stream.service
サービスを有効化します。システム起動時にサービスが自動的に開始されるようになります。

sudo systemctl disable cam-stream.service

サービスを無効化します。システム起動時にサービスが自動的に開始されなくなります。

sudo systemctl start cam-stream.service

サービスを開始します。

sudo systemctl stop cam-stream.service

サービスを停止します。

sudo systemctl status cam-stream.service

サービスの現在の状態を表示します。これには、サービスが実行中かどうか、最後に実行されたのはいつか、およびサービスに関連するログメッセージが含まれます。

sudo systemctl list-units –type=service

システム上のすべてのサービスユニットを一覧表示します。これにより、現在実行中のサービスや、それらの状態を確認できます。

sudo journalctl

システムログ(journal)の内容を表示します。これには、カーネルメッセージ、システムサービス、およびアプリケーションからのログメッセージが含まれます。

sudo journalctl -u cam-stream.service

指定したサービス(この場合はcam-stream.service)に関連するログメッセージのみを表示します。これにより、特定のサービスに関連する問題をトラブルシューティングするのに役立ちます。

 

実際に使うときは、このように使うでしょう。デーモンをリロード、サービスを有効、サービスを開始、サービスの状態を確認、エラーログを確認という感じです。

sudo systemctl daemon-reload

sudo systemctl enable cam-stream.service

sudo systemctl start cam-stream.service

sudo systemctl status cam-stream.service

sudo journalctl -u cam-stream.service

以上でUSBカメラを毎回自動的に起動してSRTで繋がるまで再起動を繰り返すシステムができました。切断してもまたつながるまで繰り返します。