# Building an Application

这一章节，我们将讨论GStreamer的基础概念和最常用的objects，例如elements，pads和buffers。我们将使用这些objects的可视化表示，以便能够可视化您稍后将学习构建的更复杂的pipeline。你将对GStreamer的API有一个初步印象，足以构建基本的应用。之后你将学着构建一个基础的命令行应用。

### Initializing GStreamer

在开发GStreamer应用的过程中，可以简单的只包含`gst/gst.h`即可访问大部分库函数，除了某些特殊plugin的定制化API需要单独包含头文件。

此外，为了能够调用GStreamer库，在主程序中必须调用`gst_init(&argc, &argv)`函数完成必须的初始化工作以及命令行参数的分析。

### Element

GStreamer应用中最重要的对象是GstElement对象，element是多媒体pipeline基本的构建组件，所有的高级组件都集成自GstElement。

Gstreamer中主要有三种elements：sink element，src element，filter-like element，element的类型由其具备哪些pads决定（pad相关内容将在后续小节展开介绍）。

<figure><img src="/files/yKHaEbyfNBT4cofeEdux" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/s0cYrFEZEiCERRBNrzaB" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/csiIlNo6gK3mAD1sMXu2" alt=""><figcaption></figcaption></figure>

#### Creating a GstElement

```
gst_init (&argc, &argv);

GstElement* element;
element = gst_element_factory_make("factory name", "unique element name");
// or
GstElementFactory* factory;
GstElement* element;
factory = gst_element_factory_find ("factory name");
element = gst_element_factory_create (factory, "unique element name");
```

#### Using an element as a GObject

一个GstElement能够拥有一些属性，这些属性是使用标准GObject的属性实现的。每个GstElement至少继承了GObject的“name”属性，也即创建GstElement时所传递的“unique element name”。GObject为common属性提供了setter和getter，但更一般的做法是使用`g_object_set/get`。

```
g_object_set(G_OBJECT(element), "propeerty name", &value, NULL);
g_object_get(G_OBJECT(element), "propeerty name", &value, NULL);
```

在属性的查询上，GStreamer提供了`gst-inspect-1.0`工具用于查询指定element的属性和简单描述。

除了属性，通常一个GstElement还提供一些GObject信号以实现灵活的**回调机制**用于pipeline和application交互。

#### More about element factories

Element factories是GStreamer注册表中检索的基本单位，他们描述了所有GStreamer能够创建的插件和elements。

从上面创建GstElement对象的过程来看，可以理解为我们从一个GstElementFactory中创建了一个GstElemnt对象，但是我对于GstElementFactory的理解很粗糙，官方文档也没有详细的设计说明。这个GstElementFactory和工厂模式不太一样，在GStreamer中一个plugin就是一个工厂，或许是因为一个plugin可能由多个elements实现。

GstElementFactory最重要的特性就是它拥有其所属于的plugin所支持的pads的完整描述，而不需要将element真正加载进内存。

#### Link Elements

两个elements的连接，实际是src-pad和sink-pad之间的negotiation，因此连接需要两个pad的caps具备交集以及elements处于同一个GstBin。

```
gst_element_link();
gst_elemenet_link_many();
gst_element_link_pads();
```

#### Element States

GStreamer的elements仅有四种状态，四种状态从NULL<->READY<->PAUSE<->PLAYING必须依次切换，即使越级切换状态成功也是接口内部完成了相关的操作。

`GST_STATE_NULL`：默认状态，在这个状态不会申请任何资源，当element的引用计数变为0时必须处于NULL状态。其他状态切换到这个状态会释放掉所有已申请的资源。

`GST_STATE_READY`：在这个状态下element会申请相关的全局资源，但不涉及stream数据。简单来说就是NULL->READY仅是打开相关的硬件设备，申请buffer；PLAYING->READY就是把停止读取stream数据。

`GST_STATE_PAUSE`：这个状态实际是GStreamer中最常见的一个状态，在这个阶段pipeline打开了stream但并未处理它，例如sink element已经读取到了视频文件的第一帧并准备播放。

`GST_STATE_PLAYING`：PLAYING状态和PAUSE状态实际并没有区别，只是PLAYING允许clock 润run。

通常只需要设置bin或者pipeline元素的状态即可自动完成其内含的所有elements的状态切换，但假如动态的向一条处于PLAYING状态的pipeline添加element，则需要手动完成这个element的状态切换。

### Bin

GstBin可以将一系列elements组合形成一个逻辑上的element，以便从整体上操控和管理elements。

* 最外层的bin即使pipeline。
* GstBin管理它内部elements的状态。

### Bus

GstBus是将stream线程消息转发给应用程序线程的系统。

* GstBus本身运行在应用程序的上下文中，但能够自动监听GStreamer内的线程。
* 每条pipeline都自带一条GstBus，开发人员仅需为其设定handler以便在接收到消息是能或者正确的处理。

```
#include <gst/gst.h>

static GMainLoop *loop;

static gboolean
my_bus_callback (GstBus * bus, GstMessage * message, gpointer data)
{
  g_print ("Got %s message\n", GST_MESSAGE_TYPE_NAME (message));

  switch (GST_MESSAGE_TYPE (message)) {
    case GST_MESSAGE_ERROR:{
      GError *err;
      gchar *debug;

      gst_message_parse_error (message, &err, &debug);
      g_print ("Error: %s\n", err->message);
      g_error_free (err);
      g_free (debug);

      g_main_loop_quit (loop);
      break;
    }
    case GST_MESSAGE_EOS:
      /* end-of-stream */
      g_main_loop_quit (loop);
      break;
    default:
      /* unhandled message */
      break;
  }

  /* we want to be notified again the next time there is a message
   * on the bus, so returning TRUE (FALSE means we want to stop watching
   * for messages on the bus and our callback should not be called again)
   */
  return TRUE;
}

gint
main (gint argc, gchar * argv[])
{
  GstElement *pipeline;
  GstBus *bus;
  guint bus_watch_id;

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

  /* create pipeline, add handler */
  pipeline = gst_pipeline_new ("my_pipeline");

  /* adds a watch for new message on our pipeline's message bus to
   * the default GLib main context, which is the main context that our
   * GLib main loop is attached to below
   */
  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  bus_watch_id = gst_bus_add_watch (bus, my_bus_callback, NULL);
  gst_object_unref (bus);

  /* [...] */

  /* create a mainloop that runs/iterates the default GLib main context
   * (context NULL), in other words: makes the context check if anything
   * it watches for has happened. When a message has been posted on the
   * bus, the default main context will automatically call our
   * my_bus_callback() function to notify us of that message.
   * The main loop will be run until someone calls g_main_loop_quit()
   */
  loop = g_main_loop_new (NULL, FALSE);
  g_main_loop_run (loop);

  /* clean up */
  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref (pipeline);
  g_source_remove (bus_watch_id);
  g_main_loop_unref (loop);

  return 0;
}

```

loop线程（主线程）会定期检查它所监听的消息是否发生。

* GstBus的消息监听是异步的，无法处理同步需求。
* `gst_bus_add_watch()`会处理所有类型的GstBus消息，可以在handler中使用switch语句进行细化，也可以用`gst_bus_add_signal_watch(bus)`和`g_signal_connect(bus, "message::eos", G_CALLBACK(cb_message_eos), NULL)`来为特定类型的消息创建一个handler。

### Pads and Capabilities

Pad是一个element与外部交互的接口，数据从一个element的src-pad传递给另一个element的sink-pad。Pad的Capabilities表明element能处理的数据。

#### Dynamic(or sometimes) pads

* 某些elements在创建时并不会带有pads，例如demuxer，demuxer在创建时并不带有sink-pad，直到pipeline处于PAUSE状态读取到了stream的足够信息。demuxer将解析读取的stream中的所有基本流(Video，Audio)数据并为它们分别创建一个能处理对应流数据类型的cap的pad。
* `pad-added`：对于具有sometimes pad的element，它将在创建一个新的pad的时候发出一个signal，用户需要对这个信号做一定的处理以便能正常的连接pipeline中的elemnet。

```
static void cb_new_pad(GstElement* element, GstPad* pad, gpointer data);
g_signal_connect(demux, "pad-added", G_CALLBACK(cb_new_pad), NULL);
```

#### Request pads

request pads是根据请求才创建的pad，广泛应用于muxer，aggregator，tee中，大多数情况下用户不需要处理request pads。

```
gst_element_request_pad_simple(tee, "src_%d");
gst_element_get_compatiable_pad(mux, tolink_pad, NULL);
```

#### Capabilities of a pad

Capabilities是用于描述一个pad能够处理或正在处理的数据类型的机制。GStreamer使用GstCaps描述pads的capabilities，一个GstCaps将含有一个或多个GStructure来描述媒体类型，但对于已经完成negotiation的pad，其GstCaps的GStructure是唯一的，并且属性值是固定的。

#### What capabilities are used for

* Autoplugging：基于pad的的caps自动查找能与其link的element；
* Compatibility detection：为pad negotiation提供支持；
* Metadata：读取pad的caps，可以或缺当前正在播放的流 的信息；
* Filtering：用于限制两个pad之间支持的流类型，常被置于convert elements之后，用于指定上一个与其连接的elements的输出数据格式。

#### Using capabilities for metadata

```
const GStructure* structure;

structure = gst_caps_get_structure(caps, 0);
gst_structure_get_int(structure, "width", &width);
```

#### Creating capabilities of filtering

```
GstCaps* caps;
gboolean link_ok;

caps = gst_caps_new_simple ("video/x-raw",
                             "format", G_TYPE_STRING, "I420",
                             "width", G_TYPE_INT, 384,
                             "height", G_TYPE_INT, 288,
                             "framerate", GST_TYPE_FRACTION, 25, 1,
                             NULL);

link_ok = gst_element_link_filtered (element1, element2, caps);
gst_caps_unref (caps);
// or
GstElement* capfilter;

capfilter = gst_element_factory_make("capfilter", "capfilter0");
g_object_set(G_OBJECT(capfilter), "caps", caps, NULL);
gst_bin_add_many(GST_BIN(pipeline), capfilter, NULL);
gst_element_link_many(elem1, capfilter, elem2, NULL);
```

`gst_element_link_filtered()`内部会自动根据caps创建一个capfilter element并将其插在两个待链接的元素之间。

<br>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ricardolu.gitbook.io/gstreamer/basic-theory-1/building-an-application.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
