将 EoS 发送到文件接收器,同时从 tee 中删除分支

2024-01-06

我写了一个v4l2src同时显示和记录的代码。 我的管道看起来像:

               / [queue] ! [videosink]
v4l2src ! tee !  
               \ [queue] ! [filesink]

目前我可以一起显示+记录,还可以随意动态启动和停止记录分支(使用 ctrl+c sigint 处理程序进行启动/停止)。 我使用了@thiagoss的建议this https://stackoverflow.com/questions/27740644/in-gstreamer-adding-and-removing-queue-of-a-tee-dynamically答案以及部分内容this https://coaxion.net/blog/2014/01/gstreamer-dynamic-pipelines/文章。

问题 :

我面临的唯一问题是在取消链接时将 EoS 发送到 filesink 分支。我要发送到哪个元素gst_element_send_event(-->?<--, gst_event_new_eos());事件到?我无法将其发送到整个管道,因为 1. 分支现在已取消链接,2. 即使我这样做,它也会关闭视频接收器。

我尝试过的:在删除 mp4mux 并仅保存 h264 编码视频时,我可以使用 gst-playbin 来查看视频,这意味着分支创建和取消链接正确发生。

以下是我的代码。

#include <string.h>
#include <gst/gst.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

// v4l2src ! tee name=t t. ! x264enc ! mp4mux ! filesink location=/home/rish/Desktop/okay.264 t. ! videoconvert ! autovideosink

static GMainLoop *loop;
static GstElement *pipeline, *src, *tee, *encoder, *muxer, *filesink, *videoconvert, *videosink, *queue_record, *queue_display;
static GstBus *bus;
static GstPad *teepad;
static gboolean recording = FALSE;
static gint counter = 0;

static gboolean
message_cb (GstBus * bus, GstMessage * message, gpointer user_data)
{
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_ERROR:{
  GError *err = NULL;
  gchar *name, *debug = NULL;

  name = gst_object_get_path_string (message->src);
  gst_message_parse_error (message, &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);

  g_main_loop_quit (loop);
  break;
}
case GST_MESSAGE_WARNING:{
    GError *err = NULL;
    gchar *name, *debug = NULL;

    name = gst_object_get_path_string (message->src);
    gst_message_parse_warning (message, &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;
}
case GST_MESSAGE_EOS:{
    g_print ("Got EOS\n");
    g_main_loop_quit (loop);
    gst_element_set_state (pipeline, GST_STATE_NULL);
    g_main_loop_unref (loop);
    gst_object_unref (pipeline);
    exit(0);
    break;
}
default:
    break;
}

return TRUE;
}

static GstPadProbeReturn unlink_cb(GstPad *pad, GstPadProbeInfo *info, gpointer user_data) {
g_print("Unlinking...");
GstPad *sinkpad;
sinkpad = gst_element_get_static_pad (queue_record, "sink");
gst_pad_unlink (teepad, sinkpad);
gst_object_unref (sinkpad);

gst_element_send_event(filesink, gst_event_new_eos());

sleep(1);
gst_bin_remove(GST_BIN (pipeline), queue_record);
gst_bin_remove(GST_BIN (pipeline), encoder);
// gst_bin_remove(GST_BIN (pipeline), muxer);
gst_bin_remove(GST_BIN (pipeline), filesink);

gst_element_set_state(queue_record, GST_STATE_NULL);
gst_element_set_state(encoder, GST_STATE_NULL);
// gst_element_set_state(muxer, GST_STATE_NULL);
gst_element_set_state(filesink, GST_STATE_NULL);

gst_object_unref(queue_record);
gst_object_unref(encoder);
// gst_object_unref(muxer);
gst_object_unref(filesink);

gst_element_release_request_pad (tee, teepad);
gst_object_unref (teepad);

g_print("Unlinked\n");

return GST_PAD_PROBE_REMOVE;
}

void stopRecording() {
    g_print("stopRecording\n");
    gst_pad_add_probe(teepad, GST_PAD_PROBE_TYPE_IDLE, unlink_cb, NULL, (GDestroyNotify) g_free);
    recording = FALSE;
}

 void startRecording() {
g_print("startRecording\n");
GstPad *sinkpad;
GstPadTemplate *templ;

templ = gst_element_class_get_pad_template(GST_ELEMENT_GET_CLASS(tee), "src_%u");
teepad = gst_element_request_pad(tee, templ, NULL, NULL);
queue_record = gst_element_factory_make("queue", "queue_record");
encoder = gst_element_factory_make("x264enc", NULL);
// muxer = gst_element_factory_make("mp4mux", NULL);
filesink = gst_element_factory_make("filesink", NULL);
char *file_name = (char*) malloc(100*sizeof(char));
sprintf(file_name, "/home/rish/Desktop/rec%d.mp4", counter++);
g_print(file_name);
g_object_set(filesink, "location", file_name, NULL);
g_object_set(encoder, "tune", 4, NULL);
free(file_name);

gst_bin_add_many(GST_BIN(pipeline), gst_object_ref(queue_record), gst_object_ref(encoder), gst_object_ref(filesink), NULL);
gst_element_link_many(queue_record, encoder, filesink, NULL);

gst_element_sync_state_with_parent(queue_record);
gst_element_sync_state_with_parent(encoder);
// gst_element_sync_state_with_parent(muxer);
gst_element_sync_state_with_parent(filesink);

sinkpad = gst_element_get_static_pad(queue_record, "sink");
gst_pad_link(teepad, sinkpad);
gst_object_unref(sinkpad);

recording = TRUE;
}

int sigintHandler(int unused) {
g_print("You ctrl-c!\n");
if (recording)
    stopRecording();
else
    startRecording();
return 0;
}

int main(int argc, char *argv[])
{
signal(SIGINT, sigintHandler);
gst_init (&argc, &argv);

pipeline = gst_pipeline_new(NULL);
src = gst_element_factory_make("v4l2src", NULL);
tee = gst_element_factory_make("tee", "tee");
queue_display = gst_element_factory_make("queue", "queue_display");
videoconvert = gst_element_factory_make("videoconvert", NULL);
videosink = gst_element_factory_make("autovideosink", NULL);

if (!pipeline || !src || !tee || !videoconvert || !videosink || !queue_display) {
    g_error("Failed to create elements");
    return -1;
}

gst_bin_add_many(GST_BIN(pipeline), src, tee, queue_display, videoconvert, videosink, NULL);
if (!gst_element_link_many(src, tee, NULL) 
    || !gst_element_link_many(tee, queue_display, videoconvert, videosink, NULL)) {
    g_error("Failed to link elements");
    return -2;
}

startRecording();
loop = g_main_loop_new(NULL, FALSE);

bus = gst_pipeline_get_bus(GST_PIPELINE (pipeline));
gst_bus_add_signal_watch(bus);
g_signal_connect(G_OBJECT(bus), "message", G_CALLBACK(message_cb), NULL);
gst_object_unref(GST_OBJECT(bus));

gst_element_set_state(pipeline, GST_STATE_PLAYING);

g_print("Starting loop\n");
g_main_loop_run(loop);

return 0;
}

您应该将其发送到编码器,以便他们可以正确完成其工作,并将其转发到复用器,该复用器还需要通过写入只能在末尾写入的标头部分来包装文件。

另外,删除该睡眠,您需要确保 filesink 发布了一条 EOS 消息,以确保它已全部处理并且可以安全地删除它们。您可能需要启用message-forward对于管道,否则它将保留 EOS 消息,直到所有接收器发布其 EOS(由于视频接收器,这在您的情况下不会发生)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将 EoS 发送到文件接收器,同时从 tee 中删除分支 的相关文章

随机推荐

  • JSONDecoder 的基元类型的自定义初始化程序

    如何自定义 JSONDecoder 的行为primitive像 Int Bool 这样的类型 问题是这样的 类型不能依赖后端 例如 Bool 可以是 true false 或 true false bool 可以用双引号括起来 我们至少有
  • 如何在文件夹层次结构中找到所有不同的文件扩展名?

    在 Linux 机器上 我想遍历文件夹层次结构并获取其中所有不同文件扩展名的列表 从 shell 实现此目的的最佳方法是什么 试试这个 不确定这是否是最好的方法 但它有效 find type f perl ne print 1 if m s
  • 总和时间 odoo 9

    当在树视图中使用计算时 总和不可见 当使用 onChange sum 时 任何解决方案都可见如何修复它 我需要在从 csv 插入数据后自动填充 time total 字段进行计算 例子 Source class my data models
  • 动态类别的数据库架构

    Added 更新 1 请检查问题末尾 谢谢 Friends 我正在设计一个产品列表 其中包含类别和与类别相关的过滤器 我目前有一个带有静态类别的数据库架构 我必须让它们充满活力 我找不到正确的架构来使其动态化 因此我在表单中对架构进行了硬编
  • 创建一个不断扩大的圈子 iOS

    如何创建一个随着时间的推移而扩大的圈子 我想做这样的事情 UIView animateWithDuration 5 animations void Expand the circle Get the contextRef CGContext
  • 向 GridView 动态添加命令按钮

    我在尝试向网格添加按钮时遇到问题 我的 GridView 首先在 PageLoad 事件中加载数据 然后 我获取每行第一个单元格中的数据 并创建一个链接到 URL 的按钮 为了获取 URL 我必须使用第一个单元格中的数据作为参数来运行查询
  • 警告:mysql_result() 期望参数 1 为资源,给定布尔值[重复]

    这个问题在这里已经有答案了 我的 PHP 函数脚本昨晚工作正常 现在当我今天登录并进一步处理它时 我得到了 警告 mysql result 期望参数 1 为资源 给定布尔值 我不知道为什么这不起作用 我已经在线阅读了 PHP 手册 甚至还看
  • 如何将README.md文件添加到HEXO生成的博客的根目录中?

    我的博客是基于Github Pages我正在使用的程序是HEXO 生成的文件HEXO不包含 README md 文件 因此我无法在 Github 存储库页面上声明我的博客 所以我想将 README md 文件添加到该文件夹 中HEXO生成
  • Spring Boot Keycloak - 承载:如何解决 NOT_ATTEMPTED:仅承载?

    角度 v v4 0 2 Spring Boot v 1 5 2 RELEASE Keycloak v 2 4 0 Final 稍后会升级 我读到了关于同一问题的电子邮件对话 http keycloak user 88327 x6 nabbl
  • 如何查找堆中对象的数量

    如何在Java程序中找到堆上存活对象的数量 jmap 是标准的 java 实用程序 可用于捕获堆转储和统计信息 我不能说 jmap 使用什么协议连接到 JVM 来获取此信息 并且不清楚此信息是否可用于直接在 JVM 中运行的程序 尽管我确信
  • Snort 消息 - 警告:没有为策略 0 配置预处理器

    我已经安装并配置了snort 2 9 7 2并且它运行没有问题 但是 我的问题是 以下警告是什么意思 没有为策略 0 配置预处理器 当我运行命令时显示此消息 snort v 此消息表明没有加载 snort 预处理器 为了消除此警告 请使用以
  • Domino Designer 中的 javascript 版本?

    在处理基于 Web 的 Domino 表单时 onChange字段 JavaScript 的事件 我无法使用某些语法 因为它会抛出错误并且代码将无法保存 例子有 代替var 我想用const and let 但它不会接受它 此外 当尝试使用
  • 我应该将哪个 .NET Azure 服务总线库用于队列?

    Microsoft 在 NuGet 上有两个 Azure 服务总线包 WindowsAzure ServiceBus https www nuget org packages WindowsAzure ServiceBus Use this
  • addField类型图像和缩略图路径

    我有一个 Magento 网上商店 刚刚创建了一个带有扩展名 Modulecreator 的自定义模块 该模块带有一个标准的管理界面 可以处理文件上传 我发现 如果您想显示缩略图 可以使用 图像 字段类型 addField myfield
  • 如何在 Objective-C 中记录每个被调用的类方法的名称? [复制]

    这个问题在这里已经有答案了 当我想查看对象方法调用的顺序时 我必须像这样记录我实现的每个方法 void updateTime float time NSLog s PRETTY FUNCTION 因此我必须把这段代码放在类的每个方法中 每次
  • 如果我想建立一个自定义数据库,我该怎么做? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我构建了一个以前的程序 它获取客户信息并将其存储在 txt 文件的文件夹中 不太切实际 但现在我想升级该程序以提高效率 并将信息放入某
  • 如何更改 Joomla 管理 URL

    默认情况下 Joomla 管理 URL 是您的站点名称 管理员 我如何为 Joomla 1 5 更改此设置 以便它不会影响我的模块和组件或后端的任何其他内容 我认为这不是一个好主意 因为模块 组件中指向 administrator 的一些链
  • 无法从 Gstreamer 找到 get-launch-1.0

    我在 OSX Mac 上运行 我已经在此处找到的 GStreamer 1 5 1 中安装了各种软件包 http gstreamer freedesktop org data pkg osx http gstreamer freedeskto
  • 为什么这个 rust HashMap 宏不再起作用?

    我以前用过 macro export macro rules map T ident key expr gt value expr gt let mut m T new m insert key value m 要创建对象 如下所示 let
  • 将 EoS 发送到文件接收器,同时从 tee 中删除分支

    我写了一个v4l2src同时显示和记录的代码 我的管道看起来像 queue videosink v4l2src tee queue filesink 目前我可以一起显示 记录 还可以随意动态启动和停止记录分支 使用 ctrl c sigin