diff --git a/spa/include/spa/buffer/alloc.h b/spa/include/spa/buffer/alloc.h index b6349a0ba..054c8afc4 100644 --- a/spa/include/spa/buffer/alloc.h +++ b/spa/include/spa/buffer/alloc.h @@ -29,6 +29,7 @@ extern "C" { #include +/** information about the buffer layout */ struct spa_buffer_alloc_info { #define SPA_BUFFER_ALLOC_FLAG_INLINE_META (1<<0) /**< add metadata data in the skeleton */ #define SPA_BUFFER_ALLOC_FLAG_INLINE_CHUNK (1<<1) /**< add chunk data in the skeleton */ @@ -49,12 +50,33 @@ struct spa_buffer_alloc_info { size_t mem_size; /**< size of the total memory if not inlined */ }; +/** + * Fill buffer allocation information + * + * Fill \a info with allocation information needed to allocate buffers + * with the given number of metadata and data members. + * + * The required size of the skeleton (the struct spa_buffer) information + * and the memory (for the metadata, chunk and buffer memory) will be + * calculated. + * + * The flags member in \a info should be configured before calling this + * functions. + * + * \param info the information to fill + * \param n_metas the number of metadatas for the buffer + * \param metas an array of metadata items + * \param n_datas the number of datas for the buffer + * \param datas an array of \a n_datas items + * \param data_aligns \a n_datas alignments + * \return 0 on success. + * */ static inline int spa_buffer_alloc_fill_info(struct spa_buffer_alloc_info *info, uint32_t n_metas, struct spa_meta metas[], uint32_t n_datas, struct spa_data datas[], uint32_t data_aligns[]) { - size_t size; + size_t size, *target; uint32_t i; info->n_metas = n_metas; @@ -64,7 +86,54 @@ static inline int spa_buffer_alloc_fill_info(struct spa_buffer_alloc_info *info, info->data_aligns = data_aligns; info->max_align = 16; info->mem_size = 0; - + /* + * The buffer skeleton is placed in memory like below and can + * be accessed as a regular structure. + * + * +==============================+ + * | struct spa_buffer | + * | uint32_t n_metas | number of metas + * | uint32_t n_datas | number of datas + * +-| struct spa_meta *metas | pointer to array of metas + * +|-| struct spa_data *datas | pointer to array of datas + * || +------------------------------+ + * |+>| struct spa_meta | + * | | uint32_t type | metadata + * | | uint32_t size | size of metadata + * +|--| void *data | pointer to metadata + * || | ... | more spa_meta follow + * || +------------------------------+ + * |+->| struct spa_data | + * | | uint32_t type | memory type + * | | uint32_t flags | + * | | int fd | fd of shared memory block + * | | uint32_t mapoffset | offset in shared memory of data + * | | uint32_t maxsize | size of data block + * | +-| void *data | pointer to data + * |+|-| struct spa_chunk *chunk | pointer to chunk + * ||| | ... | more spa_data follow + * ||| +==============================+ + * VVV + * + * metadata, chunk and memory can either be placed right + * after the skeleton (inlined) or in a separate piece of memory. + * + * vvv + * ||| +==============================+ + * +-->| meta data memory | metadata memory, 8 byte aligned + * || | ... | + * || +------------------------------+ + * +->| struct spa_chunk | memory for n_datas chunks + * | | uint32_t offset | + * | | uint32_t size | + * | | int32_t stride | + * | | int32_t dummy | + * | | ... chunks | + * | +------------------------------+ + * +>| data | memory for n_datas data, aligned + * | ... blocks | according to alignments + * +==============================+ + */ info->skel_size = sizeof(struct spa_buffer); info->skel_size += n_metas * sizeof(struct spa_meta); info->skel_size += n_datas * sizeof(struct spa_data); @@ -74,15 +143,17 @@ static inline int spa_buffer_alloc_fill_info(struct spa_buffer_alloc_info *info, info->meta_size = size; if (SPA_FLAG_CHECK(info->flags, SPA_BUFFER_ALLOC_FLAG_INLINE_META)) - info->skel_size += info->meta_size; + target = &info->skel_size; else - info->mem_size += info->meta_size; + target = &info->mem_size; + *target += info->meta_size; info->chunk_size = n_datas * sizeof(struct spa_chunk); if (SPA_FLAG_CHECK(info->flags, SPA_BUFFER_ALLOC_FLAG_INLINE_CHUNK)) - info->skel_size += info->chunk_size; + target = &info->skel_size; else - info->mem_size += info->chunk_size; + target = &info->mem_size; + *target += info->chunk_size; for (i = 0, size = 0; i < n_datas; i++) { info->max_align = SPA_MAX(info->max_align, data_aligns[i]); @@ -92,20 +163,34 @@ static inline int spa_buffer_alloc_fill_info(struct spa_buffer_alloc_info *info, info->data_size = size; if (!SPA_FLAG_CHECK(info->flags, SPA_BUFFER_ALLOC_FLAG_NO_DATA) && - SPA_FLAG_CHECK(info->flags, SPA_BUFFER_ALLOC_FLAG_INLINE_DATA)) { - info->skel_size = SPA_ROUND_UP_N(info->skel_size, n_datas ? data_aligns[0] : 1); - info->skel_size += info->data_size; - info->skel_size = SPA_ROUND_UP_N(info->skel_size, info->max_align); - } - else { - info->mem_size = SPA_ROUND_UP_N(info->mem_size, n_datas ? data_aligns[0] : 1); - info->mem_size += info->data_size; - info->mem_size = SPA_ROUND_UP_N(info->mem_size, info->max_align); - } + SPA_FLAG_CHECK(info->flags, SPA_BUFFER_ALLOC_FLAG_INLINE_DATA)) + target = &info->skel_size; + else + target = &info->mem_size; + + *target = SPA_ROUND_UP_N(*target, n_datas ? data_aligns[0] : 1); + *target += info->data_size; + *target = SPA_ROUND_UP_N(*target, info->max_align); return 0; } +/** + * Fill skeleton and data according to the allocation info + * + * Use the allocation info to create a \ref struct spa_buffer into + * \a skel_mem and \a data_mem. + * + * Depending on the flags given when calling \ref + * spa_buffer_alloc_fill_info(), the buffer meta, chunk and memory + * will be referenced in either skel_mem or data_mem. + * + * \param info an allocation info + * \param skel_mem memory to hold the \ref struct spa_buffer and the + * pointers to meta, chunk and memory. + * \param data_mem memory to hold the meta, chunk and memory + * \return a \ref struct spa_buffer in \a skel_mem + */ static inline struct spa_buffer * spa_buffer_alloc_layout(struct spa_buffer_alloc_info *info, void *skel_mem, void *data_mem) @@ -165,6 +250,25 @@ spa_buffer_alloc_layout(struct spa_buffer_alloc_info *info, return b; } +/** + * Layout an array of buffers + * + * Use the allocation info to layout the memory of an array of buffers. + * + * \a skel_mem should point to at least info->skel_size * \a n_buffers bytes + * of memory. + * \a data_mem should point to at least info->mem_size * \a n_buffers bytes + * of memory. + * + * \param info the allocation info for one buffer + * \param n_buffers the number of buffers to create + * \param buffer a array with space to hold \a n_buffers pointers to buffers + * \param skel_mem memory for the \ref struct spa_buffer + * \param data_mem memory for the meta, chunk, memory of the buffer if not + * inlined in the skeleton. + * \return 0 on success. + * + */ static inline int spa_buffer_alloc_layout_array(struct spa_buffer_alloc_info *info, uint32_t n_buffers, struct spa_buffer *buffers[], @@ -179,6 +283,27 @@ spa_buffer_alloc_layout_array(struct spa_buffer_alloc_info *info, return 0; } +/** + * Allocate an array of buffers + * + * Allocate \a n_buffers with the given metadata, memory and alignment + * information. + * + * The buffer array, structures, data and metadata will all be allocated + * in one block of memory with the proper requested alignment. + * + * \param n_buffers the number of buffers to create + * \param flags extra flags + * \param n_metas number of metadatas + * \param metas \a n_metas metadata specification + * \param n_datas number of datas + * \param datas \a n_datas memory specification + * \param data_aligns \a n_datas alignment specifications + * \returns an array of \a n_buffers pointers to \ref struct spa_buffer + * with the given metadata, data and alignment or NULL when + * allocation failed. + * + */ static inline struct spa_buffer ** spa_buffer_alloc_array(uint32_t n_buffers, uint32_t flags, uint32_t n_metas, struct spa_meta metas[], @@ -194,6 +319,8 @@ spa_buffer_alloc_array(uint32_t n_buffers, uint32_t flags, buffers = (struct spa_buffer **)calloc(1, info.max_align + n_buffers * (sizeof(struct spa_buffer *) + info.skel_size)); + if (buffers == NULL) + return NULL; skel = SPA_MEMBER(buffers, sizeof(struct spa_buffer *) * n_buffers, void); skel = SPA_PTR_ALIGN(skel, info.max_align, void); diff --git a/src/pipewire/link.c b/src/pipewire/link.c index 90a4f7e07..47d4a0680 100644 --- a/src/pipewire/link.c +++ b/src/pipewire/link.c @@ -350,69 +350,7 @@ static struct spa_pod *find_param(struct spa_pod **params, uint32_t n_params, ui return NULL; } -/* Allocate an array of buffers that can be shared. - * - * All information will be allocated in \a mem. A pointer to a - * newly allocated memory with an array of buffer pointers is - * returned. - * - * \return an array of freshly allocated buffer pointers. free() - * after usage. - * - * Array of allocated buffers: - * - * +==============================+ - * +-| struct spa_buffer * | array of n_buffers of pointers - * | | ... | - * | +==============================+ - * +>| struct spa_buffer | - * | uint32_t id | id of buffer - * | uint32_t n_metas | number of metas - * +-| struct spa_meta *metas | pointer to array of metas - * | | uint32_t n_datas | number of datas - * +|-| struct spa_data *datas | pointer to array of datas - * || +------------------------------+ - * |+>| struct spa_meta | - * | | uint32_t type | metadata - * +|--| void *data | pointer to mmaped metadata in shared memory - * || | uint32_t size | size of metadata - * || | ... | more metas follow - * || +------------------------------+ - * |+->| struct spa_data | - * | | uint32_t type | memory type, either MemFd or INVALID - * | | uint32_t flags | - * | | int fd | fd of shared memory block - * | | uint32_t mapoffset | offset in shared memory of data - * | | uint32_t maxsize | size of data block - * | +-| void *data | pointer to mmaped data in shared memory - * |+|-| struct spa_chunk *chunk | pointer to mmaped chunk in shared memory - * ||| | ... | more datas follow - * ||| +==============================+ - * ||| | ... | more buffer/meta/data definitions follow - * ||| +==============================+ - * ||| - * ||| - * ||| shared memory block: - * ||| - * ||| +==============================+ - * +-->| meta data memory | metadata memory - * || | ... | - * || +------------------------------+ - * +->| struct spa_chunk | memory for n_datas chunks - * | | uint32_t offset | - * | | uint32_t size | - * | | int32_t stride | - * | | ... chunks | - * | +------------------------------+ - * +>| data | memory for n_datas data - * | ... blocks | - * +==============================+ - * | ... | repeated for each buffer - * +==============================+ - * - * The shared memory block should not contain any types or structure, - * just the actual metadata contents. - */ +/* Allocate an array of buffers that can be shared */ static int alloc_buffers(struct pw_link *this, uint32_t n_buffers, uint32_t n_params,