| @@ -27,12 +27,9 @@ | |||||
| #include "libavutil/avstring.h" | #include "libavutil/avstring.h" | ||||
| #include "libavutil/opt.h" | #include "libavutil/opt.h" | ||||
| #define FF_INTERNAL_FIELDS 1 | |||||
| #include "framequeue.h" | |||||
| #include "avfilter.h" | #include "avfilter.h" | ||||
| #include "bufferqueue.h" | |||||
| #include "formats.h" | #include "formats.h" | ||||
| #include "filters.h" | |||||
| #include "internal.h" | #include "internal.h" | ||||
| #include "audio.h" | #include "audio.h" | ||||
| #include "video.h" | #include "video.h" | ||||
| @@ -40,7 +37,7 @@ | |||||
| typedef struct InterleaveContext { | typedef struct InterleaveContext { | ||||
| const AVClass *class; | const AVClass *class; | ||||
| int nb_inputs; | int nb_inputs; | ||||
| struct FFBufQueue *queues; | |||||
| int64_t pts; | |||||
| } InterleaveContext; | } InterleaveContext; | ||||
| #define OFFSET(x) offsetof(InterleaveContext, x) | #define OFFSET(x) offsetof(InterleaveContext, x) | ||||
| @@ -52,58 +49,78 @@ static const AVOption filt_name##_options[] = { \ | |||||
| { NULL } \ | { NULL } \ | ||||
| } | } | ||||
| inline static int push_frame(AVFilterContext *ctx) | |||||
| static int activate(AVFilterContext *ctx) | |||||
| { | { | ||||
| AVFilterLink *outlink = ctx->outputs[0]; | |||||
| InterleaveContext *s = ctx->priv; | InterleaveContext *s = ctx->priv; | ||||
| AVFrame *frame; | |||||
| int i, queue_idx = -1; | |||||
| int64_t pts_min = INT64_MAX; | |||||
| int64_t q_pts, pts = INT64_MAX; | |||||
| int i, nb_eofs = 0, input_idx = -1; | |||||
| FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); | |||||
| /* look for oldest frame */ | |||||
| for (i = 0; i < ctx->nb_inputs; i++) { | for (i = 0; i < ctx->nb_inputs; i++) { | ||||
| struct FFBufQueue *q = &s->queues[i]; | |||||
| if (!ff_outlink_get_status(ctx->inputs[i]) && | |||||
| !ff_inlink_queued_frames(ctx->inputs[i])) | |||||
| break; | |||||
| } | |||||
| if (!q->available && !ctx->inputs[i]->status_out) | |||||
| return 0; | |||||
| if (q->available) { | |||||
| frame = ff_bufqueue_peek(q, 0); | |||||
| if (frame->pts < pts_min) { | |||||
| pts_min = frame->pts; | |||||
| queue_idx = i; | |||||
| if (i == ctx->nb_inputs) { | |||||
| for (i = 0; i < ctx->nb_inputs; i++) { | |||||
| AVFrame *frame; | |||||
| if (ff_outlink_get_status(ctx->inputs[i])) | |||||
| continue; | |||||
| frame = ff_inlink_peek_frame(ctx->inputs[i], 0); | |||||
| if (frame->pts == AV_NOPTS_VALUE) { | |||||
| int ret; | |||||
| av_log(ctx, AV_LOG_WARNING, | |||||
| "NOPTS value for input frame cannot be accepted, frame discarded\n"); | |||||
| ret = ff_inlink_consume_frame(ctx->inputs[i], &frame); | |||||
| if (ret < 0) | |||||
| return ret; | |||||
| av_frame_free(&frame); | |||||
| return AVERROR_INVALIDDATA; | |||||
| } | |||||
| q_pts = av_rescale_q(frame->pts, ctx->inputs[i]->time_base, AV_TIME_BASE_Q); | |||||
| if (q_pts < pts) { | |||||
| pts = q_pts; | |||||
| input_idx = i; | |||||
| } | } | ||||
| } | } | ||||
| } | |||||
| /* all inputs are closed */ | |||||
| if (queue_idx < 0) | |||||
| return AVERROR_EOF; | |||||
| if (input_idx >= 0) { | |||||
| AVFrame *frame; | |||||
| int ret; | |||||
| frame = ff_bufqueue_get(&s->queues[queue_idx]); | |||||
| av_log(ctx, AV_LOG_DEBUG, "queue:%d -> frame time:%f\n", | |||||
| queue_idx, frame->pts * av_q2d(AV_TIME_BASE_Q)); | |||||
| return ff_filter_frame(ctx->outputs[0], frame); | |||||
| } | |||||
| ret = ff_inlink_consume_frame(ctx->inputs[input_idx], &frame); | |||||
| if (ret < 0) | |||||
| return ret; | |||||
| static int filter_frame(AVFilterLink *inlink, AVFrame *frame) | |||||
| { | |||||
| AVFilterContext *ctx = inlink->dst; | |||||
| InterleaveContext *s = ctx->priv; | |||||
| unsigned in_no = FF_INLINK_IDX(inlink); | |||||
| frame->pts = s->pts = pts; | |||||
| return ff_filter_frame(outlink, frame); | |||||
| } | |||||
| } | |||||
| if (frame->pts == AV_NOPTS_VALUE) { | |||||
| av_log(ctx, AV_LOG_WARNING, | |||||
| "NOPTS value for input frame cannot be accepted, frame discarded\n"); | |||||
| av_frame_free(&frame); | |||||
| return AVERROR_INVALIDDATA; | |||||
| for (i = 0; i < ctx->nb_inputs; i++) { | |||||
| if (ff_inlink_queued_frames(ctx->inputs[i])) | |||||
| continue; | |||||
| if (ff_outlink_frame_wanted(outlink) && | |||||
| !ff_outlink_get_status(ctx->inputs[i])) { | |||||
| ff_inlink_request_frame(ctx->inputs[i]); | |||||
| return 0; | |||||
| } | |||||
| nb_eofs++; | |||||
| } | } | ||||
| /* queue frame */ | |||||
| frame->pts = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q); | |||||
| av_log(ctx, AV_LOG_DEBUG, "frame pts:%f -> queue idx:%d available:%d\n", | |||||
| frame->pts * av_q2d(AV_TIME_BASE_Q), in_no, s->queues[in_no].available); | |||||
| ff_bufqueue_add(ctx, &s->queues[in_no], frame); | |||||
| if (nb_eofs == ctx->nb_inputs) { | |||||
| ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); | |||||
| return 0; | |||||
| } | |||||
| return push_frame(ctx); | |||||
| return FFERROR_NOT_READY; | |||||
| } | } | ||||
| static av_cold int init(AVFilterContext *ctx) | static av_cold int init(AVFilterContext *ctx) | ||||
| @@ -112,10 +129,6 @@ static av_cold int init(AVFilterContext *ctx) | |||||
| const AVFilterPad *outpad = &ctx->filter->outputs[0]; | const AVFilterPad *outpad = &ctx->filter->outputs[0]; | ||||
| int i, ret; | int i, ret; | ||||
| s->queues = av_calloc(s->nb_inputs, sizeof(s->queues[0])); | |||||
| if (!s->queues) | |||||
| return AVERROR(ENOMEM); | |||||
| for (i = 0; i < s->nb_inputs; i++) { | for (i = 0; i < s->nb_inputs; i++) { | ||||
| AVFilterPad inpad = { 0 }; | AVFilterPad inpad = { 0 }; | ||||
| @@ -123,7 +136,6 @@ static av_cold int init(AVFilterContext *ctx) | |||||
| if (!inpad.name) | if (!inpad.name) | ||||
| return AVERROR(ENOMEM); | return AVERROR(ENOMEM); | ||||
| inpad.type = outpad->type; | inpad.type = outpad->type; | ||||
| inpad.filter_frame = filter_frame; | |||||
| switch (outpad->type) { | switch (outpad->type) { | ||||
| case AVMEDIA_TYPE_VIDEO: | case AVMEDIA_TYPE_VIDEO: | ||||
| @@ -144,14 +156,8 @@ static av_cold int init(AVFilterContext *ctx) | |||||
| static av_cold void uninit(AVFilterContext *ctx) | static av_cold void uninit(AVFilterContext *ctx) | ||||
| { | { | ||||
| InterleaveContext *s = ctx->priv; | |||||
| int i; | |||||
| for (i = 0; i < ctx->nb_inputs; i++) { | |||||
| ff_bufqueue_discard_all(&s->queues[i]); | |||||
| av_freep(&s->queues[i]); | |||||
| for (int i = 0; i < ctx->nb_inputs; i++) | |||||
| av_freep(&ctx->input_pads[i].name); | av_freep(&ctx->input_pads[i].name); | ||||
| } | |||||
| } | } | ||||
| static int config_output(AVFilterLink *outlink) | static int config_output(AVFilterLink *outlink) | ||||
| @@ -190,23 +196,6 @@ static int config_output(AVFilterLink *outlink) | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| static int request_frame(AVFilterLink *outlink) | |||||
| { | |||||
| AVFilterContext *ctx = outlink->src; | |||||
| InterleaveContext *s = ctx->priv; | |||||
| int i, ret; | |||||
| for (i = 0; i < ctx->nb_inputs; i++) { | |||||
| if (!s->queues[i].available && !ctx->inputs[i]->status_out) { | |||||
| ret = ff_request_frame(ctx->inputs[i]); | |||||
| if (ret != AVERROR_EOF) | |||||
| return ret; | |||||
| } | |||||
| } | |||||
| return push_frame(ctx); | |||||
| } | |||||
| #if CONFIG_INTERLEAVE_FILTER | #if CONFIG_INTERLEAVE_FILTER | ||||
| DEFINE_OPTIONS(interleave, AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM); | DEFINE_OPTIONS(interleave, AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM); | ||||
| @@ -217,7 +206,6 @@ static const AVFilterPad interleave_outputs[] = { | |||||
| .name = "default", | .name = "default", | ||||
| .type = AVMEDIA_TYPE_VIDEO, | .type = AVMEDIA_TYPE_VIDEO, | ||||
| .config_props = config_output, | .config_props = config_output, | ||||
| .request_frame = request_frame, | |||||
| }, | }, | ||||
| { NULL } | { NULL } | ||||
| }; | }; | ||||
| @@ -228,6 +216,7 @@ AVFilter ff_vf_interleave = { | |||||
| .priv_size = sizeof(InterleaveContext), | .priv_size = sizeof(InterleaveContext), | ||||
| .init = init, | .init = init, | ||||
| .uninit = uninit, | .uninit = uninit, | ||||
| .activate = activate, | |||||
| .outputs = interleave_outputs, | .outputs = interleave_outputs, | ||||
| .priv_class = &interleave_class, | .priv_class = &interleave_class, | ||||
| .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, | .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, | ||||
| @@ -245,7 +234,6 @@ static const AVFilterPad ainterleave_outputs[] = { | |||||
| .name = "default", | .name = "default", | ||||
| .type = AVMEDIA_TYPE_AUDIO, | .type = AVMEDIA_TYPE_AUDIO, | ||||
| .config_props = config_output, | .config_props = config_output, | ||||
| .request_frame = request_frame, | |||||
| }, | }, | ||||
| { NULL } | { NULL } | ||||
| }; | }; | ||||
| @@ -256,6 +244,7 @@ AVFilter ff_af_ainterleave = { | |||||
| .priv_size = sizeof(InterleaveContext), | .priv_size = sizeof(InterleaveContext), | ||||
| .init = init, | .init = init, | ||||
| .uninit = uninit, | .uninit = uninit, | ||||
| .activate = activate, | |||||
| .outputs = ainterleave_outputs, | .outputs = ainterleave_outputs, | ||||
| .priv_class = &ainterleave_class, | .priv_class = &ainterleave_class, | ||||
| .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, | .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, | ||||