• 【GStreamer开发】GStreamer播放教程02——字幕管理


    目标

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

          如何选择选择字幕流

          如何引入外部的字幕

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


    介绍

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

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

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

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


    多语言字幕的播放器

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

    工作流程

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

    [objc] view plain copy
     在CODE上查看代码片派生到我的代码片
    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
     在CODE上查看代码片派生到我的代码片
    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的属性。这里再强调一下,切换流不会马上起作用,因为缓冲了许多解码好的数据了。

  • 相关阅读:
    你了解RTOS吗?
    [转] git rebase 详解
    MinGW vs MinGW-W64及其它
    【转】Video Rendering with 8-Bit YUV Formats
    Ubuntu假死
    渐进增强和优雅降级的区别
    JavaScript 插件的学习
    菜单的制作
    生活
    小组学习情况
  • 原文地址:https://www.cnblogs.com/huty/p/8517347.html
Copyright © 2020-2023  润新知