int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in) { int use_generic = 1; int len = in->nb_samples; int p; if (ac->dc) { /* dithered conversion */ av_dlog(ac->avr, "%d samples - audio_convert: %s to %s (dithered)\n", len, av_get_sample_fmt_name(ac->in_fmt), av_get_sample_fmt_name(ac->out_fmt)); return ff_convert_dither(ac->dc, out, in); } /* determine whether to use the optimized function based on pointer and samples alignment in both the input and output */ if (ac->has_optimized_func) { int ptr_align = FFMIN(in->ptr_align, out->ptr_align); int samples_align = FFMIN(in->samples_align, out->samples_align); int aligned_len = FFALIGN(len, ac->samples_align); if (!(ptr_align % ac->ptr_align) && samples_align >= aligned_len) { len = aligned_len; use_generic = 0; } } av_dlog(ac->avr, "%d samples - audio_convert: %s to %s (%s)\n", len, av_get_sample_fmt_name(ac->in_fmt), av_get_sample_fmt_name(ac->out_fmt), use_generic ? ac->func_descr_generic : ac->func_descr); if (ac->apply_map) { ChannelMapInfo *map = &ac->avr->ch_map_info; if (!av_sample_fmt_is_planar(ac->out_fmt)) { av_log(ac->avr, AV_LOG_ERROR, "cannot remap packed format during conversion\n"); return AVERROR(EINVAL); } if (map->do_remap) { if (av_sample_fmt_is_planar(ac->in_fmt)) { conv_func_flat *convert = use_generic ? ac->conv_flat_generic : ac->conv_flat; for (p = 0; p < ac->planes; p++) if (map->channel_map[p] >= 0) convert(out->data[p], in->data[map->channel_map[p]], len); } else { uint8_t *data[AVRESAMPLE_MAX_CHANNELS]; conv_func_deinterleave *convert = use_generic ? ac->conv_deinterleave_generic : ac->conv_deinterleave; for (p = 0; p < ac->channels; p++) data[map->input_map[p]] = out->data[p]; convert(data, in->data[0], len, ac->channels); } } if (map->do_copy || map->do_zero) { for (p = 0; p < ac->planes; p++) { if (map->channel_copy[p]) memcpy(out->data[p], out->data[map->channel_copy[p]], len * out->stride); else if (map->channel_zero[p]) av_samples_set_silence(&out->data[p], 0, len, 1, ac->out_fmt); } } } else { switch (ac->func_type) { case CONV_FUNC_TYPE_FLAT: { if (!in->is_planar) len *= in->channels; if (use_generic) { for (p = 0; p < ac->planes; p++) ac->conv_flat_generic(out->data[p], in->data[p], len); } else { for (p = 0; p < ac->planes; p++) ac->conv_flat(out->data[p], in->data[p], len); } break; } case CONV_FUNC_TYPE_INTERLEAVE: if (use_generic) ac->conv_interleave_generic(out->data[0], in->data, len, ac->channels); else ac->conv_interleave(out->data[0], in->data, len, ac->channels); break; case CONV_FUNC_TYPE_DEINTERLEAVE: if (use_generic) ac->conv_deinterleave_generic(out->data, in->data[0], len, ac->channels); else ac->conv_deinterleave(out->data, in->data[0], len, ac->channels); break; } } out->nb_samples = in->nb_samples; return 0; }
int ff_audio_mix(AudioMix *am, AudioData *src) { int use_generic = 1; int len = src->nb_samples; int i, j; /* determine whether to use the optimized function based on pointer and samples alignment in both the input and output */ if (am->has_optimized_func) { int aligned_len = FFALIGN(len, am->samples_align); if (!(src->ptr_align % am->ptr_align) && src->samples_align >= aligned_len) { len = aligned_len; use_generic = 0; } } av_dlog(am->avr, "audio_mix: %d samples - %d to %d channels (%s)\n", src->nb_samples, am->in_channels, am->out_channels, use_generic ? am->func_descr_generic : am->func_descr); if (am->in_matrix_channels && am->out_matrix_channels) { uint8_t **data; uint8_t *data0[AVRESAMPLE_MAX_CHANNELS] = { NULL }; if (am->out_matrix_channels < am->out_channels || am->in_matrix_channels < am->in_channels) { for (i = 0, j = 0; i < FFMAX(am->in_channels, am->out_channels); i++) { if (am->input_skip[i] || am->output_skip[i] || am->output_zero[i]) continue; data0[j++] = src->data[i]; } data = data0; } else { data = src->data; } if (use_generic) am->mix_generic(data, am->matrix, len, am->out_matrix_channels, am->in_matrix_channels); else am->mix(data, am->matrix, len, am->out_matrix_channels, am->in_matrix_channels); } if (am->out_matrix_channels < am->out_channels) { for (i = 0; i < am->out_channels; i++) if (am->output_zero[i]) av_samples_set_silence(&src->data[i], 0, len, 1, am->fmt); } ff_audio_data_set_channels(src, am->out_channels); /* clip protection is only enabled when the internal sample format is fltp, so we don't need to check here */ if (am->clip_protection) { for(i = 0; i < src->nb_samples; i++) { for (j = 0; j < src->channels; j++) { const float sample = fabs(((float **)src->data)[j][i]); if (sample > am->clip_max) { am->clip_max = sample; av_log(am->avr, AV_LOG_INFO, "Clipping protection at %.3f\n", sample); } if (am->clip_max > 1.0f) ((float **)src->data)[j][i] /= am->clip_max; } } } return 0; }
static int return_audio_frame(AVFilterContext *ctx) { AVFilterLink *link = ctx->outputs[0]; FifoContext *s = ctx->priv; AVFilterBufferRef *head = s->root.next->buf; AVFilterBufferRef *buf_out; int ret; if (!s->buf_out && head->audio->nb_samples >= link->request_samples && calc_ptr_alignment(head) >= 32) { if (head->audio->nb_samples == link->request_samples) { buf_out = head; queue_pop(s); } else { buf_out = avfilter_ref_buffer(head, AV_PERM_READ); if (!buf_out) return AVERROR(ENOMEM); buf_out->audio->nb_samples = link->request_samples; buffer_offset(link, head, link->request_samples); } } else { int nb_channels = av_get_channel_layout_nb_channels(link->channel_layout); if (!s->buf_out) { s->buf_out = ff_get_audio_buffer(link, AV_PERM_WRITE, link->request_samples); if (!s->buf_out) return AVERROR(ENOMEM); s->buf_out->audio->nb_samples = 0; s->buf_out->pts = head->pts; s->allocated_samples = link->request_samples; } else if (link->request_samples != s->allocated_samples) { av_log(ctx, AV_LOG_ERROR, "request_samples changed before the " "buffer was returned.\n"); return AVERROR(EINVAL); } while (s->buf_out->audio->nb_samples < s->allocated_samples) { int len = FFMIN(s->allocated_samples - s->buf_out->audio->nb_samples, head->audio->nb_samples); av_samples_copy(s->buf_out->extended_data, head->extended_data, s->buf_out->audio->nb_samples, 0, len, nb_channels, link->format); s->buf_out->audio->nb_samples += len; if (len == head->audio->nb_samples) { avfilter_unref_buffer(head); queue_pop(s); if (!s->root.next && (ret = ff_request_frame(ctx->inputs[0])) < 0) { if (ret == AVERROR_EOF) { av_samples_set_silence(s->buf_out->extended_data, s->buf_out->audio->nb_samples, s->allocated_samples - s->buf_out->audio->nb_samples, nb_channels, link->format); s->buf_out->audio->nb_samples = s->allocated_samples; break; } return ret; } head = s->root.next->buf; } else { buffer_offset(link, head, len); } } buf_out = s->buf_out; s->buf_out = NULL; } return ff_filter_samples(link, buf_out); }
static int filter_frame(AVFilterLink *inlink, AVFrame *buf) { AVFilterContext *ctx = inlink->dst; ASyncContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; int nb_channels = av_get_channel_layout_nb_channels(buf->channel_layout); int64_t pts = (buf->pts == AV_NOPTS_VALUE) ? buf->pts : av_rescale_q(buf->pts, inlink->time_base, outlink->time_base); int out_size, ret; int64_t delta; int64_t new_pts; /* buffer data until we get the next timestamp */ if (s->pts == AV_NOPTS_VALUE || pts == AV_NOPTS_VALUE) { if (pts != AV_NOPTS_VALUE) { s->pts = pts - get_delay(s); } return write_to_fifo(s, buf); } if (s->first_pts != AV_NOPTS_VALUE) { handle_trimming(ctx); if (!avresample_available(s->avr)) return write_to_fifo(s, buf); } /* when we have two timestamps, compute how many samples would we have * to add/remove to get proper sync between data and timestamps */ delta = pts - s->pts - get_delay(s); out_size = avresample_available(s->avr); if (labs(delta) > s->min_delta || (s->first_frame && delta && s->first_pts != AV_NOPTS_VALUE)) { av_log(ctx, AV_LOG_VERBOSE, "Discontinuity - %"PRId64" samples.\n", delta); out_size = av_clipl_int32((int64_t)out_size + delta); } else { if (s->resample) { // adjust the compensation if delta is non-zero int delay = get_delay(s); int comp = s->comp + av_clip(delta * inlink->sample_rate / delay, -s->max_comp, s->max_comp); if (comp != s->comp) { av_log(ctx, AV_LOG_VERBOSE, "Compensating %d samples per second.\n", comp); if (avresample_set_compensation(s->avr, comp, inlink->sample_rate) == 0) { s->comp = comp; } } } // adjust PTS to avoid monotonicity errors with input PTS jitter pts -= delta; delta = 0; } if (out_size > 0) { AVFrame *buf_out = ff_get_audio_buffer(outlink, out_size); if (!buf_out) { ret = AVERROR(ENOMEM); goto fail; } if (s->first_frame && delta > 0) { int ch; av_samples_set_silence(buf_out->extended_data, 0, delta, nb_channels, buf->format); for (ch = 0; ch < nb_channels; ch++) buf_out->extended_data[ch] += delta; avresample_read(s->avr, buf_out->extended_data, out_size); for (ch = 0; ch < nb_channels; ch++) buf_out->extended_data[ch] -= delta; } else { avresample_read(s->avr, buf_out->extended_data, out_size); if (delta > 0) { av_samples_set_silence(buf_out->extended_data, out_size - delta, delta, nb_channels, buf->format); } } buf_out->pts = s->pts; ret = ff_filter_frame(outlink, buf_out); if (ret < 0) goto fail; s->got_output = 1; } else if (avresample_available(s->avr)) { av_log(ctx, AV_LOG_WARNING, "Non-monotonous timestamps, dropping " "whole buffer.\n"); } /* drain any remaining buffered data */ avresample_read(s->avr, NULL, avresample_available(s->avr)); new_pts = pts - avresample_get_delay(s->avr); /* check for s->pts monotonicity */ if (new_pts > s->pts) { s->pts = new_pts; ret = avresample_convert(s->avr, NULL, 0, 0, buf->extended_data, buf->linesize[0], buf->nb_samples); } else { av_log(ctx, AV_LOG_WARNING, "Non-monotonous timestamps, dropping " "whole buffer.\n"); ret = 0; } s->first_frame = 0; fail: av_frame_free(&buf); return ret; }
static int return_audio_frame(AVFilterContext *ctx) { AVFilterLink *link = ctx->outputs[0]; FifoContext *s = ctx->priv; AVFrame *head = s->root.next ? s->root.next->frame : NULL; AVFrame *out; int ret; /* if head is NULL then we're flushing the remaining samples in out */ if (!head && !s->out) return AVERROR_EOF; if (!s->out && head->nb_samples >= link->request_samples && calc_ptr_alignment(head) >= 32) { if (head->nb_samples == link->request_samples) { out = head; queue_pop(s); } else { out = av_frame_clone(head); if (!out) return AVERROR(ENOMEM); out->nb_samples = link->request_samples; buffer_offset(link, head, link->request_samples); } } else { int nb_channels = av_get_channel_layout_nb_channels(link->channel_layout); if (!s->out) { s->out = ff_get_audio_buffer(link, link->request_samples); if (!s->out) return AVERROR(ENOMEM); s->out->nb_samples = 0; s->out->pts = head->pts; s->allocated_samples = link->request_samples; } else if (link->request_samples != s->allocated_samples) { av_log(ctx, AV_LOG_ERROR, "request_samples changed before the " "buffer was returned.\n"); return AVERROR(EINVAL); } while (s->out->nb_samples < s->allocated_samples) { int len; if (!s->root.next) { ret = ff_request_frame(ctx->inputs[0]); if (ret == AVERROR_EOF) { av_samples_set_silence(s->out->extended_data, s->out->nb_samples, s->allocated_samples - s->out->nb_samples, nb_channels, link->format); s->out->nb_samples = s->allocated_samples; break; } else if (ret < 0) return ret; av_assert0(s->root.next); // If ff_request_frame() succeeded then we should have a frame } head = s->root.next->frame; len = FFMIN(s->allocated_samples - s->out->nb_samples, head->nb_samples); av_samples_copy(s->out->extended_data, head->extended_data, s->out->nb_samples, 0, len, nb_channels, link->format); s->out->nb_samples += len; if (len == head->nb_samples) { av_frame_free(&head); queue_pop(s); } else { buffer_offset(link, head, len); } } out = s->out; s->out = NULL; } return ff_filter_frame(link, out); }