ffmpeg: Flush filters before re-initialization.

Also add another condition for re-initialization: if the
input resolution changes. This triggers the filter graph
to re-build and adjust to the new resolution, when CPU
encoders are in use.
This commit is contained in:
Josh Allmann
2024-08-16 00:27:53 +00:00
parent b5181eb92c
commit be728e92af
3 changed files with 42 additions and 4 deletions

View File

@@ -332,7 +332,7 @@ reopen_out_err:
return ret;
}
static int encode(AVCodecContext* encoder, AVFrame *frame, struct output_ctx* octx, AVStream* ost)
int encode(AVCodecContext* encoder, AVFrame *frame, struct output_ctx* octx, AVStream* ost)
{
int ret = 0;
AVPacket *pkt = NULL;

View File

@@ -12,5 +12,6 @@ void free_output(struct output_ctx *octx);
int process_out(struct input_ctx *ictx, struct output_ctx *octx, AVCodecContext *encoder, AVStream *ost,
struct filter_ctx *filter, AVFrame *inf);
int mux(AVPacket *pkt, AVRational tb, struct output_ctx *octx, AVStream *ost);
int encode(AVCodecContext* encoder, AVFrame *frame, struct output_ctx* octx, AVStream* ost);
#endif // _LPMS_ENCODER_H_

View File

@@ -1,4 +1,5 @@
#include "filter.h"
#include "encoder.h"
#include "logging.h"
#include <libavfilter/buffersrc.h>
@@ -282,9 +283,45 @@ int filtergraph_write(AVFrame *inf, struct input_ctx *ictx, struct output_ctx *o
// We have to reset the filter because we initially set the filter
// before the decoder is fully ready, and the decoder may change HW params
// XXX: Unclear if this path is hit on all devices
if (is_video && inf && inf->hw_frames_ctx && filter->hwframes &&
inf->hw_frames_ctx->data != filter->hwframes) {
free_filter(&octx->vf); // XXX really should flush filter first
if (is_video && inf && (
(inf->hw_frames_ctx && filter->hwframes &&
inf->hw_frames_ctx->data != filter->hwframes) ||
(filter->src_ctx->nb_outputs > 0 &&
filter->src_ctx->outputs[0]->w != inf->width &&
filter->src_ctx->outputs[0]->h != inf->height))) {
// flush video filter
ret = av_buffersrc_write_frame(filter->src_ctx, NULL);
if (ret < 0) LPMS_ERR(fg_write_cleanup, "Error closing filter for reinit");
while (!ret) {
ret = filtergraph_read(ictx, octx, filter, is_video);
if (AVERROR(EAGAIN) == ret || AVERROR_EOF == ret) break;
AVFrame *frame = filter->frame;
AVCodecContext *encoder = octx->vc;
// TODO does clipping need to be handled?
// TODO calculate signature?
// Set GOP interval if necessary
if (octx->gop_pts_len && frame && frame->pts >= octx->next_kf_pts) {
frame->pict_type = AV_PICTURE_TYPE_I;
octx->next_kf_pts = frame->pts + octx->gop_pts_len;
}
if (frame) {
// rescale pts to match encoder timebase if necessary (eg, fps passthrough)
AVRational filter_tb = av_buffersink_get_time_base(filter->sink_ctx);
if (av_cmp_q(filter_tb, encoder->time_base)) {
frame->pts = av_rescale_q(frame->pts, filter_tb, encoder->time_base);
// TODO does frame->duration needs to be rescaled too?
}
}
ret = encode(encoder, frame, octx, octx->oc->streams[octx->vi]);
if (!ret) LPMS_ERR(fg_write_cleanup, "Encoder error during filter reinit");
}
ret = 0;
free_filter(&octx->vf);
ret = init_video_filters(ictx, octx);
if (ret < 0) return lpms_ERR_FILTERS;
}