http://www.pulseaudio.org/wiki/WritingModules
简单总结一下这篇文档。
1. 要编写一个模块,你必须得实现下面两个函数
int
pa_init(pa_module* m);
void pa_done(pa_module* m);
看函数名称就可以知道pa_init是在做初始化的工作,这个函数在load-module的时候被调用。pa_done做一些资源释放的工作,在module被卸载的时候调用。
2. 还有一些可选的函数,这些函数主要用于提供信息给PA.
const
char
* pa__get_author();
const
char
* pa__get_description();
const
char
* pa__get_usage();
const
char
* pa__get_version();
单纯的实现一个简单的模块,是非常简单的。复杂程度是由这个模块做什么事情来决定的,更具体一点,复杂程度由这个模块和MAINLOOP进行数据交 互控制信息交互的复杂程度决定的。最简单的模块,初始化的时候打印"Hello Module", 结束的时候打印"Goodbye, Module"。如此简单,几分钟就可以搞定。
稍微复杂一点的模块,如module-pipe-sink。这个模块将pulseaudio api播放的声音数据写到一个fifo文件里面,这个时候就有控制信息的传递和数据的传递,情况就要复杂不少了。
1. 要建立一个线程用于处理消息。
2. 要利用poll来建立线程间的消息同步。
3. 要建立io_event用于模块线程和mainloop进行通信。
4. 要建立memblock用于pulseaudio和module之间传递数据。
还有些实时性的设置,模块加载参数的设定等等。并且每个具体项中还有些细节问题是需要弄清楚的,像memory block机制 ,rtpoll机制等等。
。。。。。。
PA插件的编写要点
都有那些函数要实现,上面提到了,函数本身没有什么,就是普通的C函数。但是为了和pulseaudio core一起工作,能够和 pa core进行通信和数据传递,有一些基本过程和规则是要遵守的,简单说,就是要按照一定顺序调用Pa提供的特定的函数。以一个简单的module- pipe-sink为例子:
一. 数据结构的定义
一个插件都要维护一个自己的私有数据,将这些数据组织起来称之为userdata,至于个结构都需要包含那些数据,完全是根据这个插件工作的需要来决定 的,不用担心pa core无法识别不同结构的问题,因为,他根本就不需要知道,所定义的这个结构也就是在自己实现插件的时候才会使用。
二. pa_init函数实现
这个函数主要干4件事情
1. 解析模块加载时传递进来的参数
2. 建立控制消息的通道,注意数据通道采用share memory机制的memblock
3. 建立模块的线程,该线程取数据播放
4. 建立pa_sink对象,并将他放入系统列表
三. 取数据的过程分析
建立了pa_sink对象,并且放入了系统列表,如果有client希望通过这个sink播放,就会触发rtpoll, 此时这个sink的线程就开始工作,于是开始取数据播放。那么,此时如何取数据,client端又如何将数据写入buff,二者如何同步。
单看如何取数据,非常简单,调用pa_sink_render就可以了。对于client如何写数据到buff,如何同步,参考这里
模块和mainloop通信机制的建立
和mainloop通信采用三种event( 参考这里t ),一般模块至少建立io_event, 那模块如何建立event,模块线程怎么样和manloop同步。
建立io_event
这个函数,pa_thread_mq_init,所有的模块都通过这个函数建立io_event. PA在mainloop里采用poll的方式同步消息,从下面的代码里,可以看到建立io_event的同时指定了fd,被poll使用。
void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rtpoll) {
pa_assert(q);
pa_assert(mainloop);
q->mainloop = mainloop;
pa_assert_se(q->inq = pa_asyncmsgq_new(0));
pa_assert_se(q->outq = pa_asyncmsgq_new(0));
pa_assert_se(pa_asyncmsgq_read_before_poll(q->outq) == 0);
pa_assert_se(q->read_event = mainloop->io_new(mainloop, pa_asyncmsgq_read_fd(q->outq), PA_IO_EVENT_INPUT, asyncmsgq_read_cb, q));
pa_asyncmsgq_write_before_poll(q->inq);
pa_assert_se(q->write_event = mainloop->io_new(mainloop, pa_asyncmsgq_write_fd(q->inq), PA_IO_EVENT_INPUT, asyncmsgq_write_cb, q));
pa_rtpoll_item_new_asyncmsgq_read(rtpoll, PA_RTPOLL_EARLY, q->inq);
pa_rtpoll_item_new_asyncmsgq_write(rtpoll, PA_RTPOLL_LATE, q->outq);
}
模块线程的同步
模块线程也采用poll同步机制,pulseaudio实现rtpoll模块,上面函数实现的最后两行代码,他们调用的函数为消息同步的poll指定了fd,实际上是调用pa_rtpoll_item_new增加了一个rtpll item,如下:
pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) {
pa_rtpoll_item *i;
struct pollfd *pollfd;
pa_assert(p);
pa_assert(q);
i = pa_rtpoll_item_new(p, prio, 1);
pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
pollfd->fd = pa_asyncmsgq_read_fd(q);
pollfd->events = POLLIN;
i->before_cb = asyncmsgq_read_before;
i->after_cb = asyncmsgq_read_after;
i->work_cb = asyncmsgq_read_work;
i->userdata = q;
return
i;
}