【GStreamer开发】GStreamer播放教程02——字幕管理
目标
这篇教程和上一篇非常相似,但不是切换音频流,而是字幕了。这次我们会展示:
如何选择选择字幕流
如何引入外部的字幕
如何客制化字幕使用的字体
介绍
我们都知道一个文件可以有多个音视频流并且可以使用playerbin2的current-audio和current-video属性很方便的进行切换。切换字幕也是一样的方便。
就和音视频一样,playbin2会选择解码好的字幕,而且GStreamer的插件设计也很容易支持一种新的文件结构。
但字幕还是有自己的特殊之处,除了可以嵌入文件里面,playbin2还支持使用外界的URI来提供字幕。
本教程会打开一个包含5个字幕流的文件,并从外界在导入一个字幕(希腊语)。
多语言字幕的播放器
- <span style="font-size:14px;">#include <gst/gst.h>
- /* Structure to contain all our information, so we can pass it around */
- typedef struct _CustomData {
- ; /* Our one and only element */
- gint n_video; /* Number of embedded video streams */
- gint n_audio; /* Number of embedded audio streams */
- gint n_text; /* Number of embedded subtitle streams */
- gint current_video; /* Currently playing video stream */
- gint current_audio; /* Currently playing audio stream */
- gint current_text; /* Currently playing subtitle stream */
- GMainLoop *main_loop; /* GLib's Main Loop */
- } CustomData;
- /* playbin2 flags */
- typedef enum {
- << 0), /* We want video output */
- << 1), /* We want audio output */
- << 2) /* We want subtitle output */
- } GstPlayFlags;
- /* Forward definition for the message and keyboard processing functions */
- static gboolean handle_message (GstBus *bus, GstMessage *msg, CustomData *data);
- static gboolean handle_keyboard (GIOChannel *source, GIOCondition cond, CustomData *data);
- int main(int argc, charchar *argv[]) {
- CustomData data;
- GstBus *bus;
- GstStateChangeReturn ret;
- gint flags;
- GIOChannel *io_stdin;
- /* Initialize GStreamer */
- gst_init (&argc, &argv);
- /* Create the elements */
- data.playbin2 = gst_element_factory_make ("playbin2", "playbin2");
- if (!data.playbin2) {
- g_printerr ("Not all elements could be created.\n");
- ;
- }
- /* Set the URI to play */
- g_object_set (data.playbin2, "uri", "http://docs.gstreamer.com/media/sintel_trailer-480p.ogv", NULL);
- /* Set the subtitle URI to play and some font description */
- g_object_set (data.playbin2, "suburi", "http://docs.gstreamer.com/media/sintel_trailer_gr.srt", NULL);
- g_object_set (data.playbin2, "subtitle-font-desc", "Sans, 18", NULL);
- /* Set flags to show Audio, Video and Subtitles */
- g_object_get (data.playbin2, "flags", &flags, NULL);
- flags |= GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_TEXT;
- g_object_set (data.playbin2, "flags", flags, NULL);
- /* Add a bus watch, so we get notified when a message arrives */
- bus = gst_element_get_bus (data.playbin2);
- gst_bus_add_watch (bus, (GstBusFunc)handle_message, &data);
- /* Add a keyboard watch so we get notified of keystrokes */
- #ifdef _WIN32
- 2_new_fd (fileno (stdin));
- #else
- io_stdin = g_io_channel_unix_new (fileno (stdin));
- #endif
- g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc)handle_keyboard, &data);
- /* Start playing */
- ret = gst_element_set_state (data.playbin2, GST_STATE_PLAYING);
- if (ret == GST_STATE_CHANGE_FAILURE) {
- g_printerr ("Unable to set the pipeline to the playing state.\n");
- gst_object_unref (data.playbin2);
- ;
- }
- /* Create a GLib Main Loop and set it to run */
- data.main_loop = g_main_loop_new (NULL, FALSE);
- g_main_loop_run (data.main_loop);
- /* Free resources */
- g_main_loop_unref (data.main_loop);
- g_io_channel_unref (io_stdin);
- gst_object_unref (bus);
- gst_element_set_state (data.playbin2, GST_STATE_NULL);
- gst_object_unref (data.playbin2);
- ;
- }
- /* Extract some metadata from the streams and print it on the screen */
- static void analyze_streams (CustomData *data) {
- gint i;
- GstTagList *tags;
- gchar *str;
- guint rate;
- /* Read some properties */
- , "n-video", &data->n_video, NULL);
- , "n-audio", &data->n_audio, NULL);
- , "n-text", &data->n_text, NULL);
- g_print ("%d video stream(s), %d audio stream(s), %d text stream(s)\n",
- data->n_video, data->n_audio, data->n_text);
- g_print ("\n");
- ; i < data->n_video; i++) {
- tags = NULL;
- /* Retrieve the stream's video tags */
- , "get-video-tags", i, &tags);
- if (tags) {
- g_print ("video stream %d:\n", i);
- gst_tag_list_get_string (tags, GST_TAG_VIDEO_CODEC, &str);
- g_print (" codec: %s\n", str ? str : "unknown");
- g_free (str);
- gst_tag_list_free (tags);
- }
- }
- g_print ("\n");
- ; i < data->n_audio; i++) {
- tags = NULL;
- /* Retrieve the stream's audio tags */
- , "get-audio-tags", i, &tags);
- if (tags) {
- g_print ("audio stream %d:\n", i);
- if (gst_tag_list_get_string (tags, GST_TAG_AUDIO_CODEC, &str)) {
- g_print (" codec: %s\n", str);
- g_free (str);
- }
- if (gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &str)) {
- g_print (" language: %s\n", str);
- g_free (str);
- }
- if (gst_tag_list_get_uint (tags, GST_TAG_BITRATE, &rate)) {
- g_print (" bitrate: %d\n", rate);
- }
- gst_tag_list_free (tags);
- }
- }
- g_print ("\n");
- ; i < data->n_text; i++) {
- tags = NULL;
- /* Retrieve the stream's subtitle tags */
- g_print ("subtitle stream %d:\n", i);
- , "get-text-tags", i, &tags);
- if (tags) {
- if (gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &str)) {
- g_print (" language: %s\n", str);
- g_free (str);
- }
- gst_tag_list_free (tags);
- } else {
- g_print (" no tags found\n");
- }
- }
- , "current-video", &data->current_video, NULL);
- , "current-audio", &data->current_audio, NULL);
- , "current-text", &data->current_text, NULL);
- g_print ("\n");
- g_print ("Currently playing video stream %d, audio stream %d and subtitle stream %d\n",
- data->current_video, data->current_audio, data->current_text);
- g_print ("Type any number and hit ENTER to select a different subtitle stream\n");
- }
- /* Process messages from GStreamer */
- static gboolean handle_message (GstBus *bus, GstMessage *msg, CustomData *data) {
- GError *err;
- gchar *debug_info;
- switch (GST_MESSAGE_TYPE (msg)) {
- case GST_MESSAGE_ERROR:
- gst_message_parse_error (msg, &err, &debug_info);
- g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
- g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
- g_clear_error (&err);
- g_free (debug_info);
- g_main_loop_quit (data->main_loop);
- break;
- case GST_MESSAGE_EOS:
- g_print ("End-Of-Stream reached.\n");
- g_main_loop_quit (data->main_loop);
- break;
- case GST_MESSAGE_STATE_CHANGED: {
- GstState old_state, new_state, pending_state;
- gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
- )) {
- if (new_state == GST_STATE_PLAYING) {
- /* Once we are in the playing state, analyze the streams */
- analyze_streams (data);
- }
- }
- } break;
- default:
- break;
- }
- /* We want to keep receiving messages */
- return TRUE;
- }
- /* Process keyboard input */
- static gboolean handle_keyboard (GIOChannel *source, GIOCondition cond, CustomData *data) {
- gchar *str = NULL;
- if (g_io_channel_read_line (source, &str, NULL, NULL, NULL) == G_IO_STATUS_NORMAL) {
- int index = atoi (str);
- || index >= data->n_text) {
- g_printerr ("Index out of bounds\n");
- } else {
- /* If the input was a valid subtitle stream index, set the current subtitle stream */
- g_print ("Setting current subtitle stream to %d\n", index);
- , "current-text", index, NULL);
- }
- }
- g_free (str);
- return TRUE;
- }
- </span>
工作流程
这篇教程和上篇教程的例子只有很小的差别,让我们就看看这些不同的地方吧。
- <span style="font-size:14px;"> /* Set the subtitle URI to play and some font description */
- g_object_set (data.playbin2, "suburi", "http://docs.gstreamer.com/media/sintel_trailer_gr.srt", NULL);
- 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
- <span style="font-size:14px;"> /* Set flags to show Audio, Video and Subtitles */
- g_object_get (data.playbin2, "flags", &flags, NULL);
- flags |= GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_TEXT;
- g_object_set (data.playbin2, "flags", flags, NULL);</span>
我们设置flags属性,把音频,视频和字幕的开关都打开。
剩下的部分和上一篇里面的例子是一样的,除了键盘输入是改变current-text的属性而不是current-audio的属性。这里再强调一下,切换流不会马上起作用,因为缓冲了许多解码好的数据了。
【GStreamer开发】GStreamer播放教程02——字幕管理的更多相关文章
- 【GStreamer开发】GStreamer基础教程13——播放速度
目标 快进,倒放和慢放是trick模式的共同技巧,它们有一个共同点就是它们都修改了播放的速度.本教程会展示如何来获得这些效果和如何进行逐帧的跳跃.主要内容是: 如何来变换播放的速度,变快或者变慢,前进 ...
- 【GStreamer开发】GStreamer播放教程04——既看式流
目的 在<GStreamer基础教程--流>里面我们展示了如何在较差的网络条件下使用缓冲这个机制来提升用户体验.本教程在<GStreamer基础教程--流>的基础上在扩展了一下 ...
- 【GStreamer开发】GStreamer基础教程05——集成GUI工具
目标 本教程展示了如何在GStreamer集成一个GUI(比如:GTK+).最基本的原则是GStreamer处理多媒体的播放而GUI处理和用户的交互. 在这个教程里面,我们可以学到: 如何告诉GStr ...
- gstreamer应用开发(播放器)之旅
GStreamer开发,主要分为两块:应用开发.插件开发. 插件开发人员,通常是编解码库的作者(做出了编解码库后,希望gstreamer能用起来这个库,因此增加这个适配层).芯片原厂人员(将自家的hw ...
- 安装gstreamer开发环境
ubuntu中安装gstreamer开发环境: * 安装gstreamer基本库,工具,以及插件 sudo apt--dev gstreamer-tools gstreamer0.-tools gst ...
- 【转】一步一步教你在Ubuntu12.04搭建gstreamer开发环境
原文网址:http://blog.csdn.net/xsl1990/article/details/8333062 闲得蛋疼 无聊寂寞冷 随便写写弄弄 看到网上蛮多搭建gstreamer开 ...
- 【Zigbee技术入门教程-02】一图读懂ZStack协议栈的基本架构和工作机理
[Zigbee技术入门教程-02]一图读懂ZStack协议栈的基本架构和工作机理 广东职业技术学院 欧浩源 ohy3686@foxmail.com Z-Stack协议栈是一个基于任务轮询方式的操作 ...
- 【Zigbee技术入门教程-02】一图读懂ZStack协议栈的核心思想与工作机理
[Zigbee技术入门教程-02]一图读懂ZStack协议栈的核心思想与工作机理 广东职业技术学院 欧浩源 Z-Stack协议栈是一个基于任务轮询方式的操作系统,其任务调度和资源分配由操作系统抽 ...
- Expression Blend实例中文教程(11) - 视觉管理器快速入门Visual State Manager(VSM)
Expression Blend实例中文教程(11) - 视觉管理器快速入门Visual State Manager(V 时间:2010-04-12 16:06来源:SilverlightChina. ...
随机推荐
- Win32 Error
一.Win32错误 也就是Win32子系统产生的错误.当我们在自己的代码里调用Windows系统的API函数,系统执行API内部代码,当API内部代码出现错误,会将预先定义好的错误代码写到调用这个AP ...
- Mac下打开/usr/local目录
Mac下/usr/local目录默认是对于Finder是隐藏,如果需要到/usr/local下去,打开Finder,然后使用command+shift+G,在弹出的目录中填写/usr/local就可以 ...
- Quartz.NET浅谈一 : 简单Job使用(定时发送QQ邮件)
Quartz.NET是一个全功能的开源作业调度系统,可用于从最小的应用程序到大型企业系统. 直接上代码吧... 一.新建一个控制台项目 略过 二.安装Nuget包 三.创建发送邮箱辅助工具类 stat ...
- 怎么把分化成元,并且保留两位小数,用vue来做
<el-table-column prop="amount" label="申请提现金额" width="120" align=&qu ...
- computed的用法
其实在摸板中也是可以做简单的计算的,但是会看起来会很乱 ,可以用computed来做计算 <!DOCTYPE html> <html lang="en"> ...
- 利用python的matplotlib处理计算数据
#!/usr/bin/python # -*- coding: UTF-8 -*- import numpy as np import matplotlib.pyplot as plt import ...
- OpenFOAM显示残差
本文主要讲解两种方法用来显示OpenFOAM的计算残差,一种是采用OpenFOAM自带的foamMonitor来输出残差,另一种就是大家经常看见的采用pyFoam来输出残差.不管采用哪一种方法都必须安 ...
- FLUENT求解传热系数surfaceheattransfercoef.的参考值的设置【转载】
转载自:http://blog.sina.com.cn/s/blog_7ef78d170101ch30.html surface heat transfer coef. 计算公式: FLUENT求解传 ...
- laravel删除文件
laravel删除文件 一.总结 一句话总结: 1.注意disk:disk决定路径 2.删单个文件的时候就用删单个文件的方式,别用删多个文件的方式(也就是参数别数组) public function ...
- Nexus3.X忘记admin密码找回
一.问题背景 nexus3 这种东西,传完一次,很少动了,很容易忘记密码,不要急有方法找回. 官方网站关于解决该问题的方法: https://support.sonatype.com/hc/en-us ...