pulseaudio模块的编写

 

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;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值