序章
この記事では主に、前の記事の残りのトラッカー モジュールを補足して、プロセス全体をより完璧にすることを目的としています。また、rtsp ストリームの異常問題については、gstreamer などのツールでの解決策もまとめてあります。
Gst-nvtracker の概要
Gst-nvtracker プラグインを使用すると、DeepStream パイプラインが基礎となるトラッカーを使用して、一意の ID を持つ検出オブジェクトを追跡できるようになります。NvDCF、KLT、IOU トラッカーの 3 つのリファレンス実装を含む、NvDsTracker API を実装する基盤となるライブラリをサポートします。この API の一部として、プラグインは基礎となるライブラリに、入力形式とメモリ タイプに関する機能と要件を問い合わせます。次に、これらのクエリの結果に基づいて、プラグインは入力フレームバッファを基礎となるライブラリによって要求された形式に変換します。たとえば、KLT トラッカーは Luma 固有の形式を使用し、NvDCF と DeepSORT は NV12 または RGBA 形式を使用し、IOU はバッファを必要としません。4 つのトラッカー ライブラリは、具体的には次のようなさまざまな追跡アルゴリズムをサポートしています。
- KLT トラッカーは、Kanade-Lucas-Tomasi (KLT) トラッカー アルゴリズムの CPU ベースの実装を使用します。このライブラリには構成ファイルは必要ありません。
- IOU トラッカーは、2 つの連続するフレーム間の検出器境界ボックスの IOU 値を使用して、それらの間の関連付けを実行するか、新しい ID を割り当てます。このライブラリは、オプションの構成ファイルを受け入れます。
- NvDCF トラッカーは、視覚的ターゲット トラッカーとして相関フィルターに基づくオンライン判別学習アルゴリズムを使用し、マルチターゲット追跡にはデータ関連付けアルゴリズムを使用します。このライブラリは、オプションの構成ファイルを受け入れます。
- DeepSORT: DeepSORT トラッカーは、ディープ コサイン メトリクス学習と Re-ID ニューラル ネットワークを使用する公式 DeepSORT トラッカーを再実装したものです。この実装により、ユーザーは、NVIDIA の TensorRT™ フレームワークでサポートされている限り、任意の Re-ID ネットワークを使用できるようになります。
これら 4 つのトラッカーの比較とトレードオフは次のとおりです。
トラッカーのタイプ | GPU コンピューティング | CPU コンピューティング | アドバンテージ | 欠点がある | 最適な使用例 |
---|---|---|---|---|---|
借用書 | バツ | とても低い | 軽量 | - 照合用の視覚的な署名がないため、トラッカー ID の切り替えや不具合が頻繁に発生する傾向があります。動きの速いシーンには適していません。 | - オブジェクトの位置はまばらで、サイズも異なります 。 - 検出器はフレームごと、または非常に頻繁に (例: 1 フレームおき) 実行されることが予想されます。 |
KLT | バツ | 高い | 単純なシーンではかなりうまく機能します | - CPU 使用率が高い。影、非剛体変形、面外回転、部分的なオクルージョンなどのノイズや外乱による外観の変化に対して脆弱です。テクスチャーの低いオブジェクトを処理できません。 | - オブジェクトには強力なテクスチャとシンプルな背景があります。 - 理想的には高い CPU リソース可用性。 |
NvDCF | 中くらい | 低い | - 部分的なオクルージョン、影、その他の一時的な視覚的変化に対して非常に堅牢です。 ・IDの切り替え頻度が低い。 - 精度を大幅に損なうことなく、PGIE 間隔 > 0 で使用可能 アプリケーション要件に応じてパラメータを簡単に調整し、精度とパフォーマンスをトレードオフ |
- 視覚的特徴抽出の計算量が増加するため、IOU よりも遅くなります。 | - 部分的なオクルージョンがある場合でも複数オブジェクトの複雑なシーン - PGIE 間隔 > 0 |
DeepSORT | 高い | 低い | - 外観の一致のためのカスタム Re-ID モデルが可能 - 使用される Re-ID モデルに応じて高度な識別が可能 |
- 物体ごとに推論が必要なため、計算コストが高くなる - 検出器の bbox が利用可能な場合にのみ追跡を実行できる - Re-ID モデルを切り替えない限り、精度/パフォーマンス レベルを簡単に調整できない |
- NvDCF と同じ (優先 PGIE 間隔 = 0 を除く) |
上記のコンテンツはディープストリーム SDK 6.1 および 5.1 ドキュメントからのもので、6.1 ドキュメントでは KLT トラッカーが削除され、ディープソート命令が追加されています。ダイナミック ライブラリは追跡用にさらに巨大で、/opt/nvidia/deepstream/deepstream-5.1/lib
ディープストリーム 5.1 に含まれる so ファイルは次のとおりです。
libnvds_tracker.so
libnvds_mot_iou.so
libnvds_mot_klt.so
libnvds_nvdcf.so
名前から、それらがどのアルゴリズムに属しているかは明らかであり、deepstream 6.1 の同じディレクトリ内で、so ファイルが 1 つだけコンパイルされます。
libnvds_nvmultiobjecttracker.so
また、1台のマシンに2つのdockerイメージをインストールしたために環境が入れ替わってしまったということも分かりましたが、なぜ入れ替わってしまったのかというと、悲しい話です。。。
ディープストリーム 6.0 以降、nvidia は 3 つのトラッカー アルゴリズム (IOU、NvDCF、DeepSORT) を 1 つのアーキテクチャに統合し、バッチ モードでマルチストリームとマルチオブジェクト トラッキングをサポートし、CPU と GPU で効率的に処理できるようになりました。
ここでは詳細には触れませんが、興味がある場合は、 Gst - nvtrackerの NvMultiObjectTracker ライブラリのワークフローとコア モジュールのセクションlibnvds_nvmultiobjecttracker.so
に移動して、サポートされている関係の構築と共有モジュールのデータ関連付けテーブルを確認してください。ターゲット追跡に関しては、次のテスト プロセスでは追跡モジュールのみが使用され、構成ファイルは依然として DvDCF の yaml に基づいています。deepstream-python-app
deepstream-test2
py ルーチンは他のいくつかの状況を考慮していないと思います。原理をさらに詳しく知りたい場合は、C 側の deepstream-app を参照する必要があります。その現在のディレクトリには、非常に包括的な構成ファイルが含まれているためです。
root@$$:/opt/nvidia/deepstream/deepstream-6.1/samples/configs/deepstream-app# ls | grep config_tracker
config_tracker_DeepSORT.yml
config_tracker_IOU.yml
config_tracker_NvDCF_accuracy.yml
config_tracker_NvDCF_max_perf.yml
config_tracker_NvDCF_perf.yml
ここでトラッカープラグインを紹介し、アクセス部分は以下から始まります。
Gst-nvtracker アクセス プロセス
ここでは、上記のセクションをdeepstream-imagedata-multistream
テンプレートとして使用して、test2 でトラッカーを接続します。これは実際には非常に簡単です。トラッカーの要素パラメータを設定した後、それを推論プラグインに追加して、リンクします。詳細については、次のようになります。まずパラメータ設定を見てください。
gstトラッカーパラメータ
nvidiaの公式Webサイトの紹介によると、トラッキングモジュールのパラメータテーブルは次のとおりです。
財産 | 意味 | 種類と範囲 | ノート |
---|---|---|---|
トラッカー幅 | トラッカーが動作するフレーム幅 (ピクセル単位)。 | 整数、0 ~ 4,294,967,295 | tracker-width=640 (32 の倍数) |
トラッカーの高さ | トラッカーが実行されるフレームの高さ (ピクセル単位)。 | 整数、0 ~ 4,294,967,295 | tracker-height=384 (32 の倍数) |
ll-lib-ファイル | Gst-nvtracker ロードするトラッカー ライブラリのパス名。 | 弦 | ll-lib-file=/opt/nvidia/deepstream/deepstream/lib/ libnvds_nvmultiobjecttracker .so |
ll-config-ファイル | ライブラリの設定ファイルは不要です。 | 設定ファイルへのパス | ll-config-file=config_tracker_NvDCF_perf.yml |
GPUID | デバイス/統合メモリが割り当てられる GPU の ID、およびバッファ コピー/スケーリングが実行される GPU の ID。(dGPU のみ。) | 整数、0 ~ 4,294,967,295 | GPUID=0 |
バッチ処理を有効にする | バッチモードを有効/無効にします。基礎となるライブラリがバッチ処理とストリームごとの処理の両方をサポートしている場合にのみ機能します。(オプション) (デフォルトは 1) | ブール値 | バッチ処理を有効にする=1 |
過去のフレームを有効にする | 過去のフレーム データ モードのレポートを有効/無効にします。基礎となるライブラリがサポートしている場合にのみ機能します。(オプション) (デフォルトは 0) | ブール値 | 過去のフレームを有効にする=1 |
追跡面の種類 | 表面流のタイプを追跡するように設定します。(デフォルト値は0) | 整数,≥0 | トラッキングサーフェスタイプ=0 |
表示追跡ID | OSD でのトラッキング ID 表示を有効にします。 | ブール値 | 表示追跡ID=1 |
コンピューティングハードウェア | スケーリング用の計算エンジン。0 - デフォルト 1 - GPU 2 - VIC (Jetson のみ) | 整数、0 ~ 2 | 計算ハードウェア=1 |
追跡IDリセットモード | パイプライン イベントに基づいてトレース ID を強制的にリセットできます。トレース ID リセットが有効になってそのようなイベントが発生すると、トレース ID の下位 32 ビットが 0 にリセットされます。 0 : ストリーム リセットまたは EOS イベントが発生したときにトレース ID をリセットしません。 1: ストリーム リセットが発生したときに、現在のトレース ID をすべて終了します。トラッカーを用意し、ストリームに新しい ID を割り当てます (GST_NVEVENT_STREAM_RESET) 2: EOS イベント (GST_NVEVENT_STREAM_EOS) を受信した後、トラッキング ID を 0 から開始します (注: トラッキング ID の下位 32 ビットのみが 0 から始まります) 3: 有効にするオプション 1 と 2 |
整数、0 ~ 3 | 追跡IDリセットモード=0 |
上記の説明の一部は、私自身の理解と翻訳に基づいて変更されています。config_infer_primary_yoloV5.txt
いくつかの一般的なパラメーターがわかったら、前のセクションのファイルにトラッカー クラスを追加できます。
[tracker]
tracker-width=640
tracker-height=384
gpu-id=0
ll-lib-file=/opt/nvidia/deepstream/deepstream/lib/libnvds_nvmultiobjecttracker.so
# ll-lib-file = /opt/nvidia/deepstream/deepstream/lib/libnvds_mot_klt.so
# ll-config-file=config_tracker_NvDCF_perf.yml
enable-past-frame=1
enable-batch-process=1
display-tracking-id=1
ここで注意すべき点は、公式ルーチンには最後の display-tracking-id パラメータがなく、enable-past-frame パラメータもオフになっている点です。前者は必要なパラメータですが、後者は現時点では役に立たないのです。 for エフェクトを改善しましょう、と言いましたが、実行してみるとオンになっていないことがわかりました。主な問題は、長時間デバッグしても出力が見つからないことでした。もちろん、トレースは正常にロードされましたが、後でパラメーターが欠落していることがわかりました。。。幅と高さは希望に応じて指定できますが、できれば 32 の倍数です。その他はデフォルトであるか、追加されていません。
トラッカーコードへのアクセス
まず、main 関数で、このようなタックラー要素を初期化します。
tracker = Gst.ElementFactory.make("nvtracker", "tracker")
if not tracker:
sys.stderr.write(" Unable to create tracker \n")
次に、txt に書き込んだ構成情報をロードします。
#Set properties of tracker
config = configparser.ConfigParser()
config.read('dstest2_tracker_config.txt')
config.sections()
for key in config['tracker']:
if key == 'tracker-width' :
tracker_width = config.getint('tracker', key)
tracker.set_property('tracker-width', tracker_width)
if key == 'tracker-height' :
tracker_height = config.getint('tracker', key)
tracker.set_property('tracker-height', tracker_height)
if key == 'gpu-id' :
tracker_gpu_id = config.getint('tracker', key)
tracker.set_property('gpu_id', tracker_gpu_id)
if key == 'll-lib-file' :
tracker_ll_lib_file = config.get('tracker', key)
tracker.set_property('ll-lib-file', tracker_ll_lib_file)
if key == 'll-config-file' :
tracker_ll_config_file = config.get('tracker', key)
tracker.set_property('ll-config-file', tracker_ll_config_file)
if key == 'enable-batch-process' :
tracker_enable_batch_process = config.getint('tracker', key)
tracker.set_property('enable_batch_process', tracker_enable_batch_process)
if key == 'enable-past-frame' :
tracker_enable_past_frame = config.getint('tracker', key)
tracker.set_property('enable_past_frame', tracker_enable_past_frame)
if key == 'display-tracking-id' :
tracker_tracking_id = config.getint('tracker', key)
tracker.set_property('display_tracking_id', tracker_tracking_id )
この要素を完了したら、パイプラインに参加してリンクできます。
# add部分
pipeline.add(tracker)
# link部分
pgie.link(tracker)
tracker.link(nvvidconv)
元の推論モジュールは画像コンバーターに接続されていますが、現在は中央にトラッカー モジュールを挿入することと同等であり、他は変更されません。この時点で、メイン関数には問題はありません。プローブ内のコールバック関数からトラッカーの情報を取得できます。データを取得するコード全体は次のとおりです。
#past traking meta data
if(past_tracking_meta[0]==1):
l_user=batch_meta.batch_user_meta_list
while l_user is not None:
try:
# Note that l_user.data needs a cast to pyds.NvDsUserMeta
# The casting is done by pyds.NvDsUserMeta.cast()
# The casting also keeps ownership of the underlying memory
# in the C code, so the Python garbage collector will leave
# it alone
user_meta=pyds.NvDsUserMeta.cast(l_user.data)
except StopIteration:
break
if(user_meta and user_meta.base_meta.meta_type==pyds.NvDsMetaType.NVDS_TRACKER_PAST_FRAME_META):
try:
# Note that user_meta.user_meta_data needs a cast to pyds.NvDsPastFrameObjBatch
# The casting is done by pyds.NvDsPastFrameObjBatch.cast()
# The casting also keeps ownership of the underlying memory
# in the C code, so the Python garbage collector will leave
# it alone
pPastFrameObjBatch = pyds.NvDsPastFrameObjBatch.cast(user_meta.user_meta_data)
except StopIteration:
break
for trackobj in pyds.NvDsPastFrameObjBatch.list(pPastFrameObjBatch):
print("streamId=",trackobj.streamID)
print("surfaceStreamID=",trackobj.surfaceStreamID)
for pastframeobj in pyds.NvDsPastFrameObjStream.list(trackobj):
print("numobj=",pastframeobj.numObj)
print("uniqueId=",pastframeobj.uniqueId)
print("classId=",pastframeobj.classId)
print("objLabel=",pastframeobj.objLabel)
for objlist in pyds.NvDsPastFrameObjList.list(pastframeobj):
print('frameNum:', objlist.frameNum)
print('tBbox.left:', objlist.tBbox.left)
print('tBbox.width:', objlist.tBbox.width)
print('tBbox.top:', objlist.tBbox.top)
print('tBbox.right:', objlist.tBbox.height)
print('confidence:', objlist.confidence)
print('age:', objlist.age)
try:
l_user=l_user.next
except StopIteration:
break
这部分代码跟infer基本一致,代码也是紧接着infer取数后来的,跟infer共用batch_meta,而batch_meta是deepstream从哈希buffer中拿到的所有的info。这里相当于就只多了个id,前面的推理数据就可以注释了。首行的past_tracking_meta判断可以直接给0,虽然说前面我在制作element的时候有加载进这个配置,该参数就相当于一个开关,程序启动后从用户输入中获取选择0或者1,我一般都给0并且跳过判断,虽然说目前我这边还没有上线,目前感觉作用不大。
那到此为止,deepstream-imagedata-multistream
的例程就改造完成,可以重新跑整个demo,并创建管道图,跑出来的图如下:
另外,还有一个现象是加入tracker会输出如下日志:
gstnvtracker: Loading low-level lib at /opt/nvidia/deepstream/deepstream/lib/libnvds_nvmultiobjecttracker.so
gstnvtracker: Optional NvMOT_ProcessPast not implemented
gstnvtracker: Optional NvMOT_RemoveStreams not implemented
gstnvtracker: Batch processing is OFF
gstnvtracker: Past frame output is OFF
这是模型加载的时候爆出来的日志,仅仅是有些东西没有开,对整个结果可能只是精度上的影响,作为测试的话影响不大。
介绍完这个问题后,我还想说明的一个问题就是rtsp流的事情。tracker深入的参数调优文档,可以看官方的说明:
https://docs.nvidia.com/metropolis/deepstream/dev-guide/text/DS_plugin_NvMultiObjectTracker_parameter_tuning_guide.html
rtsp流异常说明
这个问题,是我长时间跑rtsp流遇到的一个bug,或者说当我测试gstreamer对于流断的异常处理,这就引起了我的一个思考,然而印了那张表情包,30分钟后,思考崩溃,能用就行,emmm。
这个问题在C的源码里是不存在的,原因是C的bus_callback函数差不多写了2/3百行,大大小小所有情况都考虑清楚了,而python的,nvidia在每个版本里,都是定义在common中,为:
import gi
import sys
gi.require_version('Gst', '1.0')
from gi.repository import Gst
def bus_call(bus, message, loop):
t = message.type
if t == Gst.MessageType.EOS:
sys.stdout.write("End-of-stream\n")
loop.quit()
elif t==Gst.MessageType.WARNING:
err, debug = message.parse_warning()
sys.stderr.write("Warning: %s: %s\n" % (err, debug))
elif t == Gst.MessageType.ERROR:
err, debug = message.parse_error()
sys.stderr.write("Error: %s: %s\n" % (err, debug))
loop.quit()
return True
这个程序说明,除了警告,主要遇到EOS与ERROR,程序就会退出,我尝试过将警告和EOS(EOS的意思可以理解为流媒体结束的一个标志,即EOS of stream)注释掉,但发现整个程序陷入了假死状态,因为pipeline已经无法分析了,FPS会变成0,然后我针对这个问题,进行了一些资料查找,当然,最好的办法是看懂C端的解决方案,但我发现我看完了还是没得办法,因为API不同步,比如C里有个reset_pipeline_xxx
好像是这名字,在python中我并没有找到类似的,这种有很多,于是有了如下简单的解决方案:
首先,我们可以从pipeline考虑,如果中间出现问题,比如说输入源这种,不是内部element报错,那么我们可以利用pipeline的特性,先暂停管道,然后运行完自定义事件,再重新开启:
pipeline.set_state(Gst.State.NULL)
//do your stuff for example, change some elements, remove some elements etc:
pipeline.set_state(Gst.State.PLAYING)
这个过程可以参照stackoverflow中Sink restart on failure without stopping the pipeline的方案,为:
def event_probe2(pad, info, *args):
Gst.Pad.remove_probe(pad, info.id)
tee.link(opusenc1)
opusenc1.set_state(Gst.State.PLAYING)
oggmux1.set_state(Gst.State.PLAYING)
queue1.set_state(Gst.State.PLAYING)
shout2send.set_state(Gst.State.PLAYING)
return Gst.PadProbeReturn.OK
def reconnect():
pad = tee.get_static_pad('src_1')
pad.add_probe(Gst.PadProbeType.BLOCK_DOWNSTREAM, event_probe2, None)
def event_probe(pad, info, *args):
Gst.Pad.remove_probe(pad, info.id)
tee.unlink(opusenc1)
opusenc1.set_state(Gst.State.NULL)
oggmux1.set_state(Gst.State.NULL)
queue1.set_state(Gst.State.NULL)
shout2send.set_state(Gst.State.NULL)
GLib.timeout_add_seconds(interval, reconnect)
return Gst.PadProbeReturn.OK
def message_handler(bus, message):
if message.type == Gst.MessageType.ERROR:
if message.src == shout2send:
pad = tee.get_static_pad('src_1')
pad.add_probe(Gst.PadProbeType.BLOCK_DOWNSTREAM, event_probe, None)
else:
print(message.parse_error())
pipeline.set_state(Gst.State.NULL)
exit(1)
else:
print(message.type)
而这是错误发生时的解决,关于EOS,我找到的一个类似方案为Restarting/Reconnecting RTSP source on EOS
其中部分代码为:
msg_type = msg.type
if msg_type == Gst.MessageType.EOS:
ret = self.pipeline.set_state(Gst.State.PAUSED)
self.loop.quit()
Gst.debug_bin_to_dot_file(self.pipeline, Gst.DebugGraphDetails.ALL, "EOS")
print("Setting Pipeline to Paused State")
time.sleep(10)
print("Trying to set back to playing state")
if ret == Gst.StateChangeReturn.SUCCESS or ret == Gst.StateChangeReturn.NO_PREROLL:
flush_start = self.pipeline.send_event(Gst.Event.new_flush_start())
print("Managed to Flush Start: ", flush_start)
flush_stop = self.pipeline.send_event(Gst.Event.new_flush_stop(True))
print("Managed to Flush Stop: ", flush_stop)
i = 0
uri = configFile['source%u' % int(i)]['uri']
padname = "sink_%u" % int(i)
removed_state = self.remove_source_bin()
if all(element == 1 for element in removed_state):
self.nbin = self.create_source_bin(i, uri)
added_state = self.pipeline.add(self.nbin)
print("Added state: ", added_state)
self.streammux_sinkpad = self.streammux.get_request_pad(padname)
if not self.streammux_sinkpad:
sys.stderr.write("Unable to create sink pad bin \n")
print("Pad name: ", padname)
self.srcpad = self.nbin.get_static_pad("src")
self.srcpad.link(self.streammux_sinkpad)
Gst.debug_bin_to_dot_file(self.pipeline, Gst.DebugGraphDetails.ALL, "Resetting_Source")
self.bus = self.pipeline.get_bus()
self.bus.add_signal_watch()
self.bus.connect("message", self.bus_call, self.loop)
self.pipeline.set_state(Gst.State.PLAYING)
self.nbin.set_state(Gst.State.PLAYING)
nbin_check = self.nbin.get_state(Gst.CLOCK_TIME_NONE)[0]
if nbin_check == Gst.StateChangeReturn.SUCCESS or nbin_check == Gst.StateChangeReturn.NO_PREROLL:
self.uri_decode_bin.set_state(Gst.State.PLAYING)
uridecodebin_check = self.uri_decode_bin.get_state(Gst.CLOCK_TIME_NONE)[0]
if uridecodebin_check == Gst.StateChangeReturn.SUCCESS or uridecodebin_check == Gst.StateChangeReturn.NO_PREROLL:
self.streammux.set_state(Gst.State.PLAYING)
streammux_check = self.streammux.get_state(Gst.CLOCK_TIME_NONE)[0]
if streammux_check == Gst.StateChangeReturn.SUCCESS or streammux_check == Gst.StateChangeReturn.NO_PREROLL:
self.pipeline.set_state(Gst.State.PLAYING)
pipeline_check = self.pipeline.get_state(Gst.CLOCK_TIME_NONE)[0]
if pipeline_check == Gst.StateChangeReturn.SUCCESS or pipeline_check == Gst.StateChangeReturn.NO_PREROLL:
print("We did it boys!")
Gst.debug_bin_to_dot_file(self.pipeline, Gst.DebugGraphDetails.ALL, "Trying_Playing")
else:
print("pipeline failed us")
else:
print("streammux failed us")
else:
print("uridecodebin failed us")
else:
print("nbin failed us")
self.loop.run()
我感觉很有参考意义,虽然我目前还没有处理EOS的问题,因为我是rtsp流,不过我后来改写的解决方案与上述类似,主要参考nvidia写得deepstream_rt_src_add_del
例程,这个引用部分删除资源函数为:
def stop_release_source(source_id):
global g_num_sources
global g_source_bin_list
global streammux
global pipeline
#Attempt to change status of source to be released
state_return = g_source_bin_list[source_id].set_state(Gst.State.NULL)
if state_return == Gst.StateChangeReturn.SUCCESS:
print("STATE CHANGE SUCCESS\n")
pad_name = "sink_%u" % source_id
print(pad_name)
#Retrieve sink pad to be released
sinkpad = streammux.get_static_pad(pad_name)
#Send flush stop event to the sink pad, then release from the streammux
sinkpad.send_event(Gst.Event.new_flush_stop(False))
streammux.release_request_pad(sinkpad)
print("STATE CHANGE SUCCESS\n")
#Remove the source bin from the pipeline
pipeline.remove(g_source_bin_list[source_id])
source_id -= 1
g_num_sources -= 1
elif state_return == Gst.StateChangeReturn.FAILURE:
print("STATE CHANGE FAILURE\n")
elif state_return == Gst.StateChangeReturn.ASYNC:
state_return = g_source_bin_list[source_id].get_state(Gst.CLOCK_TIME_NONE)
pad_name = "sink_%u" % source_id
print(pad_name)
sinkpad = streammux.get_static_pad(pad_name)
sinkpad.send_event(Gst.Event.new_flush_stop(False))
streammux.release_request_pad(sinkpad)
print("STATE CHANGE ASYNC\n")
pipeline.remove(g_source_bin_list[source_id])
source_id -= 1
g_num_sources -= 1
def delete_sources(data):
global loop
global g_num_sources
global g_eos_list
global g_source_enabled
#First delete sources that have reached end of stream
for source_id in range(MAX_NUM_SOURCES):
if (g_eos_list[source_id] and g_source_enabled[source_id]):
g_source_enabled[source_id] = False
stop_release_source(source_id)
#Quit if no sources remaining
if (g_num_sources == 0):
loop.quit()
print("All sources stopped quitting")
return False
#Randomly choose an enabled source to delete
source_id = random.randrange(0, MAX_NUM_SOURCES)
while (not g_source_enabled[source_id]):
source_id = random.randrange(0, MAX_NUM_SOURCES)
#Disable the source
g_source_enabled[source_id] = False
#Release the source
print("Calling Stop %d " % source_id)
stop_release_source(source_id)
#Quit if no sources remaining
if (g_num_sources == 0):
loop.quit()
print("All sources stopped quitting")
return False
return True
异常接收改为:
elif t == Gst.MessageType.ELEMENT:
struct = message.get_structure()
#Check for stream-eos message
if struct is not None and struct.has_name("stream-eos"):
parsed, stream_id = struct.get_uint("stream-id")
if parsed:
#Set eos status of stream to True, to be deleted in delete-sources
print("Got EOS from stream %d" % stream_id)
g_eos_list[stream_id] = True
我针对于此改写成我自己业务想要的样子,不过我目前发现还有点bug在于,rtsp流如果中途断开,我的整个pipeline会出现5s左右的延迟,只有当坏的流完全释放掉,才能恢复,我不清楚这个是哪里的问题,有bug,感觉还是要研究一下,之后解决了会在这里补充说明,现在先略过,如果有大佬会或者有啥好资料可以评论区教一手或者私信我,我将不胜感激。
最後に、ストリーム自体が最初で壊れた場合の対処法です。解決策はたくさんありますが、特に適切なものが見つからなかったので、自分で書きました。接続されていないものを除外するために ffprobe を使用しました。ストリーム、 ffprobe 関数は次のとおりです。
def get_rtsp_format(self,strFileName):
strCmd = 'ffprobe -v quiet -print_format json -show_format -show_streams -i "{0}"' + format(strFileName)
mystring = os.popen(strCmd).read()
result = json.loads(mystring)
return result["format"]
もちろん他の方法もあります。ffmpeg や gstreamer のコマンドラインでも代用できますが、解析ツールを使用する前に rtsp ストリームを解析するか、ストリーム リソースに直接 ping するのが最も簡単な方法です。判断条件も
ここまででこのnoteは終わりです。