关于GStreamer 保存摄像头数据为MP4

news/2024/11/24 13:04:15/

最近项目用到了这个需求,然后使用gstreamer代码去实现把usb摄像头的数据保存为MP4数据,以前弄过转换为h264的,所以一开始都很顺利了,但是发现保存下来的文件播放器打不开,用工具检测发现没有PTS也就是时间戳,想起来h264是没有时间的,保存为MP4需要自己加PTS时间戳。

所以就研究怎么加,一开始怎么加都不行,直接在appsrc里面手动为GST_BUFFER_PTS修改也不行,用网上其他人的办法,但是一用pipeline里的clock又报错,纠结时了。

命令行执行时发现加-e就可以实现,问题又来到了-e怎么添加进代码,后面发现src有个eos的actions,在每次结束时手动为他发送这个信号然后就可以了。

"end-of-stream" :  GstFlowReturn user_function (GstElement* object);

做个笔记,具体详情以后再补充把。

注册bus的结束信号,然后结束录像通过发送信号来结束。

g_signal_connect (G_OBJECT (bus), "message::eos", (GCallback)eos_cb, c_data);

这是结束录像的接口。


/*** @brief gstreamer_yuv2mp4_init** @param CustomData *c_data  : customdata* @return 1*/
int gstreamer_yuv2mp4_deinit(CustomData *c_data)
{g_print ("gstreamer_h264enc_deinit.\n");GstFlowReturn ret;g_signal_emit_by_name (c_data->app_source, "end-of-stream", &ret);/*等待线程终止*/pthread_join(pid, NULL);// printf("pthread join\n");
}

放入录像数据的时候要插入pts。

/*** @brief yuv2_to_mp4** @param CustomData *data  : gstreamer customdata* @param void *data  : picture yuv data* @return 1*/
int gstreamer_yuv2mp4_send(CustomData *c_data, void *src_data)
{GstBuffer *buffer;GstFlowReturn ret;GstMapInfo map;/* Create a new empty buffer */buffer = gst_buffer_new_and_alloc (c_data->src_data_size);/* Set its timestamp and duration */struct timeval tv;struct timezone tz;long long t = 0;gettimeofday(&tv, &tz);t = (tv.tv_sec*1000000 + tv.tv_usec)*1000 - my_basetime;GST_BUFFER_PTS(buffer) = t; //插入pts,配合eos生成带pts的MP4GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale_int(1, GST_SECOND, 30 / 1);gst_buffer_map (buffer, &map, GST_MAP_WRITE);memcpy(map.data, src_data, c_data->src_data_size);gst_buffer_unmap (buffer, &map);g_signal_emit_by_name (c_data->app_source, "push-buffer", buffer, &ret);gst_buffer_unref (buffer);if ( !!(ret) ) {/* We got some error, stop sending data */return FALSE;}return TRUE;
}

不是很会讲,直接贴代码吧。下面是完整的部分


#include <stdio.h>
#include <string.h>
#include <pthread.h>#include "gst_yuv2mp4.h"/*
gst-launch-1.0 appsrc ! \'video/x-raw, width=(int)1280, height=(int)720, \format=(string)YUY2, framerate=(fraction)30/1' ! nvvidconv ! \'video/x-raw(memory:NVMM), format=(string)I420' ! nvv4l2h264enc ! h264parse ! qtmux \! filesink \location=test.mp4 -e
*/static void  _gstreamer_h264enc_init (CustomData *c_data);
static void  _gstreamer_h264enc_run (void *arg);static pthread_t pid;
static long long my_basetime;/*** @brief gstreamer_yuv2mp4_init** @param CustomData *c_data  : customdata* @param int w,int h : picture high wight* @param int size : picture size * @param char *file : must *.mp4* @return 1*/
int gstreamer_yuv2mp4_init(CustomData *c_data, int w, int h, long size, char *file)
{/* Initialize custom data structure */memset(c_data, 0, sizeof (CustomData));c_data->src_data_size = size;c_data->width = w;c_data->height = h;c_data->file = file;_gstreamer_h264enc_init(c_data);// _gstreamer_h264enc_init(c_data);if( pthread_create(&pid, NULL, (void *)_gstreamer_h264enc_run, (void *)c_data) != 0){printf("create thread fail\n");}
}/*** @brief gstreamer_yuv2mp4_init** @param CustomData *c_data  : customdata* @return 1*/
int gstreamer_yuv2mp4_deinit(CustomData *c_data)
{g_print ("gstreamer_h264enc_deinit.\n");GstFlowReturn ret;g_signal_emit_by_name (c_data->app_source, "end-of-stream", &ret);/*等待线程终止*/pthread_join(pid, NULL);// printf("pthread join\n");
}/*** @brief yuv2_to_mp4** @param CustomData *data  : gstreamer customdata* @param void *data  : picture yuv data* @return 1*/
int gstreamer_yuv2mp4_send(CustomData *c_data, void *src_data)
{GstBuffer *buffer;GstFlowReturn ret;GstMapInfo map;/* Create a new empty buffer */buffer = gst_buffer_new_and_alloc (c_data->src_data_size);/* Set its timestamp and duration */struct timeval tv;struct timezone tz;long long t = 0;gettimeofday(&tv, &tz);t = (tv.tv_sec*1000000 + tv.tv_usec)*1000 - my_basetime;GST_BUFFER_PTS(buffer) = t; //插入pts,配合eos生成带pts的MP4GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale_int(1, GST_SECOND, 30 / 1);gst_buffer_map (buffer, &map, GST_MAP_WRITE);memcpy(map.data, src_data, c_data->src_data_size);gst_buffer_unmap (buffer, &map);g_signal_emit_by_name (c_data->app_source, "push-buffer", buffer, &ret);gst_buffer_unref (buffer);if ( !!(ret) ) {/* We got some error, stop sending data */return FALSE;}return TRUE;
}/********************************************************************************************************************//* This function is called when an error message is posted on the bus */
static void error_cb (GstBus *bus, GstMessage *msg, CustomData *c_data) {GError *err;gchar *debug_info;/* Print error details on the screen */gst_message_parse_error (msg, &err, &debug_info);g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");g_clear_error (&err);g_free (debug_info);g_main_loop_quit (c_data->main_loop);
}/* This function is called when an eos message is posted on the bus */
static void eos_cb (GstBus *bus, GstMessage *msg, CustomData *c_data) {g_print ("End-Of-Stream reached.\n");g_main_loop_quit (c_data->main_loop);
}static void _gstreamer_h264enc_init (CustomData *c_data)
{GstCaps *caps;GstBus *bus;/* Initialize GStreamer */gst_init (NULL, NULL);/* Create the elements */c_data->app_source  = gst_element_factory_make ("appsrc", "source");c_data->capsfilter1 = gst_element_factory_make ("capsfilter", "caps1");c_data->convert     = gst_element_factory_make ("nvvidconv", "convert");c_data->capsfilter2 = gst_element_factory_make ("capsfilter", "caps2");// c_data->queue       = gst_element_factory_make ("queue", "_queue");c_data->h264enc     = gst_element_factory_make ("nvv4l2h264enc", "h264_enc");c_data->parse       = gst_element_factory_make ("h264parse", "_parse");c_data->qtmux       = gst_element_factory_make ("qtmux", "_qtmux"); //mp4 复用文件c_data->sink    = gst_element_factory_make ("filesink", "sink");/* Create the empty pipeline */c_data->pipeline = gst_pipeline_new ("test-pipeline");if (!c_data->pipeline || !c_data->app_source || !c_data->capsfilter1 ||!c_data->convert || !c_data->capsfilter2 || /*!c_data->queue || */!c_data->h264enc || !c_data->parse || !c_data->qtmux || !c_data->sink) {g_printerr ("Not all elements could be created.\n");return;}/* Configure appsrc */// g_object_set (c_data->app_source, "do-timestamp", TRUE, NULL);/* Configure capsfilter */caps = gst_caps_new_simple("video/x-raw","width", G_TYPE_INT, c_data->width,"height", G_TYPE_INT, c_data->height,"framerate", GST_TYPE_FRACTION, 30, 1,"format", G_TYPE_STRING, "YUY2",NULL);g_object_set (c_data->capsfilter1, "caps", caps, NULL);gst_caps_unref(caps);/* GStreamer-CRITICAL */// caps = gst_caps_new_simple("video/x-raw(memory:NVMM)",//             "format", G_TYPE_STRING, "I420",NULL);// g_object_set (c_data->capsfilter2, "caps", caps, NULL);// gst_caps_unref(caps);/* Configure nvv4l2h264enc *//*(0): DisablePreset    - Disable HW-Preset(1): UltraFastPreset  - UltraFastPreset for high perf(2): FastPreset       - FastPreset(3): MediumPreset     - MediumPreset(4): SlowPreset       - SlowPreset  (0): Baseline         - GST_V4L2_H264_VIDENC_BASELINE_PROFILE(2): Main             - GST_V4L2_H264_VIDENC_MAIN_PROFILE(4): High */g_object_set(G_OBJECT(c_data->h264enc), "preset-level", 4, NULL); // (1): UltraFastPresetg_object_set(G_OBJECT(c_data->h264enc), "profile", 0, NULL);g_object_set(G_OBJECT(c_data->h264enc), "maxperf-enable", 1, NULL);   //启用最大性能模式g_object_set(G_OBJECT(c_data->h264enc), "capture-io-mode", 2, NULL);   //g_object_set(G_OBJECT(c_data->h264enc), "output-io-mode" , 5, NULL);   //g_object_set(G_OBJECT(c_data->h264enc), "iframeinterval", 10, NULL);  //设置I帧间隔g_object_set(G_OBJECT(c_data->h264enc), "idrinterval",    10, NULL);g_object_set(G_OBJECT(c_data->h264enc), "control-rate", 	0, NULL);   //码率控制模式 (0:可变比特率,1:恒定比特率)g_object_set(G_OBJECT(c_data->h264enc), "bitrate", 		8*1024*1024, NULL);//码率g_object_set(G_OBJECT(c_data->h264enc), "peak-bitrate", 	8*1024*1024, NULL);//峰值比特率g_object_set(G_OBJECT(c_data->h264enc), "num-B-Frames",   0, NULL);//B帧设置为0g_object_set(G_OBJECT(c_data->h264enc), "insert-sps-pps", 1, NULL);//在IDR插入SPS和PPSg_object_set(G_OBJECT(c_data->h264enc), "insert-aud",      0, NULL);//插入 AUDg_object_set(G_OBJECT(c_data->h264enc), "poc-type",        2, NULL);//解码/编码顺序和显示顺序相同g_object_set(G_OBJECT(c_data->h264enc), "disable-cabac",   1, NULL);//解码/编码顺序和显示顺序相同disable-cabacg_object_set(G_OBJECT(c_data->sink), "location", c_data->file, NULL);g_object_set(G_OBJECT(c_data->sink), "sync", FALSE, NULL);/* Link all elements that can be automatically linked because they have "Always" pads */gst_bin_add_many (GST_BIN (c_data->pipeline), c_data->app_source, c_data->capsfilter1, c_data->convert, c_data->capsfilter2, c_data->h264enc, c_data->parse, c_data->qtmux, c_data->sink, NULL);if (gst_element_link_many (c_data->app_source, c_data->capsfilter1, c_data->convert, c_data->capsfilter2,c_data->h264enc, c_data->parse, c_data->qtmux, c_data->sink, NULL) != TRUE) {g_printerr ("Elements could not be linked.\n");gst_object_unref (c_data->pipeline);return;}/* Instruct the bus to emit signals for each received message, and connect to the interesting signals */bus = gst_element_get_bus (c_data->pipeline);gst_bus_add_signal_watch (bus);g_signal_connect (G_OBJECT (bus), "message::error", (GCallback)error_cb, c_data);g_signal_connect (G_OBJECT (bus), "message::eos", (GCallback)eos_cb, c_data);gst_object_unref (bus);
}static void  _gstreamer_h264enc_run (void *arg)
{CustomData *c_data = (CustomData *)arg;/* Start playing the pipeline */gst_element_set_state (c_data->pipeline, GST_STATE_PLAYING);/* Create a GLib Main Loop and set it to run */c_data->main_loop = g_main_loop_new (NULL, FALSE);g_main_loop_run (c_data->main_loop);/* Free resources */gst_element_set_state (c_data->pipeline, GST_STATE_NULL);gst_object_unref (c_data->pipeline);// printf("pthread exit\n");pthread_exit(NULL);
}

http://www.ppmy.cn/news/551467.html

相关文章

实现海康监控视频播放(录像回放)(抓拍,录像等功能)

1. 将需要存储的监控抓拍和录像功能存储到本地磁盘&#xff0c;使用输入报存到浏览器缓存里。在created拿取缓存数据&#xff0c;判断缓存里是否有数据。 如果没有弹出输入框。 <div v-if"cache true"><el-form><div>抓图存储地址</div><…

DXGI高帧率屏幕录像软件源码解析(声音捕获,抓屏,ffmpeg录像,MP4录像,flv录像,麦克风采集)(第4篇编码,录像部分)

本文DEMO源码下载&#xff1a; https://download.csdn.net/download/xjb2006/85109025 dxgi桌面屏幕录像&#xff08;windows屏幕录像&#xff0c;硬件编码&#xff0c;声音捕获&#xff0c;音视频同步&#xff09; 由于篇幅有限&#xff0c;分为4篇发表&#xff1a; 1、SD…

100ask_imx6ull视频监控项目-摄像头和声卡编程(五)

100ask_imx6ull视频监控项目-摄像头和声卡编程(五) 我们如果用程序怎么操作摄像头操作声卡呢 参考第二节的视频&#xff0c;mjpg-streamer库里面查看v4l2uvc.c就可以快速掌握摄像头 不是你是cmos还是什么类型的摄像头&#xff0c;我们的应用程序都可以用一套程序访问不同的硬件…

大华摄像头实现web端实时播放以及录像回放

文章目录 前言一、在乐橙云平台上注册账号并添加设备创建应用二、利用云平台的api获取到前端展示需要的相关信息三、前端页面展示内容总结 前言 前段时间对接了大华摄像头&#xff0c;在此做一次总结&#xff0c;总体思路是&#xff1a;把大华摄像头绑定到乐橙云平台上&#x…

linux系统如何添加硬盘设备

前言&#xff1a; 今天记录一下硬盘方面的知识&#xff0c;主要讲一下分区、挂载方面的知识&#xff0c;心情太郁闷了&#xff0c;假期的最后一天。 1、硬盘的命名规则 现在的硬盘设备一般都会以“/dev/sd”开头&#xff0c;而一台主机上可以有多块硬盘设备&#xff0c;因此系…

word文档解密方法

将加密的文档&#xff0c;另存为&#xff0c;保存类型&#xff0c;选择&#xff0c;Word XML文档&#xff1b;右击xml文档&#xff0c;选择&#xff0c;记事本打开&#xff0c;点击编辑&#xff0c;选择&#xff0c;查找&#xff1b;输入&#xff0c;enforcement&#xff0c;点…

word忘记密码处理

方法一&#xff1a; 用于word各版本。 1、把受保护的文件另存为XML格式&#xff0c;新存的文件用右键以记事本的方式打开&#xff0c;搜索“DocumentProtection”&#xff0c;在前面加上“un”&#xff0c;变为“unDocumentProtection”&#xff0c;保存。 2、用word打开修改后…

word密码忘了怎么解开

Word文件设置了密码&#xff0c;就可以控制自己的文件能够只给指定的人阅读&#xff0c;但是时间久了就忘记了自己设置的密码是什么了。该如何解开呢&#xff1f; 这个可以使用okfone WORD解密大师 解决&#xff0c;可以帮助解决忘记密码问题&#xff0c;也可以解决不能编辑文…