对deep stream各个元件属性和函数功能的注解
int
main (int argc, char *argv[])
{
GMainLoop *loop = NULL;
//它是组成管道的基本构件,也是框架中所有可用组件的基础
//可以描述为一个具有特定属性的黑盒子,它通过连接点(link point)与外界进行交互,向框架中的其余部分表征自己的特性或者功能.
GstElement *pipeline = NULL, *streammux = NULL, *sink = NULL, *pgie = NULL,
*queue1, *queue2, *queue3, *queue4, *queue5, *nvvidconv = NULL,
*nvosd = NULL, *tiler = NULL;
#ifdef PLATFORM_TEGRA
GstElement *transform = NULL;
#endif
GstBus *bus = NULL;
guint bus_watch_id;
GstPad *tiler_src_pad = NULL;
guint i, num_sources;
guint tiler_rows, tiler_columns;
guint pgie_batch_size;
/* Check input arguments */
if (argc < 2) {
g_printerr ("Usage: %s <uri1> [uri2] ... [uriN] \n", argv[0]);
return -1;
}
num_sources = argc - 1;
/* Standard GStreamer initialization */
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE); //创建一个main loop对象,一个main loop对象只能被一个线程使用,但一个线程可以有多个main loop对象
/* Create gstreamer elements */
/* Create Pipeline element that will form a connection of other elements */
pipeline = gst_pipeline_new ("dstest3-pipeline"); //Pipeline通过gst_pipeline_new创建
/* Create nvstreammux instance to form batches from one or more sources. */
//gst_element_factory_make(): 这个函数使用一个已存在的工厂对象和一个新的元件名来创建元件
//"nvstreammux": 可从多个输入源组成一批缓冲区
streammux = gst_element_factory_make ("nvstreammux", "stream-muxer");
if (!pipeline || !streammux) {
g_printerr ("One element could not be created. Exiting.\n");
return -1;
}
//gst_bin_add(): 在pipeline中加入element。这个方法会接受一个element作为输入参数,最后由NULL来终止
//gst_bin_add_many(): 这个方法会接受一系列的element作为输入参数
//将streammux元件加入到pipeline
gst_bin_add (GST_BIN (pipeline), streammux);
for (i = 0; i < num_sources; i++) {
GstPad *sinkpad, *srcpad;
gchar pad_name[16] = { };
// 创建视频源元件
GstElement *source_bin = create_source_bin (i, argv[i + 1]);
if (!source_bin) {
g_printerr ("Failed to create source bin. Exiting.\n");
return -1;
}
//gst_bin_add(): 在pipeline中加入element。这个方法会接受一个element作为输入参数,最后由NULL来终止
//gst_bin_add_many(): 这个方法会接受一系列的element作为输入参数
//将source_bin元件加入到pipeline
gst_bin_add (GST_BIN (pipeline), source_bin);
g_snprintf (pad_name, 15, "sink_%u", i);
//gst_element_get_request_pad(GstElement *element, const gchar *name): 获取指定element中的指定pad
//name请求的pad名称.返回值:成功返回对应的pad,失败返回NULL。
sinkpad = gst_element_get_request_pad (streammux, pad_name);
if (!sinkpad) {
g_printerr ("Streammux request sink pad failed. Exiting.\n");
return -1;
}
// 从CustomData我们可以获得转换element对象,然后使用gst_element_get_static_pad()方法可以获得sink pad。
//这个pad是我们希望和new_pad连接的pad。
//可以用element和element连接的,让GStreamer自己来选择合适的pad。
//在这里,我们是手动的把两个pad直接连接起来。
srcpad = gst_element_get_static_pad (source_bin, "src");
if (!srcpad) {
g_printerr ("Failed to get src pad of source bin. Exiting.\n");
return -1;
}
//gst_pad_link()方法会把两个pad连接起来。
//就像gst_element_link()这个方法一样,连接必须是从source到sink,连接的两个pad必须在同一个bin里面。
if (gst_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK) {
g_printerr ("Failed to link source bin to stream muxer. Exiting.\n");
return -1;
}
//释放pad
gst_object_unref (srcpad);
gst_object_unref (sinkpad);
}
/* Use nvinfer to infer on batched frame. */
//推理元件
pgie = gst_element_factory_make ("nvinfer", "primary-nvinference-engine");
/* Add queue elements between every two elements */
queue1 = gst_element_factory_make ("queue", "queue1");
queue2 = gst_element_factory_make ("queue", "queue2");
queue3 = gst_element_factory_make ("queue", "queue3");
queue4 = gst_element_factory_make ("queue", "queue4");
queue5 = gst_element_factory_make ("queue", "queue5");
/* Use nvtiler to composite the batched frames into a 2D tiled array based
* on the source of the frames. */
//Tiler插件(nvmultistreamtiler)将此批次合成为单个2D帧???
tiler = gst_element_factory_make ("nvmultistreamtiler", "nvtiler");
/* Use convertor to convert from NV12 to RGBA as required by nvosd */
//视频转换
nvvidconv = gst_element_factory_make ("nvvideoconvert", "nvvideo-converter");
/* Create OSD to draw on the converted RGBA buffer */
//画框
nvosd = gst_element_factory_make ("nvdsosd", "nv-onscreendisplay");
/* Finally render the osd output */
#ifdef PLATFORM_TEGRA
transform = gst_element_factory_make ("nvegltransform", "nvegl-transform");
#endif
sink = gst_element_factory_make ("nveglglessink", "nvvideo-renderer");
if (!pgie || !tiler || !nvvidconv || !nvosd || !sink) {
g_printerr ("One element could not be created. Exiting.\n");
return -1;
}
#ifdef PLATFORM_TEGRA
if(!transform) {
g_printerr ("One tegra element could not be created. Exiting.\n");
return -1;
}
#endif
g_object_set (G_OBJECT (streammux), "batch-size", num_sources, NULL);
g_object_set (G_OBJECT (streammux), "width", MUXER_OUTPUT_WIDTH, "height",
MUXER_OUTPUT_HEIGHT,
"batched-push-timeout", MUXER_BATCH_TIMEOUT_USEC, NULL);
/* Configure the nvinfer element using the nvinfer config file. */
g_object_set (G_OBJECT (pgie),
"config-file-path", "dstest3_pgie_config.txt", NULL);
/* Override the batch-size set in the config file with the number of sources. */
g_object_get (G_OBJECT (pgie), "batch-size", &pgie_batch_size, NULL);
if (pgie_batch_size != num_sources) {
g_printerr
("WARNING: Overriding infer-config batch-size (%d) with number of sources (%d)\n",
pgie_batch_size, num_sources);
g_object_set (G_OBJECT (pgie), "batch-size", num_sources, NULL);
}
tiler_rows = (guint) sqrt (num_sources);
g_printerr("tiler_rows = %d", tiler_rows)
tiler_columns = (guint) ceil (1.0 * num_sources / tiler_rows);
/* we set the tiler properties here */
g_object_set (G_OBJECT (tiler), "rows", tiler_rows, "columns", tiler_columns,
"width", TILED_OUTPUT_WIDTH, "height", TILED_OUTPUT_HEIGHT, NULL);
g_object_set (G_OBJECT (nvosd), "process-mode", OSD_PROCESS_MODE,
"display-text", OSD_DISPLAY_TEXT, NULL);
g_object_set (G_OBJECT (sink), "qos", 0, NULL);
/* we add a message handler */
//利用这种方法向上层应用报告错误, 尽可能的处理警告和通知消息
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
bus_watch_id = gst_bus_add_watch (bus, bus_call, loop);
gst_object_unref (bus);
/* Set up the pipeline */
/* we add all elements into the pipeline */
#ifdef PLATFORM_TEGRA
gst_bin_add_many (GST_BIN (pipeline), queue1, pgie, queue2, tiler, queue3,
nvvidconv, queue4, nvosd, queue5, transform, sink, NULL);
/* we link the elements together
* nvstreammux -> nvinfer -> nvtiler -> nvvidconv -> nvosd -> video-renderer */
if (!gst_element_link_many (streammux, queue1, pgie, queue2, tiler, queue3,
nvvidconv, queue4, nvosd, queue5, transform, sink, NULL)) {
g_printerr ("Elements could not be linked. Exiting.\n");
return -1;
}
#else
gst_bin_add_many (GST_BIN (pipeline), queue1, pgie, queue2, tiler, queue3,
nvvidconv, queue4, nvosd, queue5, sink, NULL);
/* we link the elements together
* nvstreammux -> nvinfer -> nvtiler -> nvvidconv -> nvosd -> video-renderer */
if (!gst_element_link_many (streammux, queue1, pgie, queue2, tiler, queue3,
nvvidconv, queue4, nvosd, queue5, sink, NULL)) {
g_printerr ("Elements could not be linked. Exiting.\n");
return -1;
}
#endif
/* Lets add probe to get informed of the meta data generated, we add probe to
* the sink pad of the osd element, since by that time, the buffer would have
* had got all the metadata. */
tiler_src_pad = gst_element_get_static_pad (pgie, "src");
if (!tiler_src_pad)
g_print ("Unable to get src pad\n");
else
//对pgie增加一个探头,将探头src_pad链接给回调函数,回调函数可以访问里面的数据
gst_pad_add_probe (tiler_src_pad, GST_PAD_PROBE_TYPE_BUFFER,
tiler_src_pad_buffer_probe, NULL, NULL);
gst_object_unref (tiler_src_pad);
/* Set the pipeline to "playing" state */
g_print ("Now playing:");
for (i = 0; i < num_sources; i++) {
g_print (" %s,", argv[i + 1]);
}
g_print ("\n");
//这样在元件在被创建后,它不会执行任何操作。所以你需要改变元件的状态,使得它能够做某些事情
//GST_STATE_NULL:默认状态
//GST_STATE_READY:准备状态
//GST_STATE_PAUSED:暂停状态
//GST_STATE_PLAYING:运行状态,管道会自动处理数据
//gst_element_set_state: 来改变pipeline的状态
gst_element_set_state (pipeline, GST_STATE_PLAYING);
/* Wait till pipeline encounters an error or EOS */
g_print ("Running...\n");
g_main_loop_run (loop); //进入主循环,它会一直阻塞在这里,直到让它退出为止。有事件时,它就处理事件,没事件时就睡眠。
/* Out of the main loop, clean up nicely */
g_print ("Returned, stopping playback\n");
gst_element_set_state (pipeline, GST_STATE_NULL);
g_print ("Deleting pipeline\n");
gst_object_unref (GST_OBJECT (pipeline));
g_source_remove (bus_watch_id);
g_main_loop_unref (loop); //则是用于退出主循环
return 0;
}