點擊查看系列文章目錄
0 背景
從 DS4.0 開始就除錯了 deepstream 動態增減源的功能,好長時間沒接觸,又生疏了,好記性不如爛筆頭,趁著今天回顧代碼,把程序記錄一下
1 先跑起來
NVIDIA 官方其實已經發布過 runtime_source_add_delete 模塊,見 https://github.com/NVIDIA-AI-IOT/deepstream_reference_apps
我們先把他跑起來,然后我再介紹一下這代碼的思路,以及我除錯程序中遇到的一些問題
在 /opt/nvidia/deepstream/deepstream-5.0/source/app/samples 路徑下克隆工程
git clone https://github.com/NVIDIA-AI-IOT/deepstream_reference_apps.git
cd runtime_source_add_delete
make
make 完生成可執行檔案,可以使用以下下邊的方式測驗
$ ./deepstream-test-rt-src-add-del file:///opt/nvidia/deepstream/deepstream-5.0/samples/streams/sample_1080p_h264.mp4
運行起來后的效果是:每隔 10s 新增一路輸入,通過 tiler 模式顯示,最多增加到 4 路,然后開始每隔 10s 洗掉一路,直到刪完后,結束整個行程,
2 實作思路
在 deepstream_test_rt_src_add_del.c 中,首先會創建一條 pipeline,順序如下
uridecodebin -> streammux -> pgie -> tracker -> sgie1 -> sgie2 -> sgie3 -> tiler -> nvvideoconvert -> nvosd -> sink
然后增加一個定時函式,
g_timeout_add_seconds (10, add_sources, (gpointer) g_source_bin_list);
表示每隔 10s 執行一遍 add_sources 函式,進入該函式
static gboolean
add_sources (gpointer data)
{
gint source_id = g_num_sources;
GstElement *source_bin;
GstStateChangeReturn state_return;
// 生成隨機 source_id
do {
source_id = rand () % MAX_NUM_SOURCES;
} while (g_source_enabled[source_id]);
g_source_enabled[source_id] = TRUE;
// 創建 uridecodebin
// uridecodebin 是一種能自動識別 url 類別并對應解碼的一種source bin
g_print ("Calling Start %d \n", source_id);
source_bin = create_uridecode_bin (source_id, uri);
if (!source_bin) {
g_printerr ("Failed to create source bin. Exiting.\n");
return -1;
}
// 更新全域變數,保存 source 串列
g_source_bin_list[source_id] = source_bin;
// 將生成的 source_bin 加到 pipeline 中
gst_bin_add (GST_BIN (pipeline), source_bin);
// 設定新加的 source_bin 狀態為 playing,并得到狀態回傳值
state_return =
gst_element_set_state (g_source_bin_list[source_id], GST_STATE_PLAYING);
switch (state_return) {
case GST_STATE_CHANGE_SUCCESS:
g_print ("STATE CHANGE SUCCESS\n\n");
source_id++;
break;
case GST_STATE_CHANGE_FAILURE:
g_print ("STATE CHANGE FAILURE\n\n");
break;
case GST_STATE_CHANGE_ASYNC:
g_print ("STATE CHANGE ASYNC\n\n");
state_return =
gst_element_get_state (g_source_bin_list[source_id], NULL, NULL,
GST_CLOCK_TIME_NONE);
source_id++;
break;
case GST_STATE_CHANGE_NO_PREROLL:
g_print ("STATE CHANGE NO PREROLL\n\n");
break;
default:
break;
}
// 計數
g_num_sources++;
if (g_num_sources == MAX_NUM_SOURCES) {
// 當達到設定的最大輸入數量時,開始洗掉 source
g_timeout_add_seconds (10, delete_sources, (gpointer) g_source_bin_list);
// 回傳 FALSE,會終止該定時任務
return FALSE;
}
// 回傳 TRUE,會繼續下一次定時任務
return TRUE;
}
如代碼中說明的,當達到設定的最大輸入數量時,結束新增,開始洗掉,也是通過 g_timeout_add_seconds 定時任務,進入 delete_source 函式來完成
delete_source 函式主要是對串列的一些判斷,真正執行洗掉的是 stop_release_source 函式
static void
stop_release_source (gint source_id)
{
GstStateChangeReturn state_return;
gchar pad_name[16];
GstPad *sinkpad = NULL;
// 設定source bin 為 NULL 狀態
state_return =
gst_element_set_state (g_source_bin_list[source_id], GST_STATE_NULL);
switch (state_return) {
case GST_STATE_CHANGE_SUCCESS:
g_print ("STATE CHANGE SUCCESS\n\n");
// 得到 streammux 的 sink pad
g_snprintf (pad_name, 15, "sink_%u", source_id);
sinkpad = gst_element_get_static_pad (streammux, pad_name);
// 給 streammux 的 sink pad 發送 flush stop 信號,停止視頻流傳輸
gst_pad_send_event (sinkpad, gst_event_new_flush_stop (FALSE));
gst_element_release_request_pad (streammux, sinkpad);
g_print ("STATE CHANGE SUCCESS %p\n\n", sinkpad);
// 洗掉 bin 操作
gst_object_unref (sinkpad);
gst_bin_remove (GST_BIN (pipeline), g_source_bin_list[source_id]);
source_id--;
g_num_sources--;
break;
case GST_STATE_CHANGE_FAILURE:
g_print ("STATE CHANGE FAILURE\n\n");
break;
case GST_STATE_CHANGE_ASYNC:
g_print ("STATE CHANGE ASYNC\n\n");
state_return =
gst_element_get_state (g_source_bin_list[source_id], NULL, NULL,
GST_CLOCK_TIME_NONE);
g_snprintf (pad_name, 15, "sink_%u", source_id);
sinkpad = gst_element_get_static_pad (streammux, pad_name);
gst_pad_send_event (sinkpad, gst_event_new_flush_stop (FALSE));
gst_element_release_request_pad (streammux, sinkpad);
g_print ("STATE CHANGE ASYNC %p\n\n", sinkpad);
gst_object_unref (sinkpad);
gst_bin_remove (GST_BIN (pipeline), g_source_bin_list[source_id]);
source_id--;
g_num_sources--;
break;
case GST_STATE_CHANGE_NO_PREROLL:
g_print ("STATE CHANGE NO PREROLL\n\n");
break;
default:
break;
}
}
原理其實很簡單,關于動態替換 element 的程序,我在 gstreamer 專欄中有過介紹,可以參考《Gstreamer應用開發手冊14:替換管道元件》
3 改進
經過上述的步驟,可以初步實作一個對視頻檔案源的動態增減,但是我在輸入 rtsp 執行洗掉操作的時候會報錯如下
Calling Stop 3
STATE CHANGE SUCCESS
STATE CHANGE SUCCESS 0x7e640052c0
ERROR from element source: Unhandled error
Error details: gstrtspsrc.c(6161): gst_rtspsrc_send (): /GstPipeline:dstest-pipeline/GstURIDecodeBin:source-bin-03/GstRTSPSrc:source:
Option not supported (551)
Returned, stopping playback
Deleting pipeline
這個問題我曾經在論壇中提問過,可惜沒得到好的解決方案
https://forums.developer.nvidia.com/t/error-happend-when-run-runtime-source-add-delete/115285
在論壇中查了一下類似的問題,大概原因是說 rtsp 輸入不支持暫停等操作,如下
https://forums.developer.nvidia.com/t/delete-source-dynamically-error/146830/7
這里提供一個“暴力”的方法,就是注釋掉 bus_call 里邊的 g_main_loop_quit (loop) 函式,也能運行起來,但依然會輸出相關的錯誤資訊,
另外一種方法是修改 rtsp source bin 的原始碼,因為涉及的內容比較細且通用,我會在另外一邊博文中介紹,參考《DeepStream5.0系列之修改rtsp source原始碼》,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/242812.html
標籤:其他
上一篇:排序演算法 | 交換排序 一:冒泡演算法的圖解、實作、復雜度和穩定性分析與優化
下一篇:利用衛星影像評估火災后跡地
