目标

这篇教程和上一篇非常相似,但不是切换音频流,而是字幕了。这次我们会展示:

如何选择选择字幕流

如何引入外部的字幕

如何客制化字幕使用的字体

介绍

我们都知道一个文件可以有多个音视频流并且可以使用playerbin2的current-audio和current-video属性很方便的进行切换。切换字幕也是一样的方便。

就和音视频一样,playbin2会选择解码好的字幕,而且GStreamer的插件设计也很容易支持一种新的文件结构。

但字幕还是有自己的特殊之处,除了可以嵌入文件里面,playbin2还支持使用外界的URI来提供字幕。

本教程会打开一个包含5个字幕流的文件,并从外界在导入一个字幕(希腊语)。

多语言字幕的播放器

[objc] view
plain
 copy

  1. <span style="font-size:14px;">#include <gst/gst.h>
  2. /* Structure to contain all our information, so we can pass it around */
  3. typedef struct _CustomData {
  4. ;  /* Our one and only element */
  5. gint n_video;          /* Number of embedded video streams */
  6. gint n_audio;          /* Number of embedded audio streams */
  7. gint n_text;           /* Number of embedded subtitle streams */
  8. gint current_video;    /* Currently playing video stream */
  9. gint current_audio;    /* Currently playing audio stream */
  10. gint current_text;     /* Currently playing subtitle stream */
  11. GMainLoop *main_loop;  /* GLib's Main Loop */
  12. } CustomData;
  13. /* playbin2 flags */
  14. typedef enum {
  15. << 0), /* We want video output */
  16. << 1), /* We want audio output */
  17. << 2)  /* We want subtitle output */
  18. } GstPlayFlags;
  19. /* Forward definition for the message and keyboard processing functions */
  20. static gboolean handle_message (GstBus *bus, GstMessage *msg, CustomData *data);
  21. static gboolean handle_keyboard (GIOChannel *source, GIOCondition cond, CustomData *data);
  22. int main(int argc, charchar *argv[]) {
  23. CustomData data;
  24. GstBus *bus;
  25. GstStateChangeReturn ret;
  26. gint flags;
  27. GIOChannel *io_stdin;
  28. /* Initialize GStreamer */
  29. gst_init (&argc, &argv);
  30. /* Create the elements */
  31. data.playbin2 = gst_element_factory_make ("playbin2", "playbin2");
  32. if (!data.playbin2) {
  33. g_printerr ("Not all elements could be created.\n");
  34. ;
  35. }
  36. /* Set the URI to play */
  37. g_object_set (data.playbin2, "uri", "http://docs.gstreamer.com/media/sintel_trailer-480p.ogv", NULL);
  38. /* Set the subtitle URI to play and some font description */
  39. g_object_set (data.playbin2, "suburi", "http://docs.gstreamer.com/media/sintel_trailer_gr.srt", NULL);
  40. g_object_set (data.playbin2, "subtitle-font-desc", "Sans, 18", NULL);
  41. /* Set flags to show Audio, Video and Subtitles */
  42. g_object_get (data.playbin2, "flags", &flags, NULL);
  43. flags |= GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_TEXT;
  44. g_object_set (data.playbin2, "flags", flags, NULL);
  45. /* Add a bus watch, so we get notified when a message arrives */
  46. bus = gst_element_get_bus (data.playbin2);
  47. gst_bus_add_watch (bus, (GstBusFunc)handle_message, &data);
  48. /* Add a keyboard watch so we get notified of keystrokes */
  49. #ifdef _WIN32
  50. 2_new_fd (fileno (stdin));
  51. #else
  52. io_stdin = g_io_channel_unix_new (fileno (stdin));
  53. #endif
  54. g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc)handle_keyboard, &data);
  55. /* Start playing */
  56. ret = gst_element_set_state (data.playbin2, GST_STATE_PLAYING);
  57. if (ret == GST_STATE_CHANGE_FAILURE) {
  58. g_printerr ("Unable to set the pipeline to the playing state.\n");
  59. gst_object_unref (data.playbin2);
  60. ;
  61. }
  62. /* Create a GLib Main Loop and set it to run */
  63. data.main_loop = g_main_loop_new (NULL, FALSE);
  64. g_main_loop_run (data.main_loop);
  65. /* Free resources */
  66. g_main_loop_unref (data.main_loop);
  67. g_io_channel_unref (io_stdin);
  68. gst_object_unref (bus);
  69. gst_element_set_state (data.playbin2, GST_STATE_NULL);
  70. gst_object_unref (data.playbin2);
  71. ;
  72. }
  73. /* Extract some metadata from the streams and print it on the screen */
  74. static void analyze_streams (CustomData *data) {
  75. gint i;
  76. GstTagList *tags;
  77. gchar *str;
  78. guint rate;
  79. /* Read some properties */
  80. , "n-video", &data->n_video, NULL);
  81. , "n-audio", &data->n_audio, NULL);
  82. , "n-text", &data->n_text, NULL);
  83. g_print ("%d video stream(s), %d audio stream(s), %d text stream(s)\n",
  84. data->n_video, data->n_audio, data->n_text);
  85. g_print ("\n");
  86. ; i < data->n_video; i++) {
  87. tags = NULL;
  88. /* Retrieve the stream's video tags */
  89. , "get-video-tags", i, &tags);
  90. if (tags) {
  91. g_print ("video stream %d:\n", i);
  92. gst_tag_list_get_string (tags, GST_TAG_VIDEO_CODEC, &str);
  93. g_print ("  codec: %s\n", str ? str : "unknown");
  94. g_free (str);
  95. gst_tag_list_free (tags);
  96. }
  97. }
  98. g_print ("\n");
  99. ; i < data->n_audio; i++) {
  100. tags = NULL;
  101. /* Retrieve the stream's audio tags */
  102. , "get-audio-tags", i, &tags);
  103. if (tags) {
  104. g_print ("audio stream %d:\n", i);
  105. if (gst_tag_list_get_string (tags, GST_TAG_AUDIO_CODEC, &str)) {
  106. g_print ("  codec: %s\n", str);
  107. g_free (str);
  108. }
  109. if (gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &str)) {
  110. g_print ("  language: %s\n", str);
  111. g_free (str);
  112. }
  113. if (gst_tag_list_get_uint (tags, GST_TAG_BITRATE, &rate)) {
  114. g_print ("  bitrate: %d\n", rate);
  115. }
  116. gst_tag_list_free (tags);
  117. }
  118. }
  119. g_print ("\n");
  120. ; i < data->n_text; i++) {
  121. tags = NULL;
  122. /* Retrieve the stream's subtitle tags */
  123. g_print ("subtitle stream %d:\n", i);
  124. , "get-text-tags", i, &tags);
  125. if (tags) {
  126. if (gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &str)) {
  127. g_print ("  language: %s\n", str);
  128. g_free (str);
  129. }
  130. gst_tag_list_free (tags);
  131. } else {
  132. g_print ("  no tags found\n");
  133. }
  134. }
  135. , "current-video", &data->current_video, NULL);
  136. , "current-audio", &data->current_audio, NULL);
  137. , "current-text", &data->current_text, NULL);
  138. g_print ("\n");
  139. g_print ("Currently playing video stream %d, audio stream %d and subtitle stream %d\n",
  140. data->current_video, data->current_audio, data->current_text);
  141. g_print ("Type any number and hit ENTER to select a different subtitle stream\n");
  142. }
  143. /* Process messages from GStreamer */
  144. static gboolean handle_message (GstBus *bus, GstMessage *msg, CustomData *data) {
  145. GError *err;
  146. gchar *debug_info;
  147. switch (GST_MESSAGE_TYPE (msg)) {
  148. case GST_MESSAGE_ERROR:
  149. gst_message_parse_error (msg, &err, &debug_info);
  150. g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
  151. g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
  152. g_clear_error (&err);
  153. g_free (debug_info);
  154. g_main_loop_quit (data->main_loop);
  155. break;
  156. case GST_MESSAGE_EOS:
  157. g_print ("End-Of-Stream reached.\n");
  158. g_main_loop_quit (data->main_loop);
  159. break;
  160. case GST_MESSAGE_STATE_CHANGED: {
  161. GstState old_state, new_state, pending_state;
  162. gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
  163. )) {
  164. if (new_state == GST_STATE_PLAYING) {
  165. /* Once we are in the playing state, analyze the streams */
  166. analyze_streams (data);
  167. }
  168. }
  169. } break;
  170. default:
  171. break;
  172. }
  173. /* We want to keep receiving messages */
  174. return TRUE;
  175. }
  176. /* Process keyboard input */
  177. static gboolean handle_keyboard (GIOChannel *source, GIOCondition cond, CustomData *data) {
  178. gchar *str = NULL;
  179. if (g_io_channel_read_line (source, &str, NULL, NULL, NULL) == G_IO_STATUS_NORMAL) {
  180. int index = atoi (str);
  181. || index >= data->n_text) {
  182. g_printerr ("Index out of bounds\n");
  183. } else {
  184. /* If the input was a valid subtitle stream index, set the current subtitle stream */
  185. g_print ("Setting current subtitle stream to %d\n", index);
  186. , "current-text", index, NULL);
  187. }
  188. }
  189. g_free (str);
  190. return TRUE;
  191. }
  192. </span>

工作流程

这篇教程和上篇教程的例子只有很小的差别,让我们就看看这些不同的地方吧。

[objc] view
plain
 copy

  1. <span style="font-size:14px;">  /* Set the subtitle URI to play and some font description */
  2. g_object_set (data.playbin2, "suburi", "http://docs.gstreamer.com/media/sintel_trailer_gr.srt", NULL);
  3. g_object_set (data.playbin2, "subtitle-font-desc", "Sans, 18", NULL);</span>

在设置媒体URI之后,我们设置了suburi属性,这就让playbin2获得了字幕流的地址。在这个例子里面,文件里本身包含了多个字幕流了,这用suburi来设置的字幕会加入这个列表一起列出,并且是默认选择的。

注意,在文件里面包含的字幕流是有元数据的(比如字幕的语言),然而外部的字幕流没有元数据。当运行这个例子时你会看到第一个字幕流没有语言的标签。

subtitle-font-desc属性允许设置字幕的文本字体。因为使用了Pango库来进行字体的渲染,所以具体可以查询相关文档。

简单概括一下,字符串字体的描述是根据[FAMILY-LIST][STYLE-OPTIONS][SIZE]来的,其中FAMILY-LIST是用逗号隔开的一系列可选字体,STYLE-OPTIONS是用空格来分开的一系列字体样式,SIZE则是字体大小。比如:

sans bold 12

serif, monospace bold italic condensed 16

normal 10

常见的字体包括:Normal,Sans,Serif和Monospace。

常用的样式包括:Normal,Oblique,Italic

常见的粗细包括:Ultra-Light,Light,Normal,Bold,Ultra-Bold,Heavy

常见的变化包括:Normal,Small_Caps

常见的拉伸包括:Ultra-Condensed,Extra-Condensed,Condensed,Semi-Condensed,Normal,Semi-Expanded,Extra-Expanded,Ultra-Expanded

[objc] view
plain
 copy

  1. <span style="font-size:14px;">  /* Set flags to show Audio, Video and Subtitles */
  2. g_object_get (data.playbin2, "flags", &flags, NULL);
  3. flags |= GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_TEXT;
  4. g_object_set (data.playbin2, "flags", flags, NULL);</span>

我们设置flags属性,把音频,视频和字幕的开关都打开。

剩下的部分和上一篇里面的例子是一样的,除了键盘输入是改变current-text的属性而不是current-audio的属性。这里再强调一下,切换流不会马上起作用,因为缓冲了许多解码好的数据了。

最新文章

  1. 利用Hexo搭建个人博客-博客发布篇
  2. R语言-实用数据对象处理函数
  3. [翻译]用神经网络做回归(Using Neural Networks With Regression)
  4. HDU 1688 Sightseeing&amp;HDU 3191 How Many Paths Are There(Dijkstra变形求次短路条数)
  5. 【AsyncTask整理 1】 AsyncTask几点要注意的地方
  6. matlab切比雪夫拟合
  7. ACM组队安排
  8. Java的内存管理与内存泄露
  9. php程序员的弱点
  10. 设置/修改wampserverd默认项目地址
  11. KMP (next数组的性质及证明)
  12. 同步博客—CSDN推广
  13. ZHS16GBK的数据库导入到字符集为AL32UTF8的数据库
  14. mysql 案例~mysql元数据的sql统计
  15. Java如何根据主机名(域名)获取IP地址?
  16. 第5月第24天 线性变换 opengl
  17. Android 7.0 出现 ”FileUriExposedException“ 和 ”解析包出现错误“ 异常的解决办法
  18. hdoj3038(带权并查集)
  19. vue学前班004(基础指令与使用技巧)
  20. 自学tensorflow——2.使用tensorflow计算线性回归模型

热门文章

  1. svn部署-linux
  2. Spring Boot|监控-Actuator
  3. 【多线程与并发】Java并发工具类
  4. 在Ubuntu下安装VWMare tools
  5. 小福bbs-冲刺集合
  6. 4 个用于执行高级数学计算的 JavaScript 库
  7. GLTF模型查看器---优化器【转】
  8. Javascript自定义事件功能与用法实例分析
  9. AndoridSQLite数据库开发基础教程(4)
  10. PHP7 MongoDB 使用方法