二十、操纵管道

news2024/11/16 12:58:31

本章将讨论如何在应用程序中使用多种方式操作管道。本章的部分内容都是很底层的,所以在开始阅读之前,请确保你需要一些编程知识,并对GStreamer有很好的理解。

这里将讨论的主题包括如何从应用程序向管道中插入数据,如何从管道中读取数据,如何操作管道的速度、长度、起点,以及如何监听管道的数据处理过程。

Using probes

监听probe最好设想对一个pad listener。从技术上讲,探针probe只不过是一个可以连接到pad上的回调函数。可以使用gst_pad_add_probe()附加探测。类似地,可以使用gst_pad_remove_probe()再次删除回调函数。探针会通知您在pad上发生的任何活动,如buffer、event和queries。您可以在添加探针时定义您感兴趣的通知类型。

探针可以在pad上通知您以下活动:

  • buffer被push或pull。您希望在注册探针时指定GST_PAD_PROBE_TYPE_BUFFER。因为pad可以以不同的方式调度,还可以通过可选的标志GST_PAD_PROBE_TYPE_PUSH和GST_PAD_PROBE_TYPE_PULL指定用户感兴趣的调度模式。
    您可以使用此探针来检查、修改或删除buffer。参见Data probes。

  • 一个bufferlist被推送push。在注册探测时,使用GST_PAD_PROBE_TYPE_BUFFER_LIST。

  • 事件在pad上传播。使用GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM和GST_PAD_PROBE_TYPE_EVENT_UPSTREAM标志来选择下游和上游事件。还有一个方便的GST_PAD_PROBE_TYPE_EVENT_BOTH,用于通知发生在上游和下游的事件。默认情况下,flush事件不会触发通知。用户需要显式启用GST_PAD_PROBE_TYPE_EVENT_FLUSH来接收刷出事件的回调函数。只有在推送模式下才会通知事件。
    您可以使用此探针来检查、修改或删除事件

  • 查询在一个pad上传递。使用GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM和GST_PAD_PROBE_TYPE_QUERY_UPSTREAM标志来选择下游和上游查询。GST_PAD_PROBE_TYPE_QUERY_BOTH也可用于选择两个方向。查询探针将被通知两次,一次是在查询上行/下行时,一次是在查询结果返回时。可以使用GST_PAD_PROBE_TYPE_PUSH和GST_PAD_PROBE_TYPE_PULL分别在执行查询和返回查询结果时选择在哪个阶段调用回调函数。
    您可以使用这个探针来检查或修改查询。还可以在probe回调函数中回答查询,方法是在查询中放置结果值,并从回调函数中返回GST_PAD_PROBE_DROP。

  • 除了通知您数据流之外,您还可以在回调函数返回时要求探针阻塞数据流。这称为阻塞探测(blocking probe),通过指定GST_PAD_PROBE_TYPE_BLOCK标志激活。您可以将此标志与其他标志一起使用,以仅阻塞选定活动上的数据流。如果移除探测,或者从回调函数中返回GST_PAD_PROBE_REMOVE,则pad将再次变得畅通。通过从回调函数返回GST_PAD_PROBE_PASS,可以只让当前被阻塞的元素通过,它会再次阻塞下一个元素。
    阻塞探测用于临时阻塞pad,因为它们未连接或因为您将要断开它们的连接。如果数据流没有被阻塞,如果数据被推送到未链接的pad上,管道将进入错误状态。我们将介绍如何使用阻塞探测对管道进行部分预滚动。参见Play a region of a media file。

  • 当pad上没有活动时收到通知。使用GST_PAD_PROBE_TYPE_IDLE标志安装该探测。你可以指定GST_PAD_PROBE_TYPE_PUSH和/或GST_PAD_PROBE_TYPE_PULL只收到通知,具体取决于pad调度模式。空闲探针也是一个阻塞探针,因为只要空闲探针安装,它就不会让任何数据在pad上传递。
    您可以使用空闲探针动态地重新链接pad。我们将看到如何使用空闲探测替换管道中的元素。另请参阅Dynamically changing the pipeline。

Data probes

数据探测器Data probes允许您在pad上有数据传递时收到通知。在添加探测时,指定GST_PAD_PROBE_TYPE_BUFFER和/或GST_PAD_PROBE_TYPE_BUFFER_LIST。

数据探测Data probes在pipeline流线程上下文中运行,所以回调应该尽量不阻塞,通常不做任何奇怪的事情,因为这可能会对pipeline性能产生负面影响,或者在出现bug的情况下,导致死锁或崩溃。更准确地说,通常不应该在probe回调中调用任何gui相关的函数,也不应该试图更改管道的状态。应用程序可以在管道的总线上发布自定义消息,以便与应用程序主线程通信,并让它执行诸如停止管道之类的操作。

在任何情况下,元素可以在_chain()函数中进行的最常见的buffer操作,也可以在probe回调函数中进行。下面的例子简要介绍了如何使用它们。

#include <gst/gst.h>

static GstPadProbeReturn
cb_have_data (GstPad          *pad,
              GstPadProbeInfo *info,
              gpointer         user_data) {
  gint x, y;
  GstMapInfo map;
  guint16 *ptr, t;
  GstBuffer *buffer;

  buffer = GST_PAD_PROBE_INFO_BUFFER (info);

  buffer = gst_buffer_make_writable (buffer);

  /* Making a buffer writable can fail (for example if it
   * cannot be copied and is used more than once)
   */
  if (buffer == NULL)
    return GST_PAD_PROBE_OK;

  /* Mapping a buffer can fail (non-writable) */
  if (gst_buffer_map (buffer, &map, GST_MAP_WRITE)) {
    ptr = (guint16 *) map.data;
    /* invert data */
    for (y = 0; y < 288; y++) {
      for (x = 0; x < 384 / 2; x++) {
        t = ptr[384 - 1 - x];
        ptr[384 - 1 - x] = ptr[x];
        ptr[x] = t;
      }
      ptr += 384;
    }
    gst_buffer_unmap (buffer, &map);
  }

  GST_PAD_PROBE_INFO_DATA (info) = buffer;

  return GST_PAD_PROBE_OK;
}

gint
main (gint   argc,
      gchar *argv[])
{
  GMainLoop *loop;
  GstElement *pipeline, *src, *sink, *filter, *csp;
  GstCaps *filtercaps;
  GstPad *pad;

  /* init GStreamer */
  gst_init (&argc, &argv);
  loop = g_main_loop_new (NULL, FALSE);

  /* build */
  pipeline = gst_pipeline_new ("my-pipeline");
  src = gst_element_factory_make ("videotestsrc", "src");
  if (src == NULL)
    g_error ("Could not create 'videotestsrc' element");

  filter = gst_element_factory_make ("capsfilter", "filter");
  g_assert (filter != NULL); /* should always exist */

  csp = gst_element_factory_make ("videoconvert", "csp");
  if (csp == NULL)
    g_error ("Could not create 'videoconvert' element");

  sink = gst_element_factory_make ("xvimagesink", "sink");
  if (sink == NULL) {
    sink = gst_element_factory_make ("ximagesink", "sink");
    if (sink == NULL)
      g_error ("Could not create neither 'xvimagesink' nor 'ximagesink' element");
  }

  gst_bin_add_many (GST_BIN (pipeline), src, filter, csp, sink, NULL);
  gst_element_link_many (src, filter, csp, sink, NULL);
  filtercaps = gst_caps_new_simple ("video/x-raw",
               "format", G_TYPE_STRING, "RGB16",
               "width", G_TYPE_INT, 384,
               "height", G_TYPE_INT, 288,
               "framerate", GST_TYPE_FRACTION, 25, 1,
               NULL);
  g_object_set (G_OBJECT (filter), "caps", filtercaps, NULL);
  gst_caps_unref (filtercaps);

  pad = gst_element_get_static_pad (src, "src");
  gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER,
      (GstPadProbeCallback) cb_have_data, NULL, NULL);
  gst_object_unref (pad);

  /* run */
  gst_element_set_state (pipeline, GST_STATE_PLAYING);

  /* wait until it's up and running or failed */
  if (gst_element_get_state (pipeline, NULL, NULL, -1) == GST_STATE_CHANGE_FAILURE) {
    g_error ("Failed to go into PLAYING state");
  }

  g_print ("Running ...\n");
  g_main_loop_run (loop);

  /* exit */
  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref (pipeline);

  return 0;
}


将上述输出与“gst-launch-1.0 videotestsrc !”Xvimagesink”,这样你就知道你在寻找什么。

严格地说,pad probe回调只允许在缓冲区buffer可写的情况下修改缓冲区buffer的内容。这种情况在很大程度上取决于管道和涉及的元素。通常情况下是这样,但有时不是,如果不是这样,那么对数据或元数据的意外修改可能会引入难以调试和跟踪的bug。使用gst_buffer_is_writable()可以检查缓冲区是否可写。由于您可以传递回一个不同的缓冲区,因此在回调函数中使用gst_buffer_make_writable()使缓冲区可写是一个好主意。

Pad探头最适合在数据通过管道时查看数据。如果你需要修改数据,你最好编写自己的GStreamer元素。像gstaudifilter, gstvidefilter或GstBaseTransform这样的基类使这变得相当容易。

如果只想在缓冲区通过管道时进行检查,甚至不需要设置pad探测器。你也可以在管道中插入一个标识元素,并连接到它的“handoff”信号。identity元素还提供了一些有用的调试工具,如"dump"属性或"last-message"属性(后者可以通过将-v开关传递给gst-launch并将identity上的silent属性设置为FALSE来启用)。

播放媒体文件的一个区域

在这个例子中,我们将展示如何播放媒体文件中的一个区域。我们的目标是仅在2秒到5秒之间播放文件,然后执行EOS。

第一步,我们将把一个uridecodebin元素设置为暂停状态,并确保我们阻塞了所有创建的source pad。当所有的source pad都被阻塞时,我们就有了所有source pad上的数据,我们说uridecodebin是预滚动的。(src元素设定为PAUSED

在预先滚动的管道中,我们可以请求媒体的持续时间,也可以执行查找操作。我们感兴趣的是在管道上执行查找操作,以选择感兴趣的媒体范围。

配置好感兴趣的区域后,可以链接sink元素,解除阻塞source pads,并将管道设置为播放状态。用户可以看到,请求的region在发送到EOS之前就已经被sink播放了。

下面是一个大致遵循该算法的示例应用程序。

#include <gst/gst.h>

static GMainLoop *loop;
static volatile gint counter;
static GstBus *bus;
static gboolean prerolled = FALSE;
static GstPad *sinkpad;

static void
dec_counter (GstElement * pipeline)
{
  if (prerolled)
    return;

  if (g_atomic_int_dec_and_test (&counter)) {
    /* all probes blocked and no-more-pads signaled, post
     * message on the bus. */
    prerolled = TRUE;

    gst_bus_post (bus, gst_message_new_application (
          GST_OBJECT_CAST (pipeline),
          gst_structure_new_empty ("ExPrerolled")));
  }
}

/* called when a source pad of uridecodebin is blocked */
static GstPadProbeReturn
cb_blocked (GstPad          *pad,
            GstPadProbeInfo *info,
            gpointer         user_data)
{
  GstElement *pipeline = GST_ELEMENT (user_data);

  if (prerolled)
    return GST_PAD_PROBE_REMOVE;

  dec_counter (pipeline);

  return GST_PAD_PROBE_OK;
}

/* called when uridecodebin has a new pad */
static void
cb_pad_added (GstElement *element,
              GstPad     *pad,
              gpointer    user_data)
{
  GstElement *pipeline = GST_ELEMENT (user_data);

  if (prerolled)
    return;

  g_atomic_int_inc (&counter);

  gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
      (GstPadProbeCallback) cb_blocked, pipeline, NULL);

  /* try to link to the video pad */
  gst_pad_link (pad, sinkpad);
}

/* called when uridecodebin has created all pads */
static void
cb_no_more_pads (GstElement *element,
                 gpointer    user_data)
{
  GstElement *pipeline = GST_ELEMENT (user_data);

  if (prerolled)
    return;

  dec_counter (pipeline);
}

/* called when a new message is posted on the bus */
static void
cb_message (GstBus     *bus,
            GstMessage *message,
            gpointer    user_data)
{
  GstElement *pipeline = GST_ELEMENT (user_data);

  switch (GST_MESSAGE_TYPE (message)) {
    case GST_MESSAGE_ERROR:
      g_print ("we received an error!\n");
      g_main_loop_quit (loop);
      break;
    case GST_MESSAGE_EOS:
      g_print ("we reached EOS\n");
      g_main_loop_quit (loop);
      break;
    case GST_MESSAGE_APPLICATION:
    {
      if (gst_message_has_name (message, "ExPrerolled")) {
        /* it's our message */
        g_print ("we are all prerolled, do seek\n");
        gst_element_seek (pipeline,
            1.0, GST_FORMAT_TIME,
            GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
            GST_SEEK_TYPE_SET, 2 * GST_SECOND,
            GST_SEEK_TYPE_SET, 5 * GST_SECOND);

        gst_element_set_state (pipeline, GST_STATE_PLAYING);
      }
      break;
    }
    default:
      break;
  }
}

gint
main (gint   argc,
      gchar *argv[])
{
  GstElement *pipeline, *src, *csp, *vs, *sink;

  /* init GStreamer */
  gst_init (&argc, &argv);
  loop = g_main_loop_new (NULL, FALSE);

  if (argc < 2) {
    g_print ("usage: %s <uri>", argv[0]);
    return -1;
  }

  /* build */
  pipeline = gst_pipeline_new ("my-pipeline");

  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  gst_bus_add_signal_watch (bus);
  g_signal_connect (bus, "message", (GCallback) cb_message,
      pipeline);

  src = gst_element_factory_make ("uridecodebin", "src");
  if (src == NULL)
    g_error ("Could not create 'uridecodebin' element");

  g_object_set (src, "uri", argv[1], NULL);

  csp = gst_element_factory_make ("videoconvert", "csp");
  if (csp == NULL)
    g_error ("Could not create 'videoconvert' element");

  vs = gst_element_factory_make ("videoscale", "vs");
  if (csp == NULL)
    g_error ("Could not create 'videoscale' element");

  sink = gst_element_factory_make ("autovideosink", "sink");
  if (sink == NULL)
    g_error ("Could not create 'autovideosink' element");

  gst_bin_add_many (GST_BIN (pipeline), src, csp, vs, sink, NULL);

  /* can't link src yet, it has no pads */
  gst_element_link_many (csp, vs, sink, NULL);

  sinkpad = gst_element_get_static_pad (csp, "sink");

  /* for each pad block that is installed, we will increment
   * the counter. for each pad block that is signaled, we
   * decrement the counter. When the counter is 0 we post
   * an app message to tell the app that all pads are
   * blocked. Start with 1 that is decremented when no-more-pads
   * is signaled to make sure that we only post the message
   * after no-more-pads */
  g_atomic_int_set (&counter, 1);

  g_signal_connect (src, "pad-added",
      (GCallback) cb_pad_added, pipeline);
  g_signal_connect (src, "no-more-pads",
      (GCallback) cb_no_more_pads, pipeline);

  gst_element_set_state (pipeline, GST_STATE_PAUSED);

  g_main_loop_run (loop);

  gst_element_set_state (pipeline, GST_STATE_NULL);

  gst_object_unref (sinkpad);
  gst_object_unref (bus);
  gst_object_unref (pipeline);
  g_main_loop_unref (loop);

  return 0;
}

注意,我们使用了一个自定义的应用消息来通知主线程uridecidebin被预滚动。主线程会向请求的region发出刷出指令。刷写操作会暂时解除对pad的阻塞,并在新数据再次到达时重新阻塞它们。我们检测第二个阻塞来删除探针。然后我们将管道设置为播放,它应该从2秒到5秒播放,然后结束并退出应用程序。

Manually adding or removing data from/to a pipeline

动态改变管道

在本节中,我们将讨论一些动态修改管道的技术。我们特别讨论的是在播放状态PLAYING下改变管道,而不中断流。

在构建动态管道时,有一些重要的事情需要考虑:

  • 当从管道中删除元素时,请确保未连接的pads上没有数据流dataflow,因为这将导致致命的管道错误。断开pad连接之前一定要阻塞source pads(in push mode)或sink pads(pull mode)之前断开连接垫。另请参阅更改管道中的元素 Changing elements in a pipeline。

  • 在向管道添加元素时,确保将元素置于正确的状态,通常与父元素的状态相同,然后允许数据流传递元素。当新创建元素时,它的状态为NULL,并在接收到数据时返回错误。另请参阅更改管道中的元素Changing elements in a pipeline。

  • 当向管道添加元素时,GStreamer会默认将元素上的时钟和基本时间设置为管道的当前值。这意味着该元素将能够构建与管道中其他元素相同的管道运行时running-time。这意味着sink将像管道中的其他sink一样同步缓冲区,并且源产生的缓冲区的运行时与其他源匹配。

  • 当从上游链upstream中断开链接时,通过向元素sink pad发送EOS事件并等待EOS离开元素(使用事件探针),始终确保刷新元素中queue的任何数据。

    • 如果不这样做,则会丢失未链接元素buffer数据。这可能会导致简单的帧丢失(一个或多个视频帧,几毫秒的音频)。但是,如果从管道中删除muxer(在某些情况下是编码器或类似的元素),则有可能得到无法正常播放的损坏文件,因为一些相关元数据(头、查找/索引表、内部同步标签)将无法正确存储或更新。另请参阅更改管道中的元素Changing elements in a pipeline。
  • live source将在管道中产生具有当前运行时的(运行时的buffer)。

    • 没有live source的管道生成buffer的运行时间从0开始。同样,在flushing seek之后,这些管道会将运行时间重置为0。

    • 运行时间running-time可以用gst_pad_set_offset()改变。为了保持同步,了解管道中各个元素的运行时间是很重要的。

  • 添加元素可能会改变管道的状态。例如,添加一个non-prerolled sink可以将管道恢复到预滚动状态。例如,删除non-prerolled sink可能会将管道更改为暂停和播放状态。

    • 添加一个live source会取消preroll阶段,并将管道置于播放状态。添加live source或其他live elements也可能会改变管道的延迟。

    • 向管道添加或删除元素可能会改变管道的时钟选择。如果新添加的元素提供了时钟,那么将流水线中的时钟更改为新时钟可能是值得的。另一方面,如果为管道提供时钟的成员被移除,则必须选择一个新的时钟。

  • 添加和删除元素可能会导致上游或下游元素重新协商caps和or分配器allocators。你实际上不需要从应用程序中做任何事情,插件很大程度上自适应新的管道拓扑,以优化它们的格式和分配策略。

重要的是,当你在管道中添加、删除或更改元素时,管道可能需要协商新的格式,而这可能会失败。通常你可以通过在需要的地方插入正确的转换器元素来解决这个问题。另请参阅更改管道中的元素。

GStreamer提供了对任何动态管道修改的支持,但它需要您在不导致管道错误的情况下执行此操作之前了解一些细节。在下面的小节中,我们将演示几个典型的用例

改变管道中的元素

在下一个例子中,我们将看到以下元素连接:
请添加图片描述我们希望在管道处于播放状态时通过element4改变element2。假设element2是一个可视化对象,您希望在管道中切换可视化对象。

我们不能只是将element2的sinkpad从element1的source pad中断开链接,因为这样会使element1的source pad断开链接,并且在将数据推送到source pad时,会导致管道中的流错误。方法是在我们用element4改变element2之前,先阻塞element1的source pad的数据流,然后恢复数据流,步骤如下:

  • 用阻塞pad probe阻塞element1的source pad。当pad被阻塞时,probe回调将被调用。

  • 在阻塞回调函数中,没有任何东西在element1和element2之间流动,直到被解阻塞。

  • Unlink element1和element2。

  • 确保数据从element2中流出。有些元素可能会在内部保留一些数据,您需要确保不会通过强制从element2中丢失数据。你可以通过将EOS推送到element2来做到这一点,如下所示:

    • 在element2的source pad上放置一个event probe。

    • 将EOS发送到element2的sinkpad。这确保了element2中的所有数据都被强制取出。

    • 等待EOS事件出现在element2的source pad上。当接收到EOS时,删除它并删除事件探针。

  • Unlink element2和element3。现在还可以从管道中删除element2,并将element2状态设置为NULL。

  • 将element4添加到管道(如果还没有添加的话)。链接element4和element3。链接element1和element4。

  • 确保element4与管道中其他元素处于相同的状态。它至少应该处于暂停状态,然后才能接收缓冲区buffer和事件event。

  • Unblock element1的source pad probe。这将让新数据进入element4并继续流。

上述方法在source被阻塞时有效,即在管道中有数据流时。如果没有数据流,(暂时)也没有必要更改元素,因此该方法也可以在暂停状态下使用。

让我们通过一个例子来展示它是如何工作的。这个例子每秒改变一个简单管道上的视频效果。

#include <gst/gst.h>

static gchar *opt_effects = NULL;

#define DEFAULT_EFFECTS "identity,exclusion,navigationtest," \
    "agingtv,videoflip,vertigotv,gaussianblur,shagadelictv,edgetv"

static GstPad *blockpad;
static GstElement *conv_before;
static GstElement *conv_after;
static GstElement *cur_effect;
static GstElement *pipeline;

static GQueue effects = G_QUEUE_INIT;

static GstPadProbeReturn
event_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
  GMainLoop *loop = user_data;
  GstElement *next;

  if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_DATA (info)) != GST_EVENT_EOS)
    return GST_PAD_PROBE_PASS;

  gst_pad_remove_probe (pad, GST_PAD_PROBE_INFO_ID (info));

  /* push current effect back into the queue */
  g_queue_push_tail (&effects, gst_object_ref (cur_effect));
  /* take next effect from the queue */
  next = g_queue_pop_head (&effects);
  if (next == NULL) {
    GST_DEBUG_OBJECT (pad, "no more effects");
    g_main_loop_quit (loop);
    return GST_PAD_PROBE_DROP;
  }

  g_print ("Switching from '%s' to '%s'..\n", GST_OBJECT_NAME (cur_effect),
      GST_OBJECT_NAME (next));

  gst_element_set_state (cur_effect, GST_STATE_NULL);

  /* remove unlinks automatically */
  GST_DEBUG_OBJECT (pipeline, "removing %" GST_PTR_FORMAT, cur_effect);
  gst_bin_remove (GST_BIN (pipeline), cur_effect);

  GST_DEBUG_OBJECT (pipeline, "adding   %" GST_PTR_FORMAT, next);
  gst_bin_add (GST_BIN (pipeline), next);

  GST_DEBUG_OBJECT (pipeline, "linking..");
  gst_element_link_many (conv_before, next, conv_after, NULL);

  gst_element_set_state (next, GST_STATE_PLAYING);

  cur_effect = next;
  GST_DEBUG_OBJECT (pipeline, "done");

  return GST_PAD_PROBE_DROP;
}

static GstPadProbeReturn
pad_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
  GstPad *srcpad, *sinkpad;

  GST_DEBUG_OBJECT (pad, "pad is blocked now");

  /* remove the probe first */
  gst_pad_remove_probe (pad, GST_PAD_PROBE_INFO_ID (info));

  /* install new probe for EOS */
  srcpad = gst_element_get_static_pad (cur_effect, "src");
  gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_BLOCK |
      GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, event_probe_cb, user_data, NULL);
  gst_object_unref (srcpad);

  /* push EOS into the element, the probe will be fired when the
   * EOS leaves the effect and it has thus drained all of its data */
  sinkpad = gst_element_get_static_pad (cur_effect, "sink");
  gst_pad_send_event (sinkpad, gst_event_new_eos ());
  gst_object_unref (sinkpad);

  return GST_PAD_PROBE_OK;
}

static gboolean
timeout_cb (gpointer user_data)
{
  gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
      pad_probe_cb, user_data, NULL);

  return TRUE;
}

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;
    }
    default:
      break;
  }
  return TRUE;
}

int
main (int argc, char **argv)
{
  GOptionEntry options[] = {
    {"effects", 'e', 0, G_OPTION_ARG_STRING, &opt_effects,
        "Effects to use (comma-separated list of element names)", NULL},
    {NULL}
  };
  GOptionContext *ctx;
  GError *err = NULL;
  GMainLoop *loop;
  GstElement *src, *q1, *q2, *effect, *filter1, *filter2, *sink;
  gchar **effect_names, **e;

  ctx = g_option_context_new ("");
  g_option_context_add_main_entries (ctx, options, NULL);
  g_option_context_add_group (ctx, gst_init_get_option_group ());
  if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
    g_print ("Error initializing: %s\n", err->message);
    g_clear_error (&amp;err);
    g_option_context_free (ctx);
    return 1;
  }
  g_option_context_free (ctx);

  if (opt_effects != NULL)
    effect_names = g_strsplit (opt_effects, ",", -1);
  else
    effect_names = g_strsplit (DEFAULT_EFFECTS, ",", -1);

  for (e = effect_names; e != NULL && *e != NULL; ++e) {
    GstElement *el;

    el = gst_element_factory_make (*e, NULL);
    if (el) {
      g_print ("Adding effect '%s'\n", *e);
      g_queue_push_tail (&effects, el);
    }
  }

  pipeline = gst_pipeline_new ("pipeline");

  src = gst_element_factory_make ("videotestsrc", NULL);
  g_object_set (src, "is-live", TRUE, NULL);

  filter1 = gst_element_factory_make ("capsfilter", NULL);
  gst_util_set_object_arg (G_OBJECT (filter1), "caps",
      "video/x-raw, width=320, height=240, "
      "format={ I420, YV12, YUY2, UYVY, AYUV, Y41B, Y42B, "
      "YVYU, Y444, v210, v216, NV12, NV21, UYVP, A420, YUV9, YVU9, IYU1 }");

  q1 = gst_element_factory_make ("queue", NULL);

  blockpad = gst_element_get_static_pad (q1, "src");

  conv_before = gst_element_factory_make ("videoconvert", NULL);

  effect = g_queue_pop_head (&effects);
  cur_effect = effect;

  conv_after = gst_element_factory_make ("videoconvert", NULL);

  q2 = gst_element_factory_make ("queue", NULL);

  filter2 = gst_element_factory_make ("capsfilter", NULL);
  gst_util_set_object_arg (G_OBJECT (filter2), "caps",
      "video/x-raw, width=320, height=240, "
      "format={ RGBx, BGRx, xRGB, xBGR, RGBA, BGRA, ARGB, ABGR, RGB, BGR }");

  sink = gst_element_factory_make ("ximagesink", NULL);

  gst_bin_add_many (GST_BIN (pipeline), src, filter1, q1, conv_before, effect,
      conv_after, q2, sink, NULL);

  gst_element_link_many (src, filter1, q1, conv_before, effect, conv_after,
      q2, sink, NULL);

  gst_element_set_state (pipeline, GST_STATE_PLAYING);

  loop = g_main_loop_new (NULL, FALSE);

  gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), bus_cb, loop);

  g_timeout_add_seconds (1, timeout_cb, loop);

  g_main_loop_run (loop);

  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref (pipeline);

  return 0;
}

补充

upstream and downstream

请添加图片描述

  • src pad传到sink pad方向,就是downstream
  • sink pad传到src pa方向,就是upstream

pull-mode and push-mode

  • src元素会使用gst_pad_push()方法将数据推送到下游元素。下游对端pad将在Chain函数中接收buffer。在push-mode中,src元素是管道中的驱动力,因为它发起了数据传输。
  • 元素也可以从上游元素中拉取数据。下游元素通过在其中一个sink pad上调用gst_pad_pull_range()来实现这一点。在pull mode下,下游元素是管道中的驱动力,因为它发起了数据传输。

参考1:Pipeline manipulation
参考2:push-pull
参考3:Downstream and upstream are the terms

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/192855.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电商项目之Mailgun邮件退信率升高

文章目录1 项目背景2 前言3 采取的措施4 排查的思路5 原因6 技术实现方案1 项目背景 运维人员进行日常巡检发现Mailgun邮件代发渠道的退信率不寻常&#xff0c;在某个时间段会飙高。这会有2个影响&#xff1a; &#xff08;1&#xff09;针对运维的角度&#xff0c;Mailgun官方…

3.SpringBoot配置

一、IOC的好处在对象依赖关系复杂的对象中&#xff0c;只需要考虑“我自己”依赖什么&#xff0c;减少程序员思考负担方便的进行依赖概念&#xff08;接口、抽象类&#xff09;的实现类对象的替换。二、什么是SpringBoot?1.SprigBoot是Spring的一个子工程&#xff0c;目标就是…

JS逆向技巧汇总---给普通爬虫学习者的吐血建议

爬虫实战的JS逆向&#xff0c;就像是做侦探。很多时候&#xff0c;我们要尝试不同方式和手段寻找线索&#xff0c;不能放过蛛丝马迹&#xff0c;通过仔细观察和比较&#xff0c;然后顺藤摸瓜&#xff0c;找到加密入口。再调试JS代码的时候&#xff0c;需要保持清晰的目标和方向…

专科逆袭入职腾讯,真是小母牛坐飞机,牛逼上天了..

在踏入腾讯&#xff0c;办理入职手续的那一天&#xff0c;作为一个男子汉&#xff0c;确实是落泪了。特地分享一波我的真实经历&#xff0c;共勉。 先说一下自己的个人情况&#xff0c;18 届应届生&#xff0c;通过校招进入到了国内某二线城市传统互联网公司&#xff0c;然后一…

优化算法:曲径步长通优处,优化半天白优化

本文来自公众号“AI大道理” 训练一个神经网络&#xff0c;我们想要得到误差最小&#xff0c;就是要我们的损失函数最小。 如何得到最小值呢&#xff1f; 这就是优化算法。 梯度下降法是众多优化中的一种。 1、损失函数 2、GD&#xff08;梯度下降法&#xff09; 3、BGD&a…

Java集合基础

文章目录集合基础一、集合介绍1. 什么是集合&#xff1f;2. 集合类型3. ArrayList 长度可变原理4. 集合和数组的使用选择二、ArrayList 集合快速入门集合创建语句泛型(<>)三、集合常用成员方法1. 增加元素2. 删除元素3. 修改元素4. 查找数据四、集合遍历1. 集合存储字符串…

国联易安:2023年网络安全“五大技术”预测

我国对网络安全非常重视&#xff0c;明确指出“安全是发展的前提&#xff0c;发展是安全的保障&#xff0c;安全和发展要同步推进”。作为国内专注于保密与非密领域的分级保护、等级保护、业务连续性安全和大数据安全产品解决方案与相关技术研究开发的领军企业&#xff0c;国联…

数学建模与数据分析 || 3. 面向数据的特征提取方法: 探索性数据分析

面向数据的特征提取方法: 探索性数据分析 文章目录面向数据的特征提取方法: 探索性数据分析1. 原始数据的准备1.1 导入 python 模块1.2 导入数据集并进行宏观认识1.3 数据集描述2. 数据的预处理2.1 缺失数据的甄别2.2 类别规模的评估3. 数据特征的处理3.1 第一个因变量- 分析范…

【Linux】Linux调试器-gdb使用

作者&#xff1a;小卢 专栏&#xff1a;《Linux》 喜欢的话&#xff1a;世间因为少年的挺身而出&#xff0c;而更加瑰丽。 ——《人民日报》 目录 1.背景 2.gdb的使用 2.1如何生成可以调试debug版本的文件&#xff1a; 2.2如何查看调试信息&…

wcf服务启动禁用Wcf测试客户端(WcfTestClient)

以下页面在WCF工程运行时&#xff0c;会自动启动wcf测试客户端。如果想禁止&#xff0c;可以通过这个方法。 如果希望F5时始终启动IE&#xff0c;可以在.csproj.user文件里增加EnableWcfTestClientForSVC属性配置以禁用WCF测试客户端&#xff1a; <Project> <ProjectE…

Excel等文件中出现新型恶意软件Dropper,通过钓鱼邮件传播

Dropper 是将 Payload 部署到失陷主机的恶意软件&#xff0c;被很多攻击者使用。2022 年第二季度研究人员发现了一些活跃的 Dropper&#xff0c;例如 Microsoft Excel 文件以及 Windows 快捷方式文件和 ISO 文件。通过与社会工程相结合的攻击方式&#xff0c;诱使受害者触发失陷…

打脸质疑者!Mobileye市值冲高,公司CEO说出心声

三个多月前&#xff0c; Mobileye以21美元发行价第二次在美股IPO上市。截止上周五&#xff0c;该公司股价升至32.98美元&#xff0c;市值达到264.47亿美元。相比于美股其他自动驾驶公司股价跌跌不休&#xff0c;显然&#xff0c;资本市场更看重企业的盈利能力和可预见的营收规模…

CVE-2023-24055漏洞复现

KeePass download | SourceForge.net 下载链接 直接咔咔安装 默认路径&#xff1a; 先汉化一下 汉化文件放进去 切换到 KeePass&#xff0c;单击 View → Change Language&#xff0c;然后选择您的语言。重新启动 KeePass 汉化成功。建立新数据库&#xff1a; 文件默认命名为…

HTTP协议详细解读

文章目录1. HTTP概念2. HTTP 特点3. HTTP 协议的工作过程4. 认识URL5. HTTP 请求数据格式6. HTTP 响应数据格式7. 总结&#x1f4c2;橙子精品文章学习推荐1. HTTP概念 HTTP&#xff1a;HyperText Transfer Protocol&#xff0c;超文本传输协议。HTTP 协议规定了浏览器和服务器…

Vue中的 v-show 和 v-if 的区别

一、v-show与v-if的共同点 我们都知道在 vue 中 v-show 与 v-if 的作用效果是相同的(不含v-else)&#xff0c;都能控制元素在页面是否显示 在用法上也是相同的 <Model v-show"isShow" /> <Model v-if"isShow" /> 当表达式为true的时候&…

【微信小程序】webview使用、限制、扩展说明

0 说明 只是一些我自己常见的用法的整理和说明 更多用法还是请看官方文档&#xff1a;web-view 1 小程序web-view支持的页面 1、关联公众号的文章页【活动、文章、引导关注】 2、服务器配置了微信安全校验文件的网页【需要放置在域名根目录下】 2 小程序web-view不支持的页面…

Maxtang大唐英特尔12代J6412无风扇双网口迷你主机真实评测

今天为大家评测一款无风扇的双网口迷你主机&#xff0c;这款主机来自于maxtang大唐采用了英特尔12代赛扬J6412处理器&#xff0c;产品最出彩的地方就是它的网络配置&#xff0c;不仅拥有双千兆网口&#xff0c;还搭载了SIM卡插槽&#xff0c;并支持4G上网。评测产品配置如下&am…

蓝桥杯-玩具蛇

没有白走的路&#xff0c;每一步都算数&#x1f388;&#x1f388;&#x1f388; 题目描述&#xff1a; 已知一个4x4的方格&#xff0c;和一个16个单位长度组成的玩具蛇&#xff0c;即蛇头&#xff0c;蛇身&#xff0c;蛇尾的长度总共是16&#xff0c; 假设蛇的一节在方格中的…

Linux安装JDK8详细步骤

目录 1.虚拟机开启后&#xff0c;在/opt目录下新建文件夹 2.将JDK8压缩包传入/opt/install文件夹中 3. 解压JDK8到/opt/soft目录下 4.更改文件名 5.配置环境变量 6.检查JDK8是否安装成功 1.虚拟机开启后&#xff0c;在/opt目录下新建文件夹 2.将JDK8压缩包传入/opt/insta…

关于【继承】这些你都了解吗?

致前行的人&#xff1a; 要努力&#xff0c;但不要着急&#xff0c;繁花锦簇&#xff0c;硕果累累&#xff0c;都需要过程&#xff01; 目录 1.继承的概念和定义 1.1继承的概念 1.2继承的定义 2.基类和派生类对象赋值转换 3.继承中的作用域 4.派生类的默认成员函数 5.继承与友元…