diff -up ./configure.orig ./configure --- ./configure.orig 2016-03-14 10:10:57.000000000 +0100 +++ ./configure 2016-08-09 13:20:27.159342444 +0200 @@ -395,7 +395,7 @@ else has_libav=false fi - if $has_libav && ! check_pkg libswscale ">=2.3.100"; then + if $has_libav && ! check_pkg libavfilter ">=4.0.0"; then has_libav=false fi @@ -421,7 +421,7 @@ else has_libav=false fi - if $has_libav && ! check_pkg libswscale ">=2.1.2"; then + if $has_libav && ! check_pkg libacvfilter ">=4.0.0"; then has_libav=false fi diff -up ./src/libav.c.orig ./src/libav.c --- ./src/libav.c.orig 2016-03-14 10:10:57.000000000 +0100 +++ ./src/libav.c 2016-08-09 13:20:27.159342444 +0200 @@ -186,4 +186,5 @@ libav_init(void) av_log_set_callback(libav_log_callback); av_log_set_level(AV_LOG_VERBOSE); av_register_all(); + avfilter_register_all(); } diff -up ./src/libav.h.orig ./src/libav.h --- ./src/libav.h.orig 2016-03-14 10:10:57.000000000 +0100 +++ ./src/libav.h 2016-08-09 13:20:27.159342444 +0200 @@ -21,6 +21,7 @@ #include +#include #include "tvheadend.h" /* diff -up ./src/plumbing/transcoding.c.orig ./src/plumbing/transcoding.c --- ./src/plumbing/transcoding.c.orig 2016-08-09 13:20:21.782349916 +0200 +++ ./src/plumbing/transcoding.c 2016-08-09 13:20:28.686340322 +0200 @@ -19,12 +19,14 @@ #include #include #include -#include +#include +#include +#include +#include #include #include #include #include -#include #if LIBAVUTIL_VERSION_MICRO >= 100 /* FFMPEG */ #define USING_FFMPEG 1 @@ -91,9 +93,12 @@ typedef struct video_stream { AVCodec *vid_ocodec; AVFrame *vid_dec_frame; - struct SwsContext *vid_scaler; AVFrame *vid_enc_frame; + AVFilterGraph *flt_graph; + AVFilterContext *flt_bufsrcctx; + AVFilterContext *flt_bufsinkctx; + int16_t vid_width; int16_t vid_height; @@ -267,6 +272,125 @@ transcoder_stream_packet(transcoder_t *t } +/* create a simple deinterlacer-scaler video filter chain */ +static int +create_video_filter(video_stream_t *vs, transcoder_t *t, + AVCodecContext *ictx, AVCodecContext *octx) +{ + AVFilterInOut *flt_inputs, *flt_outputs; + AVFilter *flt_bufsrc, *flt_bufsink; + enum AVPixelFormat pix_fmts[] = { 0, AV_PIX_FMT_NONE }; + char opt[128]; + int err; + + err = 1; + flt_inputs = flt_outputs = NULL; + flt_bufsrc = flt_bufsink = NULL; + + if (vs->flt_graph) + avfilter_graph_free(&vs->flt_graph); + + vs->flt_graph = avfilter_graph_alloc(); + if (!vs->flt_graph) + return err; + + flt_inputs = avfilter_inout_alloc(); + if (!flt_inputs) + goto out_err; + + flt_outputs = avfilter_inout_alloc(); + if (!flt_outputs) + goto out_err; + + flt_bufsrc = avfilter_get_by_name("buffer"); + flt_bufsink = avfilter_get_by_name("buffersink"); + if (!flt_bufsrc || !flt_bufsink) { + tvherror("transcode", "%04X: libav default buffers unknown", shortid(t)); + goto out_err; + } + + memset(opt, 0, sizeof(opt)); + snprintf(opt, sizeof(opt), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", + ictx->width, + ictx->height, + ictx->pix_fmt, + ictx->time_base.num, + ictx->time_base.den, + ictx->sample_aspect_ratio.num, + ictx->sample_aspect_ratio.den); + + err = avfilter_graph_create_filter(&vs->flt_bufsrcctx, flt_bufsrc, "in", + opt, NULL, vs->flt_graph); + if (err < 0) { + tvherror("transcode", "%04X: fltchain IN init error", shortid(t)); + goto out_err; + } + + err = avfilter_graph_create_filter(&vs->flt_bufsinkctx, flt_bufsink, + "out", NULL, NULL, vs->flt_graph); + if (err < 0) { + tvherror("transcode", "%04X: fltchain OUT init error", shortid(t)); + goto out_err; + } + + pix_fmts[0] = octx->pix_fmt; + err = av_opt_set_int_list(vs->flt_bufsinkctx, "pix_fmts", pix_fmts, + AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN); + if (err < 0) { + tvherror("transcode", "%08X: fltchain cannot set output pixfmt", + shortid(t)); + goto out_err; + } + + flt_outputs->name = av_strdup("in"); + flt_outputs->filter_ctx = vs->flt_bufsrcctx; + flt_outputs->pad_idx = 0; + flt_outputs->next = NULL; + flt_inputs->name = av_strdup("out"); + flt_inputs->filter_ctx = vs->flt_bufsinkctx; + flt_inputs->pad_idx = 0; + flt_inputs->next = NULL; + + /* add filters: yadif to deinterlace and a scaler */ + memset(opt, 0, sizeof(opt)); + snprintf(opt, sizeof(opt), "yadif,scale=%dx%d", + octx->width, + octx->height); + err = avfilter_graph_parse_ptr(vs->flt_graph, + opt, + &flt_inputs, + &flt_outputs, + NULL); + if (err < 0) { + tvherror("transcode", "%04X: failed to init filter chain", shortid(t)); + goto out_err; + } + + err = avfilter_graph_config(vs->flt_graph, NULL); + if (err < 0) { + tvherror("transcode", "%04X: failed to config filter chain", shortid(t)); + goto out_err; + } + + avfilter_inout_free(&flt_inputs); + avfilter_inout_free(&flt_outputs); + + return 0; /* all OK */ + +out_err: + if (flt_inputs) + avfilter_inout_free(&flt_inputs); + if (flt_outputs) + avfilter_inout_free(&flt_outputs); + if (vs->flt_graph) { + avfilter_graph_free(&vs->flt_graph); + vs->flt_graph = NULL; + } + + return err; +} + + /** * */ @@ -592,12 +716,12 @@ transcoder_stream_audio(transcoder_t *t, // Convert audio tvhtrace("transcode", "%04X: converting audio", shortid(t)); - tvhtrace("transcode", "%04X: IN : channels=%d, layout=%" PRIi64 ", rate=%d, fmt=%d, bitrate=%d", + tvhtrace("transcode", "%04X: IN : channels=%d, layout=%" PRIi64 ", rate=%d, fmt=%d, bitrate=%"PRId64, shortid(t), ictx->channels, ictx->channel_layout, ictx->sample_rate, - ictx->sample_fmt, ictx->bit_rate); - tvhtrace("transcode", "%04X: OUT: channels=%d, layout=%" PRIi64 ", rate=%d, fmt=%d, bitrate=%d", + ictx->sample_fmt, (int64_t)ictx->bit_rate); + tvhtrace("transcode", "%04X: OUT: channels=%d, layout=%" PRIi64 ", rate=%d, fmt=%d, bitrate=%"PRId64, shortid(t), octx->channels, octx->channel_layout, octx->sample_rate, - octx->sample_fmt, octx->bit_rate); + octx->sample_fmt, (int64_t)octx->bit_rate); if (transcode_opt_set_int(t, ts, as->resample_context, "in_channel_layout", ictx->channel_layout, 1)) @@ -962,9 +1086,7 @@ transcoder_stream_video(transcoder_t *t, AVCodecContext *ictx, *octx; AVDictionary *opts; AVPacket packet, packet2; - AVPicture deint_pic; - uint8_t *buf, *deint; - int length, len, ret, got_picture, got_output, got_ref; + int length, ret, got_picture, got_output, got_ref; video_stream_t *vs = (video_stream_t*)ts; streaming_message_t *sm; th_pkt_t *pkt2; @@ -980,7 +1102,6 @@ transcoder_stream_video(transcoder_t *t, icodec = vs->vid_icodec; ocodec = vs->vid_ocodec; - buf = deint = NULL; opts = NULL; got_ref = 0; @@ -1061,7 +1182,7 @@ transcoder_stream_video(transcoder_t *t, switch (ts->ts_type) { case SCT_MPEG2VIDEO: octx->codec_id = AV_CODEC_ID_MPEG2VIDEO; - octx->pix_fmt = PIX_FMT_YUV420P; + octx->pix_fmt = AV_PIX_FMT_YUV420P; octx->flags |= CODEC_FLAG_GLOBAL_HEADER; // Default settings for quantizer. Best quality unless changed by the streaming profile. @@ -1089,7 +1210,7 @@ transcoder_stream_video(transcoder_t *t, case SCT_VP8: octx->codec_id = AV_CODEC_ID_VP8; - octx->pix_fmt = PIX_FMT_YUV420P; + octx->pix_fmt = AV_PIX_FMT_YUV420P; av_dict_set(&opts, "quality", "realtime", 0); @@ -1120,7 +1241,7 @@ transcoder_stream_video(transcoder_t *t, case SCT_H264: octx->codec_id = AV_CODEC_ID_H264; - octx->pix_fmt = PIX_FMT_YUV420P; + octx->pix_fmt = AV_PIX_FMT_YUV420P; octx->flags |= CODEC_FLAG_GLOBAL_HEADER; // Qscale difference between I-frames and P-frames. @@ -1177,79 +1298,53 @@ transcoder_stream_video(transcoder_t *t, transcoder_stream_invalidate(ts); goto cleanup; } - } - - len = avpicture_get_size(ictx->pix_fmt, ictx->width, ictx->height); - deint = av_malloc(len); - avpicture_fill(&deint_pic, - deint, - ictx->pix_fmt, - ictx->width, - ictx->height); - - if (avpicture_deinterlace(&deint_pic, - (AVPicture *)vs->vid_dec_frame, - ictx->pix_fmt, - ictx->width, - ictx->height) < 0) { - tvherror("transcode", "%04X: Cannot deinterlace frame", shortid(t)); - transcoder_stream_invalidate(ts); - goto cleanup; + if (create_video_filter(vs, t, ictx, octx)) { + tvherror("transcode", "%04X: Video filter creation failed", + shortid(t)); + transcoder_stream_invalidate(ts); + goto cleanup; + } } - len = avpicture_get_size(octx->pix_fmt, octx->width, octx->height); - buf = av_malloc(len + FF_INPUT_BUFFER_PADDING_SIZE); - memset(buf, 0, len); - - avpicture_fill((AVPicture *)vs->vid_enc_frame, - buf, - octx->pix_fmt, - octx->width, - octx->height); - - vs->vid_scaler = sws_getCachedContext(vs->vid_scaler, - ictx->width, - ictx->height, - ictx->pix_fmt, - octx->width, - octx->height, - octx->pix_fmt, - 1, - NULL, - NULL, - NULL); - - if (sws_scale(vs->vid_scaler, - (const uint8_t * const*)deint_pic.data, - deint_pic.linesize, - 0, - ictx->height, - vs->vid_enc_frame->data, - vs->vid_enc_frame->linesize) < 0) { - tvherror("transcode", "%04X: Cannot scale frame", shortid(t)); + /* push decoded frame into filter chain */ + if (av_buffersrc_add_frame(vs->flt_bufsrcctx, vs->vid_dec_frame) < 0) { + tvherror("transcode", "%04X: filter input error", shortid(t)); transcoder_stream_invalidate(ts); goto cleanup; } - vs->vid_enc_frame->format = octx->pix_fmt; - vs->vid_enc_frame->width = octx->width; - vs->vid_enc_frame->height = octx->height; - - vs->vid_enc_frame->pkt_pts = vs->vid_dec_frame->pkt_pts; - vs->vid_enc_frame->pkt_dts = vs->vid_dec_frame->pkt_dts; - - if (vs->vid_dec_frame->reordered_opaque != AV_NOPTS_VALUE) - vs->vid_enc_frame->pts = vs->vid_dec_frame->reordered_opaque; - - else if (ictx->coded_frame && ictx->coded_frame->pts != AV_NOPTS_VALUE) - vs->vid_enc_frame->pts = vs->vid_dec_frame->pts; - - ret = avcodec_encode_video2(octx, &packet2, vs->vid_enc_frame, &got_output); - if (ret < 0) { - tvherror("transcode", "%04X: Error encoding frame", shortid(t)); - transcoder_stream_invalidate(ts); - goto cleanup; + /* and pull out a filtered frame */ + while (1) { + ret = av_buffersink_get_frame(vs->flt_bufsinkctx, vs->vid_enc_frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) + break; + if (ret < 0) { + tvherror("transcode", "%04X: filter output error", shortid(t)); + transcoder_stream_invalidate(ts); + goto cleanup; + } + + vs->vid_enc_frame->format = octx->pix_fmt; + vs->vid_enc_frame->width = octx->width; + vs->vid_enc_frame->height = octx->height; + + vs->vid_enc_frame->pkt_pts = vs->vid_dec_frame->pkt_pts; + vs->vid_enc_frame->pkt_dts = vs->vid_dec_frame->pkt_dts; + + if (vs->vid_dec_frame->reordered_opaque != AV_NOPTS_VALUE) + vs->vid_enc_frame->pts = vs->vid_dec_frame->reordered_opaque; + + else if (ictx->coded_frame && ictx->coded_frame->pts != AV_NOPTS_VALUE) + vs->vid_enc_frame->pts = vs->vid_dec_frame->pts; + + ret = avcodec_encode_video2(octx, &packet2, vs->vid_enc_frame, &got_output); + if (ret < 0) { + tvherror("transcode", "%04X: Error encoding frame", shortid(t)); + transcoder_stream_invalidate(ts); + goto cleanup; + } + av_frame_unref(vs->vid_enc_frame); } if (got_output) @@ -1263,12 +1358,6 @@ transcoder_stream_video(transcoder_t *t, av_free_packet(&packet); - if(buf) - av_free(buf); - - if(deint) - av_free(deint); - if(opts) av_dict_free(&opts); @@ -1548,15 +1637,17 @@ transcoder_destroy_video(transcoder_t *t if(vs->vid_dec_frame) av_free(vs->vid_dec_frame); - if(vs->vid_scaler) - sws_freeContext(vs->vid_scaler); - if(vs->vid_enc_frame) av_free(vs->vid_enc_frame); if (vs->vid_first_pkt) pkt_ref_dec(vs->vid_first_pkt); + if (vs->flt_graph) { + avfilter_graph_free(&vs->flt_graph); + vs->flt_graph = NULL; + } + free(ts); } @@ -1603,11 +1694,13 @@ transcoder_init_video(transcoder_t *t, s vs->vid_ictx->thread_count = vs->vid_octx->thread_count = transcoder_thread_count(t, sct); - vs->vid_dec_frame = avcodec_alloc_frame(); - vs->vid_enc_frame = avcodec_alloc_frame(); + vs->vid_dec_frame = av_frame_alloc(); + vs->vid_enc_frame = av_frame_alloc(); + + av_frame_unref(vs->vid_dec_frame); + av_frame_unref(vs->vid_enc_frame); - avcodec_get_frame_defaults(vs->vid_dec_frame); - avcodec_get_frame_defaults(vs->vid_enc_frame); + vs->flt_graph = NULL; /* allocated in packet processor */ LIST_INSERT_HEAD(&t->t_stream_list, (transcoder_stream_t*)vs, ts_link);