我正在尝试在 C 中使用 gstreamer 创建一个循环缓冲区。目前源是我的 PC 网络摄像头“v4l2src”。 一旦缓冲区列表达到预定义的大小,我就会刷新列表中最旧的缓冲区并插入一个新缓冲区。 我正在使用 gst_buffer_list 来实现相同的目的。我需要这个循环缓冲区连续运行,当收到任何回调时, 我复制此缓冲区列表并使用 emit 信号属性将其发送到另一个管道的 appsrc。两条管道都在以下链接下:
出于测试目的,我直接编写代码,如果 buffer_length == 50 ---> 然后创建 buffer_list 的副本(深)并发送到 pipeline2
通过 g_emit_signal(push_buffer_list)。
我成功地获得了缓冲区(有一些我不明白为什么的警告)并保存到文件中。我可以看到文件大小也达到了几MB
但文件无法播放。当我用 ffplay
我想了解我是否正确使用了 push_buffer_list 信号。可以对这些 pipeline2 做些什么来使其正常工作。
#include <gst/gst.h>
#include<stdio.h>
#include <gst/app/gstappsrc.h>
#include <time.h>
static GstElement *pipeline, *video_src, *video_sink, *videoconvert, *videoencode,*identity, *identity1, *identity2,*queue, *fake_sink, *tee, *muxer;
static GstElement *pipeline2, *appsrc, *file_sink2;
GstBufferList *buflist, *copy_buflist;
static GstPad *blockpad, *probepad,*probepad2, *id2sink_pad, *fakesink_pad;
// GstPad *tee_src1_pad, *tee_src2_pad;
GstPadTemplate *templ;
GMainLoop *loop, *loop2;
GstBuffer *buff;
static gboolean
bus_cb (GstBus * bus, GstMessage * msg, gpointer user_data)
{
GMainLoop *loop = user_data;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_ERROR:{
GError *err = NULL;
gchar *dbg;
gst_message_parse_error (msg, &err, &dbg);
gst_object_default_error (msg->src, err, dbg);
g_clear_error (&err);
g_free (dbg);
g_main_loop_quit (loop);
break;
}
case GST_MESSAGE_EOS:{
g_print ("End-Of-Stream reached.\n");
// g_main_loop_quit (loop2);
// gst_element_set_state (pipeline2, GST_STATE_NULL);
// g_main_loop_unref (loop2);
// gst_object_unref (pipeline2);
exit(0);
break;
}
case GST_MESSAGE_WARNING:{
GError *err = NULL;
gchar *name, *debug = NULL;
name = gst_object_get_path_string (msg->src);
gst_message_parse_warning (msg, &err, &debug);
g_printerr ("ERROR: from element %s: %s\n", name, err->message);
if (debug != NULL)
g_printerr ("Additional debug info:\n%s\n", debug);
g_error_free (err);
g_free (debug);
g_free (name);
break;
}
default:
break;
}
return TRUE;
}
static GstPadProbeReturn
block_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
g_print("in block prode\n");
return GST_PAD_PROBE_OK;
}
static GstPadProbeReturn
debug_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
g_print("coming in fakesink \n");
return GST_PAD_PROBE_OK;
}
static GstPadProbeReturn
pad_probe_buflist_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
GstFlowReturn retval;
g_print("coming in filesink Senfing EOS NOW\n");
// g_signal_emit_by_name (appsrc, "end-of-stream", &retval);
gst_element_send_event(pipeline2, gst_event_new_eos());
return GST_PAD_PROBE_OK;
}
static GstPadProbeReturn
pad_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
GstFlowReturn retval;
static GstClockTime timestamp = 0;
g_print("pointer comes in pad probe\n");
// g_print("probe type %d \n",info->type);
buff = gst_pad_probe_info_get_buffer(info);
// GST_BUFFER_PTS (buff) = timestamp;
// GST_BUFFER_DURATION (buff) = gst_util_uint64_scale_int (1, GST_SECOND, 2);
// timestamp += GST_BUFFER_DURATION (buff);
guint buflen = gst_buffer_list_length (buflist);
g_print("buffer length is %d \n",buflen);
if (buflen==50) // This is custom
{
copy_buflist = gst_buffer_list_copy_deep (buflist);
gst_element_set_state (pipeline2, GST_STATE_PLAYING);
g_signal_emit_by_name (appsrc, "push-buffer-list", copy_buflist, &retval);
//GstFlowReturn retval = gst_app_src_push_buffer_list((GstAppSrc*)appsrc,copy_buflist);
g_print("RETVAL %d\n", retval);
g_signal_emit_by_name (appsrc, "end-of-stream", &retval);
}
// retval = gst_app_src_end_of_stream ((GstAppSrc*)appsrc);
// g_print("RETVAL %d\n", retval);
gst_buffer_list_insert(buflist, buflen,buff);
// gst_buffer_unref (buff);
// gst_buffer (copy_buflist);
return GST_PAD_PROBE_OK;
}
int tutorial_main(int argc, char *argv[])
{
GstBus *bus;
GstMessage *msg;
GstStateChangeReturn ret,ret2;
GstStateChangeReturn sret;
GstStateChangeReturn pause_flag =0;
GstState state;
GstPad *srcpad;
gst_init (&argc, &argv);
video_src = gst_element_factory_make ("v4l2src", "source");
appsrc = gst_element_factory_make ("appsrc", "source");
videoconvert = gst_element_factory_make("videoconvert","convert");
videoencode = gst_element_factory_make("x264enc","encode");
video_sink = gst_element_factory_make ("filesink", "sink");
fake_sink = gst_element_factory_make ("fakesink", "fakesink");
identity1 = gst_element_factory_make ("identity", "identity1");
identity2 = gst_element_factory_make ("identity", "identity2");
muxer = gst_element_factory_make("flvmux", "muxer");
queue = gst_element_factory_make ("queue", "queue");
tee = gst_element_factory_make("tee","tee");
file_sink2 = gst_element_factory_make ("filesink", "filesink");
pipeline = gst_pipeline_new ("test-pipeline");
pipeline2 = gst_pipeline_new ("filesink-pipeline");
g_object_set(file_sink2,"location","/home/jafar/gps.mp4",NULL); /// YOU NEED TO UPDATE THE FILE LOCATION
if (!pipeline || !video_src || !video_sink ||!videoconvert ||!videoencode ||!identity1 ||!identity2 ||!fake_sink ) {
g_printerr ("Not all elements could be created.\n");
return -1;
}
//Pipeline1 v4l2---->createbuffer---->fakesink
gst_bin_add_many (GST_BIN (pipeline), video_src,videoconvert,videoencode,identity1,identity2,fake_sink, NULL);
if ((gst_element_link_many(video_src,videoconvert,videoencode,identity1, identity2, fake_sink, NULL) != TRUE )) {
g_printerr ("Elements could not be linked.\n");
gst_object_unref (pipeline);
return -1;
}
//pipeline2 buffer--->appsrc---->filesink
gst_bin_add_many (GST_BIN (pipeline2), appsrc,file_sink2, NULL);
if ((gst_element_link_many(appsrc,file_sink2, NULL) != TRUE )) {
g_printerr ("Elements could not be linked.\n");
gst_object_unref (pipeline2);
return -1;
}
fakesink_pad = gst_element_get_static_pad(fake_sink, "sink");
id2sink_pad = gst_element_get_static_pad(identity2, "sink");
//Get forurce pad
probepad = gst_element_get_static_pad (identity1, "src");
probepad2 = gst_element_get_static_pad (file_sink2, "sink");
buflist = gst_buffer_list_new(); //init bufffer list
//install a probe on identity1 src pad
gst_pad_add_probe (probepad, GST_PAD_PROBE_TYPE_BUFFER, pad_probe_cb, loop, NULL);
g_print("COde blocks here!!!\n");
time_t now = time(NULL);
/* Start playing */
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
ret2 = gst_element_set_state (pipeline2, GST_STATE_PAUSED);
bus = gst_element_get_bus (pipeline);
loop = g_main_loop_new (NULL, FALSE);
loop2 = g_main_loop_new (NULL, FALSE);
gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), bus_cb, loop);
gst_bus_add_watch (GST_ELEMENT_BUS (pipeline2), bus_cb, loop2);
// g_timeout_add_seconds (20, timeout_cb, loop);
g_main_loop_run (loop);
g_main_loop_run (loop2);
/* Free resources */
gst_object_unref (bus);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_element_set_state (pipeline2, GST_STATE_NULL);
gst_object_unref (blockpad);
gst_object_unref (probepad);
gst_bus_remove_watch (GST_ELEMENT_BUS (pipeline));
gst_object_unref (pipeline);
gst_object_unref (pipeline2);
g_main_loop_unref (loop);
g_main_loop_unref (loop2);
return 0;
}
int
main (int argc, char *argv[])
{
#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
return gst_macos_main (tutorial_main, argc, argv, NULL);
#else
return tutorial_main (argc, argv);
#endif
}
编译代码执行如下:
gcc buffer-tutorial-2.c -o buffer-tutorial-2 `pkg-config --cflags --libs gstreamer-1.0 gstreamer-audio-1.0` -lgstapp-1.0
看起来这里的答案是通过
typefind
提供的Gstreamer
功能找出视频流的格式,并在第二个管道中相应地设置caps
元素的appsrc
。另外,我现在使用 g_signal_emit_by_name (appsrc, "push-buffer-list", copy_buflist, &retval)
而不是使用 GstFlowReturn retval = gst_app_src_push_buffer_list((GstAppSrc*)appsrc,copy_buflist)
。第二个功能由 gstreamer 文档中的 appsrc API 专门提供。我假设这两个函数的行为相同,但我不知道有什么区别。这需要查看 Gst 源代码。还有一个值得注意的有趣点是,当发送带有 gst_pad_send_event(appsrccpad, gst_event_new_eos())
的 EOS 时,视频创建失败了,但是,如果我发送带有此
g_signal_emit_by_name (appsrc, "end-of-stream", &retval)
的 EOS,我从缓冲区列表中得到了生成的视频。我在这里发布上面重构和更正的代码。有很多额外的变量,没有使用 unrefs,不要介意它们。
#include <gst/gst.h>
#include<stdio.h>
#include <gst/app/gstappsrc.h>
#include <time.h>
typedef struct _CustomData {
GstElement *pipeline; /* Our one and only element */
gboolean playing; /* Are we in the PLAYING state? */
gboolean terminate; /* Should we terminate execution? */
gboolean seek_enabled; /* Is seeking enabled for this media? */
gboolean seek_done; /* Have we performed the seek already? */
gint64 duration; /* How long does this media last, in nanoseconds */
} CustomData;
static GstElement *pipeline, *video_src, *video_sink, *videoconvert, *videoencode,*identity, *identity1, *identity2,*identity3,*queue, *fake_sink, *tee, *muxer;
static GstElement *pipeline2, *appsrc, *file_sink2, *decodebin;
GstBufferList *buflist, *copy_buflist;
static GstPad *blockpad, *probepad,*probepad2, *id2sink_pad, *fakesink_pad, *srccpad;
GstPad *tee_src1_pad, *tee_src2_pad;
GstPadTemplate *templ;
GMainLoop *loop, *loop2;
static gboolean
bus_cb (GstBus * bus, GstMessage * msg, gpointer user_data)
{
GMainLoop *loop = user_data;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_ERROR:{
GError *err = NULL;
gchar *dbg;
gst_message_parse_error (msg, &err, &dbg);
gst_object_default_error (msg->src, err, dbg);
g_clear_error (&err);
g_free (dbg);
g_main_loop_quit (loop);
break;
}
case GST_MESSAGE_EOS:{
g_print ("End-Of-Stream reached.\n");
// g_main_loop_quit (loop2);
// gst_element_set_state (pipeline2, GST_STATE_NULL);
// g_main_loop_unref (loop2);
// gst_object_unref (pipeline2);
// exit(0);
break;
}
case GST_MESSAGE_WARNING:{
GError *err = NULL;
gchar *name, *debug = NULL;
name = gst_object_get_path_string (msg->src);
gst_message_parse_warning (msg, &err, &debug);
g_printerr ("ERROR: from element %s: %s\n", name, err->message);
if (debug != NULL)
g_printerr ("Additional debug info:\n%s\n", debug);
g_error_free (err);
g_free (debug);
g_free (name);
break;
}
default:
break;
}
return TRUE;
}
static GstPadProbeReturn
pad_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
GstFlowReturn retval;
static GstClockTime timestamp = 0;
GstMapInfo map;
GstBuffer *buff;
GstBuffer *new_buffer;
g_print("pointer comes in pad probe\n");
// g_print("probe type %d \n",info->type);
buff = gst_pad_probe_info_get_buffer(info);
// GST_BUFFER_PTS (buff) = timestamp;
// GST_BUFFER_DURATION (buff) = gst_util_uint64_scale_int (1, GST_SECOND, 2);
// timestamp += GST_BUFFER_DURATION (buff);
guint buflen = gst_buffer_list_length (buflist);
g_print("%ld\n",gst_buffer_get_size(buff));
g_print("buffer length is %d \n",buflen);
if (buflen==50)
{
copy_buflist = gst_buffer_list_copy_deep (buflist);
gst_element_set_state (pipeline2, GST_STATE_PLAYING);
GstFlowReturn retval = gst_app_src_push_buffer_list((GstAppSrc*)appsrc,copy_buflist);
g_print("RETVAL %d\n", retval);
g_print("Sending EOS!!!!!!!");
g_signal_emit_by_name (appsrc, "end-of-stream", &retval);
// gst_buffer_list_unref(copy_buflist);
}
g_print("writable buffer %d\n",gst_buffer_is_writable (buff));
// gst_clear_buffer (&buff);
new_buffer = gst_buffer_copy_deep(buff);
g_print(" timestamp : %ld\n",GST_BUFFER_PTS (new_buffer)/1000000000);
// g_signal_emit_by_name (appsrc, "push-buffer", new_buffer, &retval);
g_print("new buffer size %ld\n",gst_buffer_get_size(new_buffer));
gst_buffer_list_add(buflist, new_buffer);
// gst_buffer_unref (new_buffer);
return GST_PAD_PROBE_OK;
}
static gboolean print_field (GQuark field, const GValue * value, gpointer pfx) {
gchar *str = gst_value_serialize (value);
g_print ("%s %15s: %s\n", (gchar *) pfx, g_quark_to_string (field), str);
g_free (str);
return TRUE;
}
static void print_caps (const GstCaps * caps, const gchar * pfx) {
guint i;
g_return_if_fail (caps != NULL);
if (gst_caps_is_any (caps)) {
g_print ("%sANY\n", pfx);
return;
}
if (gst_caps_is_empty (caps)) {
g_print ("%sEMPTY\n", pfx);
return;
}
for (i = 0; i < gst_caps_get_size (caps); i++) {
GstStructure *structure = gst_caps_get_structure (caps, i);
g_print ("%s%s\n", pfx, gst_structure_get_name (structure));
gst_structure_foreach (structure, print_field, (gpointer) pfx);
}
}
/* Shows the CURRENT capabilities of the requested pad in the given element */
static void print_pad_capabilities (GstElement *element, gchar *pad_name) {
GstPad *pad = NULL;
GstCaps *caps = NULL;
/* Retrieve pad */
pad = gst_element_get_static_pad (element, pad_name);
if (!pad) {
g_printerr ("Could not retrieve pad '%s'\n", pad_name);
return;
}
/* Retrieve negotiated caps (or acceptable caps if negotiation is not finished yet) */
caps = gst_pad_get_current_caps (pad);
if (!caps)
caps = gst_pad_query_caps (pad, NULL);
/* Print and free */
g_print ("Caps for the %s pad:\n", pad_name);
print_caps (caps, " ");
gst_caps_unref (caps);
gst_object_unref (pad);
}
static void
cb_typefound (GstElement *typefind,
guint probability,
GstCaps *caps,
gpointer data)
{
GMainLoop *loop = data;
gchar *type;
type = gst_caps_to_string (caps);
g_print ("Media type %s found, probability %d%%\n", type, probability);
g_free (type);
/* since we connect to a signal in the pipeline thread context, we need
* to set an idle handler to exit the main loop in the mainloop context.
* Normally, your app should not need to worry about such things. */
// g_idle_add (idle_exit_loop, loop);
}
int tutorial_main(int argc, char *argv[])
{
GstBus *bus;
GstMessage *msg;
GstStateChangeReturn ret,ret2;
GstStateChangeReturn sret;
GstStateChangeReturn pause_flag =0;
GstState state;
GstPad *srcpad;
CustomData data;
data.playing = FALSE;
data.terminate = FALSE;
data.seek_enabled = FALSE;
data.seek_done = FALSE;
data.duration = GST_CLOCK_TIME_NONE;
gst_init (&argc, &argv);
video_src = gst_element_factory_make ("v4l2src", "source");
appsrc = gst_element_factory_make ("appsrc", "source");
videoconvert = gst_element_factory_make("videoconvert","convert");
videoencode = gst_element_factory_make("x264enc","encode");
video_sink = gst_element_factory_make ("filesink", "sink");
fake_sink = gst_element_factory_make ("fakesink", "fakesink");
identity1 = gst_element_factory_make ("identity", "identity1");
identity3 = gst_element_factory_make ("identity", "identity3");
decodebin = gst_element_factory_make ("decodebin", "dec");
muxer = gst_element_factory_make("mp4mux", "muxer");
queue = gst_element_factory_make ("queue", "queue");
tee = gst_element_factory_make("tee","tee");
GstElement *typefind = gst_element_factory_make("typefind","typefind");
file_sink2 = gst_element_factory_make ("filesink", "filesink");
// file_sink2 = gst_element_factory_make ("autovideosink", "filesink");
pipeline = gst_pipeline_new ("test-pipeline");
pipeline2 = gst_pipeline_new ("filesink-pipeline");
g_object_set (G_OBJECT (appsrc), "caps",
gst_caps_new_simple ("video/x-raw",
"format", G_TYPE_STRING, "YUY2",
"width", G_TYPE_INT, 1920,
"height", G_TYPE_INT, 1080,
"framerate", GST_TYPE_FRACTION, 5, 1,
"colorimetry", G_TYPE_STRING, "2:4:5:1",
"interlace-mode", G_TYPE_STRING,"progressive",
NULL), NULL);
g_object_set (G_OBJECT (appsrc),
"stream-type", 0,
"format", GST_FORMAT_TIME, NULL);
// g_object_set(file_sink,"location","/home/jafar/gs.mp4",NULL);
g_object_set(file_sink2,"location","/home/jafar/gpps.mp4",NULL);
// g_object_set(G_OBJECT(appsrc), "caps", caps, NULL);
if (!pipeline || !video_src || !video_sink ||!videoconvert ||!videoencode ||!identity1 ||!queue ||!fake_sink ||! tee ||!file_sink2) {
g_printerr ("Not all elements could be created.\n");
return -1;
}
// gst_bin_add_many (GST_BIN (pipeline), video_src,videoconvert,videoencode, identity1, tee, fake_sink, identity2,file_sink, NULL);
// if ((gst_element_link_many(video_src,videoconvert,videoencode,identity1, tee, NULL) != TRUE )||
// (gst_element_link_many(identity2,file_sink,NULL))!=TRUE) {
// g_printerr ("Elements could not be linked.\n");
// gst_object_unref (pipeline);
// return -1;
// }
//Pipeline1 v4l2---->buffer---->fakesink
gst_bin_add_many (GST_BIN (pipeline), video_src,identity1,fake_sink, NULL);
if ((gst_element_link_many(video_src,identity1, fake_sink, NULL) != TRUE )) {
g_printerr ("Elements could not be linked.\n");
gst_object_unref (pipeline);
return -1;
}
//pipeline2 appsrc---->filesink
//videoencode,identity3,muxer
gst_bin_add_many (GST_BIN (pipeline2), appsrc,videoconvert,videoencode,muxer,file_sink2, NULL);
if ((gst_element_link_many(appsrc,videoconvert,videoencode,muxer,file_sink2, NULL) != TRUE )) {
g_printerr ("Elements could not be linkedin pipeline2.\n");
gst_object_unref (pipeline2);
return -1;
}
//Link Tee elements to fakesink and filesink
//link tee srcpad to sinkpad of filesink
// templ = gst_element_class_get_pad_template(GST_ELEMENT_GET_CLASS(tee), "src_%u");
// tee_src1_pad = gst_element_request_pad(tee, templ,"src_1",NULL);
fakesink_pad = gst_element_get_static_pad(fake_sink, "sink");
// tee_src2_pad = gst_element_request_pad(tee, templ,"src_2",NULL);
id2sink_pad = gst_element_get_static_pad(identity2, "sink");
// if (gst_pad_link (tee_src1_pad, fakesink_pad) != GST_PAD_LINK_OK ||
// gst_pad_link (tee_src2_pad, id2sink_pad) != GST_PAD_LINK_OK)
// {
// g_printerr ("Tee could not be linked\n");
// gst_object_unref (data.pipeline);
// return -1;
// }
print_pad_capabilities (appsrc, "src");
print_pad_capabilities (videoconvert, "src");
print_pad_capabilities (videoconvert, "sink");
// srccpad = gst_element_get_static_pad(video_src,"src");
// gst_pad_set_caps (srccpad, caps);
// if (!gst_pad_set_caps (srccpad, caps))
// {
// g_print("ERROR in CAPS");
// return GST_FLOW_ERROR;
// }
//Get forurce pad
probepad = gst_element_get_static_pad (identity1, "src");
// probepad2 = gst_element_get_static_pad (file_sink2, "sink");
// blockpad = gst_element_get_static_pad (tee, "src_2");
buflist = gst_buffer_list_new();
gst_pad_add_probe (probepad, GST_PAD_PROBE_TYPE_BUFFER, pad_probe_cb, loop, NULL);
g_print("COde blocks here!!!\n");
time_t now = time(NULL);
/* Start playing */
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
ret2 = gst_element_set_state (pipeline2, GST_STATE_PLAYING);
data.pipeline = pipeline;
/* Wait until error or EOS */
bus = gst_element_get_bus (pipeline);
loop = g_main_loop_new (NULL, FALSE);
loop2 = g_main_loop_new (NULL, FALSE);
gst_bus_add_watch (GST_ELEMENT_BUS (data.pipeline), bus_cb, loop);
gst_bus_add_watch (GST_ELEMENT_BUS (pipeline2), bus_cb, loop2);
g_signal_connect (typefind, "have-type", G_CALLBACK (cb_typefound), loop);
g_main_loop_run (loop);
g_main_loop_run (loop2);
/* Free resources */
gst_object_unref (bus);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_element_set_state (pipeline2, GST_STATE_NULL);
gst_object_unref (blockpad);
gst_object_unref (probepad);
gst_bus_remove_watch (GST_ELEMENT_BUS (pipeline));
gst_object_unref (pipeline);
gst_object_unref (pipeline2);
g_main_loop_unref (loop);
g_main_loop_unref (loop2);
return 0;
}
int
main (int argc, char *argv[])
{
#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
return gst_macos_main (tutorial_main, argc, argv, NULL);
#else
return tutorial_main (argc, argv);
#endif
}
可以使用问题中描述的相同命令编译和执行代码。