DeepStream/Gstreamer queue实现预缓存功能

需求:现在根据推理事件触发保存mp4事件,但这样保存的视频会延后事件,所以需要先缓存一定时间的数据,当事件触发时就能够保存事件前后的数据了。

该如何先缓存数据呢?

1.将数据通过数据回调将数据拷贝出来然后丢入缓存队列(当达到限制时丢旧缓存数据)自己来控制。这种后续实现操作比较多。

2.通过原有的queue元件实现缓存,如何实现呢?通过研究发现queue具备以下max-size-bytes(最大内存大小限制)、max-size-time(最大时间限制)、max-size-buffers(最大帧数据个数限制)三个最大值设置项三者都有默认值,当达到一个条件时就不会继续缓存数据,会导致缓存数据量没到预期值,所以当选择一个设置时,其它必须设置为0禁用,不然无效。leaky(在元件输入丢(丢新数据)还是输出丢(丢旧数据))属性,这些设置项就能够使queue成为一个固定大小的丢旧数据的循环队列,当通过tee的复制特性将保存mp4的分支元件加到管道并设置为playing状态时数据就会流通,将存储设置大小的数据量到queue并设置leaky属性使用新数据刷新队列,不需要等到事件到来时从当前数据开始存储,所以需要阻塞当前管道的运行,直到存事件触发。

那么如何设置阻塞呢?

之前没接触过gstreamer后面发现gstpad存在一个阻塞回调函数,这样数据就不会继续往下传输了。所以当元件加入管道连接成功后设置为playing状态时,直接将queue的src  pad获取到并设置阻塞回调,就可以了,当事件触发时删除阻塞回调即可。

示例:

//在回调返回的时候令探针阻塞数据流。这叫做阻塞探针,可以指定GST_PAD_PROBE_TYPE_BLOCK标志来激活。
//你可以同时使用其他标志来选定某种活动发生时阻塞数据流。移除探针或回调返回GST_PAD_PROBE_REMOVE时,
//阻塞的pad会重新疏通。你可以通过令回调返回GST_PAD_PROBE_PASS仅通过现在阻塞的项目,这会在下一个项目上再次阻塞
//。阻塞探针用于临时阻塞你将要解除链接或没有链接的探针。
//如果数据流没有阻塞,管道会由于数据推入一个没有链接的帕的而进入错误状态。我们会探究如何使用阻塞探针来部分地预滚管道

//阻塞pad回调函数
GstPadProbeReturn AIStreamSaveToMp4::pad_probe_cb(GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
	/*GstPad *srcpad, *sinkpad;*/
	AIStreamSaveToMp4 *pAIStreamSaveToMp4 = (AIStreamSaveToMp4*)user_data;
	/*GST_DEBUG_OBJECT(pad, "pad is blocked now");*/
	//dzlog_info("pad is blocked now cb id:%d", info->id);
	return GST_PAD_PROBE_OK;
}

int AIStreamSaveToMp4::Init(int nStreamID)
{
	dzlog_info("StreamMp4 begin streamID:%d", nStreamID);
	char file_name[128];
	memset(file_name, 0, sizeof(file_name));
	time_t nowtime = time(NULL);
	sprintf(file_name, "/home/..../test%d_%ld.mp4", nStreamID, nowtime);
	gchar elem_name[50] = { 0 };
	gchar tee_src_pad_name[16] = { };

	g_snprintf(elem_name, sizeof(elem_name), "file-sink%d", nStreamID);
	GstElement *file_sink = gst_element_factory_make("filesink", elem_name);
	g_snprintf(elem_name, sizeof(elem_name), "mp4_mux%d", nStreamID);
	GstElement *mp4mux = gst_element_factory_make("mp4mux", elem_name);
	g_snprintf(elem_name, sizeof(elem_name), "record_queue%d", nStreamID);
	GstElement *record_queue = gst_element_factory_make(NVDS_ELEM_QUEUE, elem_name);

	g_object_set(G_OBJECT(file_sink), "location", file_name, NULL);
	g_snprintf(elem_name, sizeof(elem_name), "filter%d", nStreamID);
	GstElement * caps_mp4mux_filter = gst_element_factory_make("capsfilter", elem_name);

	GstCaps * caps_to_mp4mux = gst_caps_new_simple("video/x-h264",
		"stream-format", G_TYPE_STRING, "avc",
		NULL);
	g_object_set(caps_mp4mux_filter, "caps", caps_to_mp4mux, NULL);
	gst_caps_unref(caps_to_mp4mux);

	gst_bin_add_many(GST_BIN(m_pipeline), record_queue, caps_mp4mux_filter, mp4mux, file_sink, NULL);

	gst_element_sync_state_with_parent(record_queue);

	if (gst_element_link_many(record_queue, caps_mp4mux_filter, mp4mux, file_sink, NULL) != TRUE)
	{
		g_printerr("GstData.mp4mux could not link GstData.file_sink\n");

		return FALSE;
	}
	
	g_snprintf(elem_name, sizeof(elem_name), "tee%d", nStreamID);
	GstElement *tee = gst_bin_get_by_name(GST_BIN(m_pipeline), elem_name);

	设置为0时代表禁用,此处必须要禁用这些参数才能达到效果(因为最大值,是以先达到的条件为准)
	g_object_set(G_OBJECT(record_queue), "max-size-bytes", 0, NULL);
	g_object_set(G_OBJECT(record_queue), "max-size-time", 0, NULL);
	g_object_set(G_OBJECT(record_queue), "max-size-buffers", 200, NULL);
	g_object_set(G_OBJECT(record_queue), "leaky", 2, NULL); //设置丢弃旧缓冲区数据


	g_snprintf(tee_src_pad_name, 15, "src_%d", nRecodePadID);
	GstPad *tee_src_recodepad = gst_element_get_static_pad(tee, tee_src_pad_name);
	if (NULL == tee_src_recodepad)
	{
		dzlog_error("get tee_src_recodepad failed");
		return FALSE;
	}
	GstPad *record_queue_sink_pad = gst_element_get_static_pad(record_queue, "sink");
	if (NULL == record_queue_sink_pad)
	{
		dzlog_error("get record_queue record_queue_sink_pad failed");
		return FALSE;
	}

	// link the pads
	if (gst_pad_link(tee_src_recodepad, record_queue_sink_pad) != GST_PAD_LINK_OK) {
		gchar *n1 = gst_pad_get_name(tee_src_recodepad);
		gchar *n2 = gst_pad_get_name(record_queue_sink_pad);
		dzlog_error("Failed to link '%s' and '%s'", n1, n2);
		g_free(n1);
		g_free(n2);
		return FALSE;
		// handle errors in any way you see fit (maybe exit)
	}
	// don't forget to unref your pad pointers
	gst_object_unref(tee_src_recodepad);
	gst_object_unref(record_queue_sink_pad);
	
	GstPad *record_queue_src_pad = gst_element_get_static_pad(record_queue, "src");
	if (!record_queue_src_pad)
		dzlog_info("Unable to get src pad\n");
	else

	{
		m_vecBlockid[nStreamID] = gst_pad_add_probe(record_queue_src_pad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
			pad_probe_cb, this, NULL);//设置阻塞探针回调

	}
	gst_object_unref(record_queue_src_pad);

	dzlog_info("StreamMp4 end ");

	return TRUE;
}


int AIStreamSaveToMp4::StartMp4(int nStreamID)
{
	gchar elem_name[50] = { 0 };
	g_snprintf(elem_name, sizeof(elem_name), "record_queue%d", nStreamID);
	GstElement *record_queue = gst_bin_get_by_name(GST_BIN(m_pipeline), elem_name);
	g_snprintf(elem_name, sizeof(elem_name), "file-sink%d", nStreamID);
	GstElement *file_sink = gst_bin_get_by_name(GST_BIN(m_pipeline), elem_name);
	if (NULL == file_sink)
	{
		dzlog_error("file_sink is null");
        return FALSE;
	}
	GstPad *record_queue_src_pad = gst_element_get_static_pad(record_queue, "src");
	if (!record_queue_src_pad)
		dzlog_info("Unable to get src pad\n");
	else
	{
		gst_pad_remove_probe(record_queue_src_pad, m_vecBlockid[nStreamID]);  //删除阻塞探针回调
	}
		
	gst_object_unref(record_queue_src_pad);
	gst_element_set_state(m_pipeline, GST_STATE_PLAYING);
	return TRUE;
}

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值