diff --git a/libavfilter/audio.c b/libavfilter/audio.c index 6a86597342..0ebec3c2d0 100644 --- a/libavfilter/audio.c +++ b/libavfilter/audio.c @@ -156,7 +156,8 @@ static void default_filter_samples(AVFilterLink *link, ff_filter_samples(link->dst->outputs[0], samplesref); } -void ff_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref) +void ff_filter_samples_framed(AVFilterLink *link, + AVFilterBufferRef *samplesref) { void (*filter_samples)(AVFilterLink *, AVFilterBufferRef *); AVFilterPad *dst = link->dstpad; @@ -195,3 +196,48 @@ void ff_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref) filter_samples(link, buf_out); ff_update_link_current_pts(link, pts); } + +void ff_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref) +{ + int insamples = samplesref->audio->nb_samples, inpos = 0, nb_samples; + AVFilterBufferRef *pbuf = link->partial_buf; + int nb_channels = av_get_channel_layout_nb_channels(link->channel_layout); + + if (!link->min_samples || + (!pbuf && + insamples >= link->min_samples && insamples <= link->max_samples)) { + ff_filter_samples_framed(link, samplesref); + return; + } + /* Handle framing (min_samples, max_samples) */ + while (insamples) { + if (!pbuf) { + AVRational samples_tb = { 1, link->sample_rate }; + int perms = link->dstpad->min_perms | AV_PERM_WRITE; + pbuf = ff_get_audio_buffer(link, perms, link->partial_buf_size); + if (!pbuf) { + av_log(link->dst, AV_LOG_WARNING, + "Samples dropped due to memory allocation failure.\n"); + return; + } + avfilter_copy_buffer_ref_props(pbuf, samplesref); + pbuf->pts = samplesref->pts + + av_rescale_q(inpos, samples_tb, link->time_base); + pbuf->audio->nb_samples = 0; + } + nb_samples = FFMIN(insamples, + link->partial_buf_size - pbuf->audio->nb_samples); + av_samples_copy(pbuf->extended_data, samplesref->extended_data, + pbuf->audio->nb_samples, inpos, + nb_samples, nb_channels, link->format); + inpos += nb_samples; + insamples -= nb_samples; + pbuf->audio->nb_samples += nb_samples; + if (pbuf->audio->nb_samples >= link->min_samples) { + ff_filter_samples_framed(link, pbuf); + pbuf = NULL; + } + } + avfilter_unref_buffer(samplesref); + link->partial_buf = pbuf; +} diff --git a/libavfilter/audio.h b/libavfilter/audio.h index d4282b59fe..cab1a6c722 100644 --- a/libavfilter/audio.h +++ b/libavfilter/audio.h @@ -73,4 +73,11 @@ AVFilterBufferRef *ff_get_audio_buffer(AVFilterLink *link, int perms, */ void ff_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref); +/** + * Send a buffer of audio samples to the next link, without checking + * min_samples. + */ +void ff_filter_samples_framed(AVFilterLink *link, + AVFilterBufferRef *samplesref); + #endif /* AVFILTER_AUDIO_H */ diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index 0474192b1a..01f34423c9 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -28,6 +28,7 @@ #include "avfilter.h" #include "formats.h" #include "internal.h" +#include "audio.h" char *ff_get_ref_perms_string(char *buf, size_t buf_size, int perms) { @@ -320,13 +321,20 @@ void ff_tlog_link(void *ctx, AVFilterLink *link, int end) int ff_request_frame(AVFilterLink *link) { + int ret = -1; FF_TPRINTF_START(NULL, request_frame); ff_tlog_link(NULL, link, 1); if (link->srcpad->request_frame) - return link->srcpad->request_frame(link); + ret = link->srcpad->request_frame(link); else if (link->src->inputs[0]) - return ff_request_frame(link->src->inputs[0]); - else return -1; + ret = ff_request_frame(link->src->inputs[0]); + if (ret == AVERROR_EOF && link->partial_buf) { + AVFilterBufferRef *pbuf = link->partial_buf; + link->partial_buf = NULL; + ff_filter_samples_framed(link, pbuf); + return 0; + } + return ret; } int ff_poll_frame(AVFilterLink *link) diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index b679635805..e08a389275 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -590,6 +590,32 @@ struct AVFilterLink { * It is similar to the r_frae_rate field in AVStream. */ AVRational frame_rate; + + /** + * Buffer partially filled with samples to achieve a fixed/minimum size. + */ + AVFilterBufferRef *partial_buf; + + /** + * Size of the partial buffer to allocate. + * Must be between min_samples and max_samples. + */ + int partial_buf_size; + + /** + * Minimum number of samples to filter at once. If filter_samples() is + * called with fewer samples, it will accumulate them in partial_buf. + * This field and the related ones must not be changed after filtering + * has started. + * If 0, all related fields are ignored. + */ + int min_samples; + + /** + * Maximum number of samples to filter at once. If filter_samples() is + * called with more samples, it will split them. + */ + int max_samples; }; /**