09.流信息与动态切换
2026/1/21大约 4 分钟playbinstreams
GStreamer学习笔记:09.流信息与动态切换
本示例尝试使用 playbin 播放多媒体文件,并提取流信息(视频、音频、字幕)以及动态切换不同的流。
核心概念
1. Playbin 基础
Playbin 是 GStreamer 提供的高级播放元素,内部自动创建完整的播放 pipeline。
设置播放源
data.playbin = gst_element_factory_make("playbin", "playbin");
g_object_set(data.playbin, "uri", "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_cropped_multilingual.webm", NULL);添加外部字幕
g_object_set(data.playbin, "suburi", "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer_gr.srt", NULL);
g_object_set(data.playbin, "subtitle-font-desc", "Sans, 18", NULL);subtitle-font-desc格式:[FAMILY-LIST(字体)] [STYLE-OPTIONS(字体属性)] [SIZE(字号)]
2. Playbin Flags
Playbin 使用 flags 控制哪些流应该被输出。
typedef enum
{
GST_PLAY_FLAG_VIDEO = (1 << 0), // 我们要视频输出
GST_PLAY_FLAG_AUDIO = (1 << 1), // 我们要音频输出
GST_PLAY_FLAG_TEXT = (1 << 2) // 我们要字幕输出
} GstPlayFlags;配置 Flags
gint flags;
g_object_get(data.playbin, "flags", &flags, NULL); // 获取默认值
// 使用音频和视频,取消字幕,其他值保留默认值
flags |= GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO;
flags &= ~GST_PLAY_FLAG_TEXT;
g_object_set(data.playbin, "flags", flags, NULL);3. 网络连接速度
// 网络连接速度,单位 kbps。默认值:0(未知)
g_object_set(data.playbin, "connection-speed", 56, NULL); // 56kbps这会告诉 playbin 当前网络连接的最大速度,playbin 会选择最合适版本的视频流。
4. 流信息提取
获取流数量
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);获取流标签(Metadata)
视频流标签
g_signal_emit_by_name(data->playbin, "get-video-tags", idx, &tags);
if (tags)
{
g_print("video stream %d:\n", idx);
if (gst_tag_list_get_string(tags, GST_TAG_VIDEO_CODEC, &str))
{
g_print("\t codec: %s\n", str); // 例如:codec: On2 VP8
g_free(str);
}
gst_tag_list_free(tags);
}音频流标签
g_signal_emit_by_name(data->playbin, "get-audio-tags", idx, &tags);
if (tags)
{
g_print("audio stream %d:\n", idx);
// 获取音频编码器名称
if (gst_tag_list_get_string(tags, GST_TAG_AUDIO_CODEC, &str))
{
g_print("\t codec: %s\n", str); // 例如:codec: Vorbis
g_free(str);
}
// 获取语言缩写代码
if (gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &str))
{
g_print("\t language: %s\n", str); // 例如:language: pt
g_free(str);
}
// 获取比特率
guint rate;
if (gst_tag_list_get_uint(tags, GST_TAG_BITRATE, &rate))
{
g_print("\t bitrate: %d\n", rate); // 例如:bitrate: 160000
}
gst_tag_list_free(tags);
}字幕流标签
g_signal_emit_by_name(data->playbin, "get-text-tags", idx, &tags);
if (tags)
{
g_print("subtitle stream %d:\n", idx);
if (gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &str))
{
g_print("\t language: %s\n", str);
g_free(str);
}
gst_tag_list_free(tags);
}获取当前播放的流
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);5. 动态切换流
g_object_set(data->playbin, "current-audio", index, NULL);注意:
- 这种切换不是立即生效的
- 一些之前解码好的音频数据将仍然在 pipeline 中流动
- 虽然新的流已经开始解码
- 延迟取决于容器中流的特定多路复用和 playbin 的内部 queue 的长度(这取决于网络状况)
6. 事件监听
Bus 监听
bus = gst_element_get_bus(data.playbin);
gst_bus_add_watch(bus, (GstBusFunc)handle_message, &data);键盘输入监听
#ifdef G_OS_WIN32
io_stdin = g_io_channel_win32_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);7. 状态变化处理
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;8. 键盘输入处理
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 = g_ascii_strtoull(str, NULL, 0);
if (index < 0 || index >= data->n_audio)
{
g_printerr("Index out of bounds\n");
}
else
{
g_print("Setting current audio stream to %d\n", index);
g_object_set(data->playbin, "current-audio", index, NULL);
}
}
g_free(str);
return TRUE;
}输出示例
1 video stream(s), 2 audio stream(s), 3 text stream(s)
video stream 0:
codec: On2 VP8
audio stream 0:
codec: Vorbis
language: pt
bitrate: 160000
audio stream 1:
codec: Vorbis
language: en
bitrate: 160000
subtitle stream 0:
language: pt
subtitle stream 1:
language: es
subtitle stream 2:
language: gr
Currently playing video stream 0, audio stream 0 and text stream -1
Type any number and hit ENTER to select a different audio stream完整代码
#include <gst/gst.h>
#include <stdio.h>
typedef struct _CustomData
{
GstElement *playbin;
gint n_video, n_audio, n_text;
gint current_video, current_audio, current_text;
GMainLoop *main_loop;
} CustomData;
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);
static void analyze_streams(CustomData *data);
int main(int argc, char *argv[])
{
CustomData data;
GstBus *bus;
GstStateChangeReturn ret;
gint flags;
GIOChannel *io_stdin;
gst_init(&argc, &argv);
data.playbin = gst_element_factory_make("playbin", "playbin");
if (!data.playbin)
{
g_printerr("Not all elements could be created.\n");
return -1;
}
g_object_set(data.playbin, "uri", "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_cropped_multilingual.webm", NULL);
g_object_set(data.playbin, "suburi", "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer_gr.srt", NULL);
g_object_set(data.playbin, "subtitle-font-desc", "Sans, 18", NULL);
g_object_get(data.playbin, "flags", &flags, NULL);
flags |= GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO;
flags &= ~GST_PLAY_FLAG_TEXT;
g_object_set(data.playbin, "flags", flags, NULL);
g_object_set(data.playbin, "connection-speed", 56, NULL);
bus = gst_element_get_bus(data.playbin);
gst_bus_add_watch(bus, (GstBusFunc)handle_message, &data);
#ifdef G_OS_WIN32
io_stdin = g_io_channel_win32_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);
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;
}
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;
}编译和运行
gcc main.c -o main.out $(pkg-config --cflags --libs gstreamer-1.0)
./main.out总结
本示例展示了:
Playbin 高级功能:
- 播放多媒体文件
- 添加外部字幕
- 配置 flags 控制输出流
- 设置网络连接速度
流信息提取:
- 获取流数量
- 提取流标签(编解码器、语言、比特率)
- 获取当前播放的流
动态切换流:
- 切换音频流、视频流、字幕流
- 理解切换延迟的机制
事件监听:
- Bus 消息监听
- 键盘输入监听
- 状态变化处理
Playbin 提供了完整的媒体播放解决方案,可以处理复杂的多媒体场景,是构建媒体播放器的理想选择。