diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index a3ff89688..917057115 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include +#include #define NAME "audioconvert" @@ -40,44 +42,6 @@ #define PROP_DEFAULT_TRUNCATE false #define PROP_DEFAULT_DITHER 0 -struct impl; - -struct props { - bool truncate; - uint32_t dither; -}; - -static void props_reset(struct props *props) -{ - props->truncate = PROP_DEFAULT_TRUNCATE; - props->dither = PROP_DEFAULT_DITHER; -} - -struct buffer { - struct spa_list link; -#define BUFFER_FLAG_OUT (1 << 0) - uint32_t flags; - struct spa_buffer *outbuf; - struct spa_meta_header *h; -}; - -struct port { - uint32_t id; - - struct spa_io_buffers *io; - struct spa_port_info info; - - bool have_format; - struct spa_audio_info format; - int stride; - int blocks; - - struct buffer buffers[MAX_BUFFERS]; - uint32_t n_buffers; - - struct spa_list queue; -}; - struct type { uint32_t node; uint32_t format; @@ -116,10 +80,49 @@ static inline void init_type(struct type *type, struct spa_type_map *map) spa_type_param_meta_map(map, &type->param_meta); spa_type_param_io_map(map, &type->param_io); } +struct props { + bool truncate; + uint32_t dither; +}; +static void props_reset(struct props *props) +{ + props->truncate = PROP_DEFAULT_TRUNCATE; + props->dither = PROP_DEFAULT_DITHER; +} -#include "fmt-ops.c" -#include "channelmix-ops.c" +struct buffer { + struct spa_list link; +#define BUFFER_FLAG_OUT (1 << 0) + uint32_t flags; + struct spa_buffer *outbuf; + struct spa_meta_header *h; +}; + +struct port { + uint32_t id; + + struct spa_io_buffers *io; + + bool have_format; + struct spa_audio_info format; + + struct spa_node *node; +}; + +struct link { + struct spa_node *out_node; + uint32_t out_port; + const struct spa_port_info *out_info; + struct spa_node *in_node; + uint32_t in_port; + const struct spa_port_info *in_info; + struct spa_io_buffers io; + struct spa_audio_info *info; + bool negotiated; + uint32_t n_buffers; + struct spa_buffer **buffers; +}; struct impl { struct spa_handle handle; @@ -137,16 +140,20 @@ struct impl { struct port in_port; struct port out_port; + int n_links; + struct link links[4]; + bool started; - const struct buffer *src; + struct spa_handle *hnd_fmt_in; + struct spa_handle *hnd_channelmix; + struct spa_handle *hnd_resample; + struct spa_handle *hnd_fmt_out; - int n_conv; - const struct conv_info *conv[5]; - - convert_func_t convert; - - uint8_t temp[8192]; + struct spa_node *fmt_in; + struct spa_node *channelmix; + struct spa_node *resample; + struct spa_node *fmt_out; }; #define CHECK_PORT(this,d,id) (id == 0) @@ -154,96 +161,304 @@ 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 void convert_generic (void *data, int n_dst, void *dst[n_dst], - int n_src, const void *src[n_src], int n_bytes) +static int make_link(struct impl *this, + struct spa_node *out_node, uint32_t out_port, + struct spa_node *in_node, uint32_t in_port, + struct spa_audio_info *info) { -#if 0 - struct port *inport, *outport; + struct link *l = &this->links[this->n_links++]; + struct type *t = &this->type; - inport = GET_PORT(this, SPA_DIRECTION_INPUT, 0); - outport = GET_PORT(this, SPA_DIRECTION_OUTPUT, 0); + l->out_node = out_node; + l->out_port = out_port; + l->in_node = in_node; + l->in_port = in_port; + l->negotiated = false; + l->io.status = SPA_STATUS_NEED_BUFFER; + l->io.buffer_id = SPA_ID_INVALID; + l->n_buffers = 0; + l->info = info; - if (inport->format.info.raw.layout == SPA_AUDIO_LAYOUT_INTERLEAVED) { + if (out_node != NULL) { + spa_node_port_get_info(out_node, + SPA_DIRECTION_OUTPUT, out_port, + &l->out_info); + spa_node_port_set_io(out_node, + SPA_DIRECTION_OUTPUT, out_port, + t->io.Buffers, + &l->io, sizeof(l->io)); } - else { + if (in_node != NULL) { + spa_node_port_get_info(in_node, + SPA_DIRECTION_INPUT, in_port, + &l->in_info); + spa_node_port_set_io(in_node, + SPA_DIRECTION_INPUT, in_port, + t->io.Buffers, + &l->io, sizeof(l->io)); } -#endif + return 0; +} + +static int negotiate_link_format(struct impl *this, struct link *link) +{ + struct type *t = &this->type; + struct spa_pod_builder b = { 0 }; + uint8_t buffer[4096]; + uint32_t state; + struct spa_pod *format, *filter; + int res; + + if (link->negotiated) + return 0; + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + if (link->info) { + filter = spa_pod_builder_object(&b, + 0, t->format, + "I", link->info->media_type, + "I", link->info->media_subtype, + ":", t->format_audio.format, "I", link->info->info.raw.format, + ":", t->format_audio.layout, "i", link->info->info.raw.layout, + ":", t->format_audio.rate, "i", link->info->info.raw.rate, + ":", t->format_audio.channels, "i", link->info->info.raw.channels); + } + else + filter = NULL; + + if (filter) + spa_debug_pod(filter, SPA_DEBUG_FLAG_FORMAT); + + if (link->out_node != NULL) { + state = 0; + if ((res = spa_node_port_enum_params(link->out_node, + SPA_DIRECTION_OUTPUT, link->out_port, + t->param.idEnumFormat, &state, + filter, &format, &b)) <= 0) + return -ENOTSUP; + + filter = format; + } + if (filter) + spa_debug_pod(filter, SPA_DEBUG_FLAG_FORMAT); + if (link->in_node != NULL) { + state = 0; + if ((res = spa_node_port_enum_params(link->in_node, + SPA_DIRECTION_INPUT, link->in_port, + t->param.idEnumFormat, &state, + filter, &format, &b)) <= 0) + return -ENOTSUP; + + filter = format; + } + + spa_pod_fixate(filter); + spa_debug_pod(filter, SPA_DEBUG_FLAG_FORMAT); + + if (link->out_node != NULL) { + if ((res = spa_node_port_set_param(link->out_node, + SPA_DIRECTION_OUTPUT, link->out_port, + t->param.idFormat, 0, + filter)) < 0) + return res; + } + if (link->in_node != NULL) { + if ((res = spa_node_port_set_param(link->in_node, + SPA_DIRECTION_INPUT, link->in_port, + t->param.idFormat, 0, + filter)) < 0) + return res; + } + link->negotiated = true; + + return 0; } static int setup_convert(struct impl *this) { struct port *inport, *outport; - const struct conv_info *conv; - uint32_t src_fmt, dst_fmt; - struct type *t = &this->type; + struct spa_node *prev = NULL; + int i, res; inport = GET_PORT(this, SPA_DIRECTION_INPUT, 0); outport = GET_PORT(this, SPA_DIRECTION_OUTPUT, 0); - 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.format, inport->format.info.raw.channels, inport->format.info.raw.rate, inport->format.info.raw.layout, - dst_fmt, + outport->format.info.raw.format, outport->format.info.raw.channels, outport->format.info.raw.rate, outport->format.info.raw.layout); - /* find fast path */ - if (inport->format.info.raw.channels == outport->format.info.raw.channels && - inport->format.info.raw.rate == outport->format.info.raw.rate) { - this->conv[0] = find_conv_info(&t->audio_format, 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; - } - } - - this->n_conv = 0; - /* unpack */ - if ((conv = find_conv_info(&t->audio_format, src_fmt, t->audio_format.F32)) == NULL) - return -ENOTSUP; - - this->conv[this->n_conv++] = conv; + make_link(this, NULL, 0, this->fmt_in, 0, &inport->format); + prev = this->fmt_in; /* down mix */ if (inport->format.info.raw.channels > outport->format.info.raw.channels) { - this->conv[this->n_conv++] = conv; + make_link(this, prev, 0, this->channelmix, 0, NULL); + prev = this->channelmix; } /* resample */ if (inport->format.info.raw.rate != outport->format.info.raw.rate) { - this->conv[this->n_conv++] = conv; + make_link(this, prev, 0, this->resample, 0, NULL); + prev = this->resample; } /* up mix */ if (inport->format.info.raw.channels < outport->format.info.raw.channels) { - this->conv[this->n_conv++] = conv; + make_link(this, prev, 0, this->channelmix, 0, NULL); + prev = this->channelmix; } + make_link(this, prev, 0, this->fmt_out, 0, NULL); /* pack */ - if ((conv = find_conv_info(&t->audio_format, t->audio_format.F32, dst_fmt)) == NULL) - return -ENOTSUP; + make_link(this, this->fmt_out, 0, NULL, 0, &outport->format); - this->conv[this->n_conv++] = conv; - this->convert = convert_generic; + if ((res = negotiate_link_format(this, &this->links[0])) < 0) + return res; + if ((res = negotiate_link_format(this, &this->links[this->n_links-1])) < 0) + return res; + + for (i = 1; i < this->n_links-1; i++) + if ((res = negotiate_link_format(this, &this->links[i])) < 0) + return res; + + return 0; +} + +static int negotiate_link_buffers(struct impl *this, struct link *link) +{ + struct type *t = &this->type; + uint8_t buffer[4096]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + uint32_t state; + struct spa_pod *param = NULL; + int res, i; + bool in_alloc, out_alloc; + int32_t size, buffers, blocks, align, flags; + uint32_t *aligns; + struct spa_data *datas; + + if (link->n_buffers > 0) + return 0; + + if (link->out_node != NULL) { + state = 0; + if ((res = spa_node_port_enum_params(link->out_node, + SPA_DIRECTION_OUTPUT, link->out_port, + t->param.idBuffers, &state, + param, ¶m, &b)) <= 0) + return -ENOTSUP; + } + if (link->in_node != NULL) { + state = 0; + if ((res = spa_node_port_enum_params(link->in_node, + SPA_DIRECTION_INPUT, link->in_port, + t->param.idBuffers, &state, + param, ¶m, &b)) <= 0) + return -ENOTSUP; + } + + spa_pod_fixate(param); + + if (link->in_info) + in_alloc = SPA_FLAG_CHECK(link->in_info->flags, SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS); + else + in_alloc = false; + + if (link->out_info) + out_alloc = SPA_FLAG_CHECK(link->out_info->flags, SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS); + else + out_alloc = false; + + flags = 0; + if (out_alloc || in_alloc) { + flags |= SPA_BUFFER_ALLOC_FLAG_NO_DATA; + if (out_alloc) + in_alloc = false; + } + + if (spa_pod_object_parse(param, + ":", t->param_buffers.buffers, "i", &buffers, + ":", t->param_buffers.blocks, "i", &blocks, + ":", t->param_buffers.size, "i", &size, + ":", t->param_buffers.align, "i", &align, + NULL) < 0) + return -EINVAL; + + datas = alloca(sizeof(struct spa_data) * blocks); + memset(datas, 0, sizeof(struct spa_data) * blocks); + aligns = alloca(sizeof(uint32_t) * blocks); + for (i = 0; i < blocks; i++) { + datas[i].type = t->data.MemPtr; + datas[i].maxsize = size; + aligns[i] = align; + } + + link->buffers = spa_buffer_alloc_array(buffers, flags, 0, NULL, blocks, datas, aligns); + if (link->buffers == NULL) + return -ENOMEM; + + link->n_buffers = buffers; + + if (link->out_node != NULL) { + if (out_alloc) { + if ((res = spa_node_port_alloc_buffers(link->out_node, + SPA_DIRECTION_OUTPUT, link->out_port, + NULL, 0, + link->buffers, &link->n_buffers)) < 0) + return res; + } + else { + if ((res = spa_node_port_use_buffers(link->out_node, + SPA_DIRECTION_OUTPUT, link->out_port, + link->buffers, link->n_buffers)) < 0) + return res; + } + } + if (link->in_node != NULL) { + if (in_alloc) { + if ((res = spa_node_port_alloc_buffers(link->in_node, + SPA_DIRECTION_INPUT, link->in_port, + NULL, 0, + link->buffers, &link->n_buffers)) < 0) + return res; + } + else { + if ((res = spa_node_port_use_buffers(link->in_node, + SPA_DIRECTION_INPUT, link->in_port, + link->buffers, link->n_buffers)) < 0) + return res; + } + } + return 0; +} + +static int setup_buffers(struct impl *this, enum spa_direction direction) +{ + int i, res; + + spa_log_info(this->log, NAME " %p: %d", this, direction); + + if (direction == SPA_DIRECTION_INPUT) { + for (i = 1; i < this->n_links-1; i++) { + if ((res = negotiate_link_buffers(this, &this->links[i])) < 0) + spa_log_error(this->log, NAME " %p: buffers %d failed %s", + this, i, spa_strerror(res)); + } + } else { + for (i = this->n_links-2; i > 0 ; i--) { + if ((res = negotiate_link_buffers(this, &this->links[i])) < 0) + spa_log_error(this->log, NAME " %p: buffers %d failed %s", + this, i, spa_strerror(res)); + } + } return 0; } @@ -365,77 +580,8 @@ impl_node_port_get_info(struct spa_node *node, spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); port = GET_PORT(this, direction, port_id); - *info = &port->info; - return 0; -} - -static int port_enum_formats(struct spa_node *node, - enum spa_direction direction, uint32_t port_id, - uint32_t *index, - struct spa_pod **param, - struct spa_pod_builder *builder) -{ - struct impl *this = SPA_CONTAINER_OF(node, struct impl, node); - struct type *t = &this->type; - - switch (*index) { - case 0: - *param = spa_pod_builder_object(builder, - t->param.idEnumFormat, t->format, - "I", t->media_type.audio, - "I", t->media_subtype.raw, - ":", t->format_audio.format, "Ieu", t->audio_format.S16, - SPA_POD_PROP_ENUM(11, t->audio_format.U8, - t->audio_format.S16, - t->audio_format.S16_OE, - t->audio_format.F32, - t->audio_format.F32_OE, - t->audio_format.S32, - t->audio_format.S32_OE, - t->audio_format.S24, - t->audio_format.S24_OE, - t->audio_format.S24_32, - t->audio_format.S24_32_OE), - ":", t->format_audio.layout, "ieu", SPA_AUDIO_LAYOUT_INTERLEAVED, - SPA_POD_PROP_ENUM(2, SPA_AUDIO_LAYOUT_INTERLEAVED, - SPA_AUDIO_LAYOUT_NON_INTERLEAVED), - ":", t->format_audio.rate, "iru", 44100, - SPA_POD_PROP_MIN_MAX(1, INT32_MAX), - ":", t->format_audio.channels, "iru", 2, - SPA_POD_PROP_MIN_MAX(1, INT32_MAX)); - break; - default: - return 0; - } - return 1; -} - -static int port_get_format(struct spa_node *node, - enum spa_direction direction, uint32_t port_id, - uint32_t *index, - struct spa_pod **param, - struct spa_pod_builder *builder) -{ - struct impl *this = SPA_CONTAINER_OF(node, struct impl, node); - struct type *t = &this->type; - struct port *port = GET_PORT(this, direction, port_id); - - if (!port->have_format) - return -EIO; - if (*index > 0) - return 0; - - *param = spa_pod_builder_object(builder, - t->param.idFormat, t->format, - "I", t->media_type.audio, - "I", t->media_subtype.raw, - ":", t->format_audio.format, "I", port->format.info.raw.format, - ":", t->format_audio.layout, "i", port->format.info.raw.layout, - ":", t->format_audio.rate, "i", port->format.info.raw.rate, - ":", t->format_audio.channels, "i", port->format.info.raw.channels); - - return 1; + return spa_node_port_get_info(port->node, direction, port_id, info); } static int @@ -447,124 +593,18 @@ impl_node_port_enum_params(struct spa_node *node, struct spa_pod_builder *builder) { struct impl *this; - struct type *t; struct port *port; - struct spa_pod *param; - struct spa_pod_builder b = { 0 }; - uint8_t buffer[1024]; - int res; spa_return_val_if_fail(node != NULL, -EINVAL); - spa_return_val_if_fail(index != NULL, -EINVAL); - spa_return_val_if_fail(builder != NULL, -EINVAL); this = SPA_CONTAINER_OF(node, struct impl, node); - t = &this->type; spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); port = GET_PORT(this, direction, port_id); - next: - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - if (id == t->param.idList) { - uint32_t list[] = { t->param.idEnumFormat, - t->param.idFormat, - t->param.idBuffers, - t->param.idMeta, - t->param_io.idBuffers }; - - if (*index < SPA_N_ELEMENTS(list)) - param = spa_pod_builder_object(&b, id, t->param.List, - ":", t->param.listId, "I", list[*index]); - else - return 0; - } - else if (id == t->param.idEnumFormat) { - if ((res = port_enum_formats(node, direction, port_id, index, ¶m, &b)) <= 0) - return res; - } - else if (id == t->param.idFormat) { - if ((res = port_get_format(node, direction, port_id, index, ¶m, &b)) <= 0) - return res; - } - else if (id == t->param.idBuffers) { - if (!port->have_format) - return -EIO; - if (*index > 0) - return 0; - - param = spa_pod_builder_object(&b, - id, t->param_buffers.Buffers, - ":", 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) { - if (!port->have_format) - return -EIO; - - switch (*index) { - case 0: - param = spa_pod_builder_object(&b, - id, t->param_meta.Meta, - ":", t->param_meta.type, "I", t->meta.Header, - ":", t->param_meta.size, "i", sizeof(struct spa_meta_header)); - break; - default: - return 0; - } - } - else if (id == t->param_io.idBuffers) { - switch (*index) { - case 0: - param = spa_pod_builder_object(&b, - id, t->param_io.Buffers, - ":", t->param_io.id, "I", t->io.Buffers, - ":", t->param_io.size, "i", sizeof(struct spa_io_buffers)); - break; - default: - return 0; - } - } - else - return -ENOENT; - - (*index)++; - - if (spa_pod_filter(builder, result, param, filter) < 0) - goto next; - - 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) { - spa_log_info(this->log, NAME " %p: clear buffers %p", this, port); - port->n_buffers = 0; - spa_list_init(&port->queue); - } - return 0; + return spa_node_port_enum_params(port->node, direction, port_id, id, index, + filter, result, builder); } static int port_set_format(struct spa_node *node, @@ -582,11 +622,7 @@ static int port_set_format(struct spa_node *node, other = GET_PORT(this, SPA_DIRECTION_REVERSE(direction), port_id); if (format == NULL) { - if (port->have_format) { - port->have_format = false; - clear_buffers(this, port); - } - this->convert = NULL; + port->have_format = false; } else { struct spa_audio_info info = { 0 }; @@ -604,16 +640,6 @@ 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); @@ -627,34 +653,9 @@ impl_node_port_set_param(struct spa_node *node, enum spa_direction direction, uint32_t port_id, uint32_t id, uint32_t flags, const struct spa_pod *param) -{ - struct impl *this; - struct type *t; - - spa_return_val_if_fail(node != NULL, -EINVAL); - - this = SPA_CONTAINER_OF(node, struct impl, node); - t = &this->type; - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - if (id == t->param.idFormat) { - return port_set_format(node, direction, port_id, flags, param); - } - else - return -ENOENT; -} - -static int -impl_node_port_use_buffers(struct spa_node *node, - enum spa_direction direction, - uint32_t port_id, - struct spa_buffer **buffers, - uint32_t n_buffers) { struct impl *this; struct port *port; - uint32_t i; struct type *t; spa_return_val_if_fail(node != NULL, -EINVAL); @@ -666,36 +667,36 @@ impl_node_port_use_buffers(struct spa_node *node, port = GET_PORT(this, direction, port_id); - spa_return_val_if_fail(port->have_format, -EIO); + if (id == t->param.idFormat) + return port_set_format(node, direction, port_id, flags, param); + else + return spa_node_port_set_param(port->node, direction, port_id, id, flags, param); +} - spa_log_info(this->log, NAME " %p: use buffers %d on port %d", this, n_buffers, port_id); +static int +impl_node_port_use_buffers(struct spa_node *node, + enum spa_direction direction, + uint32_t port_id, + struct spa_buffer **buffers, + uint32_t n_buffers) +{ + struct impl *this; + struct port *port; + int res; - clear_buffers(this, port); + spa_return_val_if_fail(node != NULL, -EINVAL); - for (i = 0; i < n_buffers; i++) { - struct buffer *b; - struct spa_data *d = buffers[i]->datas; + this = SPA_CONTAINER_OF(node, struct impl, node); - b = &port->buffers[i]; - b->flags = 0; - b->outbuf = buffers[i]; - b->h = spa_buffer_find_meta(buffers[i], t->meta.Header); + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - if (!((d[0].type == t->data.MemPtr || - d[0].type == t->data.MemFd || - d[0].type == t->data.DmaBuf) && d[0].data != NULL)) { - spa_log_error(this->log, NAME " %p: invalid memory on buffer %p", this, - buffers[i]); - return -EINVAL; - } - if (direction == SPA_DIRECTION_OUTPUT) - spa_list_append(&port->queue, &b->link); - else - SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); - } - port->n_buffers = n_buffers; + port = GET_PORT(this, direction, port_id); - return 0; + res = spa_node_port_use_buffers(port->node, direction, port_id, buffers, n_buffers); + if (res < 0) + return res; + + return setup_buffers(this, direction); } static int @@ -707,7 +708,19 @@ impl_node_port_alloc_buffers(struct spa_node *node, struct spa_buffer **buffers, uint32_t *n_buffers) { - return -ENOTSUP; + struct impl *this; + struct port *port; + + spa_return_val_if_fail(node != NULL, -EINVAL); + + this = SPA_CONTAINER_OF(node, struct impl, node); + + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + + port = GET_PORT(this, direction, port_id); + + return spa_node_port_alloc_buffers(port->node, direction, port_id, + params, n_params, buffers, n_buffers); } static int @@ -730,42 +743,14 @@ impl_node_port_set_io(struct spa_node *node, if (id == t->io.Buffers) port->io = data; - else - return -ENOENT; - return 0; + return spa_node_port_set_io(port->node, direction, port_id, id, data, size); } -static void recycle_buffer(struct impl *this, uint32_t id) -{ - struct port *port = GET_OUT_PORT(this, 0); - struct buffer *b = &port->buffers[id]; - - if (SPA_FLAG_CHECK(b->flags, BUFFER_FLAG_OUT)) { - spa_list_append(&port->queue, &b->link); - SPA_FLAG_UNSET(b->flags, BUFFER_FLAG_OUT); - spa_log_trace(this->log, NAME " %p: recycle buffer %d", this, id); - } -} - -static struct buffer *dequeue_buffer(struct impl *this, struct port *port) -{ - struct buffer *b; - - if (spa_list_is_empty(&port->queue)) - return NULL; - - b = spa_list_first(&port->queue, struct buffer, link); - spa_list_remove(&b->link); - SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); - - return b; -} - - static int impl_node_port_reuse_buffer(struct spa_node *node, uint32_t port_id, uint32_t buffer_id) { struct impl *this; + struct port *port; spa_return_val_if_fail(node != NULL, -EINVAL); @@ -773,9 +758,9 @@ static int impl_node_port_reuse_buffer(struct spa_node *node, uint32_t port_id, spa_return_val_if_fail(CHECK_PORT(this, SPA_DIRECTION_OUTPUT, port_id), -EINVAL); - recycle_buffer(this, buffer_id); + port = GET_PORT(this, SPA_DIRECTION_OUTPUT, port_id); - return 0; + return spa_node_port_reuse_buffer(port->node, port_id, buffer_id); } static int @@ -783,75 +768,36 @@ impl_node_port_send_command(struct spa_node *node, enum spa_direction direction, uint32_t port_id, const struct spa_command *command) -{ - return -ENOTSUP; -} - -static int impl_node_process(struct spa_node *node) { struct impl *this; - struct port *outport, *inport; - struct spa_io_buffers *outio, *inio; - struct buffer *sbuf, *dbuf; + struct port *port; spa_return_val_if_fail(node != NULL, -EINVAL); this = SPA_CONTAINER_OF(node, struct impl, node); - outport = GET_OUT_PORT(this, 0); - inport = GET_IN_PORT(this, 0); + spa_return_val_if_fail(CHECK_PORT(this, SPA_DIRECTION_OUTPUT, port_id), -EINVAL); - outio = outport->io; - inio = inport->io; + port = GET_PORT(this, direction, port_id); - spa_return_val_if_fail(outio != NULL, -EIO); - spa_return_val_if_fail(inio != NULL, -EIO); + return spa_node_port_send_command(port->node, direction, port_id, command); +} - spa_log_trace(this->log, NAME " %p: status %d", this, outio->status); +static int impl_node_process(struct spa_node *node) +{ + struct impl *this; + int i; - if (outio->status == SPA_STATUS_HAVE_BUFFER) - return outio->status; + spa_return_val_if_fail(node != NULL, -EINVAL); - if (inio->status != SPA_STATUS_HAVE_BUFFER) - return SPA_STATUS_NEED_BUFFER; + this = SPA_CONTAINER_OF(node, struct impl, node); - /* recycle */ - if (outio->buffer_id < outport->n_buffers) { - recycle_buffer(this, outio->buffer_id); - outio->buffer_id = SPA_ID_INVALID; - } + spa_log_trace(this->log, NAME " %p: process %d", this, this->n_links); - if (inio->buffer_id >= inport->n_buffers) - return inio->status = -EINVAL; + for (i = 1; i < this->n_links; i++) + spa_node_process(this->links[i].out_node); - if ((dbuf = dequeue_buffer(this, outport)) == NULL) - return outio->status = -EPIPE; - - sbuf = &inport->buffers[inio->buffer_id]; - - { - 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; - - return outio->status; + return SPA_STATUS_OK; } static const struct spa_node impl_node = { @@ -898,11 +844,22 @@ static int impl_clear(struct spa_handle *handle) return 0; } +extern const struct spa_handle_factory spa_fmtconvert_factory; +extern const struct spa_handle_factory spa_channelmix_factory; +extern const struct spa_handle_factory spa_resample_factory; + static size_t impl_get_size(const struct spa_handle_factory *factory, const struct spa_dict *params) { - return sizeof(struct impl); + size_t size; + + size = sizeof(struct impl); + size += spa_handle_factory_get_size(&spa_fmtconvert_factory, params) * 2; + size += spa_handle_factory_get_size(&spa_channelmix_factory, params); + size += spa_handle_factory_get_size(&spa_resample_factory, params); + + return size; } static int @@ -915,6 +872,8 @@ impl_init(const struct spa_handle_factory *factory, struct impl *this; struct port *port; uint32_t i; + size_t size; + void *iface; spa_return_val_if_fail(factory != NULL, -EINVAL); spa_return_val_if_fail(handle != NULL, -EINVAL); @@ -938,15 +897,46 @@ impl_init(const struct spa_handle_factory *factory, this->node = impl_node; + this->hnd_fmt_in = SPA_MEMBER(this, sizeof(struct impl), struct spa_handle); + spa_handle_factory_init(&spa_fmtconvert_factory, + this->hnd_fmt_in, + info, support, n_support); + size = spa_handle_factory_get_size(&spa_fmtconvert_factory, info); + + this->hnd_channelmix = SPA_MEMBER(this->hnd_fmt_in, size, struct spa_handle); + spa_handle_factory_init(&spa_channelmix_factory, + this->hnd_channelmix, + info, support, n_support); + size = spa_handle_factory_get_size(&spa_channelmix_factory, info); + + this->hnd_fmt_out = SPA_MEMBER(this->hnd_channelmix, size, struct spa_handle); + spa_handle_factory_init(&spa_fmtconvert_factory, + this->hnd_fmt_out, + info, support, n_support); + size = spa_handle_factory_get_size(&spa_fmtconvert_factory, info); + + this->hnd_resample = SPA_MEMBER(this->hnd_fmt_out, size, struct spa_handle); + spa_handle_factory_init(&spa_resample_factory, + this->hnd_resample, + info, support, n_support); + size = spa_handle_factory_get_size(&spa_resample_factory, info); + + spa_handle_get_interface(this->hnd_fmt_in, this->type.node, &iface); + this->fmt_in = iface; + spa_handle_get_interface(this->hnd_fmt_out, this->type.node, &iface); + this->fmt_out = iface; + spa_handle_get_interface(this->hnd_channelmix, this->type.node, &iface); + this->channelmix = iface; + spa_handle_get_interface(this->hnd_resample, this->type.node, &iface); + this->resample = iface; + port = GET_OUT_PORT(this, 0); port->id = 0; - port->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS; - spa_list_init(&port->queue); + port->node = this->fmt_out; port = GET_IN_PORT(this, 0); port->id = 0; - port->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS; - spa_list_init(&port->queue); + port->node = this->fmt_in; props_reset(&this->props); diff --git a/spa/plugins/audioconvert/channelmix-ops.c b/spa/plugins/audioconvert/channelmix-ops.c index 49150c72c..5afc5152c 100644 --- a/spa/plugins/audioconvert/channelmix-ops.c +++ b/spa/plugins/audioconvert/channelmix-ops.c @@ -59,12 +59,12 @@ channelmix_f32_1_2(void *data, int n_dst, void *dst[n_dst], { int n, n_samples; float **d = (float **) dst; - float **s = (float **) src; + const float *s = src[0]; n_samples = n_bytes / sizeof(float); for (n = 0; n < n_samples; n++) { - d[0][n] = s[0][n]; - d[1][n] = s[0][n]; + d[0][n] = s[n]; + d[1][n] = s[n]; } } @@ -73,12 +73,12 @@ channelmix_f32_2_1(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], void *matrix, int n_bytes) { int n, n_samples; - float **d = (float **) dst; + float *d = dst[0]; float **s = (float **) src; n_samples = n_bytes / sizeof(float); for (n = 0; n < n_samples; n++) - d[0][n] = (s[0][n] + s[1][n]) * 0.5f; + d[n] = (s[0][n] + s[1][n]) * 0.5f; } typedef void (*channelmix_func_t) (void *data, int n_dst, void *dst[n_dst], @@ -95,6 +95,7 @@ static const struct channelmix_info { uint32_t flags; } channelmix_table[] = { + { -2, -2, channelmix_copy, CHANNELMIX_INFO_FLAG_NO_MATRIX }, { 1, 2, channelmix_f32_1_2, CHANNELMIX_INFO_FLAG_NO_MATRIX }, { 2, 1, channelmix_f32_2_1, CHANNELMIX_INFO_FLAG_NO_MATRIX }, { -1, -1, channelmix_f32_n_m, 0 }, @@ -106,7 +107,10 @@ static const struct channelmix_info *find_channelmix_info(uint32_t src_chan, uin { int i; - for (i = 0; i < SPA_N_ELEMENTS(channelmix_table); i++) { + if (src_chan == dst_chan) + return &channelmix_table[0]; + + for (i = 1; i < SPA_N_ELEMENTS(channelmix_table); i++) { if (MATCH_CHAN(channelmix_table[i].src_chan, src_chan) && MATCH_CHAN(channelmix_table[i].dst_chan, dst_chan)) return &channelmix_table[i]; diff --git a/spa/plugins/audioconvert/channelmix.c b/spa/plugins/audioconvert/channelmix.c index c6f79db01..ff9d61b57 100644 --- a/spa/plugins/audioconvert/channelmix.c +++ b/spa/plugins/audioconvert/channelmix.c @@ -133,8 +133,6 @@ struct impl { bool started; - const struct buffer *src; - channelmix_func_t convert; float matrix[4096]; }; @@ -776,7 +774,7 @@ static int impl_node_process(struct spa_node *node) spa_return_val_if_fail(outio != NULL, -EIO); spa_return_val_if_fail(inio != NULL, -EIO); - spa_log_trace(this->log, NAME " %p: status %d", this, outio->status); + spa_log_trace(this->log, NAME " %p: status %d %d", this, inio->status, outio->status); if (outio->status == SPA_STATUS_HAVE_BUFFER) return outio->status; @@ -824,6 +822,8 @@ static int impl_node_process(struct spa_node *node) outio->status = SPA_STATUS_HAVE_BUFFER; outio->buffer_id = dbuf->outbuf->id; + inio->status = SPA_STATUS_NEED_BUFFER; + return outio->status; } diff --git a/spa/plugins/audioconvert/fmt-ops.c b/spa/plugins/audioconvert/fmt-ops.c index dac3946ed..ea3fa5269 100644 --- a/spa/plugins/audioconvert/fmt-ops.c +++ b/spa/plugins/audioconvert/fmt-ops.c @@ -127,12 +127,12 @@ conv_s16d_to_f32(void *data, int n_dst, void *dst[n_dst], int n_src, const void { const int16_t **s = (const int16_t **) src; float *d = dst[0]; - int i, j; + int i, n, n_samples; - n_bytes /= (sizeof(int16_t) * n_src); - for (j = 0; j < n_bytes; j++) { + n_samples = n_bytes / sizeof(int16_t); + for (n = 0; n < n_samples; n++) { for (i = 0; i < n_src; i++) - *d++ = S16_TO_F32(s[i][j]); + *d++ = S16_TO_F32(s[i][n]); } } @@ -172,12 +172,12 @@ conv_s32d_to_f32(void *data, int n_dst, void *dst[n_dst], int n_src, const void { const int32_t **s = (const int32_t **) src; float *d = dst[0]; - int i, j; + int i, n, n_samples; - n_bytes /= (sizeof(int32_t) * n_src); - for (j = 0; j < n_bytes; j++) { + n_samples = n_bytes / sizeof(int32_t); + for (n = 0; n < n_samples; n++) { for (i = 0; i < n_src; i++) - *d++ = S32_TO_F32(s[i][j]); + *d++ = S32_TO_F32(s[i][n]); } } @@ -223,10 +223,10 @@ conv_s24d_to_f32(void *data, int n_dst, void *dst[n_dst], int n_src, const void { const int8_t **s = (const int8_t **) src; float *d = dst[0]; - int i, j; + int i, n, n_samples; - n_bytes /= (3 * n_src); - for (j = 0; j < n_bytes; j++) { + n_samples = n_bytes / 3; + for (n = 0; n < n_samples; n++) { for (i = 0; i < n_src; i++) { *d++ = S24_TO_F32(READ24(s[i])); s += 3; @@ -268,12 +268,12 @@ conv_s24_32d_to_f32(void *data, int n_dst, void *dst[n_dst], int n_src, const vo { const int32_t **s = (const int32_t **) src; float *d = dst[0]; - int i, j; + int i, n, n_samples; - n_bytes /= (sizeof(int32_t) * n_src); - for (j = 0; j < n_bytes; j++) { + n_samples = n_bytes / sizeof(int32_t); + for (n = 0; n < n_samples; n++) { for (i = 0; i < n_src; i++) - *d++ = S24_TO_F32(s[i][j]); + *d++ = S24_TO_F32(s[i][n]); } } @@ -319,12 +319,12 @@ conv_f32d_to_u8(void *data, int n_dst, void *dst[n_dst], int n_src, const void * { const int8_t **s = (const int8_t **) src; float *d = dst[0]; - int i, j; + int i, n, n_samples; - n_bytes /= (sizeof(float) * n_src); - for (j = 0; j < n_bytes; j++) { + n_samples = n_bytes / sizeof(float); + for (n = 0; n < n_samples; n++) { for (i = 0; i < n_src; i++) - *d++ = F32_TO_U8(s[i][j]); + *d++ = F32_TO_U8(s[i][n]); } } @@ -370,12 +370,12 @@ conv_f32d_to_s16(void *data, int n_dst, void *dst[n_dst], int n_src, const void { const float **s = (const float **) src; int16_t *d = dst[0]; - int i, j; + int i, n, n_samples; - n_bytes /= (sizeof(float) * n_src); - for (j = 0; j < n_bytes; j++) { + n_samples = n_bytes / sizeof(float); + for (n = 0; n < n_samples; n++) { for (i = 0; i < n_src; i++) - *d++ = F32_TO_S16(s[i][j]); + *d++ = F32_TO_S16(s[i][n]); } } @@ -421,12 +421,12 @@ conv_f32d_to_s32(void *data, int n_dst, void *dst[n_dst], int n_src, const void { const float **s = (const float **) src; int32_t *d = dst[0]; - int i, j; + int i, n, n_samples; - n_bytes /= (sizeof(float) * n_src); - for (j = 0; j < n_bytes; j++) { + n_samples = n_bytes / sizeof(float); + for (n = 0; n < n_samples; n++) { for (i = 0; i < n_src; i++) - *d++ = F32_TO_S32(s[i][j]); + *d++ = F32_TO_S32(s[i][n]); } } @@ -483,12 +483,12 @@ conv_f32d_to_s24(void *data, int n_dst, void *dst[n_dst], int n_src, const void { const float **s = (const float **) src; int8_t *d = dst[0]; - int i, j; + int i, n, n_samples; - n_bytes /= (sizeof(float) * n_src); - for (j = 0; j < n_bytes; j++) { + n_samples = n_bytes / sizeof(float); + for (n = 0; n < n_samples; n++) { for (i = 0; i < n_src; i++) { - WRITE24(d, F32_TO_S24(s[i][j])); + WRITE24(d, F32_TO_S24(s[i][n])); d += 3; } } @@ -529,12 +529,12 @@ conv_f32d_to_s24_32(void *data, int n_dst, void *dst[n_dst], int n_src, const vo { const float **s = (const float **) src; int32_t *d = dst[0]; - int i, j; + int i, n, n_samples; - n_bytes /= (sizeof(float) * n_src); - for (j = 0; j < n_bytes; j++) { + n_samples = n_bytes / sizeof(float); + for (n = 0; n < n_samples; n++) { for (i = 0; i < n_src; i++) - *d++ = F32_TO_S24(s[i][j]); + *d++ = F32_TO_S24(s[i][n]); } } diff --git a/spa/plugins/audioconvert/fmtconvert.c b/spa/plugins/audioconvert/fmtconvert.c index fb8d49c39..283aa0cbf 100644 --- a/spa/plugins/audioconvert/fmtconvert.c +++ b/spa/plugins/audioconvert/fmtconvert.c @@ -73,6 +73,8 @@ struct port { uint32_t blocks; uint32_t size; + uint32_t offset; + struct buffer buffers[MAX_BUFFERS]; uint32_t n_buffers; @@ -153,22 +155,6 @@ 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 void convert_generic (void *data, int n_dst, void *dst[n_dst], - int n_src, const void *src[n_src], int n_bytes) -{ -#if 0 - struct port *inport, *outport; - - inport = GET_PORT(this, SPA_DIRECTION_INPUT, 0); - outport = GET_PORT(this, SPA_DIRECTION_OUTPUT, 0); - - if (inport->format.info.raw.layout == SPA_AUDIO_LAYOUT_INTERLEAVED) { - } - else { - } -#endif -} - static int setup_convert(struct impl *this) { struct port *inport, *outport; @@ -181,12 +167,12 @@ static int setup_convert(struct impl *this) 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, + spa_log_info(this->log, NAME " %p: %s/%d@%d.%d->%s/%d@%d.%d", this, + spa_type_map_get_type(this->map, src_fmt), inport->format.info.raw.channels, inport->format.info.raw.rate, inport->format.info.raw.layout, - dst_fmt, + spa_type_map_get_type(this->map, dst_fmt), outport->format.info.raw.channels, outport->format.info.raw.rate, outport->format.info.raw.layout); @@ -214,16 +200,7 @@ static int setup_convert(struct impl *this) } return 0; } - - /* go through intermediate format */ - this->conv[0] = find_conv_info(&t->audio_format, src_fmt, t->audio_format.F32); - this->conv[1] = find_conv_info(&t->audio_format, t->audio_format.F32, dst_fmt); - if (this->conv[0] == NULL || this->conv[1] == NULL) - return -ENOTSUP; - - this->convert = convert_generic; - - return 0; + return -ENOTSUP; } static int impl_node_enum_params(struct spa_node *node, @@ -368,17 +345,9 @@ static int port_enum_formats(struct spa_node *node, "I", t->media_type.audio, "I", t->media_subtype.raw, ":", t->format_audio.format, "Ieu", other->format.info.raw.format, - SPA_POD_PROP_ENUM(11, t->audio_format.U8, - t->audio_format.S16, - t->audio_format.S16_OE, - t->audio_format.F32, - t->audio_format.F32_OE, - t->audio_format.S32, - t->audio_format.S32_OE, - t->audio_format.S24, - t->audio_format.S24_OE, - t->audio_format.S24_32, - t->audio_format.S24_32_OE), + SPA_POD_PROP_ENUM(3, other->format.info.raw.format, + t->audio_format.F32, + t->audio_format.F32_OE), ":", t->format_audio.layout, "ieu", other->format.info.raw.layout, SPA_POD_PROP_ENUM(2, SPA_AUDIO_LAYOUT_INTERLEAVED, SPA_AUDIO_LAYOUT_NON_INTERLEAVED), @@ -834,7 +803,7 @@ static int impl_node_process(struct spa_node *node) spa_return_val_if_fail(outio != NULL, -EIO); spa_return_val_if_fail(inio != NULL, -EIO); - spa_log_trace(this->log, NAME " %p: status %d", this, outio->status); + spa_log_trace(this->log, NAME " %p: status %d %d", this, inio->status, outio->status); if (outio->status == SPA_STATUS_HAVE_BUFFER) return outio->status; @@ -857,24 +826,33 @@ static int impl_node_process(struct spa_node *node) sbuf = &inport->buffers[inio->buffer_id]; { - int i, n_bytes; + int i, n_bytes, maxsize; struct spa_buffer *sb = sbuf->outbuf, *db = dbuf->outbuf; uint32_t n_src_datas = sb->n_datas; uint32_t n_dst_datas = db->n_datas; const void *src_datas[n_src_datas]; void *dst_datas[n_dst_datas]; + uint32_t size; - n_bytes = sb->datas[0].chunk->size; + size = sb->datas[0].chunk->size; + maxsize = (db->datas[0].maxsize / outport->stride) * inport->stride; + n_bytes = SPA_MIN(size - inport->offset, maxsize); for (i = 0; i < n_src_datas; i++) - src_datas[i] = sb->datas[i].data; + src_datas[i] = SPA_MEMBER(sb->datas[i].data, inport->offset, void); for (i = 0; i < n_dst_datas; i++) { dst_datas[i] = db->datas[i].data; - db->datas[i].chunk->size = - (n_bytes / inport->stride) * outport->stride; + db->datas[i].chunk->size = (n_bytes / inport->stride) * outport->stride; } this->convert(this, n_dst_datas, dst_datas, n_src_datas, src_datas, n_bytes); + + inport->offset += n_bytes; + if (inport->offset >= size) { + inio->status = SPA_STATUS_NEED_BUFFER; + inport->offset = 0; + } + } outio->status = SPA_STATUS_HAVE_BUFFER; diff --git a/spa/plugins/audioconvert/meson.build b/spa/plugins/audioconvert/meson.build index 5849d5f87..bb1bc5d01 100644 --- a/spa/plugins/audioconvert/meson.build +++ b/spa/plugins/audioconvert/meson.build @@ -1,4 +1,8 @@ -audioconvert_sources = ['fmtconvert.c', 'audioconvert.c', 'channelmix.c', 'plugin.c'] +audioconvert_sources = ['fmtconvert.c', + 'channelmix.c', + 'resample.c', + 'audioconvert.c', + 'plugin.c'] audioconvertlib = shared_library('spa-audioconvert', audioconvert_sources, diff --git a/spa/plugins/audioconvert/plugin.c b/spa/plugins/audioconvert/plugin.c index 9e59f33af..c11fd3d7f 100644 --- a/spa/plugins/audioconvert/plugin.c +++ b/spa/plugins/audioconvert/plugin.c @@ -24,6 +24,7 @@ extern const struct spa_handle_factory spa_audioconvert_factory; extern const struct spa_handle_factory spa_fmtconvert_factory; extern const struct spa_handle_factory spa_channelmix_factory; +extern const struct spa_handle_factory spa_resample_factory; int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index) @@ -41,6 +42,9 @@ spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *ind case 2: *factory = &spa_channelmix_factory; break; + case 3: + *factory = &spa_resample_factory; + break; default: return 0; } diff --git a/spa/plugins/audioconvert/resample.c b/spa/plugins/audioconvert/resample.c new file mode 100644 index 000000000..da0c80b0e --- /dev/null +++ b/spa/plugins/audioconvert/resample.c @@ -0,0 +1,921 @@ +/* Spa + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define NAME "resample" + +#define MAX_BUFFERS 32 + +struct impl; + +struct props { + int dummy; +}; + +static void props_reset(struct props *props) +{ +} + +struct buffer { + struct spa_list link; +#define BUFFER_FLAG_OUT (1 << 0) + uint32_t flags; + struct spa_buffer *outbuf; + struct spa_meta_header *h; +}; + +struct port { + uint32_t id; + + struct spa_io_buffers *io; + struct spa_port_info info; + + bool have_format; + struct spa_audio_info format; + uint32_t stride; + uint32_t blocks; + uint32_t size; + + struct buffer buffers[MAX_BUFFERS]; + uint32_t n_buffers; + + struct spa_list queue; +}; + +struct type { + uint32_t node; + uint32_t format; + uint32_t prop_truncate; + uint32_t prop_dither; + struct spa_type_io io; + struct spa_type_param param; + struct spa_type_media_type media_type; + struct spa_type_media_subtype media_subtype; + struct spa_type_format_audio format_audio; + struct spa_type_audio_format audio_format; + struct spa_type_command_node command_node; + struct spa_type_meta meta; + struct spa_type_data data; + struct spa_type_param_buffers param_buffers; + struct spa_type_param_meta param_meta; + struct spa_type_param_io param_io; +}; + +static inline void init_type(struct type *type, struct spa_type_map *map) +{ + type->node = spa_type_map_get_id(map, SPA_TYPE__Node); + type->format = spa_type_map_get_id(map, SPA_TYPE__Format); + type->prop_truncate = spa_type_map_get_id(map, SPA_TYPE_PROPS__truncate); + type->prop_dither = spa_type_map_get_id(map, SPA_TYPE_PROPS__ditherType); + spa_type_io_map(map, &type->io); + spa_type_param_map(map, &type->param); + spa_type_media_type_map(map, &type->media_type); + spa_type_media_subtype_map(map, &type->media_subtype); + spa_type_format_audio_map(map, &type->format_audio); + spa_type_audio_format_map(map, &type->audio_format); + spa_type_command_node_map(map, &type->command_node); + spa_type_meta_map(map, &type->meta); + spa_type_data_map(map, &type->data); + spa_type_param_buffers_map(map, &type->param_buffers); + spa_type_param_meta_map(map, &type->param_meta); + spa_type_param_io_map(map, &type->param_io); +} + + +struct impl { + struct spa_handle handle; + struct spa_node node; + + struct type type; + struct spa_type_map *map; + struct spa_log *log; + + struct props props; + + const struct spa_node_callbacks *callbacks; + void *user_data; + + struct port in_port; + struct port out_port; + + bool started; +}; + +#define CHECK_PORT(this,d,id) (id == 0) +#define GET_IN_PORT(this,id) (&this->in_port) +#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 int setup_convert(struct impl *this, + enum spa_direction direction, + const struct spa_audio_info *info) +{ + const struct spa_audio_info *src_info, *dst_info; + + if (direction == SPA_DIRECTION_INPUT) { + src_info = info; + dst_info = &GET_OUT_PORT(this, 0)->format; + } else { + src_info = &GET_IN_PORT(this, 0)->format; + dst_info = info; + } + + spa_log_info(this->log, NAME " %p: %d/%d@%d.%d->%d/%d@%d.%d", this, + src_info->info.raw.format, + src_info->info.raw.channels, + src_info->info.raw.rate, + src_info->info.raw.layout, + dst_info->info.raw.format, + dst_info->info.raw.channels, + dst_info->info.raw.rate, + dst_info->info.raw.layout); + + if (src_info->info.raw.channels != dst_info->info.raw.channels) + return -EINVAL; + + return 0; +} + +static int impl_node_enum_params(struct spa_node *node, + uint32_t id, uint32_t *index, + const struct spa_pod *filter, + struct spa_pod **param, + struct spa_pod_builder *builder) +{ + return -ENOTSUP; +} + +static int impl_node_set_param(struct spa_node *node, uint32_t id, uint32_t flags, + const struct spa_pod *param) +{ + return -ENOTSUP; +} + +static int impl_node_send_command(struct spa_node *node, const struct spa_command *command) +{ + struct impl *this; + + spa_return_val_if_fail(node != NULL, -EINVAL); + spa_return_val_if_fail(command != NULL, -EINVAL); + + this = SPA_CONTAINER_OF(node, struct impl, node); + + if (SPA_COMMAND_TYPE(command) == this->type.command_node.Start) { + this->started = true; + } else if (SPA_COMMAND_TYPE(command) == this->type.command_node.Pause) { + this->started = false; + } else + return -ENOTSUP; + + return 0; +} + +static int +impl_node_set_callbacks(struct spa_node *node, + const struct spa_node_callbacks *callbacks, + void *user_data) +{ + struct impl *this; + + spa_return_val_if_fail(node != NULL, -EINVAL); + + this = SPA_CONTAINER_OF(node, struct impl, node); + + this->callbacks = callbacks; + this->user_data = user_data; + + return 0; +} + +static int +impl_node_get_n_ports(struct spa_node *node, + uint32_t *n_input_ports, + uint32_t *max_input_ports, + uint32_t *n_output_ports, + uint32_t *max_output_ports) +{ + spa_return_val_if_fail(node != NULL, -EINVAL); + + if (n_input_ports) + *n_input_ports = 1; + if (max_input_ports) + *max_input_ports = 1; + if (n_output_ports) + *n_output_ports = 1; + if (max_output_ports) + *max_output_ports = 1; + + return 0; +} + +static int +impl_node_get_port_ids(struct spa_node *node, + uint32_t *input_ids, + uint32_t n_input_ids, + uint32_t *output_ids, + uint32_t n_output_ids) +{ + spa_return_val_if_fail(node != NULL, -EINVAL); + + if (n_input_ids && input_ids) + input_ids[0] = 0; + if (n_output_ids > 0 && output_ids) + output_ids[0] = 0; + + return 0; +} + +static int impl_node_add_port(struct spa_node *node, enum spa_direction direction, uint32_t port_id) +{ + return -ENOTSUP; +} + +static int +impl_node_remove_port(struct spa_node *node, enum spa_direction direction, uint32_t port_id) +{ + return -ENOTSUP; +} + +static int +impl_node_port_get_info(struct spa_node *node, + enum spa_direction direction, + uint32_t port_id, + const struct spa_port_info **info) +{ + struct impl *this; + struct port *port; + + spa_return_val_if_fail(node != NULL, -EINVAL); + spa_return_val_if_fail(info != NULL, -EINVAL); + + this = SPA_CONTAINER_OF(node, struct impl, node); + + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + + port = GET_PORT(this, direction, port_id); + *info = &port->info; + + return 0; +} + +static int port_enum_formats(struct spa_node *node, + enum spa_direction direction, uint32_t port_id, + uint32_t *index, + struct spa_pod **param, + struct spa_pod_builder *builder) +{ + struct impl *this = SPA_CONTAINER_OF(node, struct impl, node); + struct type *t = &this->type; + struct port *other; + + other = GET_PORT(this, SPA_DIRECTION_REVERSE(direction), 0); + + switch (*index) { + case 0: + if (other->have_format) { + *param = spa_pod_builder_object(builder, + t->param.idEnumFormat, t->format, + "I", t->media_type.audio, + "I", t->media_subtype.raw, + ":", t->format_audio.format, "I", t->audio_format.F32, + ":", t->format_audio.layout, "i", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, + ":", t->format_audio.rate, "iru", other->format.info.raw.rate, + SPA_POD_PROP_MIN_MAX(1, INT32_MAX), + ":", t->format_audio.channels, "i", other->format.info.raw.channels); + } else { + *param = spa_pod_builder_object(builder, + t->param.idEnumFormat, t->format, + "I", t->media_type.audio, + "I", t->media_subtype.raw, + ":", t->format_audio.format, "I", t->audio_format.F32, + ":", t->format_audio.layout, "i", SPA_AUDIO_LAYOUT_NON_INTERLEAVED, + ":", t->format_audio.rate, "iru", 44100, + SPA_POD_PROP_MIN_MAX(1, INT32_MAX), + ":", t->format_audio.channels, "iru", 2, + SPA_POD_PROP_MIN_MAX(1, INT32_MAX)); + } + break; + default: + return 0; + } + return 1; +} + +static int port_get_format(struct spa_node *node, + enum spa_direction direction, uint32_t port_id, + uint32_t *index, + struct spa_pod **param, + struct spa_pod_builder *builder) +{ + struct impl *this = SPA_CONTAINER_OF(node, struct impl, node); + struct type *t = &this->type; + struct port *port = GET_PORT(this, direction, port_id); + + if (!port->have_format) + return -EIO; + if (*index > 0) + return 0; + + *param = spa_pod_builder_object(builder, + t->param.idFormat, t->format, + "I", t->media_type.audio, + "I", t->media_subtype.raw, + ":", t->format_audio.format, "I", port->format.info.raw.format, + ":", t->format_audio.layout, "i", port->format.info.raw.layout, + ":", t->format_audio.rate, "i", port->format.info.raw.rate, + ":", t->format_audio.channels, "i", port->format.info.raw.channels); + + return 1; +} + +static int +impl_node_port_enum_params(struct spa_node *node, + enum spa_direction direction, uint32_t port_id, + uint32_t id, uint32_t *index, + const struct spa_pod *filter, + struct spa_pod **result, + struct spa_pod_builder *builder) +{ + struct impl *this; + struct type *t; + struct port *port, *other; + struct spa_pod *param; + struct spa_pod_builder b = { 0 }; + uint8_t buffer[1024]; + int res; + + spa_return_val_if_fail(node != NULL, -EINVAL); + spa_return_val_if_fail(index != NULL, -EINVAL); + spa_return_val_if_fail(builder != NULL, -EINVAL); + + this = SPA_CONTAINER_OF(node, struct impl, node); + t = &this->type; + + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + + port = GET_PORT(this, direction, port_id); + other = GET_PORT(this, SPA_DIRECTION_REVERSE(direction), port_id); + + next: + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + + if (id == t->param.idList) { + uint32_t list[] = { t->param.idEnumFormat, + t->param.idFormat, + t->param.idBuffers, + t->param.idMeta, + t->param_io.idBuffers }; + + if (*index < SPA_N_ELEMENTS(list)) + param = spa_pod_builder_object(&b, id, t->param.List, + ":", t->param.listId, "I", list[*index]); + else + return 0; + } + else if (id == t->param.idEnumFormat) { + if ((res = port_enum_formats(node, direction, port_id, index, ¶m, &b)) <= 0) + return res; + } + else if (id == t->param.idFormat) { + if ((res = port_get_format(node, direction, port_id, index, ¶m, &b)) <= 0) + return res; + } + else if (id == t->param.idBuffers) { + if (!port->have_format) + return -EIO; + if (*index > 0) + return 0; + + if (other->n_buffers > 0) { + param = spa_pod_builder_object(&b, + id, t->param_buffers.Buffers, + ":", t->param_buffers.buffers, "iru", other->n_buffers, + SPA_POD_PROP_MIN_MAX(1, MAX_BUFFERS), + ":", t->param_buffers.blocks, "i", port->blocks, + ":", t->param_buffers.size, "i", (other->size / other->stride) * + port->stride, + ":", t->param_buffers.stride, "i", port->stride, + ":", t->param_buffers.align, "i", 16); + } else { + param = spa_pod_builder_object(&b, + id, t->param_buffers.Buffers, + ":", 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) { + if (!port->have_format) + return -EIO; + + switch (*index) { + case 0: + param = spa_pod_builder_object(&b, + id, t->param_meta.Meta, + ":", t->param_meta.type, "I", t->meta.Header, + ":", t->param_meta.size, "i", sizeof(struct spa_meta_header)); + break; + default: + return 0; + } + } + else if (id == t->param_io.idBuffers) { + switch (*index) { + case 0: + param = spa_pod_builder_object(&b, + id, t->param_io.Buffers, + ":", t->param_io.id, "I", t->io.Buffers, + ":", t->param_io.size, "i", sizeof(struct spa_io_buffers)); + break; + default: + return 0; + } + } + else + return -ENOENT; + + (*index)++; + + if (spa_pod_filter(builder, result, param, filter) < 0) + goto next; + + return 1; +} + +static int clear_buffers(struct impl *this, struct port *port) +{ + if (port->n_buffers > 0) { + spa_log_info(this->log, NAME " %p: clear buffers %p", this, port); + port->n_buffers = 0; + spa_list_init(&port->queue); + } + return 0; +} + +static int port_set_format(struct spa_node *node, + enum spa_direction direction, + uint32_t port_id, + uint32_t flags, + const struct spa_pod *format) +{ + struct impl *this = SPA_CONTAINER_OF(node, struct impl, node); + struct port *port, *other; + struct type *t = &this->type; + int res = 0; + + port = GET_PORT(this, direction, port_id); + other = GET_PORT(this, SPA_DIRECTION_REVERSE(direction), port_id); + + if (format == NULL) { + if (port->have_format) { + port->have_format = false; + clear_buffers(this, port); + } + } else { + struct spa_audio_info info = { 0 }; + + spa_pod_object_parse(format, + "I", &info.media_type, + "I", &info.media_subtype); + + if (info.media_type != t->media_type.audio || + info.media_subtype != t->media_subtype.raw) + return -EINVAL; + + if (spa_format_audio_raw_parse(format, &info.info.raw, &t->format_audio) < 0) + return -EINVAL; + + if (info.info.raw.format != t->audio_format.F32) + return -EINVAL; + if (info.info.raw.layout != SPA_AUDIO_LAYOUT_NON_INTERLEAVED) + return -EINVAL; + + port->stride = sizeof(float); + port->blocks = info.info.raw.channels; + + if (other->have_format) { + if ((res = setup_convert(this, direction, &info)) < 0) + return res; + } + port->format = info; + port->have_format = true; + + spa_log_info(this->log, NAME " %p: set format on port %d %d", this, port_id, res); + } + return res; +} + +static int +impl_node_port_set_param(struct spa_node *node, + enum spa_direction direction, uint32_t port_id, + uint32_t id, uint32_t flags, + const struct spa_pod *param) +{ + struct impl *this; + struct type *t; + + spa_return_val_if_fail(node != NULL, -EINVAL); + + this = SPA_CONTAINER_OF(node, struct impl, node); + t = &this->type; + + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + + if (id == t->param.idFormat) { + return port_set_format(node, direction, port_id, flags, param); + } + else + return -ENOENT; +} + +static int +impl_node_port_use_buffers(struct spa_node *node, + enum spa_direction direction, + uint32_t port_id, + struct spa_buffer **buffers, + uint32_t n_buffers) +{ + struct impl *this; + struct port *port; + uint32_t i, size = SPA_ID_INVALID; + struct type *t; + + spa_return_val_if_fail(node != NULL, -EINVAL); + + this = SPA_CONTAINER_OF(node, struct impl, node); + t = &this->type; + + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + + port = GET_PORT(this, direction, port_id); + + spa_return_val_if_fail(port->have_format, -EIO); + + spa_log_info(this->log, NAME " %p: use buffers %d on port %d", this, n_buffers, port_id); + + clear_buffers(this, port); + + for (i = 0; i < n_buffers; i++) { + struct buffer *b; + struct spa_data *d = buffers[i]->datas; + + b = &port->buffers[i]; + b->flags = 0; + b->outbuf = buffers[i]; + b->h = spa_buffer_find_meta(buffers[i], t->meta.Header); + + if (size == SPA_ID_INVALID) + size = d[0].maxsize; + else + if (size != d[0].maxsize) + return -EINVAL; + + if (!((d[0].type == t->data.MemPtr || + d[0].type == t->data.MemFd || + d[0].type == t->data.DmaBuf) && d[0].data != NULL)) { + spa_log_error(this->log, NAME " %p: invalid memory on buffer %p", this, + buffers[i]); + return -EINVAL; + } + if (direction == SPA_DIRECTION_OUTPUT) + spa_list_append(&port->queue, &b->link); + else + SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); + } + port->n_buffers = n_buffers; + port->size = size; + + return 0; +} + +static int +impl_node_port_alloc_buffers(struct spa_node *node, + enum spa_direction direction, + uint32_t port_id, + struct spa_pod **params, + uint32_t n_params, + struct spa_buffer **buffers, + uint32_t *n_buffers) +{ + return -ENOTSUP; +} + +static int +impl_node_port_set_io(struct spa_node *node, + enum spa_direction direction, uint32_t port_id, + uint32_t id, void *data, size_t size) +{ + struct impl *this; + struct port *port; + struct type *t; + + spa_return_val_if_fail(node != NULL, -EINVAL); + + this = SPA_CONTAINER_OF(node, struct impl, node); + t = &this->type; + + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + + port = GET_PORT(this, direction, port_id); + + if (id == t->io.Buffers) + port->io = data; + else + return -ENOENT; + + return 0; +} + +static void recycle_buffer(struct impl *this, uint32_t id) +{ + struct port *port = GET_OUT_PORT(this, 0); + struct buffer *b = &port->buffers[id]; + + if (SPA_FLAG_CHECK(b->flags, BUFFER_FLAG_OUT)) { + spa_list_append(&port->queue, &b->link); + SPA_FLAG_UNSET(b->flags, BUFFER_FLAG_OUT); + spa_log_trace(this->log, NAME " %p: recycle buffer %d", this, id); + } +} + +static struct buffer *dequeue_buffer(struct impl *this, struct port *port) +{ + struct buffer *b; + + if (spa_list_is_empty(&port->queue)) + return NULL; + + b = spa_list_first(&port->queue, struct buffer, link); + spa_list_remove(&b->link); + SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); + + return b; +} + + +static int impl_node_port_reuse_buffer(struct spa_node *node, uint32_t port_id, uint32_t buffer_id) +{ + struct impl *this; + + spa_return_val_if_fail(node != NULL, -EINVAL); + + this = SPA_CONTAINER_OF(node, struct impl, node); + + spa_return_val_if_fail(CHECK_PORT(this, SPA_DIRECTION_OUTPUT, port_id), -EINVAL); + + recycle_buffer(this, buffer_id); + + return 0; +} + +static int +impl_node_port_send_command(struct spa_node *node, + enum spa_direction direction, + uint32_t port_id, + const struct spa_command *command) +{ + return -ENOTSUP; +} + +static int impl_node_process(struct spa_node *node) +{ + struct impl *this; + struct port *outport, *inport; + struct spa_io_buffers *outio, *inio; + struct buffer *sbuf, *dbuf; + + spa_return_val_if_fail(node != NULL, -EINVAL); + + this = SPA_CONTAINER_OF(node, struct impl, node); + + outport = GET_OUT_PORT(this, 0); + inport = GET_IN_PORT(this, 0); + + outio = outport->io; + inio = inport->io; + + spa_return_val_if_fail(outio != NULL, -EIO); + spa_return_val_if_fail(inio != NULL, -EIO); + + spa_log_trace(this->log, NAME " %p: status %d %d", this, inio->status, outio->status); + + if (outio->status == SPA_STATUS_HAVE_BUFFER) + return outio->status; + + if (inio->status != SPA_STATUS_HAVE_BUFFER) + return SPA_STATUS_NEED_BUFFER; + + /* recycle */ + if (outio->buffer_id < outport->n_buffers) { + recycle_buffer(this, outio->buffer_id); + outio->buffer_id = SPA_ID_INVALID; + } + + if (inio->buffer_id >= inport->n_buffers) + return inio->status = -EINVAL; + + if ((dbuf = dequeue_buffer(this, outport)) == NULL) + return outio->status = -EPIPE; + + sbuf = &inport->buffers[inio->buffer_id]; + + { + int i, n_bytes; + struct spa_buffer *sb = sbuf->outbuf, *db = dbuf->outbuf; + uint32_t n_src_datas = sb->n_datas; + uint32_t n_dst_datas = db->n_datas; + const void *src_datas[n_src_datas]; + void *dst_datas[n_dst_datas]; + + n_bytes = sb->datas[0].chunk->size; + + for (i = 0; i < n_src_datas; i++) + src_datas[i] = sb->datas[i].data; + for (i = 0; i < n_dst_datas; i++) { + dst_datas[i] = db->datas[i].data; + db->datas[i].chunk->size = + (n_bytes / inport->stride) * outport->stride; + } + + for (i = 0; i < n_src_datas; i++) + memcpy(dst_datas[i], src_datas[i], n_bytes); + +// this->convert(this, n_dst_datas, dst_datas, +// n_src_datas, src_datas, +// n_bytes); + } + + outio->status = SPA_STATUS_HAVE_BUFFER; + outio->buffer_id = dbuf->outbuf->id; + + inio->status = SPA_STATUS_NEED_BUFFER; + + return outio->status; +} + +static const struct spa_node impl_node = { + SPA_VERSION_NODE, + NULL, + impl_node_enum_params, + impl_node_set_param, + impl_node_send_command, + impl_node_set_callbacks, + impl_node_get_n_ports, + impl_node_get_port_ids, + impl_node_add_port, + impl_node_remove_port, + impl_node_port_get_info, + impl_node_port_enum_params, + impl_node_port_set_param, + impl_node_port_use_buffers, + impl_node_port_alloc_buffers, + impl_node_port_set_io, + impl_node_port_reuse_buffer, + impl_node_port_send_command, + impl_node_process, +}; + +static int impl_get_interface(struct spa_handle *handle, uint32_t interface_id, void **interface) +{ + struct impl *this; + + spa_return_val_if_fail(handle != NULL, -EINVAL); + spa_return_val_if_fail(interface != NULL, -EINVAL); + + this = (struct impl *) handle; + + if (interface_id == this->type.node) + *interface = &this->node; + else + return -ENOENT; + + return 0; +} + +static int impl_clear(struct spa_handle *handle) +{ + return 0; +} + +static size_t +impl_get_size(const struct spa_handle_factory *factory, + const struct spa_dict *params) +{ + return sizeof(struct impl); +} + +static int +impl_init(const struct spa_handle_factory *factory, + struct spa_handle *handle, + const struct spa_dict *info, + const struct spa_support *support, + uint32_t n_support) +{ + struct impl *this; + struct port *port; + uint32_t i; + + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(handle != NULL, -EINVAL); + + handle->get_interface = impl_get_interface; + handle->clear = impl_clear; + + this = (struct impl *) handle; + + for (i = 0; i < n_support; i++) { + if (strcmp(support[i].type, SPA_TYPE__TypeMap) == 0) + this->map = support[i].data; + else if (strcmp(support[i].type, SPA_TYPE__Log) == 0) + this->log = support[i].data; + } + if (this->map == NULL) { + spa_log_error(this->log, "an id-map is needed"); + return -EINVAL; + } + init_type(&this->type, this->map); + + this->node = impl_node; + + port = GET_OUT_PORT(this, 0); + port->id = 0; + port->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS; + spa_list_init(&port->queue); + + port = GET_IN_PORT(this, 0); + port->id = 0; + port->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS; + spa_list_init(&port->queue); + + props_reset(&this->props); + + return 0; +} + +static const struct spa_interface_info impl_interfaces[] = { + {SPA_TYPE__Node,}, +}; + +static int +impl_enum_interface_info(const struct spa_handle_factory *factory, + const struct spa_interface_info **info, + uint32_t *index) +{ + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(info != NULL, -EINVAL); + spa_return_val_if_fail(index != NULL, -EINVAL); + + switch (*index) { + case 0: + *info = &impl_interfaces[*index]; + break; + default: + return 0; + } + (*index)++; + return 1; +} + +const struct spa_handle_factory spa_resample_factory = { + SPA_VERSION_HANDLE_FACTORY, + NAME, + NULL, + impl_get_size, + impl_init, + impl_enum_interface_info, +}; diff --git a/src/pipewire/stream.c b/src/pipewire/stream.c index cb84f5941..81bbc28f2 100644 --- a/src/pipewire/stream.c +++ b/src/pipewire/stream.c @@ -125,18 +125,19 @@ struct stream { }; -static inline void push_queue(struct stream *stream, struct queue *queue, struct buffer *buffer) +static inline int push_queue(struct stream *stream, struct queue *queue, struct buffer *buffer) { uint32_t index; if (SPA_FLAG_CHECK(buffer->flags, BUFFER_FLAG_QUEUED)) - return; + return -EINVAL; SPA_FLAG_SET(buffer->flags, BUFFER_FLAG_QUEUED); spa_ringbuffer_get_write_index(&queue->ring, &index); queue->ids[index & MASK_BUFFERS] = buffer->id; spa_ringbuffer_write_update(&queue->ring, index + 1); + return 0; } static inline struct buffer *pop_queue(struct stream *stream, struct queue *queue) @@ -624,7 +625,7 @@ static int impl_port_use_buffers(struct spa_node *node, enum spa_direction direc return res; datas[0].type = t->data.MemPtr; - datas[0].maxsize = size * 2; + datas[0].maxsize = size * 4; data_aligns[0] = 16; buffers = spa_buffer_alloc_array(n_buffers, 0, @@ -701,25 +702,33 @@ static int impl_node_process_output(struct spa_node *node) struct spa_io_buffers *io = impl->io; struct buffer *b; int res = 0; + bool do_call = true; pw_log_trace("stream %p: process out %d %d", stream, io->status, io->buffer_id); - 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->convert); - - 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; + if (io->status != SPA_STATUS_HAVE_BUFFER) { + /* recycle old buffer */ + if ((b = get_buffer(stream, io->buffer_id)) != NULL) { + push_queue(impl, &impl->dequeued, b); + } + /* pop new buffer */ + if ((b = pop_queue(impl, &impl->queued)) != NULL) { + io->buffer_id = b->id; + io->status = SPA_STATUS_HAVE_BUFFER; + 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; + } } - call_process(impl); + if (io->status == SPA_STATUS_HAVE_BUFFER && impl->use_converter) { + res = spa_node_process(impl->convert); + if (io->status == SPA_STATUS_HAVE_BUFFER) + do_call = false; + } + + if (do_call) + call_process(impl); return SPA_STATUS_HAVE_BUFFER; } @@ -1153,8 +1162,10 @@ 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 = get_buffer(stream, buffer->buffer->id)) == NULL) + if ((b = get_buffer(stream, buffer->buffer->id)) == NULL) { + pw_log_error("stream %p: invalid buffer %d", stream, buffer->buffer->id); return -EINVAL; + } pw_log_trace("stream %p: queue buffer %d", stream, b->id); push_queue(impl, &impl->queued, b);