GStreamer播放Raw PCM时无法调音量、跳转及查询时长问题
GStreamer播放PCM缓冲数据的三个问题解决
问题1:gst_element_seek_simple无法跳转、查询时长返回-1
原因
rawaudioparse本身不存储PCM数据的总时长信息,且默认配置下appsrc是不可查找的(non-seekable)。由于所有PCM数据由你手动推送,GStreamer框架无法自动感知数据总量和时间位置的对应关系,因此无法处理seek请求或返回有效时长。
解决步骤
配置appsrc为可查找并设置总时长
在获取appsrc后,设置时间格式属性并计算总时长:// 设置appsrc使用时间格式,禁用自动打时间戳 g_object_set(appsrc, "format", GST_FORMAT_TIME, "do-timestamp", FALSE, NULL); // 计算总时长:假设已获取全部PCM数据的总字节数total_bytes gint bytes_per_frame = 1 * 4; // 单通道+S32LE(4字节/采样) gint64 total_frames = total_bytes / bytes_per_frame; gint64 total_duration = (total_frames * GST_SECOND) / 44100; // 采样率44100 gst_app_src_set_duration(GST_APP_SRC(appsrc), total_duration);实现appsrc的seek回调处理跳转
需要绑定seek信号的回调函数,手动处理跳转逻辑,计算对应PCM数据的起始位置并重置推送状态:// 自定义上下文结构体,存储PCM数据和当前推送位置 typedef struct { guint8 *pcm_data; gsize total_bytes; gsize current_pos; } AudioDataCtx; // seek回调函数 static gboolean appsrc_seek_cb(GstAppSrc *src, guint64 offset, GstSeekType type, guint64 stop, GstSeekType stop_type, GstAppSeekFlags flags, gpointer user_data) { AudioDataCtx *ctx = (AudioDataCtx*)user_data; // 将时间偏移转换为字节位置 gint64 target_frames = (offset * 44100) / GST_SECOND; ctx->current_pos = target_frames * 4; // 停止当前推送,重新从目标位置推送数据 gst_app_src_end_of_stream(src); gsize remaining = ctx->total_bytes - ctx->current_pos; if (remaining > 0) { GstBuffer* buffer = gst_buffer_new_allocate(NULL, remaining, NULL); gst_buffer_fill(buffer, 0, ctx->pcm_data + ctx->current_pos, remaining); gst_app_src_push_buffer(src, buffer); gst_app_src_end_of_stream(src); } return TRUE; } // 在获取appsrc后绑定回调 AudioDataCtx ctx = {pcm_data, total_bytes, 0}; g_signal_connect(appsrc, "seek", G_CALLBACK(appsrc_seek_cb), &ctx);正确时机查询时长
需等待pipeline进入PAUSED或PLAYING状态后再查询时长:gst_element_set_state(pipeline, GST_STATE_PLAYING); // 等待状态切换完成 gst_element_get_state(pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); gint64 duration; if (gst_element_query_duration(pipeline, GST_FORMAT_TIME, &duration)) { std::cout << "Duration: " << duration << std::endl; }
问题2:gst_stream_volume_set_volume无法调节音量
原因
你错误地将整个pipeline转换为GstStreamVolume调用接口,而GstStreamVolume必须作用于管道中实际的volume元素,而非整个管道对象。
解决方法
直接使用你之前获取的volume元素调用接口:
// 复用已获取的volume元素 gst_stream_volume_set_volume(volume, GST_STREAM_VOLUME_FORMAT_CUBIC, volumelevel);
注:GST_STREAM_VOLUME_FORMAT_CUBIC格式下,取值范围为0(静音)~1(原音量),大于1会放大音量但可能导致失真。
完整修正后的核心代码片段
#include <gst/gst.h> #include <iostream> typedef struct { guint8 *pcm_data; gsize total_bytes; gsize current_pos; } AudioDataCtx; static gboolean appsrc_seek_cb(GstAppSrc *src, guint64 offset, GstSeekType type, guint64 stop, GstSeekType stop_type, GstAppSeekFlags flags, gpointer user_data) { AudioDataCtx *ctx = (AudioDataCtx*)user_data; gint64 target_frames = (offset * 44100) / GST_SECOND; ctx->current_pos = target_frames * 4; gst_app_src_end_of_stream(src); gsize remaining = ctx->total_bytes - ctx->current_pos; if (remaining > 0) { GstBuffer* buffer = gst_buffer_new_allocate(NULL, remaining, NULL); gst_buffer_fill(buffer, 0, ctx->pcm_data + ctx->current_pos, remaining); gst_app_src_push_buffer(src, buffer); gst_app_src_end_of_stream(src); } return TRUE; } int main(int argc, char *argv[]) { gst_init(&argc, &argv); GstElement* pipeline = gst_parse_launch("appsrc name=src ! rawaudioparse format=pcm num-channels=1 sample-rate=44100 pcm-format=GST_AUDIO_FORMAT_S32LE ! volume name=vol ! audioconvert ! audioresample ! autoaudiosink", NULL); GstElement* appsrc = gst_bin_get_by_name(GST_BIN(pipeline), "src"); GstStreamVolume* volume = (GstStreamVolume*)gst_bin_get_by_name(GST_BIN(pipeline), "vol"); // 假设已加载PCM数据到pcm_data,总字节数total_bytes guint8 *pcm_data = ...; gsize total_bytes = ...; AudioDataCtx ctx = {pcm_data, total_bytes, 0}; // 配置appsrc g_object_set(appsrc, "format", GST_FORMAT_TIME, "do-timestamp", FALSE, NULL); gint bytes_per_frame = 1 * 4; gint64 total_frames = total_bytes / bytes_per_frame; gint64 total_duration = (total_frames * GST_SECOND) / 44100; gst_app_src_set_duration(GST_APP_SRC(appsrc), total_duration); g_signal_connect(appsrc, "seek", G_CALLBACK(appsrc_seek_cb), &ctx); // 推送初始数据 GstBuffer* buffer = gst_buffer_new_allocate(NULL, total_bytes, NULL); gst_buffer_fill(buffer, 0, pcm_data, total_bytes); gst_app_src_push_buffer(GST_APP_SRC(appsrc), buffer); gst_app_src_end_of_stream(GST_APP_SRC(appsrc)); // 启动管道 gst_element_set_state(pipeline, GST_STATE_PLAYING); gst_element_get_state(pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); // 查询时长 gint64 duration; if (gst_element_query_duration(pipeline, GST_FORMAT_TIME, &duration)) { std::cout << "Duration: " << duration << std::endl; } // 调节音量为0.5倍 gst_stream_volume_set_volume(volume, GST_STREAM_VOLUME_FORMAT_CUBIC, 0.5); // 等待播放结束 GstBus* bus = gst_element_get_bus(pipeline); gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_EOS | GST_MESSAGE_ERROR); // 释放资源 gst_object_unref(bus); gst_object_unref(volume); gst_object_unref(appsrc); gst_object_unref(pipeline); return 0; }
内容的提问来源于stack exchange,提问作者bkehufi3evsd
相关产品推荐
相关产品推荐

