10.将 Appsrc 链接到 Playbin
2026/1/28大约 5 分钟appsrcplaybin
GStreamer学习笔记:10.将 Appsrc 链接到 Playbin
本示例尝试将 appsrc 作为数据源链接到 playbin,试图实现向 playbin 注入自定义数据。这对于需要将内存数据或实时生成的数据通过 playbin 播放的场景非常有用。
核心概念
1. 使用 Appsrc 协议
Playbin 支持多种 URI 协议,其中 appsrc:// 是一个特殊协议,用于使用 appsrc 作为数据源。
data.pipeline = gst_parse_launch("playbin uri=appsrc://", NULL);当 playbin 检测到 URI 为 appsrc:// 时,它会在内部创建一个 appsrc 元素。
2. Source-Setup 信号
当 playbin 创建了 appsrc 元素后,它会发出 source-setup 信号,通知应用程序来配置这个 appsrc。
g_signal_connect(data.pipeline, "source-setup", G_CALLBACK(source_setup), &data);3. 配置 Appsrc
在 source_setup 回调函数中配置 appsrc:
static void source_setup(GstElement *pipeline, GstElement *source, CustomData *data)
{
GstAudioInfo info;
GstCaps *audio_caps;
g_print("Source has been created. Configuring.\n");
data->app_source = source; // 由 playbin 创建元素 appsrc
// 配置 appsrc
// info = { int16, 44.1khz, 单通道 }
gst_audio_info_set_format(&info, GST_AUDIO_FORMAT_S16, SAMPLE_RATE, 1, NULL);
audio_caps = gst_audio_info_to_caps(&info);
g_object_set(source, "caps", audio_caps, "format", GST_FORMAT_TIME, NULL);
g_signal_connect(source, "need-data", G_CALLBACK(start_feed), data);
g_signal_connect(source, "enough-data", G_CALLBACK(stop_feed), data);
gst_caps_unref(audio_caps);
}4. 数据生成与推送
与之前的 appsrc-appsink 示例类似,我们需要实现数据生成和推送:
推送数据
static gboolean push_data(CustomData *data)
{
GstBuffer *buffer;
GstFlowReturn ret;
GstMapInfo map;
gint16 *raw;
gint num_samples = CHUNK_SIZE / 2;
gfloat freq;
buffer = gst_buffer_new_and_alloc(CHUNK_SIZE);
GST_BUFFER_TIMESTAMP(buffer) = gst_util_uint64_scale(data->num_samples, GST_SECOND, SAMPLE_RATE);
GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale(num_samples, GST_SECOND, SAMPLE_RATE);
gst_buffer_map(buffer, &map, GST_MAP_WRITE);
raw = (gint16 *)map.data;
data->c += data->d;
data->d -= data->c / 1000;
freq = 1100 + 1000 * data->d;
for (int i = 0; i < num_samples; i++)
{
data->a += data->b;
data->b -= data->a / freq;
raw[i] = (gint16)(500 * data->a);
}
gst_buffer_unmap(buffer, &map);
data->num_samples += num_samples;
g_signal_emit_by_name(data->app_source, "push-buffer", buffer, &ret);
gst_buffer_unref(buffer);
return ret == GST_FLOW_OK;
}信号回调
static void start_feed(GstElement *source, guint size, CustomData *data)
{
if (data->sourceid == 0)
{
g_print("Start feeding\n");
data->sourceid = g_idle_add((GSourceFunc)push_data, data);
}
}
static void stop_feed(GstElement *source, CustomData *data)
{
if (data->sourceid != 0)
{
g_print("Stop feeding\n");
g_source_remove(data->sourceid);
data->sourceid = 0;
}
}5. 错误处理
static void error_cb(GstBus *bus, GstMessage *msg, CustomData *data)
{
GError *err;
gchar *debug_info;
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);
}6. Bus 信号监听
bus = gst_element_get_bus(data.pipeline);
gst_bus_add_signal_watch(bus);
g_signal_connect(G_OBJECT(bus), "message::error", (GCallback)error_cb, &data);
gst_object_unref(bus);7. GLib 主循环
data.main_loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(data.main_loop);Pipeline 结构
appsrc (由 playbin 内部创建)
-> [内部自动构建的播放 pipeline]当使用 playbin uri=appsrc:// 时,playbin 会:
- 创建
appsrc元素作为数据源 - 发出
source-setup信号 - 在
source-setup回调中配置appsrc - 自动构建后续的解码和播放 pipeline
与手动构建 Pipeline 的区别
手动构建 Pipeline
- 需要手动创建所有元素(解码器、转换器、播放器)
- 完全控制 pipeline 的每个环节
- 代码更复杂,但更灵活
使用 Playbin + Appsrc
- playbin 自动处理解码和播放
- 只需要关注数据生成和注入
- 代码更简洁,但定制性较低
完整代码
#include <gst/gst.h>
#include <gst/audio/audio.h>
#include <string.h>
#define CHUNK_SIZE 1024
#define SAMPLE_RATE 44100
typedef struct _CustomData
{
GstElement *pipeline;
GstElement *app_source;
guint64 num_samples;
gfloat a, b, c, d;
guint sourceid;
GMainLoop *main_loop;
} CustomData;
static gboolean push_data(CustomData *data);
static void start_feed(GstElement *source, guint size, CustomData *data);
static void stop_feed(GstElement *source, CustomData *data);
static void error_cb(GstBus *bus, GstMessage *msg, CustomData *data);
static void source_setup(GstElement *pipeline, GstElement *source, CustomData *data);
int main(int argc, char *argv[])
{
CustomData data;
GstBus *bus;
memset(&data, 0, sizeof(data));
data.b = 1;
data.d = 1;
gst_init(&argc, &argv);
data.pipeline = gst_parse_launch("playbin uri=appsrc://", NULL);
g_signal_connect(data.pipeline, "source-setup", G_CALLBACK(source_setup), &data);
bus = gst_element_get_bus(data.pipeline);
gst_bus_add_signal_watch(bus);
g_signal_connect(G_OBJECT(bus), "message::error", (GCallback)error_cb, &data);
gst_object_unref(bus);
gst_element_set_state(data.pipeline, GST_STATE_PLAYING);
data.main_loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(data.main_loop);
gst_element_set_state(data.pipeline, GST_STATE_NULL);
gst_object_unref(data.pipeline);
return 0;
}
static void source_setup(GstElement *pipeline, GstElement *source, CustomData *data)
{
GstAudioInfo info;
GstCaps *audio_caps;
g_print("Source has been created. Configuring.\n");
data->app_source = source;
gst_audio_info_set_format(&info, GST_AUDIO_FORMAT_S16, SAMPLE_RATE, 1, NULL);
audio_caps = gst_audio_info_to_caps(&info);
g_object_set(source, "caps", audio_caps, "format", GST_FORMAT_TIME, NULL);
g_signal_connect(source, "need-data", G_CALLBACK(start_feed), data);
g_signal_connect(source, "enough-data", G_CALLBACK(stop_feed), data);
gst_caps_unref(audio_caps);
}
static gboolean push_data(CustomData *data)
{
GstBuffer *buffer;
GstFlowReturn ret;
GstMapInfo map;
gint16 *raw;
gint num_samples = CHUNK_SIZE / 2;
gfloat freq;
buffer = gst_buffer_new_and_alloc(CHUNK_SIZE);
GST_BUFFER_TIMESTAMP(buffer) = gst_util_uint64_scale(data->num_samples, GST_SECOND, SAMPLE_RATE);
GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale(num_samples, GST_SECOND, SAMPLE_RATE);
gst_buffer_map(buffer, &map, GST_MAP_WRITE);
raw = (gint16 *)map.data;
data->c += data->d;
data->d -= data->c / 1000;
freq = 1100 + 1000 * data->d;
for (int i = 0; i < num_samples; i++)
{
data->a += data->b;
data->b -= data->a / freq;
raw[i] = (gint16)(500 * data->a);
}
gst_buffer_unmap(buffer, &map);
data->num_samples += num_samples;
g_signal_emit_by_name(data->app_source, "push-buffer", buffer, &ret);
gst_buffer_unref(buffer);
return ret == GST_FLOW_OK;
}
static void start_feed(GstElement *source, guint size, CustomData *data)
{
if (data->sourceid == 0)
{
g_print("Start feeding\n");
data->sourceid = g_idle_add((GSourceFunc)push_data, data);
}
}
static void stop_feed(GstElement *source, CustomData *data)
{
if (data->sourceid != 0)
{
g_print("Stop feeding\n");
g_source_remove(data->sourceid);
data->sourceid = 0;
}
}
static void error_cb(GstBus *bus, GstMessage *msg, CustomData *data)
{
GError *err;
gchar *debug_info;
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);
}编译和运行
gcc main.c -o main.out $(pkg-config --cflags --libs gstreamer-1.0)
./main.out应用场景
将 appsrc 链接到 playbin 的典型应用场景包括:
- 内存数据播放:从内存中读取音频数据并播放
- 实时音频生成:实时合成音频并播放(如合成器、音效处理)
- 网络流处理:接收网络数据并播放
- 音频处理:对音频进行实时处理后再播放
- 游戏音频:将游戏音频引擎的输出通过 playbin 播放
总结
本示例展示了:
- Appsrc 协议:使用
appsrc://URI 让 playbin 创建 appsrc - Source-Setup 信号:在 playbin 创建 appsrc 后配置它
- 数据注入:向 playbin 注入自定义生成的数据
- 简化流程:利用 playbin 自动处理解码和播放,简化代码
这种方法结合了 appsrc 的灵活性和 playbin 的便捷性,是构建自定义媒体播放应用的有效方式。