• 【GStreamer开发】GStreamer基础教程12——流


    目标

          直接播放Internet上的文件而不在本地保存就被称为流播放。我们在前面教程里已经这样做过了,使用了http://的URL。本教程展示的是在播放流的时候需要记住的几个点,特别是:

          如何设置缓冲

          如何从打断中恢复(因为失去了时钟)


    介绍

          当在播放流的时候,一旦从网络上取到媒体数据块就会进行解码和放入显示队列。这意味着如果网络来的数据延迟了,那么显示队列就可能没有数据,播放就会停下来。

          解决这个问题的办法是建立缓冲,这就是说,在开始播放前允许队列里已经存储了一些数据。这样的话,播放虽然晚了一点开始,但如果网络有什么延时,那么还有一定的缓冲数据可以播放。

          这个方案已经在GStreamer里面实现了,但前面的教程中没有涉及到这个方面。有些element,像在playbin2里面用到的queue2和multiqueue,都可以建立自己的缓冲然后根据缓冲的等级发送消息到总线上。一个应用如果希望能更好的适应各种网络环境,那么就该关注这些消息,当缓冲等级低到一定程度时就要暂停播放。

          为了在多个sink中同步,我们使用了一个全局的时钟。这个时钟是GStreamer在所有的可以提供时钟的element中选出来的。在某些情况下,例如,一个RTP资源切换流或者更换输出设备,那么时钟就可能丢失。这时就需要重新建立一个时钟,这个过程在本教程会解释一下。

          当时钟丢失的时候,应用会从总线上得到一个消息。要建立一个新的时钟,应用仅仅把pipeline设置到PAUSED状态然后重新置成PLAYING即可。


    一个适应网络的例子

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <span style="font-size:14px;">#include <gst/gst.h>  
    2. #include <string.h>  
    3.     
    4. typedef struct _CustomData {  
    5.   gboolean is_live;  
    6.   GstElement *pipeline;  
    7.   GMainLoop *loop;  
    8. } CustomData;  
    9.     
    10. static void cb_message (GstBus *bus, GstMessage *msg, CustomData *data) {  
    11.     
    12.   switch (GST_MESSAGE_TYPE (msg)) {  
    13.     case GST_MESSAGE_ERROR: {  
    14.       GError *err;  
    15.       gchar *debug;  
    16.         
    17.       gst_message_parse_error (msg, &err, &debug);  
    18.       g_print ("Error: %s ", err->message);  
    19.       g_error_free (err);  
    20.       g_free (debug);  
    21.         
    22.       gst_element_set_state (data->pipeline, GST_STATE_READY);  
    23.       g_main_loop_quit (data->loop);  
    24.       break;  
    25.     }  
    26.     case GST_MESSAGE_EOS:  
    27.       /* end-of-stream */  
    28.       gst_element_set_state (data->pipeline, GST_STATE_READY);  
    29.       g_main_loop_quit (data->loop);  
    30.       break;  
    31.     case GST_MESSAGE_BUFFERING: {  
    32.       gint percent = 0;  
    33.         
    34.       /* If the stream is live, we do not care about buffering. */  
    35.       if (data->is_live) break;  
    36.         
    37.       gst_message_parse_buffering (msg, &percent);  
    38.       g_print ("Buffering (%3d%%) ", percent);  
    39.       /* Wait until buffering is complete before start/resume playing */  
    40.       if (percent < 100)  
    41.         gst_element_set_state (data->pipeline, GST_STATE_PAUSED);  
    42.       else  
    43.         gst_element_set_state (data->pipeline, GST_STATE_PLAYING);  
    44.       break;  
    45.     }  
    46.     case GST_MESSAGE_CLOCK_LOST:  
    47.       /* Get a new clock */  
    48.       gst_element_set_state (data->pipeline, GST_STATE_PAUSED);  
    49.       gst_element_set_state (data->pipeline, GST_STATE_PLAYING);  
    50.       break;  
    51.     default:  
    52.       /* Unhandled message */  
    53.       break;  
    54.     }  
    55. }  
    56.     
    57. int main(int argc, charchar *argv[]) {  
    58.   GstElement *pipeline;  
    59.   GstBus *bus;  
    60.   GstStateChangeReturn ret;  
    61.   GMainLoop *main_loop;  
    62.   CustomData data;  
    63.     
    64.   /* Initialize GStreamer */  
    65.   gst_init (&argc, &argv);  
    66.     
    67.   /* Initialize our data structure */  
    68.   memset (&data, 0sizeof (data));  
    69.     
    70.   /* Build the pipeline */  
    71.   pipeline = gst_parse_launch ("playbin2 uri=http://docs.gstreamer.com/media/sintel_trailer-480p.webm"NULL);  
    72.   bus = gst_element_get_bus (pipeline);  
    73.     
    74.   /* Start playing */  
    75.   ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);  
    76.   if (ret == GST_STATE_CHANGE_FAILURE) {  
    77.     g_printerr ("Unable to set the pipeline to the playing state. ");  
    78.     gst_object_unref (pipeline);  
    79.     return -1;  
    80.   } else if (ret == GST_STATE_CHANGE_NO_PREROLL) {  
    81.     data.is_live = TRUE;  
    82.   }  
    83.     
    84.   main_loop = g_main_loop_new (NULL, FALSE);  
    85.   data.loop = main_loop;  
    86.   data.pipeline = pipeline;  
    87.     
    88.   gst_bus_add_signal_watch (bus);  
    89.   g_signal_connect (bus, "message", G_CALLBACK (cb_message), &data);  
    90.     
    91.   g_main_loop_run (main_loop);  
    92.     
    93.   /* Free resources */  
    94.   g_main_loop_unref (main_loop);  
    95.   gst_object_unref (bus);  
    96.   gst_element_set_state (pipeline, GST_STATE_NULL);  
    97.   gst_object_unref (pipeline);  
    98.   return 0;  
    99. }</span>  

    工作流程

          这个例子中唯一特殊的是对特定消息的相互作用, 因此,初始化代码非常简单清晰。唯一新加的一点就是对实时流的检测:

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <span style="font-size:14px;">  /* Start playing */  
    2.   ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);  
    3.   if (ret == GST_STATE_CHANGE_FAILURE) {  
    4.     g_printerr ("Unable to set the pipeline to the playing state. ");  
    5.     gst_object_unref (pipeline);  
    6.     return -1;  
    7.   } else if (ret == GST_STATE_CHANGE_NO_PREROLL) {  
    8.     data.is_live = TRUE;  
    9.   }</span>  

          实时流是不能暂停的,所以在PAUSED状态的行为和PLAYING状态是一样的。在设置实时流到PAUSED成功后,会返回GST_STATE_CHANGE_NO_PREROLL,而不是平常的GST_STATE_CHANGE_SUCCESS。因为状态的变化是渐变的(从NULL到READY,从PAUSED到PLAYING),所以我们把pipeline设置到PLAYING状态,也会收到NO_PROROLL这个返回值。

          我们关注实时流是因为我们希望可以关闭缓冲,所以我们一直在关注gst_element_set_state()的返回值并根据这个值来设置is_live变量。

          现在我们看一下消息解析的回调函数里的关键部分:

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. case GST_MESSAGE_BUFFERING: {  
    2.   gint percent = 0;  
    3.     
    4.   /* If the stream is live, we do not care about buffering. */  
    5.   if (data->is_live) break;  
    6.     
    7.   gst_message_parse_buffering (msg, &percent);  
    8.   g_print ("Buffering (%3d%%) ", percent);  
    9.   /* Wait until buffering is complete before start/resume playing */  
    10.   if (percent < 100)  
    11.     gst_element_set_state (data->pipeline, GST_STATE_PAUSED);  
    12.   else  
    13.     gst_element_set_state (data->pipeline, GST_STATE_PLAYING);  
    14.   break;  
    15. }  
          首先,如果是一个实时源,就不用关心这个缓冲消息。

          我们使用gst_message_parse_buffering()来解析缓冲消息,从而获得缓冲等级。

          其次,我们在缓冲等级小于100%时把pipeline设置成PAUSED状态,并把消息在调试区域打印出来;反之,我们就把pipeline设置成PLAYING状态。

          在启动的时候,我们会看见在播放之前缓冲等级上升到100%,这就是我们希望达到的。如果在后面,网络变慢了或者失去响应,我们的缓冲也耗光了,我们会收到新的缓冲消息告诉我们缓冲等级已经低于100%,我们就把pipeline设置成PAUSED知道重新获得足够的数据。

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. case GST_MESSAGE_CLOCK_LOST:  
    2.   /* Get a new clock */  
    3.   gst_element_set_state (data->pipeline, GST_STATE_PAUSED);  
    4.   gst_element_set_state (data->pipeline, GST_STATE_PLAYING);  
    5.   break;  
          对于丢失时钟这个网络问题,我们简单地把pipeline设置成PAUSED状态然后在切换到PLAYING,这样一个新的时钟会被选择上,等待收到新的媒体数据。

  • 相关阅读:
    格式化数字,将字符串格式的数字,如:1000000 改为 1 000 000 这种展示方式
    jquery图片裁剪插件
    前端开发采坑之安卓和ios的兼容问题
    页面消息提示,上下滚动
    可以使用css的方式让input不能输入文字吗?
    智慧农村“三网合一”云平台测绘 大数据 农业 信息平台 应急
    三维虚拟城市平台测绘 大数据 规划 三维 信息平台 智慧城市
    农业大数据“一张图”平台测绘 大数据 房产 国土 农业 信息平台
    应急管理管理局安全生产预警平台应急管理系统不动产登记 测绘 大数据 规划 科教 三维 信息平台
    地下综合管廊管理平台测绘 大数据 地下管线 三维 信息平台
  • 原文地址:https://www.cnblogs.com/huty/p/8517308.html
Copyright © 2020-2023  润新知