Linux 平台 PulseAudio 音频播放数据通路 II

PulseAudio 音频服务中,大部分音频数据缓冲区都用 pa_memblock 结构来管理,包括音频数据在 PulseAudio 音频服务和它的客户端之间的跨进程传递,PulseAudio 音频服务的数据通路中各个节点之间的音频数据传递,及 PulseAudio 音频服务和 ALSA 音频设备之间的音频数据交换。pa_memblock 结构定义 (位于 pulseaudio/src/pulsecore/memblock.c) 如下:

struct pa_memblock {
    PA_REFCNT_DECLARE; /* the reference counter */
    pa_mempool *pool;

    pa_memblock_type_t type;

    bool read_only:1;
    bool is_silence:1;

    pa_atomic_ptr_t data;
    size_t length;

    pa_atomic_t n_acquired;
    pa_atomic_t please_signal;

    union {
        struct {
            /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */
            pa_free_cb_t free_cb;
            /* If type == PA_MEMBLOCK_USER this is passed as free_cb argument */
            void *free_cb_data;
        } user;

        struct {
            uint32_t id;
            pa_memimport_segment *segment;
        } imported;
    } per_type;
};

pa_memblock 结构中,实际保存音频数据的内存缓冲区的地址保存在 data 原子指针字段中,音频数据内存缓冲区的长度保存在 length 字段中。

不同场合下使用的音频数据缓冲区具有不同的特性,音频数据缓冲区因特性不同有多种不同的类型,pa_memblock 结构即有多种不同的类型,类型由 pa_memblock 结构的 type 字段描述。类型主要有如下这些:

typedef enum pa_memblock_type {
    PA_MEMBLOCK_POOL,             /* Memory is part of the memory pool */
    PA_MEMBLOCK_POOL_EXTERNAL,    /* Data memory is part of the memory pool but the pa_memblock structure itself is not */
    PA_MEMBLOCK_APPENDED,         /* The data is appended to the memory block */
    PA_MEMBLOCK_USER,             /* User supplied memory, to be freed with free_cb */
    PA_MEMBLOCK_FIXED,            /* Data is a pointer to fixed memory that needs not to be freed */
    PA_MEMBLOCK_IMPORTED,         /* Memory is imported from another process via shm */
    PA_MEMBLOCK_TYPE_MAX
} pa_memblock_type_t;

各个 pa_memblock 类型特性如下:

  • PA_MEMBLOCK_POOLpa_memblock 结构本身和它所管理的音频数据内存缓冲区都在 内存池 中分配;
  • PA_MEMBLOCK_POOL_EXTERNALpa_memblock 结构本身在堆上分配,它所管理的音频数据内存缓冲区在 内存池 中分配;
  • PA_MEMBLOCK_APPENDEDpa_memblock 结构本身和它所管理的音频数据内存缓冲区都在堆上分配;
  • PA_MEMBLOCK_USERpa_memblock 结构本身在堆上分配,它所管理的音频数据内存缓冲区由使用方传入,音频数据内存缓冲区的释放方法也由使用方传入;
  • PA_MEMBLOCK_FIXEDpa_memblock 结构本身在堆上分配,它所管理的音频数据内存缓冲区由使用方传入,但这块音频数据内存缓冲区不需要释放,如 PulseAudio 音频服务和 ALSA 音频设备交换音频数据时,通过 mmap 获得的内核音频数据缓冲区;
  • PA_MEMBLOCK_IMPORTEDpa_memblock 结构本身在堆上分配,它所管理的音频数据内存缓冲区来自于通过共享内存获得的其它进程。

内存池 是 PulseAudio 音频服务中使用的基本的音频数据内存管理机制,它的基本思路是,预先分配一块巨大的内存,将这一大块内存分割为大小相等的小内存块,每次需要音频数据缓冲区时,则取一个小内存块来用。音频数据的传递和处理中,所需的音频数据缓冲区大小并不会经常剧烈变化,因而将内存池的内存划分为大小相等的小内存块对于兼顾音频数据缓冲区的分配/释放效率和内存利用率是值得的。

PulseAudio 音频服务中 内存池pa_mempool 结构描述,这个结构定义 (位于 pulseaudio/src/pulsecore/memblock.c) 如下:

struct pa_mempool {
   /* Reference count the mempool
    *
    * Any block allocation from the pool itself, or even just imported from
    * another process through SHM and attached to it (PA_MEMBLOCK_IMPORTED),
    * shall increase the refcount.
    *
    * This is done for per-client mempools: global references to blocks in
    * the pool, or just to attached ones, can still be lingering around when
    * the client connection dies and all per-client objects are to be freed.
    * That is, current PulseAudio design does not guarantee that the client
    * mempool blocks are referenced only by client-specific objects.
    *
    * For further details, please check:
    * https://lists.freedesktop.org/archives/pulseaudio-discuss/2016-February/025587.html
    */
   PA_REFCNT_DECLARE;

   pa_semaphore *semaphore;
   pa_mutex *mutex;

   pa_shm memory;

   bool global;

   size_t block_size;
   unsigned n_blocks;
   bool is_remote_writable;

   pa_atomic_t n_init;

   PA_LLIST_HEAD(pa_memimport, imports);
   PA_LLIST_HEAD(pa_memexport, exports);

   /* A list of free slots that may be reused */
   pa_flist *free_slots;

   pa_mempool_stat stat;
};

内存池 的大内存块由 pa_shm 结构描述,这个结构定义 (位于 pulseaudio/src/pulsecore/shm.h) 如下:

typedef struct pa_shm {
    pa_mem_type_t type;
    unsigned id;
    void *ptr;
    size_t size;

    /* Only for type = PA_MEM_TYPE_SHARED_POSIX */
    bool do_unlink:1;

    /* Only for type = PA_MEM_TYPE_SHARED_MEMFD
     *
     * To avoid fd leaks, we keep this fd open only until we pass it
     * to the other PA endpoint over unix domain socket.
     *
     * When we don't have ownership for the memfd fd in question (e.g.
     * pa_shm_attach()), or the file descriptor has now been closed,
     * this is set to -1.
     *
     * For the special case of a global mempool, we keep this fd
     * always open. Check comments on top of pa_mempool_new() for
     * rationale. */
    int fd;
} pa_shm;

根据大内存块的创建方式,大内存块分为 3 种类型,如:

typedef enum pa_mem_type {
    PA_MEM_TYPE_SHARED_POSIX,         /* Data is shared and created using POSIX shm_open() */
    PA_MEM_TYPE_SHARED_MEMFD,         /* Data is shared and created using Linux memfd_create() */
    PA_MEM_TYPE_PRIVATE,              /* Data is private and created using classic memory allocation
                                         (posi
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值