【Gstreamer】自定义Plugin及调用Plugin

news2025/1/25 9:16:45

Gstreamer自定义Plugin及调用自定义Plugin

Gstreamer支持开发者自己创建Plugin,创建后的Plugin可以通过工具gst-inspect-1.0查看,并在代码中调用自定义的plugin。

Gstreamer 官网中给出了Plugin创建教程,但实际上如果按照教程一步步走,最后会因编译失败的问题无法编译出Plugin的lib库(至少目前在Ubuntu20.04是这样)

官网Plugin教程地址:
https://gstreamer.freedesktop.org/documentation/plugin-development/basics/boiler.html?gi-language=c
这里结合Gstreamer 官网的教程,记录以下内容:

  • 自定义Gstreamer Plugin
  • 通过工具(gst-inspect-1.0)查看自定义Plugin信息
  • 调用自定义的Plugin

系统:Ubuntu
Gstream版本:1.0
可以通过下述命令在Ubuntu上安装Gstreamer开发环境

apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio

编译的时候,通过pkg可以直接查询Gstreamer的头文件、lib等参数。

gcc xxxxx  `pkg-config --cflags --libs gstreamer-1.0`

关于Gstreamer的安装,官网给出了很详细的教程,参考官网即可。

自定义Gstreamer Plugin

下载Gstreamer提供的Plugin创建工具。

git clone https://gitlab.freedesktop.org/gstreamer/gst-template.git

下载完之后目录大概是这样的。gst-plugin/tools/目录下的make_element就是用于创建Plugin的工具。
Gsteamer通过Plugin的方式,集成了大量的Element,每个Element提供了特定的功能,将这些Element连接在一起组成了Pipleline。
在这里插入图片描述
目前如果直接使用make_element,并使用gstreamer1.0版本编译生成的工具生成的源文件是编译不过的。所以通过git,将gst-template这个仓库切到特定提交。
我切到到了这个Commit。
在这里插入图片描述
然后执行make_element创建自定义的Plugin

cd gst-template/gst-plugin/src
../tools/make_element MyTestFilter

执行后,在“gst-template/gst-plugin/src”这个目下多出来两个文件 gstmytestfliter.hgstmytestfliter.c。这个两个就是MyTestFilter这个Plugin,对应的源码实现。简单分析一下源码。
gstmytestfliter.h这个头文件中,需要手动修改一处内容,否会会编译不过。
在这里插入图片描述

/*
 * GStreamer
 * Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
 * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
 * Copyright (C) 2020 Niels De Graef <niels.degraef@gmail.com>
 * Copyright (C) 2023  <<user@hostname.org>>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Alternatively, the contents of this file may be used under the
 * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
 * which case the following provisions apply instead of the ones
 * mentioned above:
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#ifndef __GST_MYTESTFLITER_H__
#define __GST_MYTESTFLITER_H__

#include <gst/gst.h>

G_BEGIN_DECLS

#define GST_TYPE_MYTESTFLITER (gst_my_test_fliter_get_type())
G_DECLARE_FINAL_TYPE (GstMyTestFliter, gst_my_test_fliter,
    GST, MYTESTFLITER, GstElement)  
// 注意:
// 上一行底代码中的MYTESTFLITER是手动修改后的
// 工具生成的值是PLUGIN_TEMPLATE,如果不修改,编译时会失败(找不到GST_MYTESTFLITER这个定义)
struct _GstMyTestFliter
{
  GstElement element;

  GstPad *sinkpad, *srcpad;

  gboolean silent;
};

G_END_DECLS

#endif /* __GST_MYTESTFLITER_H__ */

gstmytestfliter.c:源码中主要关注两个函数,gst_my_test_fliter_class_init 用于初始化PluginClass(类似C++类的构造函数); gst_my_test_fliter_init 用于初始化Element,当Plugin被创建时会调用这个函数。

/*
 * GStreamer
 * Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
 * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
 * Copyright (C) 2023  <<user@hostname.org>>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Alternatively, the contents of this file may be used under the
 * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
 * which case the following provisions apply instead of the ones
 * mentioned above:
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/**
 * SECTION:element-mytestfliter
 *
 * FIXME:Describe mytestfliter here.
 *
 * <refsect2>
 * <title>Example launch line</title>
 * |[
 * gst-launch -v -m fakesrc ! mytestfliter ! fakesink silent=TRUE
 * ]|
 * </refsect2>
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gst/gst.h>

#include "gstmytestfliter.h"

GST_DEBUG_CATEGORY_STATIC (gst_my_test_fliter_debug);
#define GST_CAT_DEFAULT gst_my_test_fliter_debug

/* Filter signals and args */
enum
{
  /* FILL ME */
  LAST_SIGNAL
};

enum
{
  PROP_0,
  PROP_SILENT
};

/* the capabilities of the inputs and outputs.
 *
 * describe the real formats here.
 */
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,  // 类型为sink
    GST_PAD_ALWAYS, // 这个PAD总是存在,Plugin创建时就自动创建了
    GST_STATIC_CAPS ("ANY") // 能力值(相当于过滤器,只有能力匹配的PAD才能连接上)
    );

static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC, // 类型为src
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("ANY")
    );

#define gst_my_test_fliter_parent_class parent_class
G_DEFINE_TYPE (GstMyTestFliter, gst_my_test_fliter, GST_TYPE_ELEMENT);

static void gst_my_test_fliter_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_my_test_fliter_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);

static gboolean gst_my_test_fliter_sink_event (GstPad * pad, GstObject * parent, GstEvent * event);
static GstFlowReturn gst_my_test_fliter_chain (GstPad * pad, GstObject * parent, GstBuffer * buf);

/* GObject vmethod implementations */

// 初始化函数,用于初始化plugin class (相当于C++类的构造函数)
/* initialize the mytestfliter's class */
static void
gst_my_test_fliter_class_init (GstMyTestFliterClass * klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;

  gobject_class->set_property = gst_my_test_fliter_set_property;
  gobject_class->get_property = gst_my_test_fliter_get_property;

  g_object_class_install_property (gobject_class, PROP_SILENT,
      g_param_spec_boolean ("silent", "Silent", "Produce verbose output ?",
          FALSE, G_PARAM_READWRITE));
 // Plugin的描述内容
  gst_element_class_set_details_simple(gstelement_class,
    "MyTestFliter",
    "FIXME:Generic",
    "FIXME:Generic Template Element",
    " <<user@hostname.org>>");
  // 注册pad templates,注册后可以使用gst_pad_new_from_static_template创建这个pad
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&src_factory));
  // 注册pad templates,注册后可以使用gst_pad_new_from_static_template创建这个pad
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&sink_factory));
}

// 当这个插件被创建时,会调用这个初始化函数
/* initialize the new element
 * instantiate pads and add them to element
 * set pad calback functions
 * initialize instance structure
 */
static void
gst_my_test_fliter_init (GstMyTestFliter * filter)
{
  // 创建sinkpad
  filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
  // 设置处理sink event的函数
  gst_pad_set_event_function (filter->sinkpad,
                              GST_DEBUG_FUNCPTR(gst_my_test_fliter_sink_event));
  // 设置sink对应的chain function(处理给到sink数据的函数)
  gst_pad_set_chain_function (filter->sinkpad,
                              GST_DEBUG_FUNCPTR(gst_my_test_fliter_chain));
  GST_PAD_SET_PROXY_CAPS (filter->sinkpad);
  gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);

  filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
  GST_PAD_SET_PROXY_CAPS (filter->srcpad);
  gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);

  filter->silent = FALSE;
}

static void
gst_my_test_fliter_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstMyTestFliter *filter = GST_MYTESTFLITER (object);

  switch (prop_id) {
    case PROP_SILENT:
      filter->silent = g_value_get_boolean (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_my_test_fliter_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstMyTestFliter *filter = GST_MYTESTFLITER (object);

  switch (prop_id) {
    case PROP_SILENT:
      g_value_set_boolean (value, filter->silent);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

/* GstElement vmethod implementations */

/* this function handles sink events */
static gboolean
gst_my_test_fliter_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
  GstMyTestFliter *filter;
  gboolean ret;

  filter = GST_MYTESTFLITER (parent);

  GST_LOG_OBJECT (filter, "Received %s event: %" GST_PTR_FORMAT,
      GST_EVENT_TYPE_NAME (event), event);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
    {
      GstCaps * caps;

      gst_event_parse_caps (event, &caps);
      /* do something with the caps */

      /* and forward */
      ret = gst_pad_event_default (pad, parent, event);
      break;
    }
    default:
      ret = gst_pad_event_default (pad, parent, event);
      break;
  }
  return ret;
}

/* chain function
 * this function does the actual processing
 */
static GstFlowReturn
gst_my_test_fliter_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
  GstMyTestFliter *filter;

  filter = GST_MYTESTFLITER (parent);

  if (filter->silent == FALSE)
    g_print ("I'm plugged, therefore I'm in.\n");

  /* just push out the incoming buffer without touching it */
  return gst_pad_push (filter->srcpad, buf);
}


/* entry point to initialize the plug-in
 * initialize the plug-in itself
 * register the element factories and other features
 */
static gboolean
mytestfliter_init (GstPlugin * mytestfliter)
{
  /* debug category for fltering log messages
   *
   * exchange the string 'Template mytestfliter' with your description
   */
  GST_DEBUG_CATEGORY_INIT (gst_my_test_fliter_debug, "mytestfliter",
      0, "Template mytestfliter");

  return gst_element_register (mytestfliter, "mytestfliter", GST_RANK_NONE,
      GST_TYPE_MYTESTFLITER);
}

/* PACKAGE: this is usually set by autotools depending on some _INIT macro
 * in configure.ac and then written into and defined in config.h, but we can
 * just set it ourselves here in case someone doesn't use autotools to
 * compile this code. GST_PLUGIN_DEFINE needs PACKAGE to be defined.
 */
#ifndef PACKAGE
#define PACKAGE "myfirstmytestfliter"
#endif

/* gstreamer looks for this structure to register mytestfliters
 *
 * exchange the string 'Template mytestfliter' with your mytestfliter description
 */
GST_PLUGIN_DEFINE (
    GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    mytestfliter,
    "Template mytestfliter",
    mytestfliter_init,
    PACKAGE_VERSION,
    GST_LICENSE,
    GST_PACKAGE_NAME,
    GST_PACKAGE_ORIGIN
)

解下来,编译Plugin生成对应的so文件。首先在gst-template\gst-plugin\meson.build中添加编译配置

mytestfilter_sources = [
  'src/gstmytestfliter.c',
  ]

gstmytestfliter = library('gstmytestfliter',
  mytestfilter_sources,
  c_args: plugin_c_args,
  dependencies : [gst_dep],
  install : true,
  install_dir : plugins_install_dir,
)

然后在gst-template目录下(就是仓库的目录)编译这个插件,前提需要安装好Gstreamer的开发环境。

meson build
ninja -C build

编译成功 输出如下内容,生成libgstmyfilter.so就是自定义的插件了。

ninja: Entering directory `build'				
[4/4] Linking target gst-plugin/libgstmyfilter.so.

通过工具查看自定义插件信息

libgstmyfilter.so 拷贝到 /usr/lib/x86_64-linux-gnu/gstreamer-1.0 目录下 (gstreamer库的安装路径,根据实际情况去copy)
copy完后,通过gst-inspect-1.0验证一下

gst-inspect-1.0 mytestfliter
# 输出如下信息:
Factory Details:								
  Rank                     none (0)								
  Long-name                MyTestFliter								
  Klass                    FIXME:Generic								
  Description              FIXME:Generic Template Element								
  Author                    <<user@hostname.org>>								
								
Plugin Details:								
  Name                     mytestfliter								
  Description              Template mytestfliter								
  Filename                 /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstmytestfliter.so								
  Version                  1.17.0.1								
  License                  LGPL								
  Source module            gst-template-plugin								
  Binary package           GStreamer template Plug-ins								
  Origin URL               https://gstreamer.freedesktop.org								
								
GObject								
 +----GInitiallyUnowned								
       +----GstObject								
             +----GstElement								
                   +----GstMyTestFliter								
								
Pad Templates:								
  SRC template: 'src'								
    Availability: Always								
    Capabilities:								
      ANY								
  								
  SINK template: 'sink'								
    Availability: Always								
    Capabilities:								
      ANY								
								
Element has no clocking capabilities.								
Element has no URI handling capabilities.								
								
Pads:								
  SINK: 'sink'								
    Pad Template: 'sink'								
  SRC: 'src'								
    Pad Template: 'src'								
								
Element Properties:								
  name                : The name of the object								
                        flags: 可读, 可写								
                        String. Default: "mytestfliter0"								
  parent              : The parent of the object								
                        flags: 可读, 可写								
                        Object of type "GstObject"								
  silent              : Produce verbose output ?								
                        flags: 可读, 可写								
                        Boolean. Default: false								
								

调用自定义Plugin

useplugin.c ,在这个程序中调用了mytestfliter这个插件,设置其silent属性为false。这样在插件收到数据时,就会输出"I’m plugged, therefore I’m in"。插件中实现源码如下

/* chain function
 * this function does the actual processing
 */
static GstFlowReturn
gst_my_test_fliter_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
  GstMyTestFliter *filter;

  filter = GST_MYTESTFLITER (parent);

  if (filter->silent == FALSE)
    g_print ("I'm plugged, therefore I'm in.\n");

  /* just push out the incoming buffer without touching it */
  return gst_pad_push (filter->srcpad, buf);
}

下面是useplugin.c 的源码中,视频文件的路径替换为自己实际的路径即可。

#include <gst/gst.h>


typedef struct _CustomData
{
  GstElement* pipeline;
  GstElement* source;
  GstElement* convert;
  GstElement* resample;
  GstElement* sink;
  GstElement* mytestfilter;
} CustomData;

static void pad_added_handler(GstElement* src, GstPad* pad, CustomData* data);

int tutorial_main(int argc, char* argv[])
{
  CustomData data;
  GstBus* bus;
  GstMessage* msg;
  GstStateChangeReturn ret;
  gboolean terminate = FALSE;

  // init
  gst_init(&argc, &argv);

  // source(pad) -> (sink)convert -> resample -> sink
  // decode element
  data.source = gst_element_factory_make("uridecodebin", "source");
  // convet audio fromat
  data.convert = gst_element_factory_make("videoconvert", "convert");
  // 重采样
  data.resample = gst_element_factory_make("audioresample", "resample");
  // sink
  data.sink = gst_element_factory_make("autovideosink", "sink");
  // test plugin
  data.mytestfilter = gst_element_factory_make("mytestfliter", "mytest");


  // Create pipeline
  data.pipeline = gst_pipeline_new("test-pipeline");

  // Check Create Success
  if (!data.pipeline
    || !data.source
    || !data.convert
    || !data.resample
    || !data.sink) {
    g_printerr("Not all elements could created\n");
    return -1;
  }

  // Add Element to pipeline
  gst_bin_add_many(GST_BIN(data.pipeline),
    data.source, data.convert, data.resample, data.mytestfilter, data.sink, NULL);
  if(!gst_element_link_many(data.convert, data.mytestfilter, data.sink, NULL)){
    g_printerr("Element could not be linked\n");
    gst_object_unref(data.pipeline);
    return -1;
  }
// 注意,这里将视频文件,替换为本地实际的视频文件
  g_object_set(data.source, "uri", 
    "file:///home/linduo/test.mp4",
    NULL);

  g_object_set(data.mytestfilter, "silent", FALSE, NULL);
  g_signal_connect(data.source, "pad-added", 
    G_CALLBACK(pad_added_handler), &data);

  // Start pipeline
  ret = gst_element_set_state(data.pipeline, GST_STATE_PLAYING);
  if (ret == GST_STATE_CHANGE_FAILURE) {
    g_printerr("Start pipeline Error=========");
    gst_object_unref(data.pipeline);
    return -1;
  }

  // Listen
  bus = gst_element_get_bus(data.pipeline);
  do {
    msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE,
      GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS);

    if (msg != NULL) {
      GError *err;
      gchar *debug_info;

      switch(GST_MESSAGE_TYPE(msg))
      {
        case GST_MESSAGE_ERROR:
          terminate = TRUE;
          break;
        case GST_MESSAGE_EOS:
          terminate = TRUE;
          break;
        case GST_MESSAGE_STATE_CHANGED:
          if (GST_MESSAGE_SRC(msg) == GST_OBJECT(data.pipeline)) {
            GstState old_state, new_state, pending_state;
            gst_message_parse_state_changed(msg, &old_state,&new_state,
              &pending_state);
            g_print("Pipeline state changed form %s to %s.\n",
              gst_element_state_get_name(old_state),
              gst_element_state_get_name(new_state));
          }
          break;
        default:
          g_printerr("==========\n");
          break;
      }

      gst_message_unref(msg);
    }
  } while(!terminate);

  gst_object_unref(bus);
  gst_element_set_state(data.pipeline, GST_STATE_NULL);
  gst_object_unref(data.pipeline);
  return 0;
}

static void
pad_added_handler(GstElement* src, GstPad* new_pad, CustomData* data)
{
  // Get Convert Sink pad
  GstPad* sink_pad = gst_element_get_static_pad(data->convert, "sink");
  GstPadLinkReturn ret;
  GstCaps* new_pad_caps = NULL;
  GstStructure* new_pad_strut = NULL;
  const gchar* new_pad_type = NULL;

    g_print ("Received new pad '%s' from '%s':\n", GST_PAD_NAME (new_pad),
      GST_ELEMENT_NAME (src));

    if (gst_pad_is_linked(sink_pad)) {
      g_print("We are already linked\n");
      goto exit;
    }

    //  获取当前的Caps
    new_pad_caps = gst_pad_get_current_caps(new_pad);
    // 获取Caps中的结构体
    new_pad_strut = gst_caps_get_structure(new_pad_caps, 0);
    // 获取Type类型
    new_pad_type = gst_structure_get_name(new_pad_strut);
    // 如果Type类型中有audio/x-raw(音频数据)
    // if (!g_str_has_prefix(new_pad_type , "audio/x-raw")) {
    //   g_print("Has not type %s", new_pad_type);
    //   goto exit;
    // }

    ret = gst_pad_link(new_pad, sink_pad);
    if (GST_PAD_LINK_FAILED(ret)) {
      g_print("Link Error\n");
    } else {
      g_print("Link succeeded [%s]\n", new_pad_type);
    }

exit:
  if (new_pad_caps != NULL)
    gst_caps_unref(new_pad_caps);

  gst_object_unref(sink_pad);
}

int main(int argc, char* argv[])
{
  return tutorial_main(argc, argv);
}

编译测试程序

gcc useplugin.c -o useplugin `pkg-config --cflags --libs gstreamer-1.0`

执行

./useplugin 						
Pipeline state changed form NULL to READY.						
Received new pad 'src_0' from 'source':						
Link succeeded [video/x-raw]						
Received new pad 'src_1' from 'source':						
We are already linked						
I'm plugged, therefore I'm in.						
Pipeline state changed form READY to PAUSED.						
Pipeline state changed form PAUSED to PLAYING.						
I'm plugged, therefore I'm in.						
I'm plugged, therefore I'm in.						
I'm plugged, therefore I'm in.						
I'm plugged, therefore I'm in
...

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

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

相关文章

机器学习笔记 - 异常检测之OneClass SVM算法简述

一、异常检测是什么? 如下图,理想中我们可以找到一个框住大部分正常样本的决策边界,而在边界外部的数据点(蓝点)即视为异常。 但实际情况下数据都没有标签,因此很难定义正常还是不正常。异常检测的主要挑战如下:正常与异常行为之间的界限往往并不明确、不同的应…

org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource

DynamicDataSource-CSDN博客 /** Copyright 2002-2020 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the L…

uni-app 微信小程序之自定义中间圆形tabbar

文章目录 1. 自定义tabbar效果2. pages新建tabbar页面3. tabbar 页面结构4. tabbar 页面完整代码 1. 自定义tabbar效果 2. pages新建tabbar页面 首先在 pages.json 文件中&#xff0c;新建一个 tabbar 页面 "pages": [ //pages数组中第一项表示应用启动页&#xff…

Spark大数据集群日常开发过程遇到的异常及解决思路汇总

原创/朱季谦 在开发Spark任务过程中&#xff0c;遇到过不少新人经常可能会遇到的坑&#xff0c;故而将这些坑都总结了下来&#xff0c;方便日后遇到时&#xff0c;可以快速定位解决&#xff0c;壁面耗费过多时间在查找问题之上。 一、出现java.lang.IllegalAccessError: tried…

Docker常见命令介绍

命令说明 docker pull 拉取镜像 docker push 推送镜像到DockerRegistry docker images 查看本地镜像 docker rmi 删除本地镜像 docker run 创建并运行容器&#xff08;不能重复创建&#xff09; docker stop 停止指定容器 docker start 启动指定容器 docker rest…

设计模式之道:解构结构型设计模式的核心原理

解构常见的三种结构型设计模式的核心原理 一、引言&#xff1a;如何学习设计模式&#xff1f;二、责任链模式2.1、代码结构2.2、符合的设计原则2.3、案例分析&#xff1a;nginx 阶段处理2.4、小结 三、装饰器模式3.1、代码结构3.2、符合的设计原则3.3、小结 四、组合模式4.1、代…

深度学习——第03章 Python程序设计语言(3.1 Python语言基础)

无论是在机器学习还是深度学习中&#xff0c;Python已经成为主导性的编程语言。而且&#xff0c;现在许多主流的深度学习框架&#xff0c;例如PyTorch、TensorFlow也都是基于Python。本课程主要是围绕“理论实战”同时进行&#xff0c;所以本章将重点介绍深度学习中Python的必备…

JOSEF 快速中间继电器 KZJ-4H-L DC220V 导轨安装

快速中间继电器KZJ-4H-LDC220V导轨安装导轨安装是广泛用于电力系统&#xff0c;能够断货开或开通大负载&#xff0c;并且具有较强的断弧能力&#xff0c;适用于交流50/60Hz。电压24380V,直流电压24280V自动控制电路中以增加保护和控制回路的触点数量与触点容量。 KZJ系列快速中…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《市场环境下运行的光热电站子系统容量优化配比研究》

这个标题涉及到对市场环境下运行的光热电站子系统进行容量优化配比的研究。让我们逐步解读&#xff1a; 市场环境下运行的光热电站&#xff1a; 这指的是光热电站在实际市场环境中的运行&#xff0c;可能包括了市场相关的经济、政策、竞争等因素。 子系统&#xff1a; 光热电站…

把握生成式AI新机遇,亚马逊云科技助力下一位独角兽

文章目录 前言亚马逊云科技生成式AI创业热潮向应用与工具链集中生成式AI初创生而全球化 赛道更细分、布局更广阔后记 前言 DoNews11月20日消息&#xff0c;当一项新技术出现&#xff0c;并成为行业主流甚至是变革的“敲门砖”时&#xff0c;企业应该如何应对&#xff1f; 202…

[WP] ISCTF2023 Web 部分题解

圣杯战争!!! 反序列化伪协议读取 where_is_the_flag 环境变量根目录当前目录 绕进你的心里 利用正则最大回溯绕过 easy_website or select 用双写绕过 空格用/**/绕&#xff0c;报错注入 wafr codesystem(ca\t /f*) webinclude 扫描得到index.bak备份文件打开为加密的代码 写…

PyLMKit(4):基于本地知识库的检索增强生成RAG

基于本地知识库的检索增强生成RAG 0.项目信息 日期&#xff1a; 2023-12-2作者&#xff1a;小知课题: RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09;是一种利用知识库检索的方法&#xff0c;提供与用户查询相关的内容&#xff0c;从而…

Hdoop学习笔记(HDP)-Part.07 安装MySQL

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …

数据链路层之网桥

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

[IIS服务]搭建unityWebGl项目服务器(用idea失败了,这次用IIS)

1、确认安装服务 没有安装的&#xff0c;点击安装&#xff0c;安装完成后下一步。 2、配置IIS服务&#xff08;很多小伙伴更新了windows找不到&#xff0c;可以使用cmd运行control admintools打开下图页面&#xff09; 打开管理器之后添加一个网站。 路径选择网站路径&#xf…

Linux驱动开发学习笔记1《字符设备驱动开发》

目录 一、字符设备驱动简介 二、chrdevbase 字符设备驱动开发实验 1.创建驱动程序的目录 2.创建vscode工程 3.编写实验程序 4.编译驱动程序和测试APP代码 &#xff08;1&#xff09;加载驱动模块 &#xff08;2&#xff09;创建设备节点文件 &#xff08;3&#xff…

设计模式-结构型模式之代理设计模式

文章目录 八、代理设计模式 八、代理设计模式 代理设计模式通过代理控制对象的访问&#xff0c;可以详细访问某个对象的方法&#xff0c;在这个方法调用处理&#xff0c;或调用后处理。既(AOP微实现) 。 代理有分静态代理和动态代理&#xff1a; 静态代理&#xff1a;在程序…

阅读笔记|A Survey of Large Language Models

阅读笔记 模型选择&#xff1a;是否一定要选择参数量巨大的模型&#xff1f;如果需要更好的泛化能力&#xff0c;用于处理非单一的任务&#xff0c;例如对话&#xff0c;则可用选更大的模型&#xff1b;而对于单一明确的任务&#xff0c;则不一定越大越好&#xff0c;参数小一…

Basemap地图绘制_Python数据分析与可视化

Basemap地图绘制 安装和使用地图投影地图背景在地图上画数据 Basemap是Matplotlib的一个子包&#xff0c;负责地图绘制。在数据可视化过程中&#xff0c;我们常需要将数据在地图上画出来。 比如说我们在地图上画出城市人口&#xff0c;飞机航线&#xff0c;军事基地&#xff0c…

Windows远程桌面提示出现身份验证错误 要求的函数不支持

现象 解决方案&#xff1a; 在cmd运行框输入&#xff1a;gpedit.msc打开组策略编辑器路径&#xff1a;计算机配置→管理模板→Windows组件→远程桌面服务→远程桌面会话主机→安全开启远程连接要求使用指定的安全层 禁用要求使用网络级别的身份验证对远程连接的用户进行身份验…