GStreamer基础教程07——多线程和Pad的有效性

目标

      GStreamer会自动处理多线程这部分,但在有些情况下,你需要手动对线程做解耦。本教程会教你怎样才能做到这一点,另外也展示了Pad的有效性。主要内容包括:

      如何针对部分的pipeline建立一个新的线程

      什么是Pad的有效性

      如何复制流


介绍

多线程

      GStreamer是一个支持多线程的框架。这就说明,如果有必要它会在内部自动创建/销毁线程。比如:在应用线程中把流解出来。而且,plugin在自身也可以任意的创建线程,比如一个视频解码器为了充分利用4核CPU的能力,可以创建4个线程。

      这里最重要的是,当应用在建立pipeline时可以明确一个branch(一部分pipeline)在另一个线程中运行(比如,让音频解码和视频解码同时运行)。

      这个可以用queue element来达到这个目的,运行的时候sink pad仅仅负责把数据放到queue里面,同时在另一个线程里把数据从queue里面取出并向下发送。这个element同样可以用来做缓冲,这点在后面讲述流的教程时可以看到。Queue的大小事可以用设置属性的方法来设置的。

一个pipeline例子

      这个例子建立的pipeline如下图所示。


      这里的源是一个合成的音频信号(一个连续的tone),这个音频信号会被tee element分成两路。一路发送信号到声卡,另一个就在屏幕上渲染一个波形。

      从图上看,queue会创建一个新的线程,所以整个pipeline有3个线程在运行。通常来说,有多于一个的sink element时就需要多个线程。这是因为在同步时,sink通常是阻塞起来等待所有其他的sink都准备好,如果仅仅只有一个线程是无法做到这一点的。

Request pads

      在《GStreamer基础教程03——动态pipeline》里面,我们看见了一个初始时没有pad的element(uridecodebin),pad会在数据流到element时才会出现。这种pad被成为Sometimes Pad,平时的那种一直存在的pad被称为Always Pad。

      第三种pad称为Request Pad,这是根据需要来建立的。经典的例子就是tee element——有1个输入pad而没有输出pad,需要有申请,tee  element才会生成。通过这种方法,输入流可以被复制成多份。和Always Pad比起来,Request Pad因为并非一直存在,所以是不能自动连接element的,这点在下面的例子中可以看到。

      另外,在PLAYING或PAUSED状态下去获得pad需要注意(Pad阻塞,本教程没有讲到这点),在NULL和READY状态去获得pad就没有这个问题。

      闲话少说,上代码。


简单地多线程例子

#include <gst/gst.h>
  
int main(int argc, char *argv[]) {
  GstElement *pipeline, *audio_source, *tee, *audio_queue, *audio_convert, *audio_resample, *audio_sink;
  GstElement *video_queue, *visual, *video_convert, *video_sink;
  GstBus *bus;
  GstMessage *msg;
  GstPadTemplate *tee_src_pad_template;
  GstPad *tee_audio_pad, *tee_video_pad;
  GstPad *queue_audio_pad, *queue_video_pad;
  
  /* Initialize GStreamer */
  gst_init (&argc, &argv);
  
  /* Create the elements */
  audio_source = gst_element_factory_make ("audiotestsrc", "audio_source");
  tee = gst_element_factory_make ("tee", "tee");
  audio_queue = gst_element_factory_make ("queue", "audio_queue");
  audio_convert = gst_element_factory_make ("audioconvert", "audio_convert");
  audio_resample = gst_element_factory_make ("audioresample", "audio_resample");
  audio_sink = gst_element_factory_make ("autoaudiosink", "audio_sink");
  video_queue = gst_element_factory_make ("queue", "video_queue");
  visual = gst_element_factory_make ("wavescope", "visual");
  video_convert = gst_element_factory_make ("ffmpegcolorspace", "csp");
  video_sink = gst_element_factory_make ("autovideosink", "video_sink");
  
  /* Create the empty pipeline */
  pipeline = gst_pipeline_new ("test-pipeline");
  
  if (!pipeline || !audio_source || !tee || !audio_queue || !audio_convert || !audio_resample || !audio_sink ||
      !video_queue || !visual || !video_convert || !video_sink) {
    g_printerr ("Not all elements could be created.\n");
    return -1;
  }
  
  /* Configure elements */
  g_object_set (audio_source, "freq", 215.0f, NULL);
  g_object_set (visual, "shader", 0, "style", 3, NULL);
  
  /* Link all elements that can be automatically linked because they have "Always" pads */
  gst_bin_add_many (GST_BIN (pipeline), audio_source, tee, audio_queue, audio_convert, audio_resample, audio_sink,
      video_queue, visual, video_convert, video_sink, NULL);
  if (gst_element_link_many (audio_source, tee, NULL) != TRUE ||
      gst_element_link_many (audio_queue, audio_convert, audio_resample, audio_sink, NULL) != TRUE ||
      gst_element_link_many (video_queue, visual, video_convert, video_sink, NULL) != TRUE) {
    g_printerr ("Elements could not be linked.\n");
    gst_object_unref (pipeline);
    return -1;
  }
  
  /* Manually link the Tee, which has "Request" pads */
  tee_src_pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (tee), "src%d");
  tee_audio_pad = gst_element_request_pad (tee, tee_src_pad_template, NULL, NULL);
  g_print ("Obtained request pad %s for audio branch.\n", gst_pad_get_name (tee_audio_pad));
  queue_audio_pad = gst_element_get_static_pad (audio_queue, "sink");
  tee_video_pad = gst_element_request_pad (tee, tee_src_pad_template, NULL, NULL);
  g_print ("Obtained request pad %s for video branch.\n", gst_pad_get_name (tee_video_pad));
  queue_video_pad = gst_element_get_static_pad (video_queue, "sink");
  if (gst_pad_link (tee_audio_pad, queue_audio_pad) != GST_PAD_LINK_OK ||
      gst_pad_link (tee_video_pad, queue_video_pad) != GST_PAD_LINK_OK) {
    g_printerr ("Tee could not be linked.\n");
    gst_object_unref (pipeline);
    return -1;
  }
  gst_object_unref (queue_audio_pad);
  gst_object_unref (queue_video_pad);
  
  /* Start playing the pipeline */
  gst_element_set_state (pipeline, GST_STATE_PLAYING);
  
  /* Wait until error or EOS */
  bus = gst_element_get_bus (pipeline);
  msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS);
  
  /* Release the request pads from the Tee, and unref them */
  gst_element_release_request_pad (tee, tee_audio_pad);
  gst_element_release_request_pad (tee, tee_video_pad);
  gst_object_unref (tee_audio_pad);
  gst_object_unref (tee_video_pad);
  
  /* Free resources */
  if (msg != NULL)
    gst_message_unref (msg);
  gst_object_unref (bus);
  gst_element_set_state (pipeline, GST_STATE_NULL);
  
  gst_object_unref (pipeline);
  return 0;
}

工作流程

  /* Create the elements */
  audio_source = gst_element_factory_make ("audiotestsrc", "audio_source");
  tee = gst_element_factory_make ("tee", "tee");
  audio_queue = gst_element_factory_make ("queue", "audio_queue");
  audio_convert = gst_element_factory_make ("audioconvert", "audio_convert");
  audio_resample = gst_element_factory_make ("audioresample", "audio_resample");
  audio_sink = gst_element_factory_make ("autoaudiosink", "audio_sink");
  video_queue = gst_element_factory_make ("queue", "video_queue");
  visual = gst_element_factory_make ("wavescope", "visual");
  video_convert = gst_element_factory_make ("ffmpegcolorspace", "csp");
  video_sink = gst_element_factory_make ("autovideosink", "video_sink");
      上面图中所有的element都在这里创建了。

      audiotestsrc会创建一个合成的tone,wavescope会像示波器一样用一个音频信号来渲染出一个波形。我们前面已经用过autoaudiosink和autovideosink这两个element了。

      转换element(audio convert,audioresample和ffmpegcolorspace)也是必须的,它们可以保证pipeline可以正确地连接。事实上,音频和视频的sink的Caps是由硬件确定的,所以你在设计时是不知道audiotestsrc和wavescope是否可以匹配上。如果Caps能够匹配,这些element的行为就类似于直通——对信号不做任何修改,这对于效率的影响基本可以忽略不计。

  /* Configure elements */
  g_object_set (audio_source, "freq", 215.0f, NULL);
  g_object_set (visual, "shader", 0, "style", 3, NULL);
      为了更好的演示做了小小的调整:audiotestsrc的“freq”属性设置成215Hz,wavescope设置“shader”和“style”,让波形连续。用gst-inspect可以更好的了解这几个element的属性。

  /* Link all elements that can be automatically linked because they have "Always" pads */
  gst_bin_add_many (GST_BIN (pipeline), audio_source, tee, audio_queue, audio_convert, audio_resample, audio_sink,
      video_queue, visual, video_convert, video_sink, NULL);
  if (gst_element_link_many (audio_source, tee, NULL) != TRUE ||
      gst_element_link_many (audio_queue, audio_convert, audio_resample, audio_sink, NULL) != TRUE ||
      gst_element_link_many (video_queue, visual, video_convert, video_sink, NULL) != TRUE) {
    g_printerr ("Elements could not be linked.\n");
    gst_object_unref (pipeline);
    return -1;
  }
      这块代码在pipeline里加入了所有的element并且把可以自动连接的element都连接了起来(就是Always Pad)。

  /* Manually link the Tee, which has "Request" pads */
  tee_src_pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (tee), "src%d");
  tee_audio_pad = gst_element_request_pad (tee, tee_src_pad_template, NULL, NULL);
  g_print ("Obtained request pad %s for audio branch.\n", gst_pad_get_name (tee_audio_pad));
  queue_audio_pad = gst_element_get_static_pad (audio_queue, "sink");
  tee_video_pad = gst_element_request_pad (tee, tee_src_pad_template, NULL, NULL);
  g_print ("Obtained request pad %s for video branch.\n", gst_pad_get_name (tee_video_pad));
  queue_video_pad = gst_element_get_static_pad (video_queue, "sink");
  if (gst_pad_link (tee_audio_pad, queue_audio_pad) != GST_PAD_LINK_OK ||
      gst_pad_link (tee_video_pad, queue_video_pad) != GST_PAD_LINK_OK) {
    g_printerr ("Tee could not be linked.\n");
    gst_object_unref (pipeline);
    return -1;
  }
  gst_object_unref (queue_audio_pad);
  gst_object_unref (queue_video_pad);

      为了连接Request Pad,需要获得对element的“requesting”。一个element可能可以创建不同种类的Request Pad,所以,当请求Pad生成时,必须提供想要的Pad模板。Pad模板可以用gst_element_class_get_pad_template()方法来获得,而且用它们的名字来区分开。在tee element的文档里面我们可以看到两个pad模板,分别被称为“sink”(sink pad)和“src%d”(Request Pad)。

      一旦我们有了Pad模板,我们就用gst_element_request_pad()方法向tee请求两个Pad——分别给音频分支和视频分支。

      然后我们去获得下游的element需要连接Request Pad的Pad,这些通常都是Always Pad,所以我们用get_element_get_static_pad()方法去获得。

      最后,我们用gst_pad_link()方法把pad连接起来。在gst_element_link()和gst_element_link_many()方法里面也是调用这个函数来连接的。

      我们获得的这个sink pad需要通过gst_object_unref()来释放。Request Pad是在我们不需要的时候释放,也就是在程序的最后。

      就像平常一样,我们设置pipeline到PLAYING状态,等待一个错误消息或者EOS消息到达。剩下的所有事情就是清楚request pad。

  /* Release the request pads from the Tee, and unref them */
  gst_element_release_request_pad (tee, tee_audio_pad);
  gst_element_release_request_pad (tee, tee_video_pad);
  gst_object_unref (tee_audio_pad);
  gst_object_unref (tee_video_pad);
      gst_element_release_request_pad()可以释放tee的pad,但还需要调用gst_object_unref()才行。



<think>好的,用户遇到了`gst_pad_add_probe`断言失败的问题,具体错误是`assertion GST_IS_PAD(pad) failed`。我需要先理解这个错误的根本原因。根据GStreamer的文档,断言`GST_IS_PAD(pad)`失败通常意味着传入的`pad`参数不是一个有效的GstPad对象,或者已经被销毁了。 首先,我需要检查用户代码中获取pad的方式是否正确。比如,是否使用了正确的方法来获取pad,比如`gst_element_get_static_pad()`或者动态获取的方式。如果pad是通过动态请求(比如有时需要连接的元素还没有pad),可能需要确保在添加probe之前pad已经存在并且有效。 另外,用户可能在pad被销毁后尝试添加probe,比如在元素状态改变到NULL之后。根据引用内容,当元素从READY状态转到NULL时,会关闭设备并重置内部状态,这时候相关的pad可能已经被释放。所以需要确认在添加probe时,元素的状态是否允许操作pad。例如,是否在元素处于PLAYING或PAUSED状态时进行操作,而不是在NULL或READY状态时。 还需要检查是否在多线程环境下操作pad,导致在某个线程中pad已经被释放,而另一个线程还在尝试访问它。这时候需要确保线程安全,比如使用GStreamer的API来安全地访问pad。 此外,用户可能在获取pad时没有增加引用计数,导致pad被提前销毁。GStreamerpad在获取后可能需要手动增加引用,以防止被自动释放。如果用户没有正确管理引用计数,可能导致pad无效。 总结下来,解决方案应包括以下几个步骤:验证pad有效性、确保元素处于正确状态、正确管理引用计数、检查多线程问题以及添加错误处理。同时,用户需要检查代码中pad的获取方式是否正确,是否存在空指针或已释放的pad被使用的情况。 最后,需要提醒用户使用GStreamer的调试工具,如GST_DEBUG环境变量来输出更多信息,帮助定位问题。同时参考GStreamer文档中关于pad元素状态管理的部分,确保代码符合最佳实践。</think>关于`gst_pad_add_probe`断言失败的问题,以下是逐步分析解决方案: --- ### 问题分析 错误`assertion GST_IS_PAD(pad) failed`表明传递给`gst_pad_add_probe()`的`pad`参数无效,可能原因包括: 1. **空指针或未初始化**:未正确获取到有效的`GstPad`对象。 2. **Pad已被销毁**:在元素状态切换到`GST_STATE_NULL`或`GST_STATE_READY`时,Pad可能被释放[^3]。 3. **线程安全问题**:在多线程环境中,Pad可能在操作时被其他线程销毁。 --- ### 解决方案 #### 1. 验证Pad有效性 确保通过合法途径获取Pad(如`gst_element_get_static_pad()`或信号回调中获取),并检查是否为`NULL`: ```c GstPad *pad = gst_element_get_static_pad(element, "sink"); if (!GST_IS_PAD(pad)) { g_printerr("无效的Pad\n"); return; } ``` #### 2. 管理元素状态 确保元素处于`GST_STATE_PLAYING`或`GST_STATE_PAUSED`时操作Pad。**避免在状态切换过程中操作**: ```c // 示例:确保元素处于PLAYING状态后再获取Pad GstStateChangeReturn ret = gst_element_set_state(element, GST_STATE_PLAYING); if (ret == GST_STATE_CHANGE_FAILURE) { g_printerr("状态切换失败\n"); return; } ``` #### 3. 增加引用计数 通过`gst_object_ref()`手动增加Pad的引用计数,防止其被提前销毁: ```c GstPad *pad = gst_element_get_static_pad(element, "sink"); gst_object_ref(pad); // 增加引用 // ... 操作完成后释放引用 gst_object_unref(pad); ``` #### 4. 检查多线程问题 若涉及多线程,使用GStreamer的线程安全API(如`gst_pad_add_probe()`的回调函数)或加锁机制。 #### 5. 调试工具 启用GStreamer调试信息,定位具体错误位置: ```bash GST_DEBUG=2 ./your_application ``` --- ### 示例代码修复 ```c // 正确获取Pad并验证 GstPad *pad = gst_element_get_static_pad(element, "sink"); if (!GST_IS_PAD(pad)) { g_printerr("无效的Pad\n"); return; } // 增加引用计数 gst_object_ref(pad); // 添加probe gulong probe_id = gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, buffer_probe_cb, NULL, NULL); // 确保在Pad销毁前移除probe gst_pad_remove_probe(pad, probe_id); gst_object_unref(pad); // 释放引用 ``` ---
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值