From 158d2e15454a7f7e2db72204697b2fa0e6e75a52 Mon Sep 17 00:00:00 2001 From: "addr-see-the-website@aetey.se" Date: Wed, 28 Jun 2017 00:27:13 +0200 Subject: [PATCH] cinepakenc: add option handling for flexibility Now it is possible to adjust compression speed vs R/D when needed and also skip vintage player compatibility at will. Signed-off-by: Diego Biurrun --- libavcodec/cinepakenc.c | 92 +++++++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 30 deletions(-) diff --git a/libavcodec/cinepakenc.c b/libavcodec/cinepakenc.c index 1b63260e4d..74d965eab4 100644 --- a/libavcodec/cinepakenc.c +++ b/libavcodec/cinepakenc.c @@ -25,7 +25,6 @@ OTHER DEALINGS IN THE SOFTWARE. * TODO: * - optimize: color space conversion (move conversion to libswscale), ... - * - implement options to set the min/max number of strips? * MAYBE: * - "optimally" split the frame into several non-regular areas * using a separate codebook pair for each area and approximating @@ -38,6 +37,8 @@ OTHER DEALINGS IN THE SOFTWARE. #include "libavutil/common.h" #include "libavutil/internal.h" #include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" + #include "avcodec.h" #include "libavutil/lfg.h" #include "elbg.h" @@ -55,9 +56,7 @@ OTHER DEALINGS IN THE SOFTWARE. #define VECTOR_MAX 6 //six or four entries per vector depending on format #define CODEBOOK_MAX 256 //size of a codebook -//#define MAX_STRIPS 32 //Note: having fewer choices regarding the number of strips speeds up encoding (obviously) -#define MAX_STRIPS 3 // This seems to be max for vintage players! -- rl -// TODO: we might want to have a "vintage compatibilty" switch +#define MAX_STRIPS 32 //Note: having fewer choices regarding the number of strips speeds up encoding (obviously) #define MIN_STRIPS 1 //Note: having more strips speeds up encoding the frame (this is less obvious) // MAX_STRIPS limits the maximum quality you can reach // when you want high quality on high resolutions, @@ -102,6 +101,7 @@ typedef struct { } strip_info; typedef struct { + const AVClass *class; AVCodecContext *avctx; unsigned char *pict_bufs[4], *strip_buf, *frame_buf; AVFrame *last_frame; @@ -124,8 +124,32 @@ typedef struct { int num_v1_mode, num_v4_mode, num_mc_mode; int num_v1_encs, num_v4_encs, num_skips; #endif +// options + int max_extra_cb_iterations; + int skip_empty_cb; + int min_min_strips; + int max_max_strips; + int strip_number_delta_range; } CinepakEncContext; +#define OFFSET(x) offsetof(CinepakEncContext, x) +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + { "max_extra_cb_iterations", "Max extra codebook recalculation passes, more is better and slower", OFFSET(max_extra_cb_iterations), AV_OPT_TYPE_INT, { .i64 = 2 }, 0, INT_MAX, VE }, + { "skip_empty_cb", "Avoid wasting bytes, ignore vintage MacOS decoder", OFFSET(skip_empty_cb), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, + { "max_strips", "Limit strips/frame, vintage compatible is 1..3, otherwise the more the better", OFFSET(max_max_strips), AV_OPT_TYPE_INT, { .i64 = 3 }, MIN_STRIPS, MAX_STRIPS, VE }, + { "min_strips", "Enforce min strips/frame, more is worse and faster, must be <= max_strips", OFFSET(min_min_strips), AV_OPT_TYPE_INT, { .i64 = MIN_STRIPS }, MIN_STRIPS, MAX_STRIPS, VE }, + { "strip_number_adaptivity", "How fast the strip number adapts, more is slightly better, much slower", OFFSET(strip_number_delta_range), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, MAX_STRIPS-MIN_STRIPS, VE }, + { NULL }, +}; + +static const AVClass cinepak_class = { + .class_name = "cinepak", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + static av_cold int cinepak_encode_init(AVCodecContext *avctx) { CinepakEncContext *s = avctx->priv_data; @@ -137,6 +161,12 @@ static av_cold int cinepak_encode_init(AVCodecContext *avctx) return AVERROR(EINVAL); } + if (s->min_min_strips > s->max_max_strips) { + av_log(avctx, AV_LOG_ERROR, "minimal number of strips can not exceed maximal (got %i and %i)\n", + s->min_min_strips, s->max_max_strips); + return AVERROR(EINVAL); + } + if (!(s->last_frame = av_frame_alloc())) return AVERROR(ENOMEM); if (!(s->best_frame = av_frame_alloc())) @@ -165,7 +195,7 @@ static av_cold int cinepak_encode_init(AVCodecContext *avctx) //and 2*256 extra flag bits per strip strip_buf_size = STRIP_HEADER_SIZE + 3 * CHUNK_HEADER_SIZE + 2 * VECTOR_MAX * CODEBOOK_MAX + 4 * (mb_count + (mb_count + 15) / 16) + (2 * CODEBOOK_MAX)/8; - frame_buf_size = CVID_HEADER_SIZE + MAX_STRIPS * strip_buf_size; + frame_buf_size = CVID_HEADER_SIZE + s->max_max_strips * strip_buf_size; if (!(s->strip_buf = av_malloc(strip_buf_size))) goto enomem; @@ -218,8 +248,8 @@ static av_cold int cinepak_encode_init(AVCodecContext *avctx) s->input_frame->linesize[1] = s->input_frame->linesize[2] = s->w >> 1; } - s->min_strips = MIN_STRIPS; - s->max_strips = MAX_STRIPS; + s->min_strips = s->min_min_strips; + s->max_strips = s->max_max_strips; #ifdef CINEPAKENC_DEBUG s->num_v1_mode = s->num_v4_mode = s->num_mc_mode = s->num_v1_encs = s->num_v4_encs = s->num_skips = 0; @@ -566,10 +596,10 @@ static int encode_mode(CinepakEncContext *s, int h, AVPicture *scratch_pict, AVP ////// MacOS vintage decoder compatibility dictates the presence of ////// the codebook chunk even when the codebook is empty - pretty dumb... ////// and also the certain order of the codebook chunks -- rl -// if(info->v4_size) + if(info->v4_size || !s->skip_empty_cb) ret += encode_codebook(s, info->v4_codebook, info->v4_size, 0x20, 0x24, buf + ret); -// if(info->v1_size) + if(info->v1_size || !s->skip_empty_cb) ret += encode_codebook(s, info->v1_codebook, info->v1_size, 0x22, 0x26, buf + ret); //update scratch picture @@ -938,6 +968,7 @@ static int rd_strip(CinepakEncContext *s, int y, int h, int keyframe, AVPicture ); if(mode != MODE_V1_ONLY){ + int extra_iterations_limit = s->max_extra_cb_iterations; // recompute the codebooks, omitting the extra blocks // we assume we _may_ come here with more blocks to encode than before info.v1_size = v1_size; @@ -964,8 +995,8 @@ static int rd_strip(CinepakEncContext *s, int y, int h, int keyframe, AVPicture , &serr #endif ); -// do we have a reason to reiterate? - if(!v1shrunk && !v4shrunk) break; +// do we have a reason to reiterate? if so, have we reached the limit? + if((!v1shrunk && !v4shrunk) || !extra_iterations_limit--) break; // recompute the codebooks, omitting the extra blocks if(v1shrunk) { info.v1_size = v1_size; @@ -1054,7 +1085,7 @@ static int write_cvid_header(CinepakEncContext *s, unsigned char *buf, int num_s return CVID_HEADER_SIZE; } -static int rd_frame(CinepakEncContext *s, AVFrame *frame, int isakeyframe, unsigned char *buf, int buf_size) +static int rd_frame(CinepakEncContext *s, const AVFrame *frame, int isakeyframe, unsigned char *buf, int buf_size) { int num_strips, strip, i, y, nexty, size, temp_size, best_size; AVPicture last_pict, pict, scratch_pict; @@ -1178,25 +1209,25 @@ static int rd_frame(CinepakEncContext *s, AVFrame *frame, int isakeyframe, unsig // let the number of strips slowly adapt to the changes in the contents, // compared to full bruteforcing every time this will occasionally lead // to some r/d performance loss but makes encoding up to several times faster -#ifdef CINEPAK_AGGRESSIVE_STRIP_NUMBER_ADAPTIVITY - s->max_strips = best_nstrips + 4; - if(s->max_strips >= MAX_STRIPS) - s->max_strips = MAX_STRIPS; - s->min_strips = best_nstrips - 4; - if(s->min_strips < MIN_STRIPS) - s->min_strips = MIN_STRIPS; -#else - if(best_nstrips == s->max_strips) { // let us try to step up - s->max_strips = best_nstrips + 1; - if(s->max_strips >= MAX_STRIPS) - s->max_strips = MAX_STRIPS; - } else { // try to step down - s->max_strips = best_nstrips; + if(!s->strip_number_delta_range) { + if(best_nstrips == s->max_strips) { // let us try to step up + s->max_strips = best_nstrips + 1; + if(s->max_strips >= s->max_max_strips) + s->max_strips = s->max_max_strips; + } else { // try to step down + s->max_strips = best_nstrips; + } + s->min_strips = s->max_strips - 1; + if(s->min_strips < s->min_min_strips) + s->min_strips = s->min_min_strips; + } else { + s->max_strips = best_nstrips + s->strip_number_delta_range; + if(s->max_strips >= s->max_max_strips) + s->max_strips = s->max_max_strips; + s->min_strips = best_nstrips - s->strip_number_delta_range; + if(s->min_strips < s->min_min_strips) + s->min_strips = s->min_min_strips; } - s->min_strips = s->max_strips - 1; - if(s->min_strips < MIN_STRIPS) - s->min_strips = MIN_STRIPS; -#endif return best_size; } @@ -1265,4 +1296,5 @@ AVCodec ff_cinepak_encoder = { .close = cinepak_encode_end, .pix_fmts = (const enum AVPixelFormat[]){AV_PIX_FMT_RGB24, AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE}, .long_name = NULL_IF_CONFIG_SMALL("Cinepak"), + .priv_class = &cinepak_class, };