pulseaudio的相关操作(二)
这篇文章主要介绍pulseaudio playback的相关API,pulseaudio playback的具体实例可以参考[2]。如果用pulseaudio实现playback,简单地说就是创建一个playback stream,然后指定这个stream的sink,再定期的向这个stream中写数据。
mainloop相关API
pa_mainloop_new
mainloop是pulseaudio的核心,所以首先要用pa_mainloop_new创建一个mainloop实例。
pa_mainloop *pa_ml = pa_mainloop_new();
pa_mainloop_get_api
获取指向mainloop api的指针
pa_mainloop_api *pa_mlapi = pa_mainloop_get_api(pa_ml);
pa_mainloop_run
启动mainloop消息处理循环,pa_mainloop_run的定义如下,主体部分为一个while循环,此函数在最后调用。
int pa_mainloop_run(pa_mainloop *m, int *retval) {int r;while ((r = pa_mainloop_iterate(m, 1, retval)) >= 0);if (r == -2)return 1;elsereturn -1;
}
context相关API
pa_context_new
创建连接上下文,"Simple PA test application"为创建的sink-input的application.name。
pa_context * pa_ctx = pa_context_new(pa_mlapi, "Simple PA test application");
pa_context_connect
把上下文连接到一个指定的server,输入NULL指定为default server。
pa_context_connect(pa_ctx, NULL, 0, NULL);
pa_context_set_state_callback
设置状态回调函数,当上下文状态改变时,回调函数会触发,pa_ready为回调函数的参数。
pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready);
回调函数的大致结果如下,根据当前的context状态做具体的操作。
void pa_state_cb(pa_context *c, void *userdata) {pa_context_state_t state = pa_context_get_state(c);switch (state) {// TODO}
}
stream相关API
pa_stream_new
pa_stream_new用来创建一个stream,并指定音频流参数,如下:
pa_sample_spec ss;
ss.rate = 44100;
ss.channels = 1;
ss.format = PA_SAMPLE_S16LE;
playstream = pa_stream_new(pa_ctx, "Playback", &ss, NULL);// create a new stream
if (!playstream) {printf("pa_stream_new failed\n");
}
设置回调函数
可以设置的回调函数比较多,具体的回调函数可以查询API手册,对playback而言最重要的回调函数是write的回调函数,当pulseaudio需要往流中写入数据时会触发该函数,每次调用回调函数的间隔并非等间隔,且每次需要写入数据的大小不定,由pulseaudio的内部机制决定。
pa_stream_set_write_callback(playstream, stream_request_cb, NULL);
write回调函数的原型如下:
typedef void(* pa_stream_request_cb_t) (pa_stream *p, size_t nbytes, void *userdata);
在回调函数中的主要任务就是调用pa_stream_write函数,把数据写入到stream中。
pa_stream_connect_playback
下来要把stream连接 到一个device上,对playback而言就是要连接到一个sink device上,pa_stream_connect_playback的函数原型如下:
int pa_stream_connect_playback (pa_stream * s,const char * dev,const pa_buffer_attr * attr,pa_stream_flags_t flags,const pa_cvolume * volume,pa_stream * sync_stream )
- s : 创建的stream
- dev:stream要连接的sink device,NULL为默认的sink device
- attr:定义了playback和record的缓冲区指标,pulseaudio对playback和record都有自己内部定义的buffer,而attr参 数即是对buffer参数的设置,需要根据自己的实际情况选择合适的参数。
参数
含义
maxlength
buffer大小(字节),-1为能支持的最大值,如果是低延时场景,此值应该设置较小且配合PA_STREAM_ADJUST_LATENCY flag,如果buffer设置小会导致更多的under run。
tlength
仅用于playback。当buffer中的数据少于tlength时会请求向stream写数据,触发write callback函数。如果设置为-1的话,pulseaudio内部会自适应设置一个值。 当 PA_STREAM_ADJUST_LATENCY 未设置时,该值仅影响每个流的播放缓冲区大小。当 PA_STREAM_ADJUST_LATENCY 设置时,sink的延迟加上播放缓冲区大小共同受此值影响。如果对调整总体延迟感兴趣,应设置 PA_STREAM_ADJUST_LATENCY。如果你只对配置服务器端每个流的播放缓冲区大小感兴趣,则不应设置 PA_STREAM_ADJUST_LATENCY。
prebuf
仅用于playback。此值为当buf中大小超过此值时,server才会播放,即初始播放的预留量,此值设为-1时表示此值和tlength一样。
minreq
仅用于playback。write back中请求的最少数据大小,如果设为-1,则由pulseaudio内部生成一个默认的值。
fragsize
仅用于recording。 server以 fragsize 字节大小的块发送数据。较大的值会降低与其他连接上下文操作的交互性,但会减少控制开销。设为-1的话pulseaudio会把其初始化为一个合理的值。如果设置了PA_STREAM_ADJUST_LATENCY,则总体源延迟将根据此值进行调整。如果不设置 PA_STREAM_ADJUST_LATENCY,则源延迟保持不变。
- flags: 设置的额外的flags,具体的参考pa_stream_flags定义。
- volume : 初始的默认音量,建议为NULL
- sync_stream:此stream是否要和其它stream同步,如果设为NULL则各stream独立。
清理工作
当mainloop退出时需要做一些清理工作,回顾前面我们所作的工作,有创建了mainloop,创建了context,创建了stream,所以我们需要在最后清理这些我们创建的实例。 其中stream不用清理,因为pulseaudio用了类似智能指针的技术,当stream的引用计数为0时,会自动清理。所以我们清理的只有context和mainloop。清理的示意代码如下:
// clean up and disconnect pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml);
参考
https://gavv.net/articles/pulseaudio-under-the-hood/ [1]
Async Playback – Developer Documentation – PulseAudio [2]