pjsip学习笔记1


目前最关心声音的采集与回放,所以第一篇从PJSIP的PJMedia声卡驱动流层开始研究:

API的使用参考: http://www.pjsip.org/docs/latest/pjmedia/docs/html/group__audio__device__api.htm

数据流:               https://trac.pjsip.org/repos/wiki/media-flow

有问题,看这里: https://trac.pjsip.org/repos/wiki/FAQ


注意pjsip源代码里面由两个audiodev.c文件, 但路径不同,如下:
pjmedia/src/pjmedia/audiodev.c             -----提供对声音设备本身的操作封装,重点在对具体设备的操作或管理    
pjmedia/src/pjmedia-audiodev/audiodev.c        -----提供与声音设备子系统初始化和设备工厂的注册管理操作

一开始没注意, 看代码看糊涂了, 设备子系统工厂管理操作包括:
    pjmedia_aud_register_factory
    pjmedia_aud_unregister_factory

    pjmedia_aud_subsys_init        
    pjmedia_aud_subsys_shutdown

pisip在pjmedia_aud_subsys_init()中采取了硬编码的方式进行了一些声音设备的静态注册,pjmedia_aud_register_factory并未在例子代码中被使用
例如alsa设备工厂注册代码:aud_subsys->drv[aud_subsys->drv_cnt].create = &pjmedia_alsa_factory;
                      然后:pjmedia_aud_driver_init(aud_subsys->drv_cnt) 中使用该create,进一步填充设备信息(设备名,该设备的工厂操作接口初始化)

1) 应用层通过以下函数遍历注册的声音设备,根据设备名,获得设备在系统中的唯一ID
     pjmedia_aud_dev_lookup( const char *drv_name, const char *dev_name, pjmedia_aud_dev_index *id)

2) 应用层通过调用工厂接口创建/销毁一个设备实例, 这些函数是对pjmedia_aud_dev_factory_op接口的封装
    pjmedia_aud_stream_create
    pjmedia_aud_stream_destory

3) 应用层通过以下函数调用设备操作接口,进行设备控制(启动、停止、配置读写), 这些函数是对pjmedia_aud_stream_op接口调用的封装
    pjmedia_aud_stream_start
    pjmedia_aud_stream_stop
    pjmedia_aud_stream_get_cap
    pjmedia_aud_stream_set_cap
    pjmedia_aud_stream_get_cap
      
***pjsip库提供了一个auddemo.c程序, 演示了使用pjmedia_aud_subsys_init注册所有声音设备后,使用控制台命令对声卡进行操作的过程

***下面讲述以下alsa声卡的alsa_stream设备实现

    1) pjmedia_aud_stream_create()函数调用工厂接口函数create_stream创建一个alsa_stream设备实例
     接口函数原型: pj_status_t (*create_stream)(pjmedia_aud_dev_factory *f,
                 const pjmedia_aud_param *param,
                 pjmedia_aud_rec_cb rec_cb,
                 pjmedia_aud_play_cb play_cb,
                 void *user_data,
                 pjmedia_aud_stream **p_aud_strm);


    2) alsa声卡设备会为PCM回放和抓取功能各自建立一个线程,线程函数入口参数就是驱动对应的stream(例如alsa_stream)
    3)  应用层在调用pjmedia_aud_stream_create()建立alsa_stream时指定回调函数, user_data作为回调参数,通常会指向一个pjmedia_port实例,表明PCM数据的来源或去向
    4)  抓取线程中通过ca_cb回调(工厂接口函数create_stream提供),把抓取的pcm帧数据提供个app层        ==》对于MT7628来说,每次读取固定帧长的数据, 大小是个常数
    5)  回访线程中通过pb_cb回调(工厂接口函数create_stream提供),从app层获得满足声卡设置的pcm帧数据  ==》对于MT7628来说,每次写入固定帧长的数据, 大小是个常数

****所以,对于MT7628板子来说,刚好会遇到下面提到的问题:

Potential Problem:

Ideally, rec_cb() and play_cb() should be called one after another, interchangeably, by the sound device. But unfortunately this is not always the case; in many low-end sound cards, it is quite common to have several consecutive rec_cb() callbacks called and then followed by several consecutive play_cb() calls. To accomodate this behavior, the internal sound device queue buffer in the conference bridge is made large enough to store eight audio frames, and this is controlled by RX_BUF_COUNT macro in conference.c. It is possible that a very very bad sound device may issue more than eight consecutive rec_cb()/play_cb() calls, which in this case it would be necessary to enlarge the RX_BUF_COUNT number.


*** 以下是pjsip中对alsa声卡的封装, 根据pjsip声卡抽象层的要求,

struct alsa_stream
{
    pjmedia_aud_stream     base;=========》struct pjmedia_aud_stream{                                   
                                struct {
                                /** Driver index */
                                unsigned drv_idx; //该设备在所有声音设备数组列表中的数组索引
                                } sys;

                                pjmedia_aud_stream_op *op;========》aud_stream操作接口函数族
                            };

    /* Common */
    pj_pool_t        *pool;
    struct alsa_factory *af;======>aud_stream工厂对象( struct alsa_factory)
                        {
                            pjmedia_aud_dev_factory     base; =======》struct pjmedia_aud_dev_factory
                                                    {
                                                        /** Internal data to be initialized by audio subsystem. */
                                                        struct {
                                                        /** Driver index */
                                                        unsigned drv_idx;
                                                        } sys;

                                                        /** Operations */
                                                        pjmedia_aud_dev_factory_op *op; ==========>aud_stream工厂操作函数族
                                                    };
                            pj_pool_factory        *pf;
                            pj_pool_t            *pool;
                            pj_pool_t            *base_pool;

                            unsigned             dev_cnt;
                            pjmedia_aud_dev_info     devs[MAX_DEVICES];
                            char                         pb_mixer_name[MAX_MIX_NAME_LEN];
                        };

    void        *user_data;      //给回调函数pb_cb和ca_cb使用的私有数据,指向一个pjmedia_port实例
    pjmedia_aud_param     param;
    int                  rec_id;
    int                  quit;

    /* Playback 线程*/
    snd_pcm_t            *pb_pcm;
    snd_pcm_uframes_t    pb_frames;
    pjmedia_aud_play_cb  pb_cb;
    unsigned             pb_buf_size;
    char                *pb_buf;
    pj_thread_t        *pb_thread;

    /* Capture 线程*/
    snd_pcm_t            *ca_pcm;
    snd_pcm_uframes_t    ca_frames;
    pjmedia_aud_rec_cb   ca_cb;
    unsigned             ca_buf_size;
    char                *ca_buf;
    pj_thread_t        *ca_thread;
};

猜你喜欢

转载自blog.csdn.net/twd_1991/article/details/80538198