GStreamer播放教程04——既看式流

目的

      在《GStreamer基础教程——流》里面我们展示了如何在较差的网络条件下使用缓冲这个机制来提升用户体验。本教程在《GStreamer基础教程——流》的基础上在扩展了一下,增加了把流的内容在本地存储。并且展示了:

      如何开启既看式下载

      如何知道下载的是什么

      如何知道在哪里下载

      如何限制下载数据的总量


介绍

      当播放流的时候,从网络上获得的数据被锁住之后,会创建称为future-data的一个小的缓冲区。然而,在数据播放渲染之后就会被丢弃。这就意味着,如果用户想要倒回前面去看,相应地数据仍然需要再次下载。

      像YouTube一样,播放流时播放器往往会裁剪,通常会把所有下载的数据都在本地保存,还会提供一个图形化的界面来显示已经下载了多少内容。

      playbin2通过DOWNLOAD标志提供了一个比较类似的功能,它会把数据在本次临时保存起来用于在播放已经下载的部分时可以保持顺畅。

      代码里面同时展示了如何使用缓冲查询,它可以让你知道哪部分的文件已经可用了。


一个适应网络并在本地存储数据的例子

#include <gst/gst.h>
#include <string.h>
  
#define GRAPH_LENGTH 80
  
/* playbin2 flags */
typedef enum {
  GST_PLAY_FLAG_DOWNLOAD      = (1 << 7) /* Enable progressive download (on selected formats) */
} GstPlayFlags;
  
typedef struct _CustomData {
  gboolean is_live;
  GstElement *pipeline;
  GMainLoop *loop;
  gint buffering_level;
} CustomData;
  
static void got_location (GstObject *gstobject, GstObject *prop_object, GParamSpec *prop, gpointer data) {
  gchar *location;
  g_object_get (G_OBJECT (prop_object), "temp-location", &location, NULL);
  g_print ("Temporary file: %s\n", location);
  /* Uncomment this line to keep the temporary file after the program exits */
  /* g_object_set (G_OBJECT (prop_object), "temp-remove", FALSE, NULL); */
}
  
static void cb_message (GstBus *bus, GstMessage *msg, CustomData *data) {
  
  switch (GST_MESSAGE_TYPE (msg)) {
    case GST_MESSAGE_ERROR: {
      GError *err;
      gchar *debug;
      
      gst_message_parse_error (msg, &err, &debug);
      g_print ("Error: %s\n", err->message);
      g_error_free (err);
      g_free (debug);
      
      gst_element_set_state (data->pipeline, GST_STATE_READY);
      g_main_loop_quit (data->loop);
      break;
    }
    case GST_MESSAGE_EOS:
      /* end-of-stream */
      gst_element_set_state (data->pipeline, GST_STATE_READY);
      g_main_loop_quit (data->loop);
      break;
    case GST_MESSAGE_BUFFERING:
      /* If the stream is live, we do not care about buffering. */
      if (data->is_live) break;
      
      gst_message_parse_buffering (msg, &data->buffering_level);
      
      /* Wait until buffering is complete before start/resume playing */
      if (data->buffering_level < 100)
        gst_element_set_state (data->pipeline, GST_STATE_PAUSED);
      else
        gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
      break;
    case GST_MESSAGE_CLOCK_LOST:
      /* Get a new clock */
      gst_element_set_state (data->pipeline, GST_STATE_PAUSED);
      gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
      break;
    default:
      /* Unhandled message */
      break;
    }
}
  
static gboolean refresh_ui (CustomData *data) {
  GstQuery *query;
  gboolean result;
  
  query = gst_query_new_buffering (GST_FORMAT_PERCENT);
  result = gst_element_query (data->pipeline, query);
  if (result) {
    gint n_ranges, range, i;
    gchar graph[GRAPH_LENGTH + 1];
    GstFormat format = GST_FORMAT_TIME;
    gint64 position = 0, duration = 0;
    
    memset (graph, ' ', GRAPH_LENGTH);
    graph[GRAPH_LENGTH] = '\0';
    
    n_ranges = gst_query_get_n_buffering_ranges (query);
    for (range = 0; range < n_ranges; range++) {
      gint64 start, stop;
      gst_query_parse_nth_buffering_range (query, range, &start, &stop);
      start = start * GRAPH_LENGTH / 100;
      stop = stop * GRAPH_LENGTH / 100;
      for (i = (gint)start; i < stop; i++)
        graph [i] = '-';
    }
    if (gst_element_query_position (data->pipeline, &format, &position) &&
        GST_CLOCK_TIME_IS_VALID (position) &&
        gst_element_query_duration (data->pipeline, &format, &duration) &&
        GST_CLOCK_TIME_IS_VALID (duration)) {
      i = (gint)(GRAPH_LENGTH * (double)position / (double)(duration + 1));
      graph [i] = data->buffering_level < 100 ? 'X' : '>';
    }
    g_print ("[%s]", graph);
    if (data->buffering_level < 100) {
      g_print (" Buffering: %3d%%", data->buffering_level);
    } else {
      g_print ("                ");
    }
    g_print ("\r");
  }
  
  return TRUE;
  
}
  
int main(int argc, char *argv[]) {
  GstElement *pipeline;
  GstBus *bus;
  GstStateChangeReturn ret;
  GMainLoop *main_loop;
  CustomData data;
  guint flags;
  
  /* Initialize GStreamer */
  gst_init (&argc, &argv);
  
  /* Initialize our data structure */
  memset (&data, 0, sizeof (data));
  data.buffering_level = 100;
  
  /* Build the pipeline */
  pipeline = gst_parse_launch ("playbin2 uri=http://docs.gstreamer.com/media/sintel_trailer-480p.webm", NULL);
  bus = gst_element_get_bus (pipeline);
  
  /* Set the download flag */
  g_object_get (pipeline, "flags", &flags, NULL);
  flags |= GST_PLAY_FLAG_DOWNLOAD;
  g_object_set (pipeline, "flags", flags, NULL);
  
  /* Uncomment this line to limit the amount of downloaded data */
  /* g_object_set (pipeline, "ring-buffer-max-size", (guint64)4000000, NULL); */
  
  /* Start playing */
  ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
  if (ret == GST_STATE_CHANGE_FAILURE) {
    g_printerr ("Unable to set the pipeline to the playing state.\n");
    gst_object_unref (pipeline);
    return -1;
  } else if (ret == GST_STATE_CHANGE_NO_PREROLL) {
    data.is_live = TRUE;
  }
  
  main_loop = g_main_loop_new (NULL, FALSE);
  data.loop = main_loop;
  data.pipeline = pipeline;
  
  gst_bus_add_signal_watch (bus);
  g_signal_connect (bus, "message", G_CALLBACK (cb_message), &data);
  g_signal_connect (pipeline, "deep-notify::temp-location", G_CALLBACK (got_location), NULL);
  
  /* Register a function that GLib will call every second */
  g_timeout_add_seconds (1, (GSourceFunc)refresh_ui, &data);
  
  g_main_loop_run (main_loop);
  
  /* Free resources */
  g_main_loop_unref (main_loop);
  gst_object_unref (bus);
  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref (pipeline);

  g_print ("\n");
  return 0;
}

工作流程

      这份代码是基于《GStreamer基础教程——流》里面例子的,我们仅仅看一下不同的地方即可。

创建

  /* Set the download flag */
  g_object_get (pipeline, "flags", &flags, NULL);
  flags |= GST_PLAY_FLAG_DOWNLOAD;
  g_object_set (pipeline, "flags", flags, NULL);

      通过设置这个标志,playbin2通知它内部的queue存储所有下载的数据。

g_signal_connect (pipeline, "deep-notify::temp-location", G_CALLBACK (got_location), NULL);
      当它们的子element属性发生变化时,playbin2就会发出deep-notify信号。在这里我们希望知道temp-location属性是什么时候变化的,了解queue2会把下载的数据存在哪里。

static void got_location (GstObject *gstobject, GstObject *prop_object, GParamSpec *prop, gpointer data) {
  gchar *location;
  g_object_get (G_OBJECT (prop_object), "temp-location", &location, NULL);
  g_print ("Temporary file: %s\n", location);
  /* Uncomment this line to keep the temporary file after the program exits */
  /* g_object_set (G_OBJECT (prop_object), "temp-remove", FALSE, NULL); */
}

      这个temp-location属性是从发出信号的element那里获得的并打印出来。

      当pipeline状态从PAUSED切换到READY时,这个文件会被删除。正如注释里面写的那样,你可以通过设置queue2的temp-remove属性位FALSE来保留下载的数据。

UI

      在main函数里我们启动了一个1s的定时器,这样可以每秒刷新一下UI界面。

  /* Register a function that GLib will call every second */
  g_timeout_add_seconds (1, (GSourceFunc)refresh_ui, &data);
      refresh_ui方法会查询pipeline来了解当前下载的数据在文件的位置以及当前播放的位置。并且用一种动画的方式在屏幕上显示出来。

[---->-------                ]
      这里的'-'代表的是下载的部分,'>'代表的是当前播放的位置(当暂停的时候变成'X'位置)。当你的网络速度足够快得时候你可能会看不到下载的动画,在开始的时候就下载结束了。

static gboolean refresh_ui (CustomData *data) {
  GstQuery *query;
  gboolean result;
  
  query = gst_query_new_buffering (GST_FORMAT_PERCENT);
  result = gst_element_query (data->pipeline, query);

      我们在refresh_ui里面做的第一件事是就是用gst_query_new_buffering()创建一个GstQuery对象,并用gst_element_query()传给playbin2。在《GStreamer基础教程04——时间管理》里面我们展示了如何用明确的方法来查询位置/播放总时间等,如果要查询更复杂一些的内容(比如缓冲),那么我们会用更通用的gst_element_query()方法。

      缓冲的查询可以基于不同的GstFormat,并非所有的element都可以响应所有格式的查询,所以需要检查在pipeline里支持哪些格式。如果gst_element_query()返回TRUE,那么查询是成功的。查询的结果用GstQuery封装起来,可以用下面的方法来解析:

    n_ranges = gst_query_get_n_buffering_ranges (query);
    for (range = 0; range < n_ranges; range++) {
      gint64 start, stop;
      gst_query_parse_nth_buffering_range (query, range, &start, &stop);
      start = start * GRAPH_LENGTH / 100;
      stop = stop * GRAPH_LENGTH / 100;
      for (i = (gint)start; i < stop; i++)
        graph [i] = '-';
    }

      数据并不需要保证被按照顺序从头开始下载,因为跳跃播放时会让下载从一个新的地方开始。因此,gst_query_get_n_buffering_ranges()返回下载块的数目或者范围,然后我们用gst_query_parse_nth_buffering_rang()方法来解析下载块的位置和大小。

      我们在调用gst_query_new_buffering()的请求会决定返回数据的格式,在这个例子里面,返回值是比例。这些查询到得数据用来绘制UI的下载动画。

    if (gst_element_query_position (data->pipeline, &format, &position) &&
        GST_CLOCK_TIME_IS_VALID (position) &&
        gst_element_query_duration (data->pipeline, &format, &duration) &&
        GST_CLOCK_TIME_IS_VALID (duration)) {
      i = (gint)(GRAPH_LENGTH * (double)position / (double)(duration + 1));
      graph [i] = data->buffering_level < 100 ? 'X' : '>';
    }

      下一步就是当前位置的查询。它也支持比例的格式,所以代码和前面应该比较类似。不过这部分目前支持不是很好,所以我们使用了时间这个格式。

      当前位置使用'>'或者'X'来表示,如果缓冲不到100%,cb_message会让pipeline处于PAUSE状态,那样我们就显示'X',如果已经满了100%,那么pipeline就在PLAYING状态,我们就显示'>'。

    if (data->buffering_level < 100) {
      g_print (" Buffering: %3d%%", data->buffering_level);
    } else {
      g_print ("                ");
    }

      最后,如果缓冲时钟小于100%,我们就把这个数据显示出来。

限制下载文件的大小

  /* Uncomment this line to limit the amount of downloaded data */
  /* g_object_set (pipeline, "ring-buffer-max-size", (guint64)4000000, NULL); */

      打开139行的注释,让我们看看这个是如何做到的。缩小临时文件的大小,这样播放过的区域就会被覆盖。





  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: GStreamer是一个功能强大的开源多媒体框架,它提供了用于处理音频和视频的工具和库。通过GStreamer,我们可以实现媒体的HTTP播放。 要使用GStreamer实现媒体的HTTP播放,首先需要安装GStreamer框架和相应的插件。然后,我们可以使用GStreamer提供的命令行工具或编写自己的应用程序来进行HTTP播放。 在命令行中,使用"gst-launch-1.0"命令可以调用GStreamer的功能,具体使用的命令取决于播放的媒体类型媒体服务器的配置。例如,使用如下命令可以播放HTTP源的视频: ``` gst-launch-1.0 playbin uri=http://example.com/video.mp4 ``` 这个命令将会从指定的URI中获取视频,并进行播放。 如果想要在自己的应用程序中使用GStreamer实现HTTP播放,需要使用GStreamer提供的API进行开发。通过编写代码,我们可以创建一个GStreamer的管道(pipeline),并设置相关的元素(element)来实现HTTP播放。然后,通过控制管道的状态,我们可以开始、暂停、停止播放。 在应用程序中,我们可以在一个界面中加入媒体控制按钮,通过与GStreamer的API交互来实现HTTP播放的操作。例如,我们可以通过调用`gst_element_set_state`函数将管道的状态设置为播放,再调用`gst_element_set_state`函数将管道的状态设置为暂停。 总之,通过GStreamer媒体框架,我们可以方便地实现HTTP播放功能。无论是命令行工具还是自己编写的应用程序,都可以利用GStreamer的功能来处理和播放HTTP媒体。 ### 回答2: gstreamer是一个功能强大的媒体框架,可用于实现各种音视频处理和播放功能。在gstreamer中,可以使用HTTP协议来播放媒体。 首先,需要在系统中安装gstreamer的开发包和插件。然后,在应用程序中使用gstreamer的API来加载和播放HTTP媒体。 使用gstreamer播放HTTP媒体需要以下步骤: 1. 创建gstreamer播放器对象。 2. 设置播放器的属性,如媒体类型、协议和URI。 3. 添加各种插件和元素到播放器中,以实现解码、音视频处理和输出等功能。例如,可以使用"uridecodebin"元素来解码并播放HTTP媒体。 4. 连接各个元素之间的管道,以确保音视频数据的动。 5. 启动播放器,开始播放媒体。 以下是一个简单的示例代码,展示了如何使用gstreamer播放HTTP媒体: ``` #include <gst/gst.h> int main(int argc, char *argv[]) { GstElement *pipeline; GstBus *bus; GstMessage *msg; // 初始化gstreamer gst_init(&argc, &argv); // 创建播放器对象 pipeline = gst_pipeline_new("audio-player"); // 创建HTTP媒体解码元素 GstElement *source = gst_element_factory_make("uridecodebin", "source"); // 设置HTTP媒体的URI g_object_set(G_OBJECT(source), "uri", "http://example.com/stream.mp3", NULL); // 将HTTP媒体解码元素添加到播放器中 gst_bin_add(GST_BIN(pipeline), source); // 连接各元素之间的管道 gst_element_link(source, audio_sink); // 启动播放器 gst_element_set_state(pipeline, GST_STATE_PLAYING); // 等待播放完成 bus = gst_element_get_bus(pipeline); msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_EOS); // 停止播放器 gst_element_set_state(pipeline, GST_STATE_NULL); // 释放资源 gst_object_unref(bus); gst_element_unref(pipeline); return 0; } ``` 这是一个简单的示例代码,你可以根据实际需求进行定制和扩展。通过使用gstreamer,你可以轻松地实现HTTP媒体的播放功能,并且可以应用各种音视频处理和特效。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值