1. 程式人生 > >gstreamer外掛編寫教程2-Writing a Plugin

gstreamer外掛編寫教程2-Writing a Plugin

 

官網:https://gstreamer.freedesktop.org/

文件教程:https://gstreamer.freedesktop.org/documentation/plugin-development/basics/index.html


目錄

Writing a Plugin

Constructing the Boilerplate

Getting the GStreamer Plugin Templates

Using the Project Stamp

Examining the Basic Code

Element metadata

GstStaticPadTemplate

Constructor Functions

The plugin_init function

Specifying the pads

The chain function

The event function

The query function

What are states?

Managing filter state

Adding Properties

Signals

Building a Test Application


Writing a Plugin

   您現在已準備好學習如何構建外掛。在本指南的這一部分中,您將學習如何應用基本的GStreamer程式設計概念來編寫一個簡單的外掛。本指南的前幾部分沒有包含明確的示例程式碼,可能使事情有點抽象,難以理解。相比之下,本節將通過開發名為“MyFilter”的示例音訊過濾器外掛來呈現應用程式和程式碼。

    示例濾波器元件將以單個輸入板和單個輸出板開始。首先,濾波器將簡單地將媒體和事件資料從其sink pad傳遞到其src pad而無需修改。但是在本指南的這一部分結束時,您將學習新增一些更有趣的功能,包括屬性和訊號處理程式。閱讀本指南的下一部分“ 高階過濾器概念”後,您將能夠為外掛新增更多功能。

You are now ready to learn how to build a plugin. In this part of the guide, you will learn how to apply basic GStreamer programming concepts to write a simple plugin. The previous parts of the guide have contained no explicit example code, perhaps making things a bit abstract and difficult to understand. In contrast, this section will present both applications and code by following the development of an example audio filter plugin called “MyFilter”.

The example filter element will begin with a single input pad and a single output pad. The filter will, at first, simply pass media and event data from its sink pad to its source pad without modification. But by the end of this part of the guide, you will learn to add some more interesting functionality, including properties and signal handlers. And after reading the next part of the guide, Advanced Filter Concepts, you will be able to add even more functionality 

Constructing the Boilerplate

在本章中,您將學習如何構建新外掛的最小程式碼。從零開始,您將看到如何獲取GStreamer模板源。然後,您將學習如何使用一些基本工具來複制和修改模板外掛以建立新外掛。如果您按照此處的示例進行操作,那麼在本章結尾處您將獲得一個功能音訊過濾器外掛,您可以在GStreamer應用程式中編譯和使用它。

In this chapter you will learn how to construct the bare minimum code for a new plugin. Starting from ground zero, you will see how to get the GStreamer template source. Then you will learn how to use a few basic tools to copy and modify a template plugin to create a new plugin. If you follow the examples here, then by the end of this chapter you will have a functional audio filter plugin that you can compile and use in GStreamer applications.

Getting the GStreamer Plugin Templates

目前有兩種方法可以為GStreamer開發新的外掛:您可以手動編寫整個外掛,也可以複製現有的外掛模板並編寫所需的外掛程式碼。到目前為止,第二種方法比較簡單,因此這裡不會描述第一種方法。(呃,就是說,“它留給了讀者一個練習。”)

第一步是檢視gst-templategit模組的副本,以獲得一個重要的工具和基本GStreamer外掛的原始碼模板。要獲取該gst-template模組,請確保已連線到Internet,並在命令控制檯中鍵入以下命令:

There are currently two ways to develop a new plugin for GStreamer: You can write the entire plugin by hand, or you can copy an existing plugin template and write the plugin code you need. The second method is by far the simpler of the two, so the first method will not even be described here. (Errm, that is, “it is left as an exercise to the reader.”)

The first step is to check out a copy of the gst-template git module to get an important tool and the source code template for a basic GStreamer plugin. To check out the gst-template module, make sure you are connected to the internet, and type the following commands at a command console:

shell $ git clone https://gitlab.freedesktop.org/gstreamer/gst-template.git
Initialized empty Git repository in /some/path/gst-template/.git/
remote: Counting objects: 373, done.
remote: Compressing objects: 100% (114/114), done.
remote: Total 373 (delta 240), reused 373 (delta 240)
Receiving objects: 100% (373/373), 75.16 KiB | 78 KiB/s, done.
Resolving deltas: 100% (240/240), done.

此命令將獲取一系列檔案和目錄 gst-template。您將使用的模板位於 gst-template/gst-plugin/目錄中。您應該檢視該目錄中的檔案,以大致瞭解外掛的源樹結構。

如果由於某種原因您無法訪問git儲存庫,您還 可以 通過gitlab Web介面下載最新版本的快照

This command will check out a series of files and directories into gst-template. The template you will be using is in the gst-template/gst-plugin/ directory. You should look over the files in that directory to get a general idea of the structure of a source tree for a plugin.

If for some reason you can't access the git repository, you can also download a snapshot of the latest revision via the gitlab web interface.

Using the Project Stamp

       製作新元素時要做的第一件事是指定一些關於它的基本細節:它的名字是什麼,是誰編寫的,它是什麼版本號等等。我們還需要定義一個物件來表示元素和儲存元素需要的資料。這些細節統稱為樣板

定義樣板的標準方法只是編寫一些程式碼,並填寫一些結構。如上一節所述,最簡單的方法是複製模板並根據需要新增功能。為了幫助您這樣做,./gst-plugin/tools/目錄中有一個工具 。此工具make_element是一個命令列實用程式,可為您建立樣板程式碼。

要使用make_element,首先開啟一個終端視窗。切換到 gst-template/gst-plugin/src目錄,然後執行該make_element 命令。make_element的引數如下

  1. 外掛的名稱

  2. 該工具將使用的原始檔。預設情況下,gstplugin使用。

例如,以下命令基於外掛模板建立MyFilter外掛,並將輸出檔案放在 gst-template/gst-plugin/src目錄中:

The first thing to do when making a new element is to specify some basic details about it: what its name is, who wrote it, what version number it is, etc. We also need to define an object to represent the element and to store the data the element needs. These details are collectively known as the boilerplate.

The standard way of defining the boilerplate is simply to write some code, and fill in some structures. As mentioned in the previous section, the easiest way to do this is to copy a template and add functionality according to your needs. To help you do so, there is a tool in the ./gst-plugin/tools/ directory. This tool, make_element, is a command line utility that creates the boilerplate code for you.

To use make_element, first open up a terminal window. Change to the gst-template/gst-plugin/src directory, and then run the make_element command. The arguments to the make_element are:

  1. the name of the plugin, and

  2. the source file that the tool will use. By default, gstplugin is used.

For example, the following commands create the MyFilter plugin based on the plugin template and put the output files in the gst-template/gst-plugin/src directory:

shell $ cd gst-template/gst-plugin/src
shell $ ../tools/make_element MyFilter

Note

大寫對於外掛的名稱很重要。請記住,在某些作業系統下,通常在指定目錄和檔名時,大小寫也很重要。

Capitalization is important for the name of the plugin. Keep in mind that under some operating systems, capitalization is also important when specifying directory and file names in general.

最後一個命令建立兩個檔案:gstmyfilter.cgstmyfilter.h

The last command creates two files: gstmyfilter.c and gstmyfilter.h.

Note

建議您gst-plugin在繼續之前建立目錄的副本

It is recommended that you create a copy of the gst-plugin directory before continuing.

現在需要調整Makefile.am以使用新檔名並且從父目錄執行autogen.sh以引導構建環境。之後,可以使用眾所周知的make && sudo make install命令構建和安裝專案。

Now one needs to adjust the Makefile.am to use the new filenames and run autogen.sh from the parent directory to bootstrap the build environment. After that, the project can be built and installed using the well known make && sudo make install commands.

Note

要知道,在預設情況下autogen.sh,並configure會選擇 /usr/local作為預設位置。為了使新的外掛可以在已安裝的GStreamer中使用,需要新增 /usr/local/lib/gstreamer-1.0GST_PLUGIN_PATH

Be aware that by default autogen.sh and configure would choose /usr/local as a default location. One would need to add /usr/local/lib/gstreamer-1.0 to GST_PLUGIN_PATH in order to make the new plugin show up in a gstreamer that's been installed from packages.

Note

FIXME:這部分略顯過時。gst-template仍然可用作最小外掛構建系統框架的示例。但是,為了建立元素,最近推薦使用gst-plugins-bad的工具gst-element-maker。

FIXME: this section is slightly outdated. gst-template is still useful as an example for a minimal plugin build system skeleton. However, for creating elements the tool gst-element-maker from gst-plugins-bad is recommended these days.

Examining the Basic Code

首先,我們將檢查您可能放在標頭檔案中的程式碼(儘管由於程式碼的介面完全由外掛系統定義,並且不依賴於讀取標頭檔案,因此這並不重要。)

First we will examine the code you would be likely to place in a header file (although since the interface to the code is entirely defined by the plugin system, and doesn't depend on reading a header file, this is not crucial.)

#include <gst/gst.h>

/* Definition of structure storing data for this element. */
typedef struct _GstMyFilter {
  GstElement element;

  GstPad *sinkpad, *srcpad;

  gboolean silent;



} GstMyFilter;

/* Standard definition defining a class for this element. */
typedef struct _GstMyFilterClass {
  GstElementClass parent_class;
} GstMyFilterClass;

/* Standard macros for defining types for this element.  */
#define GST_TYPE_MY_FILTER (gst_my_filter_get_type())
#define GST_MY_FILTER(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MY_FILTER,GstMyFilter))
#define GST_MY_FILTER_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MY_FILTER,GstMyFilterClass))
#define GST_IS_MY_FILTER(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MY_FILTER))
#define GST_IS_MY_FILTER_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MY_FILTER))

/* Standard function returning type information. */
GType gst_my_filter_get_type (void);

使用此標頭檔案,您可以使用以下巨集來設定GObject原始檔中的 基礎知識,以便適當地呼叫所有函式:

Using this header file, you can use the following macro to setup the GObject basics in your source file so that all functions will be called appropriately:

#include "filter.h"

G_DEFINE_TYPE (GstMyFilter, gst_my_filter, GST_TYPE_ELEMENT);

Element metadata

元素元資料提供額外的元素資訊。它配置有gst_element_class_set_metadata或 帶有gst_element_class_set_static_metadata以下引數:

  • 為元素命名一個長的英文名稱。

  • 元素的型別,請參閱GStreamer核心原始碼樹中的docs / design / draft-klass.txt文件以獲取詳細資訊和示例。

  • 簡要描述元素的用途。

  • 元素作者的姓名,可選地後跟尖括號中的聯絡電子郵件地址。

例如:

The Element metadata provides extra element information. It is configured with gst_element_class_set_metadataor gst_element_class_set_static_metadata which takes the following parameters:

  • A long, English, name for the element.

  • The type of the element, see the docs/design/draft-klass.txt document in the GStreamer core source tree for details and examples.

  • A brief description of the purpose of the element.

  • The name of the author of the element, optionally followed by a contact email address in angle brackets.

For example:

gst_element_class_set_static_metadata (klass,
  "An example plugin",
  "Example/FirstExample",
  "Shows the basic structure of a plugin",
  "your name <[email protected]>");

_class_init ()函式期間,元素詳細資訊在外掛中註冊,該 函式是GObject系統的一部分。該 _class_init ()功能應該為此GObject的在你註冊GLib的型別的功能進行設定。

The element details are registered with the plugin during the _class_init () function, which is part of the GObject system. The _class_init () function should be set for this GObject in the function where you register the type with GLib.

static void
gst_my_filter_class_init (GstMyFilterClass * klass)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);

[..]
  gst_element_class_set_static_metadata (element_klass,
    "An example plugin",
    "Example/FirstExample",
    "Shows the basic structure of a plugin",
    "your name <[email protected]>");

}

GstStaticPadTemplate

GstStaticPadTemplate是pad的描述,被element建立和使用。它包含:

  • pad的簡短名稱。

  • pad方向。

  • 存在屬性。這表示墊是否總是存在(“始終”墊),或僅在某些情況下(“有時”墊)或僅在應用請求這樣的墊(“請求”墊)時。

  • 此元素支援的型別(功能)。

例如:

A GstStaticPadTemplate is a description of a pad that the element will (or might) create and use. It contains:

  • A short name for the pad.

  • Pad direction.

  • Existence property. This indicates whether the pad exists always (an “always” pad), only in some cases (a “sometimes” pad) or only if the application requested such a pad (a “request” pad).

  • Supported types by this element (capabilities).

For example:

static GstStaticPadTemplate sink_factory =
GST_STATIC_PAD_TEMPLATE (
  "sink",
  GST_PAD_SINK,
  GST_PAD_ALWAYS,
  GST_STATIC_CAPS ("ANY")
);

這些pad模板在_class_init ()函式期間註冊了gst_element_class_add_pad_template ()。對於此功能,您需要一個控制代碼GstPadTemplate,它可以使用gst_static_pad_template_get ()建立靜態pad模板。有關詳細資訊,請參見下文。

Pads通過函式 gst_pad_new_from_static_template ()使用這些靜態模板在_init ()中建立。使用gst_pad_new_from_static_template ()可以從此模板中建立新的pad,您需要將pad模板宣告為全域性變數。有關此主題的更多資訊,請參閱指定pad。

Those pad templates are registered during the _class_init () function with the gst_element_class_add_pad_template (). For this function you need a handle the GstPadTemplate which you can create from the static pad template with gst_static_pad_template_get (). See below for more details on this.

Pads are created from these static templates in the element's _init () function using gst_pad_new_from_static_template (). In order to create a new pad from this template using gst_pad_new_from_static_template (), you will need to declare the pad template as a global variable. More on this subject in Specifying the pads.

static GstStaticPadTemplate sink_factory = [..],
    src_factory = [..];

static void
gst_my_filter_class_init (GstMyFilterClass * klass)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
[..]

  gst_element_class_add_pad_template (element_class,
    gst_static_pad_template_get (&src_factory));
  gst_element_class_add_pad_template (element_class,
    gst_static_pad_template_get (&sink_factory));
}

模板中的最後一個引數是其型別或支援的型別列表。在這個例子中,我們使用'ANY',這意味著該元素將接受所有輸入。在實際情況中,您可以設定媒體型別和可選的一組屬性,以確保只有支援的輸入才會進入。此表示應該是以媒體型別開頭的字串,然後是一組逗號分隔他們支援的屬性。如果音訊濾波器支援任何取樣率的原始整數16位音訊,單聲道或立體聲,正確的模板將如下所示:

The last argument in a template is its type or list of supported types. In this example, we use 'ANY', which means that this element will accept all input. In real-life situations, you would set a media type and optionally a set of properties to make sure that only supported input will come in. This representation should be a string that starts with a media type, then a set of comma-separates properties with their supported values. In case of an audio filter that supports raw integer 16-bit audio, mono or stereo at any samplerate, the correct template would look like this:


static GstStaticPadTemplate sink_factory =
GST_STATIC_PAD_TEMPLATE (
  "sink",
  GST_PAD_SINK,
  GST_PAD_ALWAYS,
  GST_STATIC_CAPS (
    "audio/x-raw, "
      "format = (string) " GST_AUDIO_NE (S16) ", "
      "channels = (int) { 1, 2 }, "
      "rate = (int) [ 8000, 96000 ]"
  )
);


由大括號(“{”和“}”)包圍的值是列表,由方括號(“[”和“]”)包圍的值是範圍。也支援多組型別,並且應以分號(“;”)分隔。稍後,在有關pad的章節中,我們將看到如何使用型別來了解流的確切格式:特定打擊墊

Values surrounded by curly brackets (“{” and “}”) are lists, values surrounded by square brackets (“[” and “]”) are ranges. Multiple sets of types are supported too, and should be separated by a semicolon (“;”). Later, in the chapter on pads, we will see how to use types to know the exact format of a stream: Specifying the pads.

Constructor Functions

每個元素都有兩個用於構造元素的函式。該_class_init()函式,用於僅初始化類一次(指定類具有哪些訊號,引數和虛擬函式以及設定全域性狀態); 和_init() 函式,用於初始化此型別的特定例項。

Each element has two functions which are used for construction of an element. The _class_init() function, which is used to initialise the class only once (specifying what signals, arguments and virtual functions the class has and setting up global state); and the _init() function, which is used to initialise a specific instance of this type.

The plugin_init function

一旦我們編寫了定義外掛所有部分的程式碼,我們就需要編寫plugin_init()函式。這是一個特殊的函式,只要載入外掛就會呼叫它,並且應該返回TRUE或FALSE,具體取決於它是否正確載入了初始化任何依賴項。此外,在此函式中,應註冊外掛中任何受支援的元素型別。

Once we have written code defining all the parts of the plugin, we need to write the plugin_init() function. This is a special function, which is called as soon as the plugin is loaded, and should return TRUE or FALSE depending on whether it loaded initialized any dependencies correctly. Also, in this function, any supported element type in the plugin should be registered.

static gboolean
plugin_init (GstPlugin *plugin)
{
  return gst_element_register (plugin, "my_filter",
                   GST_RANK_NONE,
                   GST_TYPE_MY_FILTER);
}

GST_PLUGIN_DEFINE (
  GST_VERSION_MAJOR,
  GST_VERSION_MINOR,
  my_filter,
  "My filter plugin",
  plugin_init,
  VERSION,
  "LGPL",
  "GStreamer",
  "http://gstreamer.net/"
)

請注意,plugin_init()函式返回的資訊將快取在中央登錄檔中。因此,函式始終返回相同的資訊非常重要:例如,它不能基於執行時條件使元素工廠可用。如果一個元素只能在某些條件下工作(例如,如果某個其他程序沒有使用音效卡),那麼必須通過無法進入READY狀態的元素來反映,而不是外掛試圖否認存在外掛

Note that the information returned by the plugin_init() function will be cached in a central registry. For this reason, it is important that the same information is always returned by the function: for example, it must not make element factories available based on runtime conditions. If an element can only work in certain conditions (for example, if the soundcard is not being used by some other process) this must be reflected by the element being unable to enter the READY state if unavailable, rather than the plugin attempting to deny existence of the plugin.

Specifying the pads

如前所述,pad是資料進出元素的埠,這使得它們成為元素建立過程中非常重要的專案。在樣板程式碼中,我們已經看到靜態填充模板如何處理使用元素類註冊填充模板。在這裡,我們將看到如何建立實際元素,使用 _event ()-function配置特定格式以及如何註冊函式以讓資料流過元素。

在元素_init ()函式中,您可以使用函式中的元素類向pad模板建立pad _class_init ()。建立pad後,您必須設定一個_chain ()函式指標,該指標將接收並處理接收器上的輸入資料。您也可以選擇設定_event ()函式指標和_query ()函式指標。或者,pad也可以在迴圈模式下工作,這意味著它們可以自己提取資料。稍後將詳細介紹此主題。之後,您必須使用元素註冊pad。具體如下:

As explained before, pads are the port through which data goes in and out of your element, and that makes them a very important item in the process of element creation. In the boilerplate code, we have seen how static pad templates take care of registering pad templates with the element class. Here, we will see how to create actual elements, use an _event ()-function to configure for a particular format and how to register functions to let data flow through the element.

In the element _init () function, you create the pad from the pad template that has been registered with the element class in the _class_init () function. After creating the pad, you have to set a _chain () function pointer that will receive and process the input data on the sinkpad. You can optionally also set an _event () function pointer and a _query () function pointer. Alternatively, pads can also operate in looping mode, which means that they can pull data themselves. More on this topic later. After that, you have to register the pad with the element. This happens like this:

static void
gst_my_filter_init (GstMyFilter *filter)
{
  /* pad through which data comes in to the element */
  filter->sinkpad = gst_pad_new_from_static_template (
    &sink_template, "sink");
  /* pads are configured here with gst_pad_set_*_function () */



  gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);

  /* pad through which data goes out of the element */
  filter->srcpad = gst_pad_new_from_static_template (
    &src_template, "src");
  /* pads are configured here with gst_pad_set_*_function () */



  gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);

  /* properties initial value */
  filter->silent = FALSE;
}

The chain function

鏈函式是進行所有資料處理的函式。在簡單過濾器的情況下,_chain ()函式主要是線性函式 - 因此對於每個傳入緩衝區,一個緩衝區也將熄滅。下面是一個非常簡單的鏈函式實現:

The chain function is the function in which all data processing takes place. In the case of a simple filter, _chain ()functions are mostly linear functions - so for each incoming buffer, one buffer will go out, too. Below is a very simple implementation of a chain function:

static GstFlowReturn gst_my_filter_chain (GstPad    *pad,
                                          GstObject *parent,
                                          GstBuffer *buf);

[..]

static void
gst_my_filter_init (GstMyFilter * filter)
{
[..]
  /* configure chain function on the pad before adding
   * the pad to the element */
  gst_pad_set_chain_function (filter->sinkpad,
      gst_my_filter_chain);
[..]
}

static GstFlowReturn
gst_my_filter_chain (GstPad    *pad,
                     GstObject *parent,
             GstBuffer *buf)
{
  GstMyFilter *filter = GST_MY_FILTER (parent);

  if (!filter->silent)
    g_print ("Have data of size %" G_GSIZE_FORMAT" bytes!\n",
        gst_buffer_get_size (buf));

  return gst_pad_push (filter->srcpad, buf);
}

顯然,上面沒有多大用處。您通常會在那裡處理資料,而不是列印資料。但請記住,緩衝區並不總是可寫的。

在更高階的元素(執行事件處理的元素)中,您可能還需要另外指定一個事件處理函式,該函式將在傳送流事件時呼叫(例如上限,流末尾,新段,標記等) )。

Obviously, the above doesn't do much useful. Instead of printing that the data is in, you would normally process the data there. Remember, however, that buffers are not always writeable.

In more advanced elements (the ones that do event processing), you may want to additionally specify an event handling function, which will be called when stream-events are sent (such as caps, end-of-stream, newsegment, tags, etc.).

static void
gst_my_filter_init (GstMyFilter * filter)
{
[..]
  gst_pad_set_event_function (filter->sinkpad,
      gst_my_filter_sink_event);
[..]
}



static gboolean
gst_my_filter_sink_event (GstPad    *pad,
                  GstObject *parent,
                  GstEvent  *event)
{
  GstMyFilter *filter = GST_MY_FILTER (parent);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
      /* we should handle the format here */
      break;
    case GST_EVENT_EOS:
      /* end-of-stream, we should close down all stream leftovers here */
      gst_my_filter_stop_processing (filter);
      break;
    default:
      break;
  }

  return gst_pad_event_default (pad, parent, event);
}

static GstFlowReturn
gst_my_filter_chain (GstPad    *pad,
             GstObject *parent,
             GstBuffer *buf)
{
  GstMyFilter *filter = GST_MY_FILTER (parent);
  GstBuffer *outbuf;

  outbuf = gst_my_filter_process_data (filter, buf);
  gst_buffer_unref (buf);
  if (!outbuf) {
    /* something went wrong - signal an error */
    GST_ELEMENT_ERROR (GST_ELEMENT (filter), STREAM, FAILED, (NULL), (NULL));
    return GST_FLOW_ERROR;
  }

  return gst_pad_push (filter->srcpad, outbuf);
}

在某些情況下,元素也可以控制輸入資料速率。在這種情況下,您可能想要編寫一個所謂的基於迴圈的元素。源元素(僅具有src pad)也可以是基於get的元素。這些概念將在本指南的高階部分以及專門討論src pad的部分中進行說明。

In some cases, it might be useful for an element to have control over the input data rate, too. In that case, you probably want to write a so-called loop-based element. Source elements (with only source pads) can also be get-based elements. These concepts will be explained in the advanced section of this guide, and in the section that specifically discusses source pads.

The event function

事件函式會通知您資料流中發生的特殊事件(例如上限,流末尾,新段,標記等)。事件可以在上游和下游傳播,因此您可以在sink pad和src pad上接收它們。

下面是一個非常簡單的事件函式,我們安裝在元素的接收器墊上。

The event function notifies you of special events that happen in the datastream (such as caps, end-of-stream, newsegment, tags, etc.). Events can travel both upstream and downstream, so you can receive them on sink pads as well as source pads.

Below follows a very simple event function that we install on the sink pad of our element.

static gboolean gst_my_filter_sink_event (GstPad    *pad,
                                          GstObject *parent,
                                          GstEvent  *event);

[..]

static void
gst_my_filter_init (GstMyFilter * filter)
{
[..]
  /* configure event function on the pad before adding
   * the pad to the element */
  gst_pad_set_event_function (filter->sinkpad,
      gst_my_filter_sink_event);
[..]
}

static gboolean
gst_my_filter_sink_event (GstPad    *pad,
                  GstObject *parent,
                  GstEvent  *event)
{
  gboolean ret;
  GstMyFilter *filter = GST_MY_FILTER (parent);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
      /* we should handle the format here */

      /* push the event downstream */
      ret = gst_pad_push_event (filter->srcpad, event);
      break;
    case GST_EVENT_EOS:
      /* end-of-stream, we should close down all stream leftovers here */
      gst_my_filter_stop_processing (filter);

      ret = gst_pad_event_default (pad, parent, event);
      break;
    default:
      /* just call the default handler */
      ret = gst_pad_event_default (pad, parent, event);
      break;
  }
  return ret;
}

gst_pad_event_default ()為未知事件呼叫預設事件處理程式是個好主意 。根據事件型別,預設處理程式將轉發事件或簡單地取消它。CAPS事件預設不轉發,因此我們需要在事件處理程式中自己執行此操作。

It is a good idea to call the default event handler gst_pad_event_default () for unknown events. Depending on the event type, the default handler will forward the event or simply unref it. The CAPS event is by default not forwarded so we need to do this in the event handler ourselves.

The query function

通過查詢功能,您的元素將接收必須回覆的查詢。這些是諸如位置,持續時間之類的查詢,但也包括元素支援的支援格式和排程模式。查詢可以在上游和下游傳輸,因此您可以在sink pad和src pad上接收它們。

下面是一個非常簡單的查詢函式,我們在元素的原始碼上安裝它。

Through the query function, your element will receive queries that it has to reply to. These are queries like position, duration but also about the supported formats and scheduling modes your element supports. Queries can travel both upstream and downstream, so you can receive them on sink pads as well as source pads.

Below follows a very simple query function that we install on the source pad of our element.

static gboolean gst_my_filter_src_query (GstPad    *pad,
                                         GstObject *parent,
                                         GstQuery  *query);

[..]

static void
gst_my_filter_init (GstMyFilter * filter)
{
[..]
  /* configure event function on the pad before adding
   * the pad to the element */
  gst_pad_set_query_function (filter->srcpad,
      gst_my_filter_src_query);
[..]
}

static gboolean
gst_my_filter_src_query (GstPad    *pad,
                 GstObject *parent,
                 GstQuery  *query)
{
  gboolean ret;
  GstMyFilter *filter = GST_MY_FILTER (parent);

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:
      /* we should report the current position */
      [...]
      break;
    case GST_QUERY_DURATION:
      /* we should report the duration here */
      [...]
      break;
    case GST_QUERY_CAPS:
      /* we should report the supported caps here */
      [...]
      break;
    default:
      /* just call the default handler */
      ret = gst_pad_query_default (pad, parent, query);
      break;
  }
  return ret;
}

gst_pad_query_default ()為未知查詢呼叫預設查詢處理程式是個好主意 。根據查詢型別,預設處理程式將轉發查詢或只是取消它。

It is a good idea to call the default query handler gst_pad_query_default () for unknown queries. Depending on the query type, the default handler will forward the query or simply unref it.

What are states?

狀態描述元素例項是否已初始化,是否準備好傳輸資料以及它當前是否正在處理資料。GStreamer中定義了四種狀態:

  • GST_STATE_NULL

  • GST_STATE_READY

  • GST_STATE_PAUSED

  • GST_STATE_PLAYING

從現在開始,它將簡稱為“NULL”,“READY”,“PAUSED”和“PLAYING”。

GST_STATE_NULL是元素的預設狀態。在這種狀態下,它沒有分配任何執行時資源,它沒有載入任何執行時庫,它顯然不能處理資料。

GST_STATE_READY是元素可以進入的下一個狀態。在READY狀態中,元素具有分配的所有預設資源(執行時庫,執行時記憶體)。但是,它尚未分配或定義任何特定於流的內容。當從NULL轉到READY state時,元素應該分配任何非特定於流的資源,並且應該載入執行時可載入的庫(如果有的話)。當反過來時(從READY到NUL),元素應該解除安裝這些庫並釋放所有分配的資源。這種資源的示例是硬體裝置。請注意,檔案通常是流,因此應將其視為特定於流的資源; 因此,它們應該在這種狀態下分配。

GST_STATE_PAUSED是元素準備接受和處理資料的狀態。對於大多數元素,此狀態與播放相同。此規則的唯一例外是接收器元素。接收器元素只接受一個數據緩衝區然後阻塞。此時,管道已準備好立即呈現資料。

GST_STATE_PLAYING是元素可以處於的最高狀態。對於大多數元素,此狀態與PAUSED完全相同,它們接受並處理帶有資料的事件和緩衝區。只有sink element需要區分PAUSED和PLAYING狀態。在播放狀態中,sink element實際上渲染輸入資料,例如將音訊輸出到音效卡或將視訊影象渲染到顯示卡。

A state describes whether the element instance is initialized, whether it is ready to transfer data and whether it is currently handling data. There are four states defined in GStreamer:

  • GST_STATE_NULL

  • GST_STATE_READY

  • GST_STATE_PAUSED

  • GST_STATE_PLAYING

which will from now on be referred to simply as “NULL”, “READY”, “PAUSED” and “PLAYING”.

GST_STATE_NULL is the default state of an element. In this state, it has not allocated any runtime resources, it has not loaded any runtime libraries and it can obviously not handle data.

GST_STATE_READY is the next state that an element can be in. In the READY state, an element has all default resources (runtime-libraries, runtime-memory) allocated. However, it has not yet allocated or defined anything that is stream-specific. When going from NULL to READY state (GST_STATE_CHANGE_NULL_TO_READY), an element should allocate any non-stream-specific resources and should load runtime-loadable libraries (if any). When going the other way around (from READY to NULL, GST_STATE_CHANGE_READY_TO_NULL), an element should unload these libraries and free all allocated resources. Examples of such resources are hardware devices. Note that files are generally streams, and these should thus be considered as stream-specific resources; therefore, they should not be allocated in this state.

GST_STATE_PAUSED is the state in which an element is ready to accept and handle data. For most elements this state is the same as PLAYING. The only exception to this rule are sink elements. Sink elements only accept one single buffer of data and then block. At this point the pipeline is 'prerolled' and ready to render data immediately.

GST_STATE_PLAYING is the highest state that an element can be in. For most elements this state is exactly the same as PAUSED, they accept and process events and buffers with data. Only sink elements need to differentiate between PAUSED and PLAYING state. In PLAYING state, sink elements actually render incoming data, e.g. output audio to a sound card or render video pictures to an image sink.

Managing filter state

如果可能的話,您的元素應該派生自一個新的基類(預製基類)。有針對不同型別的源,接收器和過濾器/轉換元件的現成通用基類。除此之外,還存在音訊和視訊元素等專用基類。

如果使用基類,則很少需要自己處理狀態更改。您所要做的就是覆蓋基類的start()和stop()虛擬函式(根據基類可能會有不同的呼叫),基類將為您處理所有事情。

但是,如果您不是從現成的基類派生,而是從GstElement或其他不基於基類構建的類派生,那麼您很可能必須實現自己的狀態更改函式以通知狀態更改。如果您的外掛是多路分離器或多路複用器,這絕對是必要的,因為還沒有用於多路複用器或多路分離器的基類。

可以通過虛擬函式指標通知元素狀態變化。在此函式內部,元素可以初始化元素所需的任何型別的特定資料,並且可以選擇無法從一個狀態轉到另一個狀態。

對於未處理的狀態更改,請不要g_assert; 這由GstElement基類處理。

If at all possible, your element should derive from one of the new base classes (Pre-made base classes). There are ready-made general purpose base classes for different types of sources, sinks and filter/transformation elements. In addition to those, specialised base classes exist for audio and video elements and others.

If you use a base class, you will rarely have to handle state changes yourself. All you have to do is override the base class's start() and stop() virtual functions (might be called differently depending on the base class) and the base class will take care of everything for you.

If, however, you do not derive from a ready-made base class, but from GstElement or some other class not built on top of a base class, you will most likely have to implement your own state change function to be notified of state changes. This is definitively necessary if your plugin is a demuxer or a muxer, as there are no base classes for muxers or demuxers yet.

An element can be notified of state changes through a virtual function pointer. Inside this function, the element can initialize any sort of specific data needed by the element, and it can optionally fail to go from one state to another.

Do not g_assert for unhandled state changes; this is taken care of by the GstElement base class.

static GstStateChangeReturn
gst_my_filter_change_state (GstElement *element, GstStateChange transition);

static void
gst_my_filter_class_init (GstMyFilterClass *klass)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);

  element_class->change_state = gst_my_filter_change_state;
}



static GstStateChangeReturn
gst_my_filter_change_state (GstElement *element, GstStateChange transition)
{
  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
  GstMyFilter *filter = GST_MY_FILTER (element);

  switch (transition) {
	case GST_STATE_CHANGE_NULL_TO_READY:
	  if (!gst_my_filter_allocate_memory (filter))
		return GST_STATE_CHANGE_FAILURE;
	  break;
	default:
	  break;
  }

  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
  if (ret == GST_STATE_CHANGE_FAILURE)
	return ret;

  switch (transition) {
	case GST_STATE_CHANGE_READY_TO_NULL:
	  gst_my_filter_free_memory (filter);
	  break;
	default:
	  break;
  }

  return ret;
}

請注意,向上(NULL => READY,READY => PAUSED,PAUSED => PLAYING)和向下(PLAYING => PAUSED,PAUSED => READY,READY => NULL)狀態更改在兩個單獨的塊中處理,向下狀態更改只有在我們連結到父類的狀態更改函式之後才會處理。這是為了安全地處理多個執行緒的併發訪問所必需的。

這樣做的原因是,在向下狀態更改的情況下,您不希望銷燬已分配的資源,而外掛的鏈函式(例如)仍在另一個執行緒中訪問這些資源。您的鏈功能是否正在執行取決於外掛的pad的狀態,並且這些pad的狀態與元件的狀態緊密相關。Pad狀態在GstElement類的狀態更改函式中處理,包括正確的鎖定,這就是在銷燬分配的資源之前必須進行連結的原因。

Note that upwards (NULL=>READY, READY=>PAUSED, PAUSED=>PLAYING) and downwards (PLAYING=>PAUSED, PAUSED=>READY, READY=>NULL) state changes are handled in two separate blocks with the downwards state change handled only after we have chained up to the parent class's state change function. This is necessary in order to safely handle concurrent access by multiple threads.

The reason for this is that in the case of downwards state changes you don't want to destroy allocated resources while your plugin's chain function (for example) is still accessing those resources in another thread. Whether your chain function might be running or not depends on the state of your plugin's pads, and the state of those pads is closely linked to the state of the element. Pad states are handled in the GstElement class's state change function, including proper locking, that's why it is essential to chain up before destroying allocated resources.

Adding Properties

控制元素行為方式的主要和最重要的方法是通過GObject屬性。GObject屬性在_class_init ()函式中定義。該元素可選地實現一個 _get_property ()和一個_set_property ()函式。如果應用程式更改或請求屬性的值,將通知這些函式,然後可以填寫值或採取該屬性所需的操作以在內部更改值。

您可能還希望保留一個例項變數,其中包含您在get和set函式中使用的屬性的當前配置值。請注意,GObject不會自動將例項變數設定為預設值,您必須在_init ()元素的功能中執行此操作 。

The primary and most important way of controlling how an element behaves, is through GObject properties. GObject properties are defined in the _class_init () function. The element optionally implements a_get_property () and a _set_property () function. These functions will be notified if an application changes or requests the value of a property, and can then fill in the value or take action required for that property to change value internally.

You probably also want to keep an instance variable around with the currently configured value of the property that you use in the get and set functions. Note that GObject will not automatically set your instance variable to the default value, you will have to do that in the _init () function of your element.


/* properties */
enum {
  PROP_0,
  PROP_SILENT
  /* FILL ME */
};

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

static void
gst_my_filter_class_init (GstMyFilterClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  /* define virtual function pointers */
  object_class->set_property = gst_my_filter_set_property;
  object_class->get_property = gst_my_filter_get_property;

  /* define properties */
  g_object_class_install_property (object_class, PROP_SILENT,
    g_param_spec_boolean ("silent", "Silent",
              "Whether to be very verbose or not",
              FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}

static void
gst_my_filter_set_property (GObject      *object,
                guint         prop_id,
                const GValue *value,
                GParamSpec   *pspec)
{
  GstMyFilter *filter = GST_MY_FILTER (object);

  switch (prop_id) {
    case PROP_SILENT:
      filter->silent = g_value_get_boolean (value);
      g_print ("Silent argument was changed to %s\n",
           filter->silent ? "true" : "false");
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_my_filter_get_property (GObject    *object,
                guint       prop_id,
                GValue     *value,
                GParamSpec *pspec)
{
  GstMyFilter *filter = GST_MY_FILTER (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;
  }
}

以上是如何使用屬性的一個非常簡單的示例。圖形應用程式將使用這些屬性,並將顯示使用者可控制的視窗小部件,可以使用這些視窗小部件更改這些屬性。這意味著 - 為了使該屬性儘可能方便使用者 - 您應該在屬性的定義中儘可能準確。不僅可以定義有效屬性之間的範圍(對於整數,浮點數等),還可以在屬性定義中使用非常具體的描述(更好的:國際化)字串,並且如果可能,使用列舉和標誌而不是整數。GObject文件以非常完整的方式描述了這些內容,但在下面,我們將給出一個簡短的示例,說明它的用處。請注意,在這裡使用整數可能會完全混淆使用者,因為在這種背景下它們毫無意義。這個例子是從video test src偷來的。

The above is a very simple example of how properties are used. Graphical applications will use these properties and will display a user-controllable widget with which these properties can be changed. This means that - for the property to be as user-friendly as possible - you should be as exact as possible in the definition of the property. Not only in defining ranges in between which valid properties can be located (for integers, floats, etc.), but also in using very descriptive (better yet: internationalized) strings in the definition of the property, and if possible using enums and flags instead of integers. The GObject documentation describes these in a very complete way, but below, we'll give a short example of where this is useful. Note that using integers here would probably completely confuse the user, because they make no sense in this context. The example is stolen from videotestsrc.

typedef enum {
  GST_VIDEOTESTSRC_SMPTE,
  GST_VIDEOTESTSRC_SNOW,
  GST_VIDEOTESTSRC_BLACK
} GstVideotestsrcPattern;

[..]

#define GST_TYPE_VIDEOTESTSRC_PATTERN (gst_videotestsrc_pattern_get_type ())
static GType
gst_videotestsrc_pattern_get_type (void)
{
  static GType videotestsrc_pattern_type = 0;

  if (!videotestsrc_pattern_type) {
    static GEnumValue pattern_types[] = {
      { GST_VIDEOTESTSRC_SMPTE, "SMPTE 100% color bars",    "smpte" },
      { GST_VIDEOTESTSRC_SNOW,  "Random (television snow)", "snow"  },
      { GST_VIDEOTESTSRC_BLACK, "0% Black",                 "black" },
      { 0, NULL, NULL },
    };

    videotestsrc_pattern_type =
    g_enum_register_static ("GstVideotestsrcPattern",
                pattern_types);
  }

  return videotestsrc_pattern_type;
}

[..]

static void
gst_videotestsrc_class_init (GstvideotestsrcClass *klass)
{
[..]
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PATTERN,
    g_param_spec_enum ("pattern", "Pattern",
               "Type of test pattern to generate",
                       GST_TYPE_VIDEOTESTSRC_PATTERN, GST_VIDEOTESTSRC_SMPTE,
                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
[..]
}

Signals

GObject訊號可用於通知應用程式特定於此物件的事件。但請注意,應用程式需要了解訊號及其含義,因此,如果您正在尋找應用程式元素互動的通用方法,那麼訊號可能不是您正在尋找的。然而,在許多情況下,訊號非常有用。 有關訊號的所有內部資訊,請參閱 GObject文件

GObject signals can be used to notify applications of events specific to this object. Note, however, that the application needs to be aware of signals and their meaning, so if you're looking for a generic way for application-element interaction, signals are probably not what you're looking for. In many cases, however, signals can be very useful. See the GObject documentation for all internals about signals.

Building a Test Application

通常,您需要在儘可能小的設定中測試新編寫的外掛。通常,這gst-launch-1.0是測試外掛的第一步。如果您尚未將外掛安裝在GStreamer搜尋的目錄中,則需要設定外掛路徑。將GST_PLUGIN_PATH設定為包含外掛的目錄,或使用命令列選項--gst-plugin-path。如果您將外掛基於gst-plugin模板,那麼這看起來就像是。gst-launch-1.0 --gst-plugin-path=$HOME/gst-template/gst-plugin/src/.libs TESTPIPELINE但是,您通常需要比gst-launch-1.0提供的更多測試功能,例如搜尋,事件,互動性等。編寫自己的小型測試程式是實現這一目標的最簡單方法。本節解釋 - 用幾句話 - 如何做到這一點。有關完整的應用程式開發指南,請參閱應用開發手冊

Often, you will want to test your newly written plugin in an as small setting as possible. Usually, gst-launch-1.0 is a good first step at testing a plugin. If you have not installed your plugin in a directory that GStreamer searches, then you will need to set the plugin path. Either set GST_PLUGIN_PATH to the directory containing your plugin, or use the command-line option --gst-plugin-path. If you based your plugin off of the gst-plugin template, then this will look something like gst-launch-1.0 --gst-plugin-path=$HOME/gst-template/gst-plugin/src/.libs TESTPIPELINEHowever, you will often need more testing features than gst-launch-1.0 can provide, such as seeking, events, interactivity and more. Writing your own small testing program is the easiest way to accomplish this. This section explains - in a few words - how to do that. For a complete application development guide, see the Application Development Manual.

首先,您需要通過呼叫初始化GStreamer核心庫gst_init ()。您也可以呼叫 gst_init_get_option_group (),它將返回指向GOptionGroup的指標。然後,您可以使用GOption來處理初始化,這將完成GStreamer初始化。

您可以使用建立元素gst_element_factory_make (),其中第一個引數是您要建立的元素型別,第二個引數是自由格式名稱。最後的示例使用簡單的檔案源 - 解碼器 - 音效卡輸出管道,但如果需要,您可以使用特定的除錯元素。例如, identity元素可以在管道的中間使用,以充當資料到應用程式的傳送器。這可用於檢查測試應用程式中的資料是否存在錯誤或正確性。此外,您可以使用fake sink管道末尾的元素將資料轉儲到stdout(為此,請將該dump屬性設定為TRUE)。最後,您可以使用valgrind來檢查記憶體錯誤。

在連結期間,您的測試應用程式可以使用過濾的大寫字母作為向元素或從元素驅動特定型別資料的方法。這是檢查元素中多種輸入和輸出的非常簡單有效的方法。

請注意,在執行期間,您應該至少偵聽匯流排和/或外掛/元素上的“錯誤”和“eos”訊息,以檢查是否正確處理了此訊息。此外,您應該將事件新增到管道中,並確保您的外掛正確處理這些事件(關於時鐘,內部快取等)。

永遠不要忘記清理外掛或測試應用程式中的記憶體。進入NULL狀態時,您的元素應該清理已分配的記憶體和快取。此外,它應該關閉對可能的支援庫的任何引用。您的應用程式應該unref ()管道並確保它不會崩潰。

At the start, you need to initialize the GStreamer core library by calling gst_init (). You can alternatively callgst_init_get_option_group (), which will return a pointer to GOptionGroup. You can then use GOption to handle the initialization, and this will finish the GStreamer initialization.

You can create elements using gst_element_factory_make (), where the first argument is the element type that you want to create, and the second argument is a free-form name. The example at the end uses a simple filesource - decoder - soundcard output pipeline, but you can use specific debugging elements if that's necessary. For example, an identity element can be used in the middle of the pipeline to act as a data-to-application transmitter. This can be used to check the data for misbehaviours or correctness in your test application. Also, you can use a fakesink element at the end of the pipeline to dump your data to the stdout (in order to do this, set the dump property to TRUE). Lastly, you can use valgrind to check for memory errors.

During linking, your test application can use filtered caps as a way to drive a specific type of data to or from your element. This is a very simple and effective way of checking multiple types of input