From b171361204d4a62209d33083420eb58e7a55cd9c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 5 Apr 2018 15:38:10 +0200 Subject: [PATCH] stream: for format conversion --- spa/include/spa/param/buffers.h | 23 +- spa/plugins/audioconvert/fmt-ops.c | 724 +++++++++++++++++++++----- spa/plugins/audioconvert/fmtconvert.c | 335 ++++++------ spa/tests/test-convert.c | 43 +- src/pipewire/stream.c | 292 ++++++++++- src/pipewire/stream.h | 1 + 6 files changed, 1087 insertions(+), 331 deletions(-) diff --git a/spa/include/spa/param/buffers.h b/spa/include/spa/param/buffers.h index e56ee86a2..63fe3e922 100644 --- a/spa/include/spa/param/buffers.h +++ b/spa/include/spa/param/buffers.h @@ -31,16 +31,23 @@ extern "C" { #define SPA_TYPE_PARAM__Buffers SPA_TYPE_PARAM_BASE "Buffers" #define SPA_TYPE_PARAM_BUFFERS_BASE SPA_TYPE_PARAM__Buffers ":" -#define SPA_TYPE_PARAM_BUFFERS__size SPA_TYPE_PARAM_BUFFERS_BASE "size" -#define SPA_TYPE_PARAM_BUFFERS__stride SPA_TYPE_PARAM_BUFFERS_BASE "stride" #define SPA_TYPE_PARAM_BUFFERS__buffers SPA_TYPE_PARAM_BUFFERS_BASE "buffers" -#define SPA_TYPE_PARAM_BUFFERS__align SPA_TYPE_PARAM_BUFFERS_BASE "align" +#define SPA_TYPE_PARAM_BUFFERS__blocks SPA_TYPE_PARAM_BUFFERS_BASE "blocks" + +#define SPA_TYPE_PARAM__BlockInfo SPA_TYPE_PARAM_BASE "BlockInfo" +#define SPA_TYPE_PARAM_BLOCK_INFO_BASE SPA_TYPE_PARAM__BlockInfo ":" + +#define SPA_TYPE_PARAM_BLOCK_INFO__size SPA_TYPE_PARAM_BLOCK_INFO_BASE "size" +#define SPA_TYPE_PARAM_BLOCK_INFO__stride SPA_TYPE_PARAM_BLOCK_INFO_BASE "stride" +#define SPA_TYPE_PARAM_BLOCK_INFO__align SPA_TYPE_PARAM_BLOCK_INFO_BASE "align" struct spa_type_param_buffers { uint32_t Buffers; + uint32_t buffers; + uint32_t blocks; + uint32_t BlockInfo; uint32_t size; uint32_t stride; - uint32_t buffers; uint32_t align; }; @@ -50,10 +57,12 @@ spa_type_param_buffers_map(struct spa_type_map *map, { if (type->Buffers == 0) { type->Buffers = spa_type_map_get_id(map, SPA_TYPE_PARAM__Buffers); - type->size = spa_type_map_get_id(map, SPA_TYPE_PARAM_BUFFERS__size); - type->stride = spa_type_map_get_id(map, SPA_TYPE_PARAM_BUFFERS__stride); type->buffers = spa_type_map_get_id(map, SPA_TYPE_PARAM_BUFFERS__buffers); - type->align = spa_type_map_get_id(map, SPA_TYPE_PARAM_BUFFERS__align); + type->blocks = spa_type_map_get_id(map, SPA_TYPE_PARAM_BUFFERS__blocks); + type->BlockInfo = spa_type_map_get_id(map, SPA_TYPE_PARAM__BlockInfo); + type->size = spa_type_map_get_id(map, SPA_TYPE_PARAM_BLOCK_INFO__size); + type->stride = spa_type_map_get_id(map, SPA_TYPE_PARAM_BLOCK_INFO__stride); + type->align = spa_type_map_get_id(map, SPA_TYPE_PARAM_BLOCK_INFO__align); } } diff --git a/spa/plugins/audioconvert/fmt-ops.c b/spa/plugins/audioconvert/fmt-ops.c index b4ec35823..3a0aee58b 100644 --- a/spa/plugins/audioconvert/fmt-ops.c +++ b/spa/plugins/audioconvert/fmt-ops.c @@ -22,174 +22,636 @@ #include -#define U8_TO_F32(v) (((v) / 128.0) - 1.0) +#define U8_MIN 0 +#define U8_MAX ((1U << 8) - 1) +#define U8_SCALE ((1U << 7) - 1) +#define U8_OFFS (1U << 7) -#define F32_TO_U8(v) \ -({ \ - typeof(v) _v = (v); \ - _v < -1.0f ? 0 : \ - _v >= 1.0f ? 255 : \ - (_v * 127.0f) + 128.0f; \ +#define S16_MIN -((1U << 15) - 1) +#define S16_MAX ((1U << 15) - 1) +#define S16_SCALE ((1U << 15) - 1) + +#define S24_MIN -((1U << 23) - 1) +#define S24_MAX ((1U << 23) - 1) +#define S24_SCALE ((1U << 23) - 1) + +#define S32_MIN -((1U << 31) - 1) +#define S32_MAX ((1U << 31) - 1) +#define S32_SCALE ((1U << 31) - 1) + +static void +conv_copy(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + int i; + for (i = 0; i < n_src; i++) + memcpy(dst[i], src[i], n_bytes); +} + +#define U8_TO_F32(v) (((v) * (1.0f / U8_OFFS)) - 1.0) + +static void +conv_u8_to_f32(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + int i, j; + + for (i = 0; i < n_src; i++) { + const int8_t *s = src[i]; + float *d = dst[i]; + + for (j = 0; j < n_bytes; j++) + d[j] = U8_TO_F32(s[j]); + } +} + +static void +conv_u8_to_f32d(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const int8_t *s = src[0]; + float **d = (float **) dst; + int i, j; + + n_bytes /= n_dst; + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_dst; i++) + d[i][j] = U8_TO_F32(*s++); + } +} + +static void +conv_u8d_to_f32(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const int8_t **s = (const int8_t **) src; + float *d = dst[0]; + int i, j; + + n_bytes /= n_src; + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_src; i++) + *d++ = U8_TO_F32(s[i][j]); + } +} + +#define S16_TO_F32(v) ((v) * (1.0f / S16_SCALE)) + +static void +conv_s16_to_f32(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + int i, j; + + n_bytes /= sizeof(int16_t); + for (i = 0; i < n_src; i++) { + const int16_t *s = src[i]; + float *d = dst[i]; + + for (j = 0; j < n_bytes; j++) + d[j] = S16_TO_F32(s[j]); + } +} + +static void +conv_s16_to_f32d(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const int16_t *s = src[0]; + float **d = (float **) dst; + int i, j; + + n_bytes /= (sizeof(int16_t) * n_dst); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_dst; i++) + d[i][j] = S16_TO_F32(*s++); + } +} + +static void +conv_s16d_to_f32(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const int16_t **s = (const int16_t **) src; + float *d = dst[0]; + int i, j; + + n_bytes /= (sizeof(int16_t) * n_src); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_src; i++) + *d++ = S16_TO_F32(s[i][j]); + } +} + +#define S32_TO_F32(v) ((v) * (1.0f / S32_SCALE)) + +static void +conv_s32_to_f32(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + int i, j; + + n_bytes /= sizeof(int32_t); + for (i = 0; i < n_src; i++) { + const int32_t *s = src[i]; + float *d = dst[i]; + + for (j = 0; j < n_bytes; j++) + d[j] = S32_TO_F32(s[j]); + } +} + +static void +conv_s32_to_f32d(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const int32_t *s = src[0]; + float **d = (float **) dst; + int i, j; + + n_bytes /= (sizeof(int32_t) * n_dst); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_dst; i++) + d[i][j] = S32_TO_F32(*s++); + } +} + +static void +conv_s32d_to_f32(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const int32_t **s = (const int32_t **) src; + float *d = dst[0]; + int i, j; + + n_bytes /= (sizeof(int32_t) * n_src); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_src; i++) + *d++ = S32_TO_F32(s[i][j]); + } +} + +#define READ24(s) (((uint32_t)s[0] << 16) | ((uint32_t)s[1] << 8) | ((uint32_t)s[2])) + +#define S24_TO_F32(v) ((v) * (1.0f / S24_SCALE)) + +static void +conv_s24_to_f32(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + int i, j; + + n_bytes /= 3; + for (i = 0; i < n_src; i++) { + const int8_t *s = src[i]; + float *d = dst[i]; + + for (j = 0; j < n_bytes; j++) { + d[j] = S24_TO_F32(READ24(s)); + s += 3; + } + } +} + +static void +conv_s24_to_f32d(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const int8_t *s = src[0]; + float **d = (float **) dst; + int i, j; + + n_bytes /= (3 * n_dst); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_dst; i++) { + d[i][j] = S24_TO_F32(READ24(s)); + s += 3; + } + } +} + +static void +conv_s24d_to_f32(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const int8_t **s = (const int8_t **) src; + float *d = dst[0]; + int i, j; + + n_bytes /= (3 * n_src); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_src; i++) { + *d++ = S24_TO_F32(READ24(s[i])); + s += 3; + } + } +} + +static void +conv_s24_32_to_f32(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + int i, j; + + n_bytes /= sizeof(int32_t); + for (i = 0; i < n_src; i++) { + const int32_t *s = src[i]; + float *d = dst[i]; + + for (j = 0; j < n_bytes; j++) + d[j] = S24_TO_F32(s[j]); + } +} + +static void +conv_s24_32_to_f32d(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const int32_t *s = src[0]; + float **d = (float **) dst; + int i, j; + + n_bytes /= (sizeof(int32_t) * n_dst); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_dst; i++) + d[i][j] = S24_TO_F32(*s++); + } +} + +static void +conv_s24_32d_to_f32(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const int32_t **s = (const int32_t **) src; + float *d = dst[0]; + int i, j; + + n_bytes /= (sizeof(int32_t) * n_src); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_src; i++) + *d++ = S24_TO_F32(s[i][j]); + } +} + +#define F32_TO_U8(v) \ +({ \ + typeof(v) _v = (v); \ + _v < -1.0f ? U8_MIN : \ + _v >= 1.0f ? U8_MAX : \ + (_v * U8_SCALE) + U8_OFFS; \ }) -#define S16_TO_F32(v) ((v) / 32767.0) +static void +conv_f32_to_u8(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + int i, j; + + n_bytes /= sizeof(float); + for (i = 0; i < n_src; i++) { + const float *s = src[i]; + int8_t *d = dst[i]; + + for (j = 0; j < n_bytes; j++) + d[j] = F32_TO_U8(s[j]); + } +} + +static void +conv_f32_to_u8d(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const float *s = src[0]; + int8_t **d = (int8_t **) dst; + int i, j; + + n_bytes /= (sizeof(float) * n_dst); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_dst; i++) + d[i][j] = F32_TO_U8(*s++); + } +} + +static void +conv_f32d_to_u8(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const int8_t **s = (const int8_t **) src; + float *d = dst[0]; + int i, j; + + n_bytes /= (sizeof(float) * n_src); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_src; i++) + *d++ = F32_TO_U8(s[i][j]); + } +} #define F32_TO_S16(v) \ ({ \ typeof(v) _v = (v); \ - _v < -1.0f ? -32767 : \ - _v >= 1.0f ? 32767 : \ - _v * 32767.0f; \ + _v < -1.0f ? S16_MIN : \ + _v >= 1.0f ? S16_MAX : \ + _v * S16_SCALE; \ }) static void -conv_s16_to_f32(void *dst, const void *src, int n_bytes) +conv_f32_to_s16(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) { - const int16_t *s = src; - float *d = dst; + int i, n, n_samples; - n_bytes /= sizeof(int16_t); - while (n_bytes--) - *d++ = S16_TO_F32(*s++); -} + n_samples = n_bytes / sizeof(float); + for (i = 0; i < n_src; i++) { + const float *s = src[i]; + int16_t *d = dst[i]; -static void -conv_s16_to_f32d(int n_dst, void *dst[n_dst], const void *src, int n_bytes) -{ - const int16_t *s = src; - float **d = (float **) dst; - int i; - - n_bytes /= (sizeof(int16_t) * n_dst); - while (n_bytes--) - for (i = 0; i < n_dst; i++) - *d[i]++ = S16_TO_F32(*s++); -} - -static void -conv_f32d_to_s16(void *dst, int n_src, const void *src[n_src], int n_bytes) -{ - const int16_t **s = (const int16_t **) src; - float *d = dst; - int i; - - n_bytes /= (sizeof(float) * n_src); - while (n_bytes--) - for (i = 0; i < n_src; i++) - *d++ = F32_TO_S16(*s[i]++); -} - -static void -conv_f32_to_s16(void *dst, const void *src, int n_bytes) -{ - const float *s = src; - int16_t *d = dst; - - n_bytes /= sizeof(float); - while (n_bytes--) - *d++ = F32_TO_S16(*s++); -} - - -static void -conv_u8_to_f32(void *dst, const void *src, int n_bytes) -{ - const int8_t *s = src; - float *d = dst; - - while (n_bytes--) - *d++ = U8_TO_F32(*s++); -} - -static void -conv_u8_to_f32d(int n_dst, void *dst[n_dst], const void *src, int n_bytes) -{ - const int8_t *s = src; - float **d = (float **) dst; - int i; - - n_bytes /= n_dst; - while (n_bytes--) - for (i = 0; i < n_dst; i++) - *d[i]++ = U8_TO_F32(*s++); -} - -static void -conv_f32d_to_u8(void *dst, int n_src, const void *src[n_src], int n_bytes) -{ - const int8_t **s = (const int8_t **) src; - float *d = dst; - int i; - - n_bytes /= (sizeof(float) * n_src); - while (n_bytes--) - for (i = 0; i < n_src; i++) - *d++ = F32_TO_U8(*s[i]++); -} - -static void -conv_f32_to_u8(void *dst, const void *src, int n_bytes) -{ - const float *s = src; - int8_t *d = dst; - - n_bytes /= sizeof(float); - while (n_bytes--) - *d++ = F32_TO_U8(*s++); -} - -static void -deinterleave_8(int n_dst, void *dst[n_dst], const void *src, int n_bytes) -{ - const uint8_t *s = src; - uint8_t **d = (uint8_t **) dst; - int i; - - n_bytes /= n_dst; - while (n_bytes--) { - for (i = 0; i < n_dst; i++) - *d[i]++ = *s++; + for (n = 0; n < n_samples; n++) + d[n] = F32_TO_S16(s[n]); } } static void -deinterleave_16(int n_dst, void *dst[n_dst], const void *src, int n_bytes) +conv_f32_to_s16d(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) { - const uint16_t *s = src; + const float *s = src[0]; + int16_t **d = (int16_t **) dst; + int i, n, n_samples; + + n_samples = n_bytes / (sizeof(float) * n_dst); + for (n = 0; n < n_samples; n++) { + for (i = 0; i < n_dst; i++) + d[i][n] = F32_TO_S16(*s++); + } +} + +static void +conv_f32d_to_s16(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const float **s = (const float **) src; + int16_t *d = dst[0]; + int i, j; + + n_bytes /= (sizeof(float) * n_src); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_src; i++) + *d++ = F32_TO_S16(s[i][j]); + } +} + +#define F32_TO_S32(v) \ +({ \ + typeof(v) _v = (v); \ + _v < -1.0f ? S32_MIN : \ + _v >= 1.0f ? S32_MAX : \ + _v * S32_SCALE; \ +}) + +static void +conv_f32_to_s32(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + int i, j; + + n_bytes /= sizeof(float); + for (i = 0; i < n_src; i++) { + const float *s = src[i]; + int32_t *d = dst[i]; + + for (j = 0; j < n_bytes; j++) + d[j] = F32_TO_S32(s[j]); + } +} + +static void +conv_f32_to_s32d(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const float *s = src[0]; + int32_t **d = (int32_t **) dst; + int i, j; + + n_bytes /= (sizeof(float) * n_dst); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_dst; i++) + d[i][j] = F32_TO_S32(*s++); + } +} + +static void +conv_f32d_to_s32(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const float **s = (const float **) src; + int32_t *d = dst[0]; + int i, j; + + n_bytes /= (sizeof(float) * n_src); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_src; i++) + *d++ = F32_TO_S32(s[i][j]); + } +} + +#define F32_TO_S24(v) \ +({ \ + typeof(v) _v = (v); \ + _v < -1.0f ? S24_MIN : \ + _v >= 1.0f ? S24_MAX : \ + (uint32_t) (_v * S24_SCALE); \ +}) + +#define WRITE24(d,v) \ +({ \ + typeof(v) _v = (v); \ + d[0] = (uint8_t) (_v >> 16); \ + d[1] = (uint8_t) (_v >> 8); \ + d[2] = (uint8_t) _v; \ +}) + +static void +conv_f32_to_s24(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + int i, j; + + n_bytes /= sizeof(float); + for (i = 0; i < n_src; i++) { + const float *s = src[i]; + int8_t *d = dst[i]; + + for (j = 0; j < n_bytes; j++) + WRITE24(d, F32_TO_S24(s[j])); + d += 3; + } +} + +static void +conv_f32_to_s24d(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const float *s = src[0]; + int8_t **d = (int8_t **) dst; + int i, j; + + n_bytes /= (sizeof(float) * n_dst); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_dst; i++) { + WRITE24(d[i], F32_TO_S24(*s++)); + d[i] += 3; + } + } +} + +static void +conv_f32d_to_s24(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const float **s = (const float **) src; + int8_t *d = dst[0]; + int i, j; + + n_bytes /= (sizeof(float) * n_src); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_src; i++) { + WRITE24(d, F32_TO_S24(s[i][j])); + d += 3; + } + } +} + + +static void +conv_f32_to_s24_32(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + int i, j; + + n_bytes /= sizeof(float); + for (i = 0; i < n_src; i++) { + const float *s = src[i]; + int32_t *d = dst[i]; + + for (j = 0; j < n_bytes; j++) + d[j] = F32_TO_S24(s[j]); + } +} + +static void +conv_f32_to_s24_32d(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const float *s = src[0]; + int32_t **d = (int32_t **) dst; + int i, j; + + n_bytes /= (sizeof(float) * n_dst); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_dst; i++) + d[i][j] = F32_TO_S24(*s++); + } +} + +static void +conv_f32d_to_s24_32(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const float **s = (const float **) src; + int32_t *d = dst[0]; + int i, j; + + n_bytes /= (sizeof(float) * n_src); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_src; i++) + *d++ = F32_TO_S24(s[i][j]); + } +} + +static void +deinterleave_8(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const uint8_t *s = src[0]; + uint8_t **d = (uint8_t **) dst; + int i, j; + + n_bytes /= n_dst; + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_dst; i++) + d[i][j] = *s++; + } +} + +static void +deinterleave_16(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const uint16_t *s = src[0]; uint16_t **d = (uint16_t **) dst; - int i; + int i, j; n_bytes /= (sizeof(uint16_t) * n_dst); - while (n_bytes--) { + for (j = 0; j < n_bytes; j++) { for (i = 0; i < n_dst; i++) - *d[i]++ = *s++; + d[i][j] = *s++; } } static void -deinterleave_32(int n_dst, void *dst[n_dst], const void *src, int n_bytes) +deinterleave_24(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) { - const uint32_t *s = src; + const uint8_t *s = src[0]; + uint8_t **d = (uint8_t **) dst; + int i, j; + + n_bytes /= (3 * n_dst); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_dst; i++) { + WRITE24(d[i], READ24(s)); + d += 3; + s += 3; + } + } +} + +static void +deinterleave_32(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const uint32_t *s = src[0]; uint32_t **d = (uint32_t **) dst; - int i; + int i, j; n_bytes /= (sizeof(uint32_t) * n_dst); - while (n_bytes--) { + for (j = 0; j < n_bytes; j++) { for (i = 0; i < n_dst; i++) - *d[i]++ = *s++; + d[i][j] = *s++; } } static void -interleave_32(void *dst, int n_src, const void *src[n_src], int n_bytes) +interleave_8(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) { - const int32_t **s = (const int32_t **) src; - uint32_t *d = dst; - int i; + const int8_t **s = (const int8_t **) src; + uint8_t *d = dst[0]; + int i, j; - n_bytes /= (sizeof(uint32_t) * n_src); - while (n_bytes--) { + n_bytes /= (sizeof(uint8_t) * n_src); + for (j = 0; j < n_bytes; j++) { for (i = 0; i < n_src; i++) - *d++ = *s[i]++; + *d++ = s[i][j]; + } +} + +static void +interleave_16(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const int16_t **s = (const int16_t **) src; + uint16_t *d = dst[0]; + int i, j; + + n_bytes /= (sizeof(uint16_t) * n_src); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_src; i++) + *d++ = s[i][j]; + } +} + +static void +interleave_24(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const int8_t **s = (const int8_t **) src; + uint8_t *d = dst[0]; + int i, j; + + n_bytes /= (3 * n_src); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_src; i++) { + WRITE24(d, READ24(s[i])); + d += 3; + s += 3; + } + } +} + +static void +interleave_32(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], int n_bytes) +{ + const int32_t **s = (const int32_t **) src; + uint32_t *d = dst[0]; + int i, j; + + n_bytes /= (sizeof(uint32_t) * n_src); + for (j = 0; j < n_bytes; j++) { + for (i = 0; i < n_src; i++) + *d++ = s[i][j]; } } diff --git a/spa/plugins/audioconvert/fmtconvert.c b/spa/plugins/audioconvert/fmtconvert.c index 1ece661f0..5c63e0913 100644 --- a/spa/plugins/audioconvert/fmtconvert.c +++ b/spa/plugins/audioconvert/fmtconvert.c @@ -69,7 +69,8 @@ struct port { bool have_format; struct spa_audio_info format; - int bpf; + int stride; + int blocks; struct buffer buffers[MAX_BUFFERS]; uint32_t n_buffers; @@ -119,31 +120,79 @@ static inline void init_type(struct type *type, struct spa_type_map *map) #include "fmt-ops.c" -static const struct pack_info { - off_t format; - void (*unpack_func) (int n_dst, void *dst[n_dst], const void *src, int n_bytes); - void (*unpack_func_1) (void *dst, const void *src, int n_bytes); - void (*pack_func) (void *dst, int n_src, const void *src[n_src], int n_bytes); - void (*pack_func_1) (void *dst, const void *src, int n_bytes); -} pack_table[] = +typedef void (*convert_func_t) (void *data, int n_dst, void *dst[n_dst], + int n_src, const void *src[n_src], int n_bytes); + +static const struct conv_info { + off_t src_fmt; + off_t dst_fmt; + + convert_func_t i2i; + convert_func_t i2d; + convert_func_t d2i; +} conv_table[] = { + /* to f32 */ { offsetof(struct spa_type_audio_format, U8), - conv_u8_to_f32d, conv_u8_to_f32, conv_f32d_to_u8, conv_f32_to_u8 }, + offsetof(struct spa_type_audio_format, F32), + conv_u8_to_f32, conv_u8_to_f32d, conv_u8d_to_f32 }, { offsetof(struct spa_type_audio_format, S16), - conv_s16_to_f32d, conv_s16_to_f32, conv_f32d_to_s16, conv_f32_to_s16 }, + offsetof(struct spa_type_audio_format, F32), + conv_s16_to_f32, conv_s16_to_f32d, conv_s16d_to_f32 }, { offsetof(struct spa_type_audio_format, F32), - deinterleave_32, NULL, interleave_32, NULL }, -}; + offsetof(struct spa_type_audio_format, F32), + conv_copy, deinterleave_32, interleave_32 }, + { offsetof(struct spa_type_audio_format, S32), + offsetof(struct spa_type_audio_format, F32), + conv_s32_to_f32, conv_s32_to_f32d, conv_s32d_to_f32 }, + { offsetof(struct spa_type_audio_format, S24), + offsetof(struct spa_type_audio_format, F32), + conv_s24_to_f32, conv_s24_to_f32d, conv_s24d_to_f32 }, + { offsetof(struct spa_type_audio_format, S24_32), + offsetof(struct spa_type_audio_format, F32), + conv_s24_32_to_f32, conv_s24_32_to_f32d, conv_s24_32d_to_f32 }, -struct chain { - struct chain *prev; + /* from f32 */ + { offsetof(struct spa_type_audio_format, F32), + offsetof(struct spa_type_audio_format, U8), + conv_f32_to_u8, conv_f32_to_u8d, conv_f32d_to_u8 }, + { offsetof(struct spa_type_audio_format, F32), + offsetof(struct spa_type_audio_format, S16), + conv_f32_to_s16, conv_f32_to_s16d, conv_f32d_to_s16 }, + { offsetof(struct spa_type_audio_format, F32), + offsetof(struct spa_type_audio_format, S32), + conv_f32_to_s32, conv_f32_to_s32d, conv_f32d_to_s32 }, + { offsetof(struct spa_type_audio_format, F32), + offsetof(struct spa_type_audio_format, S24), + conv_f32_to_s24, conv_f32_to_s24d, conv_f32d_to_s24 }, + { offsetof(struct spa_type_audio_format, F32), + offsetof(struct spa_type_audio_format, S24_32), + conv_f32_to_s24_32, conv_f32_to_s24_32d, conv_f32d_to_s24_32 }, - uint32_t flags; - const struct pack_info *pack; + /* u8 */ + { offsetof(struct spa_type_audio_format, U8), + offsetof(struct spa_type_audio_format, U8), + conv_copy, deinterleave_8, interleave_8 }, - struct buffer *dst; + /* s16 */ + { offsetof(struct spa_type_audio_format, S16), + offsetof(struct spa_type_audio_format, S16), + conv_copy, deinterleave_16, interleave_16 }, - int (*process) (struct impl *impl, struct chain *chain); + /* s32 */ + { offsetof(struct spa_type_audio_format, S32), + offsetof(struct spa_type_audio_format, S32), + conv_copy, deinterleave_32, interleave_32 }, + + /* s24 */ + { offsetof(struct spa_type_audio_format, S24), + offsetof(struct spa_type_audio_format, S24), + conv_copy, deinterleave_24, interleave_24 }, + + /* s24_32 */ + { offsetof(struct spa_type_audio_format, S24_32), + offsetof(struct spa_type_audio_format, S24_32), + conv_copy, deinterleave_32, interleave_32 }, }; struct impl { @@ -164,13 +213,13 @@ struct impl { bool started; - struct chain chains[10]; - struct chain *start; - const struct buffer *src; - int (*convert) (struct impl *impl, const struct buffer *src, struct buffer *dst); + const struct conv_info *conv[2]; + convert_func_t convert; + + uint8_t temp[8192]; }; #define CHECK_PORT(this,d,id) (id == 0) @@ -178,177 +227,87 @@ struct impl { #define GET_OUT_PORT(this,id) (&this->out_port) #define GET_PORT(this,d,id) (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(this,id) : GET_OUT_PORT(this,id)) -static const struct pack_info *find_pack_info(struct impl *this, uint32_t format) +static const struct conv_info *find_conv_info(struct impl *this, uint32_t src_fmt, uint32_t dst_fmt) { struct type *t = &this->type; int i; - for (i = 0; i < SPA_N_ELEMENTS(pack_table); i++) { - if (*SPA_MEMBER(&t->audio_format, pack_table[i].format, uint32_t) == format) - return &pack_table[i]; + for (i = 0; i < SPA_N_ELEMENTS(conv_table); i++) { + if (*SPA_MEMBER(&t->audio_format, conv_table[i].src_fmt, uint32_t) == src_fmt && + *SPA_MEMBER(&t->audio_format, conv_table[i].dst_fmt, uint32_t) == dst_fmt) + return &conv_table[i]; } return NULL; } -static int convert_generic(struct impl *this, const struct buffer *src, struct buffer *dst) +static void convert_generic (void *data, int n_dst, void *dst[n_dst], + int n_src, const void *src[n_src], int n_bytes) { - struct chain *start = this->start; +#if 0 + struct port *inport, *outport; - this->src = src; - start->dst = dst; + inport = GET_PORT(this, SPA_DIRECTION_INPUT, 0); + outport = GET_PORT(this, SPA_DIRECTION_OUTPUT, 0); - spa_log_trace(this->log, NAME " %p", this); - - return start->process(this, start); -} - -static int do_unpack(struct impl *this, struct chain *chain) -{ - struct spa_buffer *src, *dst; - uint32_t i, size; - - src = this->src->outbuf; - dst = chain->dst->outbuf; - - spa_log_trace(this->log, NAME " %p: %d->%d", this, src->n_datas, dst->n_datas); - - if (src->n_datas == dst->n_datas) { - for (i = 0; i < dst->n_datas; i++) { - size = src->datas[i].chunk->size; - chain->pack->unpack_func_1(dst->datas[i].data, - src->datas[i].data, size); - dst->datas[i].chunk->size = size; - } + if (inport->format.info.raw.layout == SPA_AUDIO_LAYOUT_INTERLEAVED) { } else { - void *datas[dst->n_datas]; - - for (i = 0; i < dst->n_datas; i++) - datas[i] = dst->datas[i].data; - - chain->pack->unpack_func(dst->n_datas, - datas, - src->datas[0].data, - src->datas[0].chunk->size); } - return 0; -} - -static int do_pack(struct impl *this, struct chain *chain) -{ - struct chain *prev = chain->prev; - struct spa_buffer *src, *dst; - uint32_t i, size; - - if (prev) { - prev->dst = chain->dst; - prev->process(this, prev); - src = prev->dst->outbuf; - } else { - src = this->src->outbuf; - } - dst = chain->dst->outbuf; - - spa_log_trace(this->log, NAME " %p: %d->%d", this, src->n_datas, dst->n_datas); - - if (src->n_datas == dst->n_datas) { - for (i = 0; i < dst->n_datas; i++) { - size = src->datas[i].chunk->size; - chain->pack->pack_func_1(dst->datas[i].data, - src->datas[i].data, size); - dst->datas[i].chunk->size = size; - } - } - else { - const void *datas[src->n_datas]; - - for (i = 0; i < src->n_datas; i++) - datas[i] = src->datas[i].data; - - chain->pack->pack_func(dst->datas[0].data, - src->n_datas, - datas, - src->datas[0].chunk->size); - } - return 0; -} - -static struct chain *alloc_chain(struct impl *this, struct chain *prev) -{ - struct chain *chain; - if (prev == NULL) - chain = this->chains; - else - chain = prev + 1; - chain->prev = prev; - return chain; +#endif } static int setup_convert(struct impl *this) { struct port *inport, *outport; - const struct pack_info *pack_info; - struct chain *chain = NULL; + uint32_t src_fmt, dst_fmt; struct type *t = &this->type; - uint32_t channels, rate; inport = GET_PORT(this, SPA_DIRECTION_INPUT, 0); outport = GET_PORT(this, SPA_DIRECTION_OUTPUT, 0); - spa_log_info(this->log, NAME " %p: %d/%d@%d->%d/%d@%d", this, - inport->format.info.raw.format, + src_fmt = inport->format.info.raw.format; + dst_fmt = outport->format.info.raw.format; + + spa_log_info(this->log, NAME " %p: %d/%d@%d.%d->%d/%d@%d.%d", this, + src_fmt, inport->format.info.raw.channels, inport->format.info.raw.rate, - outport->format.info.raw.format, + inport->format.info.raw.layout, + dst_fmt, outport->format.info.raw.channels, - outport->format.info.raw.rate); + outport->format.info.raw.rate, + outport->format.info.raw.layout); - channels = inport->format.info.raw.channels; - rate = inport->format.info.raw.rate; + if (inport->format.info.raw.channels != outport->format.info.raw.channels) + return -EINVAL; - /* unpack */ - if (inport->format.info.raw.format != t->audio_format.F32 || - (inport->format.info.raw.channels > 1 && - inport->format.info.raw.layout != SPA_AUDIO_LAYOUT_NON_INTERLEAVED)) { - if ((pack_info = find_pack_info(this, inport->format.info.raw.format)) == NULL) - return -EINVAL; + if (inport->format.info.raw.rate != outport->format.info.raw.rate) + return -EINVAL; - spa_log_info(this->log, NAME " %p: setup unpack", this); - chain = alloc_chain(this, chain); - chain->pack = pack_info; - chain->process = do_unpack; + /* find fast path */ + this->conv[0] = find_conv_info(this, src_fmt, dst_fmt); + if (this->conv[0] != NULL) { + if (inport->format.info.raw.layout == SPA_AUDIO_LAYOUT_INTERLEAVED) { + if (outport->format.info.raw.layout == SPA_AUDIO_LAYOUT_INTERLEAVED) + this->convert = this->conv[0]->i2i; + else + this->convert = this->conv[0]->i2d; + } + else { + if (outport->format.info.raw.layout == SPA_AUDIO_LAYOUT_INTERLEAVED) + this->convert = this->conv[0]->d2i; + else + this->convert = this->conv[0]->i2i; + } + return 0; } - /* down mix */ - if (channels > outport->format.info.raw.channels) { - spa_log_info(this->log, NAME " %p: setup downmix", this); - channels = outport->format.info.raw.channels; - } - /* resample */ - if (rate != outport->format.info.raw.rate) { - spa_log_info(this->log, NAME " %p: setup resample", this); - rate = outport->format.info.raw.rate; - } + /* go through intermediate format */ + this->conv[0] = find_conv_info(this, src_fmt, t->audio_format.F32); + this->conv[1] = find_conv_info(this, t->audio_format.F32, dst_fmt); + if (this->conv[0] == NULL || this->conv[1] == NULL) + return -ENOTSUP; - /* up mix */ - if (channels < outport->format.info.raw.channels) { - spa_log_info(this->log, NAME " %p: setup upmix", this); - channels = outport->format.info.raw.channels; - } - - /* pack */ - if (outport->format.info.raw.format != t->audio_format.F32 || - (outport->format.info.raw.channels > 1 && - outport->format.info.raw.layout != SPA_AUDIO_LAYOUT_NON_INTERLEAVED)) { - if ((pack_info = find_pack_info(this, outport->format.info.raw.format)) == NULL) - return -EINVAL; - spa_log_info(this->log, NAME " %p: setup pack", this); - chain = alloc_chain(this, chain); - chain->pack = pack_info; - chain->process = do_pack; - } - - this->start = chain; this->convert = convert_generic; return 0; @@ -496,7 +455,7 @@ static int port_enum_formats(struct spa_node *node, "I", t->media_type.audio, "I", t->media_subtype.raw, ":", t->format_audio.format, "Ieu", t->audio_format.S16, - SPA_POD_PROP_ENUM(12, t->audio_format.U8, + SPA_POD_PROP_ENUM(11, t->audio_format.U8, t->audio_format.S16, t->audio_format.S16_OE, t->audio_format.F32, @@ -505,7 +464,6 @@ static int port_enum_formats(struct spa_node *node, t->audio_format.S32_OE, t->audio_format.S24, t->audio_format.S24_OE, - t->audio_format.S24, t->audio_format.S24_32, t->audio_format.S24_32_OE), ":", t->format_audio.layout, "ieu", SPA_AUDIO_LAYOUT_INTERLEAVED, @@ -519,7 +477,7 @@ static int port_enum_formats(struct spa_node *node, "I", t->media_type.audio, "I", t->media_subtype.raw, ":", t->format_audio.format, "Ieu", t->audio_format.S16, - SPA_POD_PROP_ENUM(12, t->audio_format.U8, + SPA_POD_PROP_ENUM(11, t->audio_format.U8, t->audio_format.S16, t->audio_format.S16_OE, t->audio_format.F32, @@ -528,7 +486,6 @@ static int port_enum_formats(struct spa_node *node, t->audio_format.S32_OE, t->audio_format.S24, t->audio_format.S24_OE, - t->audio_format.S24, t->audio_format.S24_32, t->audio_format.S24_32_OE), ":", t->format_audio.layout, "ieu", SPA_AUDIO_LAYOUT_INTERLEAVED, @@ -632,11 +589,12 @@ impl_node_port_enum_params(struct spa_node *node, param = spa_pod_builder_object(&b, id, t->param_buffers.Buffers, - ":", t->param_buffers.size, "iru", 1024 * port->bpf, - SPA_POD_PROP_MIN_MAX(16 * port->bpf, INT32_MAX / port->bpf), - ":", t->param_buffers.stride, "i", 0, ":", t->param_buffers.buffers, "iru", 1, SPA_POD_PROP_MIN_MAX(1, MAX_BUFFERS), + ":", t->param_buffers.blocks, "i", port->blocks, + ":", t->param_buffers.size, "iru", 1024 * port->stride, + SPA_POD_PROP_MIN_MAX(16 * port->stride, INT32_MAX / port->stride), + ":", t->param_buffers.stride, "i", port->stride, ":", t->param_buffers.align, "i", 16); } else if (id == t->param.idMeta) { @@ -677,6 +635,20 @@ impl_node_port_enum_params(struct spa_node *node, return 1; } +static int calc_width(struct spa_audio_info *info, struct type *t) +{ + if (info->info.raw.format == t->audio_format.U8) + return 1; + else if (info->info.raw.format == t->audio_format.S16 || + info->info.raw.format == t->audio_format.S16_OE) + return 2; + else if (info->info.raw.format == t->audio_format.S24 || + info->info.raw.format == t->audio_format.S24_OE) + return 3; + else + return 4; +} + static int clear_buffers(struct impl *this, struct port *port) { if (port->n_buffers > 0) { @@ -724,6 +696,16 @@ static int port_set_format(struct spa_node *node, port->have_format = true; port->format = info; + port->stride = calc_width(&info, t); + + if (info.info.raw.layout == SPA_AUDIO_LAYOUT_INTERLEAVED) { + port->stride *= info.info.raw.channels; + port->blocks = 1; + } + else { + port->blocks = info.info.raw.channels; + } + if (other->have_format) res = setup_convert(this); @@ -939,7 +921,24 @@ static int impl_node_process(struct spa_node *node) sbuf = &inport->buffers[inio->buffer_id]; - this->convert(this, sbuf, dbuf); + { + int i, n_bytes; + uint32_t n_src_datas = sbuf->outbuf->n_datas; + uint32_t n_dst_datas = dbuf->outbuf->n_datas; + const void *src_datas[n_src_datas]; + void *dst_datas[n_dst_datas]; + + n_bytes = sbuf->outbuf->datas[0].chunk->size; + + for (i = 0; i < n_src_datas; i++) + src_datas[i] = sbuf->outbuf->datas[i].data; + for (i = 0; i < n_dst_datas; i++) + dst_datas[i] = dbuf->outbuf->datas[i].data; + + this->convert(this, n_dst_datas, dst_datas, n_src_datas, src_datas, n_bytes); + + dbuf->outbuf->datas[0].chunk->size = n_bytes / 2; + } outio->status = SPA_STATUS_HAVE_BUFFER; outio->buffer_id = dbuf->outbuf->id; diff --git a/spa/tests/test-convert.c b/spa/tests/test-convert.c index 97d3382a6..bb2505739 100644 --- a/spa/tests/test-convert.c +++ b/spa/tests/test-convert.c @@ -298,13 +298,46 @@ static int negotiate_formats(struct data *data) format)) < 0) return res; + + return 0; +} + +static int negotiate_buffers(struct data *data) +{ + int res; + uint32_t state; + struct spa_pod *param; + struct spa_pod_builder b = { 0 }; + uint8_t buffer[4096]; + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + state = 0; + if ((res = spa_node_port_enum_params(data->conv, + SPA_DIRECTION_INPUT, 0, + data->type.param.idBuffers, &state, + NULL, ¶m, &b)) <= 0) + return -EBADF; + + spa_debug_pod(param, 0); + init_buffer(data, data->in_buffers, data->in_buffer, 1, BUFFER_SIZE, 1); if ((res = spa_node_port_use_buffers(data->conv, SPA_DIRECTION_INPUT, 0, data->in_buffers, 1)) < 0) return res; - init_buffer(data, data->out_buffers, data->out_buffer, 1, BUFFER_SIZE / 2, 2); + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + state = 0; + if ((res = spa_node_port_enum_params(data->conv, + SPA_DIRECTION_OUTPUT, 0, + data->type.param.idBuffers, &state, + NULL, ¶m, &b)) <= 0) + return -EBADF; + + spa_debug_pod(param, 0); + + init_buffer(data, data->out_buffers, data->out_buffer, 1, BUFFER_SIZE, 2); if ((res = spa_node_port_use_buffers(data->conv, SPA_DIRECTION_OUTPUT, 0, data->out_buffers, 1)) < 0) @@ -346,8 +379,8 @@ static void run_convert(struct data *data) res = spa_node_process(data->conv); printf("called process %d\n", res); - spa_debug_dump_mem(data->out_buffers[0]->datas[0].data, BUFFER_SIZE / 2); - spa_debug_dump_mem(data->out_buffers[0]->datas[1].data, BUFFER_SIZE / 2); + spa_debug_dump_mem(data->out_buffers[0]->datas[0].data, BUFFER_SIZE); + spa_debug_dump_mem(data->out_buffers[0]->datas[1].data, BUFFER_SIZE); { struct spa_command cmd = SPA_COMMAND_INIT(data->type.command_node.Pause); @@ -385,6 +418,10 @@ int main(int argc, char *argv[]) printf("can't negotiate nodes: %d\n", res); return -1; } + if ((res = negotiate_buffers(&data)) < 0) { + printf("can't negotiate buffers: %d\n", res); + return -1; + } run_convert(&data); } diff --git a/src/pipewire/stream.c b/src/pipewire/stream.c index 86eca1ddd..69969babc 100644 --- a/src/pipewire/stream.c +++ b/src/pipewire/stream.c @@ -39,6 +39,7 @@ #include "extensions/client-node.h" #define MAX_BUFFERS 64 +#define MASK_BUFFERS (MAX_BUFFERS-1) struct type { uint32_t client_node; @@ -54,6 +55,7 @@ struct buffer { struct pw_buffer this; uint32_t id; #define BUFFER_FLAG_MAPPED (1 << 0) +#define BUFFER_FLAG_QUEUED (1 << 1) uint32_t flags; }; @@ -96,6 +98,7 @@ struct stream { struct queue dequeued; struct queue queued; + uint32_t n_orig_params; uint32_t n_init_params; struct spa_pod **init_params; @@ -114,6 +117,12 @@ struct stream { bool free_data; struct data data; + + bool use_converter; + struct spa_node *fmtconvert; + struct spa_node *resample; + struct spa_node *remix; + struct spa_io_buffers conv_io; }; @@ -121,8 +130,13 @@ static inline void push_queue(struct stream *stream, struct queue *queue, struct { uint32_t index; + if (SPA_FLAG_CHECK(buffer->flags, BUFFER_FLAG_QUEUED)) + return; + + SPA_FLAG_SET(buffer->flags, BUFFER_FLAG_QUEUED); + spa_ringbuffer_get_write_index(&queue->ring, &index); - queue->ids[index & (MAX_BUFFERS-1)] = buffer->id; + queue->ids[index & MASK_BUFFERS] = buffer->id; spa_ringbuffer_write_update(&queue->ring, index + 1); } @@ -130,14 +144,84 @@ static inline struct buffer *pop_queue(struct stream *stream, struct queue *queu { int32_t avail; uint32_t index, id; + struct buffer *buffer; if ((avail = spa_ringbuffer_get_read_index(&queue->ring, &index)) <= 0) return NULL; - id = queue->ids[index & (MAX_BUFFERS-1)]; + id = queue->ids[index & MASK_BUFFERS]; spa_ringbuffer_read_update(&queue->ring, index + 1); - return &stream->buffers[id]; + buffer = &stream->buffers[id]; + SPA_FLAG_UNSET(buffer->flags, BUFFER_FLAG_QUEUED); + + return buffer; +} + +/* check if the server format is compatible with the requested + * formats, if not, set up converters when allowed */ +static int configure_converter(struct stream *impl) +{ + struct pw_type *t = impl->t; + int i, res; + struct spa_pod *param; + + impl->use_converter = false; + + /* check if format compatible with filter */ + for (i = 0; i < impl->n_orig_params; i++) { + uint8_t buffer[4096]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, 4096); + struct spa_pod *filtered; + + param = impl->init_params[i]; + + if (spa_pod_is_object_type(param, t->spa_format)) { + if (spa_pod_filter(&b, &filtered, impl->format, param) >= 0) + return 0; + } + } + + /* configure the converter */ + if ((res = spa_node_port_set_param(impl->fmtconvert, + impl->direction, 0, + t->param.idFormat, 0, + impl->format)) < 0) + return res; + + + /* try to configure the other end */ + for (i = 0; i < impl->n_orig_params; i++) { + param = impl->init_params[i]; + + if (spa_pod_is_object_type(param, t->spa_format)) { + if ((res = spa_node_port_set_param(impl->fmtconvert, + SPA_DIRECTION_REVERSE(impl->direction), 0, + t->param.idFormat, + SPA_NODE_PARAM_FLAG_FIXATE, + param)) < 0) + continue; + + /* other end set and fixated */ + impl->use_converter = true; + break; + } + } + /* when we get here without valid configured converter we fail */ + if (!impl->use_converter) + return -ENOTSUP; + + res = spa_node_port_set_io(impl->fmtconvert, + impl->direction, 0, + t->io.Buffers, + impl->io, sizeof(struct spa_io_buffers)); + impl->io = &impl->conv_io; + res = spa_node_port_set_io(impl->fmtconvert, + SPA_DIRECTION_REVERSE(impl->direction), 0, + t->io.Buffers, + impl->io, sizeof(struct spa_io_buffers)); + + return 0; } static bool stream_set_state(struct pw_stream *stream, enum pw_stream_state state, const char *error) @@ -160,7 +244,7 @@ static bool stream_set_state(struct pw_stream *stream, enum pw_stream_state stat return res; } -static struct buffer *find_buffer(struct pw_stream *stream, uint32_t id) +static struct buffer *get_buffer(struct pw_stream *stream, uint32_t id) { struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); if (id < impl->n_buffers) @@ -206,6 +290,9 @@ static int impl_send_command(struct spa_node *node, const struct spa_command *co if (impl->direction == SPA_DIRECTION_INPUT) { impl->io->status = SPA_STATUS_NEED_BUFFER; + impl->io->buffer_id = SPA_ID_INVALID; + impl->conv_io.status = SPA_STATUS_NEED_BUFFER; + impl->conv_io.buffer_id = SPA_ID_INVALID; } else { call_process(impl); @@ -271,15 +358,28 @@ static int impl_get_port_ids(struct spa_node *node, static int impl_port_set_io(struct spa_node *node, enum spa_direction direction, uint32_t port_id, uint32_t id, void *data, size_t size) { - struct stream *d = SPA_CONTAINER_OF(node, struct stream, impl_node); - struct pw_type *t = d->t; + struct stream *impl = SPA_CONTAINER_OF(node, struct stream, impl_node); + struct pw_type *t = impl->t; + int res = 0; - if (id == t->io.Buffers) - d->io = data; + if (id == t->io.Buffers) { + pw_log_debug("stream %p: set io %d %p %zd", impl, id, data, size); + + if (impl->use_converter) { + impl->io = &impl->conv_io; + res = spa_node_port_set_io(impl->fmtconvert, + direction, 0, id, data, size); + res = spa_node_port_set_io(impl->fmtconvert, + SPA_DIRECTION_REVERSE(direction), 0, + id, impl->io, size); + } + else + impl->io = data; + } else - return -ENOENT; + res = -ENOENT; - return 0; + return res; } static int impl_port_get_info(struct spa_node *node, enum spa_direction direction, uint32_t port_id, @@ -355,7 +455,7 @@ static int port_set_format(struct spa_node *node, struct stream *impl = SPA_CONTAINER_OF(node, struct stream, impl_node); struct pw_stream *stream = &impl->this; struct pw_type *t = impl->t; - int count; + int res, count; pw_log_debug("stream %p: format changed", impl); @@ -369,6 +469,12 @@ static int port_set_format(struct spa_node *node, else impl->format = NULL; + + if ((res = configure_converter(impl)) < 0) { + pw_stream_finish_format(stream, res, NULL, 0); + return res; + } + count = spa_hook_list_call(&stream->listener_list, struct pw_stream_events, format_changed, impl->format); @@ -458,6 +564,81 @@ static void clear_buffers(struct pw_stream *stream) spa_ringbuffer_init(&impl->queued.ring); } +static struct spa_buffer ** alloc_buffers(struct stream *impl, + uint32_t n_buffers, + uint32_t n_metas, + uint32_t meta_sizes[n_metas], + uint32_t n_datas, + uint32_t data_sizes[n_datas]) +{ + struct spa_buffer **buffers; + size_t skel_size, data_size = 0; + struct spa_buffer *bp, *b; + void *dp, *d, *ddp; + struct spa_chunk *cdp; + int i, j; + struct pw_type *t = impl->t; + + skel_size = sizeof(struct spa_buffer *); + skel_size += sizeof(struct spa_buffer); + skel_size += n_metas * sizeof(struct spa_meta); + for (i = 0; i < n_metas; i++) + data_size += meta_sizes[i]; + skel_size += n_datas * sizeof(struct spa_data); + data_size += n_datas * sizeof(struct spa_chunk); + for (i = 0; i < n_datas; i++) + data_size += data_sizes[i]; + + buffers = malloc((skel_size + data_size) * n_buffers); + + bp = SPA_MEMBER(buffers, n_buffers * sizeof(struct spa_buffer *), struct spa_buffer); + dp = SPA_MEMBER(bp, n_buffers * skel_size, void); + + for (i = 0; i < n_buffers; i++) { + b = SPA_MEMBER(bp, skel_size * i, struct spa_buffer); + d = SPA_MEMBER(dp, data_size * i, void); + + buffers[i] = b; + b->id = i; + b->n_metas = n_metas; + b->metas = SPA_MEMBER(b, sizeof(struct spa_buffer), struct spa_meta); + for (j = 0; j < n_metas; j++) { + struct spa_meta *m = &b->metas[j]; + m->size = meta_sizes[j]; + m->data = d; + d += m->size; + } + b->n_datas = n_datas; + b->datas = SPA_MEMBER(b->metas, n_metas * sizeof(struct spa_meta), struct spa_data); + + cdp = d; + ddp = SPA_MEMBER(cdp, n_datas * sizeof(struct spa_chunk), void); + + for (j = 0; j < n_datas; j++) { + struct spa_data *d = &b->datas[j]; + + d->chunk = &cdp[j]; + if (data_sizes[j] > 0) { + d->type = t->data.MemPtr; + d->flags = 0; + d->fd = -1; + d->mapoffset = 0; + d->maxsize = data_sizes[j]; + d->data = ddp; + d->chunk->offset = 0; + d->chunk->size = data_sizes[j]; + d->chunk->stride = 0; + ddp += data_sizes[j]; + } else { + /* needs to be allocated by a node */ + d->type = SPA_ID_INVALID; + d->data = NULL; + } + } + } + return buffers; +} + static int impl_port_use_buffers(struct spa_node *node, enum spa_direction direction, uint32_t port_id, struct spa_buffer **buffers, uint32_t n_buffers) { @@ -465,14 +646,15 @@ static int impl_port_use_buffers(struct spa_node *node, enum spa_direction direc struct pw_stream *stream = &impl->this; struct pw_type *t = impl->t; int i, j, prot, res; + int size = 0; prot = PROT_READ | (direction == SPA_DIRECTION_OUTPUT ? PROT_WRITE : 0); clear_buffers(stream); for (i = 0; i < n_buffers; i++) { + int buf_size = 0; struct buffer *b = &impl->buffers[i]; - int size = 0; b->flags = 0; b->id = buffers[i]->id; @@ -489,13 +671,43 @@ static int impl_port_use_buffers(struct spa_node *node, enum spa_direction direc pw_log_error("invalid buffer mem"); return -EINVAL; } - size += d->maxsize; + buf_size += d->maxsize; } SPA_FLAG_SET(b->flags, BUFFER_FLAG_MAPPED); + + if (size > 0 && buf_size != size) { + pw_log_error("invalid buffer size %d", buf_size); + return -EINVAL; + } else + size = buf_size; } - b->this.buffer = buffers[i]; pw_log_info("got buffer %d %d datas, total size %d", i, buffers[i]->n_datas, size); + } + impl->n_buffers = n_buffers; + + if (impl->use_converter) { + uint32_t data_sizes[1]; + + spa_node_port_use_buffers(impl->fmtconvert, + impl->direction, 0, + buffers, + n_buffers); + + data_sizes[0] = size * 2; + buffers = alloc_buffers(impl, n_buffers, 0, NULL, 1, data_sizes); + + spa_node_port_use_buffers(impl->fmtconvert, + SPA_DIRECTION_REVERSE(impl->direction), 0, + buffers, + n_buffers); + + } + + for (i = 0; i < n_buffers; i++) { + struct buffer *b = &impl->buffers[i]; + + b->this.buffer = buffers[i]; if (impl->direction == SPA_DIRECTION_OUTPUT) push_queue(impl, &impl->dequeued, b); @@ -503,7 +715,6 @@ static int impl_port_use_buffers(struct spa_node *node, enum spa_direction direc spa_hook_list_call(&stream->listener_list, struct pw_stream_events, add_buffer, &b->this); } - impl->n_buffers = n_buffers; if (n_buffers > 0) stream_set_state(stream, PW_STREAM_STATE_PAUSED, NULL); @@ -534,7 +745,7 @@ static int impl_node_process_input(struct spa_node *node) if (io->status != SPA_STATUS_HAVE_BUFFER) goto done; - if ((b = find_buffer(stream, io->buffer_id)) == NULL) + if ((b = get_buffer(stream, io->buffer_id)) == NULL) goto done; push_queue(impl, &impl->dequeued, b); @@ -554,15 +765,21 @@ static int impl_node_process_output(struct spa_node *node) struct pw_stream *stream = &impl->this; struct spa_io_buffers *io = impl->io; struct buffer *b; + int res = 0; pw_log_trace("stream %p: process out %d %d", stream, io->status, io->buffer_id); - if ((b = find_buffer(stream, io->buffer_id)) != NULL) + if ((b = get_buffer(stream, io->buffer_id)) != NULL) push_queue(impl, &impl->dequeued, b); if ((b = pop_queue(impl, &impl->queued)) != NULL) { io->buffer_id = b->id; io->status = SPA_STATUS_HAVE_BUFFER; + + if (impl->use_converter) + res = spa_node_process(impl->fmtconvert); + + pw_log_trace("stream %p: pop %d %s", stream, b->id, spa_strerror(res)); } else { io->buffer_id = SPA_ID_INVALID; io->status = SPA_STATUS_NEED_BUFFER; @@ -608,6 +825,9 @@ struct pw_stream * pw_stream_new(struct pw_remote *remote, const char *name, if (props == NULL) goto no_mem; + impl->fmtconvert = pw_load_spa_interface("audioconvert/libspa-audioconvert", + "fmtconvert", SPA_TYPE__Node, NULL, 0); + this->properties = props; this->remote = remote; @@ -748,10 +968,12 @@ const char *pw_stream_state_as_string(enum pw_stream_state state) static void set_init_params(struct pw_stream *stream, - int n_init_params, - const struct spa_pod **init_params) + int n_init_params, + const struct spa_pod **init_params) { struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); + struct pw_type *t = impl->t; + bool add_audio = false; int i; if (impl->init_params) { @@ -760,12 +982,38 @@ set_init_params(struct pw_stream *stream, free(impl->init_params); impl->init_params = NULL; } - impl->n_init_params = n_init_params; if (n_init_params > 0) { impl->init_params = malloc(n_init_params * sizeof(struct spa_pod *)); - for (i = 0; i < n_init_params; i++) + for (i = 0; i < n_init_params; i++) { impl->init_params[i] = pw_spa_pod_copy(init_params[i]); + + if (spa_pod_is_object_type(impl->init_params[i], t->spa_format)) + add_audio = true; + } } + impl->n_orig_params = n_init_params; + + if (add_audio) { + uint32_t state = 0; + int res; + + while (true) { + uint8_t buffer[4096]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, 4096); + struct spa_pod *param; + + if ((res = spa_node_port_enum_params(impl->fmtconvert, + impl->direction, 0, + t->param.idEnumFormat, &state, + NULL, ¶m, &b)) <= 0) + break; + + impl->init_params = realloc(impl->init_params, + (n_init_params + 1) * sizeof(struct spa_pod *)); + impl->init_params[n_init_params++] = pw_spa_pod_copy(param); + } + } + impl->n_init_params = n_init_params; } static void set_params(struct pw_stream *stream, int n_params, struct spa_pod **params) @@ -968,7 +1216,7 @@ int pw_stream_queue_buffer(struct pw_stream *stream, struct pw_buffer *buffer) struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); struct buffer *b; - if ((b = find_buffer(stream, buffer->buffer->id)) == NULL) + if ((b = get_buffer(stream, buffer->buffer->id)) == NULL) return -EINVAL; pw_log_trace("stream %p: queue buffer %d", stream, b->id); diff --git a/src/pipewire/stream.h b/src/pipewire/stream.h index 4dab15005..6a16638e0 100644 --- a/src/pipewire/stream.h +++ b/src/pipewire/stream.h @@ -208,6 +208,7 @@ enum pw_stream_flags { PW_STREAM_FLAG_MAP_BUFFERS = (1 << 3), /**< mmap the buffers */ PW_STREAM_FLAG_DRIVER = (1 << 4), /**< be a driver */ PW_STREAM_FLAG_RT_PROCESS = (1 << 5), /**< call process from the realtime thread */ + PW_STREAM_FLAG_NO_CONVERT = (1 << 6), /**< don't convert format */ }; /** A time structure \memberof pw_stream */