zoukankan      html  css  js  c++  java
  • 【GStreamer开发】GStreamer基础教程09——收集媒体信息

    目标

          有时你需要快速的了解一个文件(或URI)包含的媒体格式或者看看是否支持这种格式。当然你可以创建一个pipeline,设置运行,观察总线上的消息,但GStreamer提供了一个工具可以帮你做这些。本教程主要讲述:

          如何获得一个URI上的信息

          如何确定一个URI是可以播放的


    介绍

          GstDiscover是一个在pbutils库提供的工具,接受输入URI或者URI列表,返回它们的信息。这个工具可以工作在同步或者异步模式下。

          在同步模式下,只有一个API可以用,就是gst_discoverer_discover_uri(),这个API会阻塞线程直到得到需要的信息。因为阻塞会带来GUI上很糟糕的体验,所以基于UI的应用主要使用异步模式,本教程也是基于异步模式的。

          检测到的信息会包括codec的描述,流的拓扑结构和可以获得的元数据。

    [html] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <span style="font-size:14px;">As an example, this is the result of discovering http://docs.gstreamer.com/media/sintel_trailer-480p.webm (Click to expand)  
    2. Duration: 0:00:52.250000000  
    3. Tags:  
    4.   video codec: On2 VP8  
    5.   language code: en  
    6.   container format: Matroska  
    7.   application name: ffmpeg2theora-0.24  
    8.   encoder: Xiph.Org libVorbis I 20090709  
    9.   encoder version: 0  
    10.   audio codec: Vorbis  
    11.   nominal bitrate: 80000  
    12.   bitrate: 80000  
    13. Seekable: yes  
    14. Stream information:  
    15.   container: WebM  
    16.     audio: Vorbis  
    17.       Tags:  
    18.         language code: en  
    19.         container format: Matroska  
    20.         audio codec: Vorbis  
    21.         application name: ffmpeg2theora-0.24  
    22.         encoder: Xiph.Org libVorbis I 20090709  
    23.         encoder version: 0  
    24.         nominal bitrate: 80000  
    25.         bitrate: 80000  
    26.     video: VP8  
    27.       Tags:  
    28.         video codec: VP8 video  
    29.         container format: Matroska</span>  
          下面的代码试着通过命令行的方式来运行discover工具来解析URI的内容,输出获得的信息。

          这是gst-discoverer工具使用的简单版本,这个应用仅仅显示数据,甚至不提供播放功能。


    GStreamer Discoverer

    [html] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <span style="font-size:14px;">#include <string.h>  
    2. #include <gst/gst.h>  
    3. #include <gst/pbutils/pbutils.h>  
    4.     
    5. /* Structure to contain all our information, so we can pass it around */  
    6. typedef struct _CustomData {  
    7.   GstDiscoverer *discoverer;  
    8.   GMainLoop *loop;  
    9. } CustomData;  
    10.     
    11. /* Print a tag in a human-readable format (name: value) */  
    12. static void print_tag_foreach (const GstTagList *tags, const gchar *tag, gpointer user_data) {  
    13.   GValue val = { 0, };  
    14.   gchar *str;  
    15.   gint depth = GPOINTER_TO_INT (user_data);  
    16.     
    17.   gst_tag_list_copy_value (&val, tags, tag);  
    18.     
    19.   if (G_VALUE_HOLDS_STRING (&val))  
    20.     str = g_value_dup_string (&val);  
    21.   else  
    22.     str = gst_value_serialize (&val);  
    23.     
    24.   g_print ("%*s%s: %s ", 2 * depth, " ", gst_tag_get_nick (tag), str);  
    25.   g_free (str);  
    26.     
    27.   g_value_unset (&val);  
    28. }  
    29.     
    30. /* Print information regarding a stream */  
    31. static void print_stream_info (GstDiscovererStreamInfo *info, gint depth) {  
    32.   gchar *desc = NULL;  
    33.   GstCaps *caps;  
    34.   const GstTagList *tags;  
    35.     
    36.   caps = gst_discoverer_stream_info_get_caps (info);  
    37.     
    38.   if (caps) {  
    39.     if (gst_caps_is_fixed (caps))  
    40.       desc = gst_pb_utils_get_codec_description (caps);  
    41.     else  
    42.       desc = gst_caps_to_string (caps);  
    43.     gst_caps_unref (caps);  
    44.   }  
    45.     
    46.   g_print ("%*s%s: %s ", 2 * depth, " ", gst_discoverer_stream_info_get_stream_type_nick (info), (desc ? desc : ""));  
    47.     
    48.   if (desc) {  
    49.     g_free (desc);  
    50.     desc = NULL;  
    51.   }  
    52.     
    53.   tags = gst_discoverer_stream_info_get_tags (info);  
    54.   if (tags) {  
    55.     g_print ("%*sTags: ", 2 * (depth + 1), " ");  
    56.     gst_tag_list_foreach (tags, print_tag_foreach, GINT_TO_POINTER (depth + 2));  
    57.   }  
    58. }  
    59.     
    60. /* Print information regarding a stream and its substreams, if any */  
    61. static void print_topology (GstDiscovererStreamInfo *info, gint depth) {  
    62.   GstDiscovererStreamInfo *next;  
    63.     
    64.   if (!info)  
    65.     return;  
    66.     
    67.   print_stream_info (info, depth);  
    68.     
    69.   next = gst_discoverer_stream_info_get_next (info);  
    70.   if (next) {  
    71.     print_topology (next, depth + 1);  
    72.     gst_discoverer_stream_info_unref (next);  
    73.   } else if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) {  
    74.     GList *tmp, *streams;  
    75.       
    76.     streams = gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO (info));  
    77.     for (tmp = streams; tmp; tmp = tmp->next) {  
    78.       GstDiscovererStreamInfo *tmpinf = (GstDiscovererStreamInfo *) tmp->data;  
    79.       print_topology (tmpinf, depth + 1);  
    80.     }  
    81.     gst_discoverer_stream_info_list_free (streams);  
    82.   }  
    83. }  
    84.     
    85. /* This function is called every time the discoverer has information regarding  
    86.  * one of the URIs we provided.*/  
    87. static void on_discovered_cb (GstDiscoverer *discoverer, GstDiscovererInfo *info, GError *err, CustomData *data) {  
    88.   GstDiscovererResult result;  
    89.   const gchar *uri;  
    90.   const GstTagList *tags;  
    91.   GstDiscovererStreamInfo *sinfo;  
    92.     
    93.   uri = gst_discoverer_info_get_uri (info);  
    94.   result = gst_discoverer_info_get_result (info);  
    95.   switch (result) {  
    96.     case GST_DISCOVERER_URI_INVALID:  
    97.       g_print ("Invalid URI '%s' ", uri);  
    98.       break;  
    99.     case GST_DISCOVERER_ERROR:  
    100.       g_print ("Discoverer error: %s ", err->message);  
    101.       break;  
    102.     case GST_DISCOVERER_TIMEOUT:  
    103.       g_print ("Timeout ");  
    104.       break;  
    105.     case GST_DISCOVERER_BUSY:  
    106.       g_print ("Busy ");  
    107.       break;  
    108.     case GST_DISCOVERER_MISSING_PLUGINS:{  
    109.       const GstStructure *s;  
    110.       gchar *str;  
    111.         
    112.       s = gst_discoverer_info_get_misc (info);  
    113.       str = gst_structure_to_string (s);  
    114.         
    115.       g_print ("Missing plugins: %s ", str);  
    116.       g_free (str);  
    117.       break;  
    118.     }  
    119.     case GST_DISCOVERER_OK:  
    120.       g_print ("Discovered '%s' ", uri);  
    121.       break;  
    122.   }  
    123.     
    124.   if (result != GST_DISCOVERER_OK) {  
    125.     g_printerr ("This URI cannot be played ");  
    126.     return;  
    127.   }  
    128.     
    129.   /* If we got no error, show the retrieved information */  
    130.     
    131.   g_print (" Duration: %" GST_TIME_FORMAT " ", GST_TIME_ARGS (gst_discoverer_info_get_duration (info)));  
    132.     
    133.   tags = gst_discoverer_info_get_tags (info);  
    134.   if (tags) {  
    135.     g_print ("Tags: ");  
    136.     gst_tag_list_foreach (tags, print_tag_foreach, GINT_TO_POINTER (1));  
    137.   }  
    138.     
    139.   g_print ("Seekable: %s ", (gst_discoverer_info_get_seekable (info) ? "yes" : "no"));  
    140.     
    141.   g_print (" ");  
    142.     
    143.   sinfo = gst_discoverer_info_get_stream_info (info);  
    144.   if (!sinfo)  
    145.     return;  
    146.     
    147.   g_print ("Stream information: ");  
    148.     
    149.   print_topology (sinfo, 1);  
    150.     
    151.   gst_discoverer_stream_info_unref (sinfo);  
    152.     
    153.   g_print (" ");  
    154. }  
    155.     
    156. /* This function is called when the discoverer has finished examining  
    157.  * all the URIs we provided.*/  
    158. static void on_finished_cb (GstDiscoverer *discoverer, CustomData *data) {  
    159.   g_print ("Finished discovering ");  
    160.     
    161.   g_main_loop_quit (data->loop);  
    162. }  
    163.     
    164. int main (int argc, char **argv) {  
    165.   CustomData data;  
    166.   GError *err = NULL;  
    167.   gchar *uri = "http://docs.gstreamer.com/media/sintel_trailer-480p.webm";  
    168.     
    169.   /* if a URI was provided, use it instead of the default one */  
    170.   if (argc > 1) {  
    171.     uri = argv[1];  
    172.   }  
    173.     
    174.   /* Initialize cumstom data structure */  
    175.   memset (&data, 0, sizeof (data));  
    176.     
    177.   /* Initialize GStreamer */  
    178.   gst_init (&argc, &argv);  
    179.     
    180.   g_print ("Discovering '%s' ", uri);  
    181.     
    182.   /* Instantiate the Discoverer */  
    183.   data.discoverer = gst_discoverer_new (5 * GST_SECOND, &err);  
    184.   if (!data.discoverer) {  
    185.     g_print ("Error creating discoverer instance: %s ", err->message);  
    186.     g_clear_error (&err);  
    187.     return -1;  
    188.   }  
    189.     
    190.   /* Connect to the interesting signals */  
    191.   g_signal_connect (data.discoverer, "discovered", G_CALLBACK (on_discovered_cb), &data);  
    192.   g_signal_connect (data.discoverer, "finished", G_CALLBACK (on_finished_cb), &data);  
    193.     
    194.   /* Start the discoverer process (nothing to do yet) */  
    195.   gst_discoverer_start (data.discoverer);  
    196.     
    197.   /* Add a request to process asynchronously the URI passed through the command line */  
    198.   if (!gst_discoverer_discover_uri_async (data.discoverer, uri)) {  
    199.     g_print ("Failed to start discovering URI '%s' ", uri);  
    200.     g_object_unref (data.discoverer);  
    201.     return -1;  
    202.   }  
    203.     
    204.   /* Create a GLib Main Loop and set it to run, so we can wait for the signals */  
    205.   data.loop = g_main_loop_new (NULL, FALSE);  
    206.   g_main_loop_run (data.loop);  
    207.     
    208.   /* Stop the discoverer process */  
    209.   gst_discoverer_stop (data.discoverer);  
    210.     
    211.   /* Free resources */  
    212.   g_object_unref (data.discoverer);  
    213.   g_main_loop_unref (data.loop);  
    214.     
    215.   return 0;  
    216. }</span>  

    工作流程

          下面是使用GstDiscoverer的主要步骤。

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <span style="font-size:14px;">  /* Instantiate the Discoverer */  
    2.   data.discoverer = gst_discoverer_new (55 * GST_SECOND, &err);  
    3.   if (!data.discoverer) {  
    4.     g_print ("Error creating discoverer instance: %s ", err->message);  
    5.     g_clear_error (&err);  
    6.     return -1;  
    7.   }</span>  
          gst_discover_new()会创建一个Discoverer对象,第一个参数是超时时间(使用纳秒做单位)。

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <span style="font-size:14px;">  /* Connect to the interesting signals */  
    2.   g_signal_connect (data.discoverer"discovered", G_CALLBACK (on_discovered_cb), &data);  
    3.   g_signal_connect (data.discoverer"finished", G_CALLBACK (on_finished_cb), &data);</span>  

          像平常一样对必要的的信号注册回调,我们在他们的回调里面继续讨论。

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <span style="font-size:14px;">  /* Start the discoverer process (nothing to do yet) */  
    2.   gst_discoverer_start (data.discoverer);</span>  
          gst_discover_start()启动检查进程,但我们还没有提供URI,下面就是提供URI了:

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <span style="font-size:14px;">  /* Add a request to process asynchronously the URI passed through the command line */  
    2.   if (!gst_discoverer_discover_uri_async (data.discoverer, uri)) {  
    3.     g_print ("Failed to start discovering URI '%s' ", uri);  
    4.     g_object_unref (data.discoverer);  
    5.     return -1;  
    6.   }</span>  
          gst_discoverer_discover_uri_async()会把需要检查的URI放入队列,这个函数支持把多个URI入队。在检查过程把所有的URI都检查过之后,会调用注册的回调函数。

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <span style="font-size:14px;">  /* Create a GLib Main Loop and set it to run, so we can wait for the signals */  
    2.   data.loop = g_main_loop_new (NULL, FALSE);  
    3.   g_main_loop_run (data.loop);</span>  

          GLib已经在运行主循环了,我们在on_finished_cb回调中调用g_main_loop_quit()来退出主循环。

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <span style="font-size:14px;">  /* Stop the discoverer process */  
    2.   gst_discoverer_stop (data.discoverer);</span>  
          一旦我们检查结束,我们会调用gst_discoverer_stop()来停止,并且调用g_object_unref()来释放资源。

          现在让我们来看一下我们注册的回调函数:

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <span style="font-size:14px;">/* This function is called every time the discoverer has information regarding 
    2.  * one of the URIs we provided.*/  
    3. static void on_discovered_cb (GstDiscoverer *discoverer, GstDiscovererInfo *info, GError *err, CustomData *data) {  
    4.   GstDiscovererResult result;  
    5.   const gchar *uri;  
    6.   const GstTagList *tags;  
    7.   GstDiscovererStreamInfo *sinfo;  
    8.     
    9.   uri = gst_discoverer_info_get_uri (info);  
    10.   result = gst_discoverer_info_get_result (info);</span>  

          当我们运行到这些代码时,discoverer已经完成了一个URI的检查了,用GstDiscovererInfo这个数据结构把内容传给我们。

          这里第一步是用gst_discoverer_info_get_uri()方法去获得调用这个回调的URI(因为可能存在多个URI),然后通过gst_discoverer_info_get_result()方法来获得数据。

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. switch (result) {  
    2.   case GST_DISCOVERER_URI_INVALID:  
    3.     g_print ("Invalid URI '%s' ", uri);  
    4.     break;  
    5.   case GST_DISCOVERER_ERROR:  
    6.     g_print ("Discoverer error: %s ", err->message);  
    7.     break;  
    8.   case GST_DISCOVERER_TIMEOUT:  
    9.     g_print ("Timeout ");  
    10.     break;  
    11.   case GST_DISCOVERER_BUSY:  
    12.     g_print ("Busy ");  
    13.     break;  
    14.   case GST_DISCOVERER_MISSING_PLUGINS:{  
    15.     const GstStructure *s;  
    16.     gchar *str;  
    17.       
    18.     s = gst_discoverer_info_get_misc (info);  
    19.     str = gst_structure_to_string (s);  
    20.       
    21.     g_print ("Missing plugins: %s ", str);  
    22.     g_free (str);  
    23.     break;  
    24.   }  
    25.   case GST_DISCOVERER_OK:  
    26.     g_print ("Discovered '%s' ", uri);  
    27.     break;  
    28. }  
    29.   
    30. if (result != GST_DISCOVERER_OK) {  
    31.   g_printerr ("This URI cannot be played ");  
    32.   return;  
    33. }  
          正如代码显示的那样,除了GST_DISCOVERER_OK之外,任何一个结果都意味着有某种问题,这个URI不能播放。不能播放的原因是多种多样的,但枚举出来后也比较清晰(GST_DISCOVERER_BUSY只可能在同步模式下出现,这里是不可能出现的)。

          如果没有发生错误,那么可以用gst_discoverer_info_get_*系列方法从GstDiscovererInfo这个结构里面获得数据了。比如,用gst_discoverer_info_get_duration()方法可以获得播放总长度。

          信息的位是由列表组成的,就像标签和流信息,需要再解析一下:

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. tags = gst_discoverer_info_get_tags (info);  
    2. if (tags) {  
    3.   g_print ("Tags: ");  
    4.   gst_tag_list_foreach (tags, print_tag_foreach, GINT_TO_POINTER (1));  
    5. }  
          标签是附着在媒体上得元数据。它们可以用gst_tag_list_foreach()方法来检查,每检查到一个标签就调用一次print_tag_foreach()。至于print_tag_foreach()方法的代码比较浅显,可以直接读懂。

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. sinfo = gst_discoverer_info_get_stream_info (info);  
    2. if (!sinfo)  
    3.   return;  
    4.   
    5. g_print ("Stream information: ");  
    6.   
    7. print_topology (sinfo, 1);  
    8.   
    9. gst_discoverer_stream_info_unref (sinfo);  
          gst_discoverer_info_get_stream_info()会返回一个GstDiscovererStreamInfo类型的数据,print_topology()函数会解析这个数据结构,然后调用gst_discoverer_stream_info_unref()来释放资源。

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. /* Print information regarding a stream and its substreams, if any */  
    2. static void print_topology (GstDiscovererStreamInfo *info, gint depth) {  
    3.   GstDiscovererStreamInfo *next;  
    4.     
    5.   if (!info)  
    6.     return;  
    7.     
    8.   print_stream_info (info, depth);  
    9.     
    10.   next = gst_discoverer_stream_info_get_next (info);  
    11.   if (next) {  
    12.     print_topology (next, depth + 1);  
    13.     gst_discoverer_stream_info_unref (next);  
    14.   } else if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) {  
    15.     GList *tmp, *streams;  
    16.       
    17.     streams = gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO (info));  
    18.     for (tmp = streams; tmp; tmp = tmp->next) {  
    19.       GstDiscovererStreamInfo *tmpinf = (GstDiscovererStreamInfo *) tmp->data;  
    20.       print_topology (tmpinf, depth + 1);  
    21.     }  
    22.     gst_discoverer_stream_info_list_free (streams);  
    23.   }  
    24. }  
          print_stream_info()函数的代码也是很简单直接的,它也调用了print_tag_foreach方法来打印流的capabilities和相关联的tags。

          print_topology方法会寻找下一个流来显示。如果get_discoverer_stream_info_get_next()返回一个非空流信息的话,它需要被显示。另一方面,如果我们是一个容器,我们每一个用get_discoverer_container_info_get_streams()方法获得的子流都需要递归的调用print_topology方法。当然,如果我们是一个叶子流了,就不在需要继续递归下去了。


  • 相关阅读:
    注册时正则验证及提示demo
    密码的修改(首先获取该用户的id,原密码判断是否一致,新密码和再次输入密码判断是否一样)
    变量和常量
    python入门基础
    python2设计一个用户登录
    python2与python3中input的区别
    python日记----2017.7.26
    python日记----2017.7.25
    python日记----2017.7.24
    python日记----2017.7.20
  • 原文地址:https://www.cnblogs.com/huty/p/8517313.html
Copyright © 2011-2022 走看看