• 【GStreamer开发】GStreamer基础教程06——媒体格式和pad的Capabilities


    目标
          Pad的Capabilities是一个GStreamer element的基础,因为framework大部分时间是自动处理的,所以我们几乎感觉不到它的存在。本教程比较偏向原理,介绍了:
          什么是Pad Capabilities
          如何获得这个东西
          什么时候应该去获得这个东西
          为什么你需要了解他们

    介绍
    Pads
          Pads允许信息进入或者离开一个element——就像曾经展示过得一样。这个Capabilities(或者简单地叫做Caps)就是指定哪些信息可以通过Pad来传输。例如:“”RGB视频,尺寸为320x200并且每秒30帧“或者”16位的音频采样,5.1声道,每秒采样44.1k“甚至可以是类似于mp3/h264之类的压缩格式。
          Pads支持多重Capabilities(比如,一个视频的sink可以支持RGB输出或者YUV输出),Capabilities可以指定一个范围而不必须是一个特定值(比如,一个音频sink可以支持从1~48000的采样率)。然而,数据从一个pad流向另一个pad的时候,必须是一个双方都能支持的格式。某一种数据形式是两个pad都能支持的,这样Pads的Capabilities就固定下来(只有一个钟个数,并且不再是一个数据区间了),这个过程被称为协商。下面的例子会更清楚的说明这一点。
          为了两个element可以连接,他们必须有一个共同的Capabilities子集(否则它们肯定不能互相连接)。这就是Capabilities存在的主要目的。
          作为一个应用开发者,我们通常都是用连接一个个element的方法来建立pipeline的。在这个例子中,你需要了解你使用的element的Pad的Caps。
    Pad模板
          Pad是由Pad模板创建的,模板里面会列出一个Pad所有可能的Capabilities。模板对于创建几个相似的Pad是很有帮助的,但也会比较早就判断出两个element是否可以相连:如果连两个Pad的模板都不具备共同的子集的化,就没必要进行更深层的协商了。
          Pad模板检查是协商流程的第一步。随着流程的逐步深入,Pad会正式初始化,也会确定他们的Capability(除非协商失败了)。

    Capabilities例子

    [plain] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. SINK template: 'sink'  
    2.   Availability: Always  
    3.   Capabilities:  
    4.     audio/x-raw-int  
    5.                signed: true  
    6.                  16  
    7.                 depth: 16  
    8.                  rate: [ 1, 2147483647 ]  
    9.              channels: [ 1, 2 ]  
    10.     audio/x-raw-int  
    11.                signed: false  
    12.                  8  
    13.                 depth: 8  
    14.                  rate: [ 1, 2147483647 ]  
    15.              channels: [ 1, 2 ]  
          这是一个element的永久sink pad。它支持2种媒体格式,都是音频的原始数据(audio/x-raw-int),16位的符号数和8位的无符号数。方括号表示一个范围,例如,频道(channels)的范围是1到2.

    [plain] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. SRC template: 'src'  
    2.   Availability: Always  
    3.   Capabilities:  
    4.     video/x-raw-yuv  
    5.                  [ 1, 2147483647 ]  
    6.                height: [ 1, 2147483647 ]  
    7.             framerate: [ 0/1, 2147483647/1 ]  
    8.                format: { I420, NV12, NV21, YV12, YUY2, Y42B, Y444, YUV9, YVU9, Y41B, Y800, Y8  , GREY, Y16 , UYVY, YVYU, IYU1, v308, AYUV, A420 }   
          video/x-raw-yur表示这个source pad用YUV格式输出视频。它支持一个很广的维数和帧率,一系列的YUV格式(用花括号列出了)。所有这些格式都显示不同的图像打包和子采样程度。

    最后的总结 

          你可以使用gst-inspect-0.10这个工具(在后面教程中会介绍)来查看一下element的Caps。

          记住有些element会查看底层的硬件来确定支持的格式和能提供的Pad的Caps(通常在进入READY或更后面的状态)。因此,这个显示的Caps由于平台的不同是不一样的,甚至可能在每一次运行都不相同(这种情况很少)。

          本教程会创建2个element,显示它们各自的Pad模板,连接起来并把pipeline设置成PLAY状态。在每个状态切换的过程中,sink element的Pad的Capabilities会显示出来,这样你就可以观察到协商过程是如何进行的,最后Pad的Caps是怎么定下来的。


    一个简单地Pad Capabilities例子

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. #include <gst/gst.h>  
    2.     
    3. /* Functions below print the Capabilities in a human-friendly format */  
    4. static gboolean print_field (GQuark field, const GValue * value, gpointer pfx) {  
    5.   gchar *str = gst_value_serialize (value);  
    6.     
    7.   g_print ("%s  %15s: %s ", (gchar *) pfx, g_quark_to_string (field), str);  
    8.   g_free (str);  
    9.   return TRUE;  
    10. }  
    11.     
    12. static void print_caps (const GstCaps * caps, const gchar * pfx) {  
    13.   guint i;  
    14.     
    15.   g_return_if_fail (caps != NULL);  
    16.     
    17.   if (gst_caps_is_any (caps)) {  
    18.     g_print ("%sANY ", pfx);  
    19.     return;  
    20.   }  
    21.   if (gst_caps_is_empty (caps)) {  
    22.     g_print ("%sEMPTY ", pfx);  
    23.     return;  
    24.   }  
    25.     
    26.   for (i = 0; i < gst_caps_get_size (caps); i++) {  
    27.     GstStructure *structure = gst_caps_get_structure (caps, i);  
    28.       
    29.     g_print ("%s%s ", pfx, gst_structure_get_name (structure));  
    30.     gst_structure_foreach (structure, print_field, (gpointer) pfx);  
    31.   }  
    32. }  
    33.     
    34. /* Prints information about a Pad Template, including its Capabilities */  
    35. static void print_pad_templates_information (GstElementFactory * factory) {  
    36.   const GList *pads;  
    37.   GstStaticPadTemplate *padtemplate;  
    38.     
    39.   g_print ("Pad Templates for %s: ", gst_element_factory_get_longname (factory));  
    40.   if (!factory->numpadtemplates) {  
    41.     g_print ("  none ");  
    42.     return;  
    43.   }  
    44.     
    45.   pads = factory->staticpadtemplates;  
    46.   while (pads) {  
    47.     padtemplate = (GstStaticPadTemplate *) (pads->data);  
    48.     pads = g_list_next (pads);  
    49.       
    50.     if (padtemplate->direction == GST_PAD_SRC)  
    51.       g_print ("  SRC template: '%s' ", padtemplate->name_template);  
    52.     else if (padtemplate->direction == GST_PAD_SINK)  
    53.       g_print ("  SINK template: '%s' ", padtemplate->name_template);  
    54.     else  
    55.       g_print ("  UNKNOWN!!! template: '%s' ", padtemplate->name_template);  
    56.       
    57.     if (padtemplate->presence == GST_PAD_ALWAYS)  
    58.       g_print ("    Availability: Always ");  
    59.     else if (padtemplate->presence == GST_PAD_SOMETIMES)  
    60.       g_print ("    Availability: Sometimes ");  
    61.     else if (padtemplate->presence == GST_PAD_REQUEST) {  
    62.       g_print ("    Availability: On request ");  
    63.     } else  
    64.       g_print ("    Availability: UNKNOWN!!! ");  
    65.       
    66.     if (padtemplate->static_caps.string) {  
    67.       g_print ("    Capabilities: ");  
    68.       print_caps (gst_static_caps_get (&padtemplate->static_caps), "      ");  
    69.     }  
    70.       
    71.     g_print (" ");  
    72.   }  
    73. }  
    74.     
    75. /* Shows the CURRENT capabilities of the requested pad in the given element */  
    76. static void print_pad_capabilities (GstElement *element, gchar *pad_name) {  
    77.   GstPad *pad = NULL;  
    78.   GstCaps *caps = NULL;  
    79.     
    80.   /* Retrieve pad */  
    81.   pad = gst_element_get_static_pad (element, pad_name);  
    82.   if (!pad) {  
    83.     g_printerr ("Could not retrieve pad '%s' ", pad_name);  
    84.     return;  
    85.   }  
    86.     
    87.   /* Retrieve negotiated caps (or acceptable caps if negotiation is not finished yet) */  
    88.   caps = gst_pad_get_negotiated_caps (pad);  
    89.   if (!caps)  
    90.     caps = gst_pad_get_caps_reffed (pad);  
    91.     
    92.   /* Print and free */  
    93.   g_print ("Caps for the %s pad: ", pad_name);  
    94.   print_caps (caps, "      ");  
    95.   gst_caps_unref (caps);  
    96.   gst_object_unref (pad);  
    97. }  
    98.     
    99. int main(int argc, charchar *argv[]) {  
    100.   GstElement *pipeline, *source, *sink;  
    101.   GstElementFactory *source_factory, *sink_factory;  
    102.   GstBus *bus;  
    103.   GstMessage *msg;  
    104.   GstStateChangeReturn ret;  
    105.   gboolean terminate = FALSE;  
    106.     
    107.   /* Initialize GStreamer */  
    108.   gst_init (&argc, &argv);  
    109.      
    110.   /* Create the element factories */  
    111.   source_factory = gst_element_factory_find ("audiotestsrc");  
    112.   sink_factory = gst_element_factory_find ("autoaudiosink");  
    113.   if (!source_factory || !sink_factory) {  
    114.     g_printerr ("Not all element factories could be created. ");  
    115.     return -1;  
    116.   }  
    117.     
    118.   /* Print information about the pad templates of these factories */  
    119.   print_pad_templates_information (source_factory);  
    120.   print_pad_templates_information (sink_factory);  
    121.     
    122.   /* Ask the factories to instantiate actual elements */  
    123.   source = gst_element_factory_create (source_factory, "source");  
    124.   sink = gst_element_factory_create (sink_factory, "sink");  
    125.     
    126.   /* Create the empty pipeline */  
    127.   pipeline = gst_pipeline_new ("test-pipeline");  
    128.     
    129.   if (!pipeline || !source || !sink) {  
    130.     g_printerr ("Not all elements could be created. ");  
    131.     return -1;  
    132.   }  
    133.     
    134.   /* Build the pipeline */  
    135.   gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL);  
    136.   if (gst_element_link (source, sink) != TRUE) {  
    137.     g_printerr ("Elements could not be linked. ");  
    138.     gst_object_unref (pipeline);  
    139.     return -1;  
    140.   }  
    141.     
    142.   /* Print initial negotiated caps (in NULL state) */  
    143.   g_print ("In NULL state: ");  
    144.   print_pad_capabilities (sink, "sink");  
    145.     
    146.   /* Start playing */  
    147.   ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);  
    148.   if (ret == GST_STATE_CHANGE_FAILURE) {  
    149.     g_printerr ("Unable to set the pipeline to the playing state (check the bus for error messages). ");  
    150.   }  
    151.     
    152.   /* Wait until error, EOS or State Change */  
    153.   bus = gst_element_get_bus (pipeline);  
    154.   do {  
    155.     msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS |  
    156.         GST_MESSAGE_STATE_CHANGED);  
    157.     
    158.     /* Parse message */  
    159.     if (msg != NULL) {  
    160.       GError *err;  
    161.       gchar *debug_info;  
    162.       
    163.       switch (GST_MESSAGE_TYPE (msg)) {  
    164.         case GST_MESSAGE_ERROR:  
    165.           gst_message_parse_error (msg, &err, &debug_info);  
    166.           g_printerr ("Error received from element %s: %s ", GST_OBJECT_NAME (msg->src), err->message);  
    167.           g_printerr ("Debugging information: %s ", debug_info ? debug_info : "none");  
    168.           g_clear_error (&err);  
    169.           g_free (debug_info);  
    170.           terminate = TRUE;  
    171.           break;  
    172.         case GST_MESSAGE_EOS:  
    173.           g_print ("End-Of-Stream reached. ");  
    174.           terminate = TRUE;  
    175.           break;  
    176.         case GST_MESSAGE_STATE_CHANGED:  
    177.           /* We are only interested in state-changed messages from the pipeline */  
    178.           if (GST_MESSAGE_SRC (msg) == GST_OBJECT (pipeline)) {  
    179.             GstState old_state, new_state, pending_state;  
    180.             gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);  
    181.             g_print (" Pipeline state changed from %s to %s: ",  
    182.                 gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));  
    183.             /* Print the current capabilities of the sink element */  
    184.             print_pad_capabilities (sink, "sink");  
    185.           }  
    186.           break;  
    187.         default:  
    188.           /* We should not reach here because we only asked for ERRORs, EOS and STATE_CHANGED */  
    189.           g_printerr ("Unexpected message received. ");  
    190.           break;  
    191.       }  
    192.       gst_message_unref (msg);  
    193.     }  
    194.   } while (!terminate);  
    195.     
    196.   /* Free resources */  
    197.   gst_object_unref (bus);  
    198.   gst_element_set_state (pipeline, GST_STATE_NULL);  
    199.   gst_object_unref (pipeline);  
    200.   gst_object_unref (source_factory);  
    201.   gst_object_unref (sink_factory);  
    202.   return 0;  
    203. }  

    工作流程

          这里的print_field,print_caps和print_pad_templates仅仅简单的用方便阅读的方式显示Capabilities结构。如果你想学到GstCaps数据结构的内在组织,那么请度以下Gstreamer文档关于Pad Caps的部分。

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. /* Shows the CURRENT capabilities of the requested pad in the given element */  
    2. static void print_pad_capabilities (GstElement *element, gchar *pad_name) {  
    3.   GstPad *pad = NULL;  
    4.   GstCaps *caps = NULL;  
    5.     
    6.   /* Retrieve pad */  
    7.   pad = gst_element_get_static_pad (element, pad_name);  
    8.   if (!pad) {  
    9.     g_printerr ("Could not retrieve pad '%s' ", pad_name);  
    10.     return;  
    11.   }  
    12.     
    13.   /* Retrieve negotiated caps (or acceptable caps if negotiation is not finished yet) */  
    14.   caps = gst_pad_get_negotiated_caps (pad);  
    15.   if (!caps)  
    16.     caps = gst_pad_get_caps_reffed (pad);  
    17.     
    18.   /* Print and free */  
    19.   g_print ("Caps for the %s pad: ", pad_name);  
    20.   print_caps (caps, "      ");  
    21.   gst_caps_unref (caps);  
    22.   gst_object_unref (pad);  
    23. }  

          gst_element_get_static_pad()方法会从给定的element里面取得Pad的名字。这个Pad是静态的,因为它会一直存在。要进一步了解Pad请阅读GStreamer文档关于Pad的部分。

          然后我们调用gst_pad_get_negotiated_caps()方法来获得Pad当前的Capabilities,无论是否已经固定下来,都会依赖协商过程的状态。这时Capabilities可能是不存在的,如果遇到这种情况,我们就调用gst_pad_get_caps_reffed()方法来建立一个当前可以接受的Pad Capabilities。这个所谓的当前可接受的Caps时Pad Template的在NULL状态下的Caps,但后面这个可能会改变,因为还会查询实际的硬件。

          gst_pad_get_caps_reffed()通常来说比gst_pad_get_caps()快一点,而且如果我们不需要改变获得的Caps的话也足够用了。然后我们就打印出这些Capabilities。

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. /* Create the element factories */  
    2. source_factory = gst_element_factory_find ("audiotestsrc");  
    3. sink_factory = gst_element_factory_find ("autoaudiosink");  
    4. if (!source_factory || !sink_factory) {  
    5.   g_printerr ("Not all element factories could be created. ");  
    6.   return -1;  
    7. }  
    8.   
    9. /* Print information about the pad templates of these factories */  
    10. print_pad_templates_information (source_factory);  
    11. print_pad_templates_information (sink_factory);  
    12.   
    13. /* Ask the factories to instantiate actual elements */  
    14. source = gst_element_factory_create (source_factory, "source");  
    15. sink = gst_element_factory_create (sink_factory, "sink");  
          在前面的教程中我们直接使用gst_element_factory_make()方法来创建element,而忽略了工厂本身,我们现在补上这一部分。一个GstElementFactory是负责通过一个工厂名来实例化一个element的。

          你可以使用gst_element_factory_find()来创建一个“videotestsrc”工厂,然后通过gst_element_factory_create()来实例化多个“videotestsrc” element。gst_element_factory_make()实际上只是这个过程的一个封装。

          通过工程,Pad模板实际上已经可以访问了,所以工厂一建立我们立刻打印这些信息。

          我们跳过pipeline的创建和启动部分,直接跳到状态切换消息的处理:

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. case GST_MESSAGE_STATE_CHANGED:  
    2.   /* We are only interested in state-changed messages from the pipeline */  
    3.   if (GST_MESSAGE_SRC (msg) == GST_OBJECT (pipeline)) {  
    4.     GstState old_state, new_state, pending_state;  
    5.     gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);  
    6.     g_print (" Pipeline state changed from %s to %s: ",  
    7.         gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));  
    8.     /* Print the current capabilities of the sink element */  
    9.     print_pad_capabilities (sink, "sink");  
    10.   }  
    11.   break;  
          这里只是在每次pipeline状态切换的时候简单地打印当前的Pad Caps。你可以看到,在输出信息里面,初始的Caps(Pad模板的Caps)是如何逐步变化直到确定下来的(仅包含一个类型而且数值固定)。
  • 相关阅读:
    集成学习(一):概述
    机器学习:处理非平衡数据集的办法
    支撑向量机 SVM(一)
    集成学习(五):xgboost 学习总结
    数组的实现(重载[]、=、==、!=运算符重载)
    运算符重载总结
    运算符重载进阶
    运算符重载入门demo
    类模板的简单使用
    static成员变量和static成员函数例程
  • 原文地址:https://www.cnblogs.com/huty/p/8517319.html
Copyright © 2020-2023  润新知