1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-11-26 19:01:44 +02:00

Cinepak encoder: adding option handling for flexibility

Now it is possible to adjust compression speed vs R/D when needed
and also skip vintage players compatibility at will.

Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
addr-see-the-website@aetey.se 2014-01-23 16:40:37 +01:00 committed by Michael Niedermayer
parent c1333a762c
commit bf23642dcc

View File

@ -33,6 +33,9 @@ OTHER DEALINGS IN THE SOFTWARE.
* (use quadtree splitting? a simple fixed-granularity grid?)
*
*
* version 2014-01-23 Rl
* - added option handling for flexibility
*
* version 2014-01-21 Rl
* - believe it or not, now we get even smaller files, with better quality
* (which means I missed an optimization earlier :)
@ -74,6 +77,7 @@ OTHER DEALINGS IN THE SOFTWARE.
#include "internal.h"
#include "libavutil/avassert.h"
#include "libavutil/opt.h"
#define CVID_HEADER_SIZE 10
#define STRIP_HEADER_SIZE 12
@ -85,9 +89,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 hight quality on high resolutions,
@ -132,6 +134,7 @@ typedef struct {
} strip_info;
typedef struct {
const AVClass *class;
AVCodecContext *avctx;
unsigned char *pict_bufs[4], *strip_buf, *frame_buf;
AVFrame *last_frame;
@ -154,8 +157,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;
@ -167,6 +194,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()))
@ -195,7 +228,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;
@ -248,8 +281,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;
@ -596,10 +629,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
@ -968,6 +1001,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;
@ -994,8 +1028,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;
@ -1084,7 +1118,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;
@ -1208,25 +1242,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;
}
@ -1297,4 +1331,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 / CVID"),
.priv_class = &cinepak_class,
};