运行效果
介绍
本教程与上一个教程非常相似,但不是 在不同的音频流之间切换,我们将使用字幕流。 这将使我们能够学习:
• 如何选择字幕流
• 如何添加外部字幕
• 如何自定义用于字幕的字体
我们已经知道(从前面的教程中)容器文件可以 保存多个音频和视频流,并且我们可以很容易地 通过更改 or 属性在它们中进行选择。切换字幕很容易。值得注意的是,就像音频和视频一样,要注意为字幕选择合适的解码器, 并且 GStreamer 的插件结构允许添加对新 格式与复制文件一样简单。除了容器中嵌入的字幕外,还提供 可以从外部 URI 添加额外的字幕流。本教程将打开一个已包含 5 个字幕流的文件, 并从另一个文件(希腊语)添加另一个 1 个文件。
GStreamer相关运行库
INCLUDEPATH += D:/Software/GStreamer/1.0/mingw_x86_64/include/gstreamer-1.0/gst
INCLUDEPATH += D:/Software/GStreamer/1.0/mingw_x86_64/include
INCLUDEPATH += D:/Software/GStreamer/1.0/mingw_x86_64/include/gstreamer-1.0
INCLUDEPATH += D:/Software/GStreamer/1.0/mingw_x86_64/include/glib-2.0
INCLUDEPATH += D:/Software/GStreamer/1.0/mingw_x86_64/lib/glib-2.0/include
LIBS += D:/Software/GStreamer/1.0/mingw_x86_64/lib/gstreamer-1.0.lib
LIBS += D:/Software/GStreamer/1.0/mingw_x86_64/lib/glib-2.0.lib
LIBS += D:/Software/GStreamer/1.0/mingw_x86_64/lib/gobject-2.0.lib
源码
#include <stdio.h>
#include <gst/gst.h>
typedef struct _CustomData
{
GstElement *playbin; /* 唯一的播放元素 */
gint n_video; /* 视频路数 */
gint n_audio; /* 音频路数 */
gint n_text; /* 字幕路数 */
gint current_video; /* 当前播放的视频流 */
gint current_audio; /* 当前播放的音频流 */
gint current_text; /* 当前播放的字母流 */
GMainLoop *main_loop; /* GLib主循环 */
} CustomData;
/* playbin的flag */
typedef enum
{
GST_PLAY_FLAG_VIDEO = (1 << 0), /* 想要视频输出 */
GST_PLAY_FLAG_AUDIO = (1 << 1), /* 想要音频频输出 */
GST_PLAY_FLAG_TEXT = (1 << 2) /* 想要字幕输出 */
} GstPlayFlags;
/* 消息和键盘处理功能的转发定义 */
static gboolean handle_message (GstBus *bus, GstMessage *msg, CustomData *data);
static gboolean handle_keyboard (GIOChannel *source, GIOCondition cond, CustomData *data);
int main(int argc, char *argv[])
{
/* 初始化GStreamer */
gst_init (&argc, &argv);
/* 创建元素 */
CustomData data;
data.playbin = gst_element_factory_make ("playbin", "playbin");
if (!data.playbin) { g_printerr ("Not all elements could be created.\n"); return -1; }
/* 设置播放的uri */
g_object_set (data.playbin, "uri", "https://gstreamer.freedesktop.org/data/media/sintel_trailer-480p.ogv", NULL);
/* 设置要播放的字幕URI和一些字体描述 */
g_object_set (data.playbin, "suburi", "https://gstreamer.freedesktop.org/data/media/sintel_trailer_gr.srt", NULL);
g_object_set (data.playbin, "subtitle-font-desc", "Sans, 18", NULL);
/* 设置标志以显示音频、视频和字幕 */
gint flags;
g_object_get (data.playbin, "flags", &flags, NULL);
flags |= GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_TEXT;
g_object_set (data.playbin, "flags", flags, NULL);
/* 添加一个bus监视,这样我们就可以在消息到达时收到通知 */
GstBus *bus = gst_element_get_bus (data.playbin);
gst_bus_add_watch (bus, (GstBusFunc)handle_message, &data);
/* 添加键盘监视,以便我们收到按键通知 */
#ifdef G_OS_WIN32
GIOChannel *io_stdin = g_io_channel_win32_new_fd (fileno (stdin));
#else
GIOChannel *io_stdin = g_io_channel_unix_new (fileno (stdin));
#endif
g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc)handle_keyboard, &data);
/* 开始播放 */
GstStateChangeReturn ret = gst_element_set_state (data.playbin, 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.playbin);
return -1;
}
/* 创建glib主循环 */
data.main_loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (data.main_loop);
/* 是否资源 */
g_main_loop_unref (data.main_loop);
g_io_channel_unref (io_stdin);
gst_object_unref (bus);
gst_element_set_state (data.playbin, GST_STATE_NULL);
gst_object_unref (data.playbin);
return 0;
}
/* 从流中提取一些元数据并将其打印在屏幕上 */
static void analyze_streams (CustomData *data)
{
GstTagList *tags;
gchar *str;
guint rate;
/* 读取属性 */
g_object_get (data->playbin, "n-video", &data->n_video, NULL);
g_object_get (data->playbin, "n-audio", &data->n_audio, NULL);
g_object_get (data->playbin, "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");
for (gint i = 0; i < data->n_video; i++)
{
tags = NULL;
/* 检索流的视频标签 */
g_signal_emit_by_name (data->playbin, "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");
for (gint i = 0; i < data->n_audio; i++)
{
tags = NULL;
/* 检索流的音频标签 */
g_signal_emit_by_name (data->playbin, "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");
for (gint i = 0; i < data->n_text; i++)
{
tags = NULL;
/* 检索流的字幕流标签 */
g_print ("subtitle stream %d:\n", i);
g_signal_emit_by_name (data->playbin, "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");
}
}
g_object_get (data->playbin, "current-video", &data->current_video, NULL);
g_object_get (data->playbin, "current-audio", &data->current_audio, NULL);
g_object_get (data->playbin, "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");
}
/* 处理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 (GST_MESSAGE_SRC (msg) == GST_OBJECT (data->playbin))
{
if (new_state == GST_STATE_PLAYING)
{
/* 分析流 */
analyze_streams (data);
}
}
} break;
}
/* 持续接收消息 */
return TRUE;
}
/* 处理键盘消息 */
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);
if (index < 0 || index >= data->n_text)
{
g_printerr ("Index out of bounds\n");
}
else
{
/* 如果输入是有效的字幕流索引,请设置当前字幕流 */
g_print ("Setting current subtitle stream to %d\n", index);
g_object_set (data->playbin, "current-text", index, NULL);
}
}
g_free (str);
return TRUE;
}
关注
笔者 - jxd