avcodec/cinepakenc: use av_malloc_array()
[ffmpeg.git] / libavcodec / cinepakenc.c
index db4aa21..7277345 100644 (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;
@@ -203,11 +236,11 @@ static av_cold int cinepak_encode_init(AVCodecContext *avctx)
     if (!(s->frame_buf = av_malloc(frame_buf_size)))
         goto enomem;
 
-    if (!(s->mb = av_malloc(mb_count*sizeof(mb_info))))
+    if (!(s->mb = av_malloc_array(mb_count, sizeof(mb_info))))
         goto enomem;
 
 #ifdef CINEPAKENC_DEBUG
-    if (!(s->best_mb = av_malloc(mb_count*sizeof(mb_info))))
+    if (!(s->best_mb = av_malloc_array(mb_count, sizeof(mb_info))))
         goto enomem;
 #endif
 
@@ -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;
@@ -590,16 +623,16 @@ static int encode_mode(CinepakEncContext *s, int h, AVPicture *scratch_pict, AVP
     int needs_extra_bit, should_write_temp;
     unsigned char temp[64]; //32/2 = 16 V4 blocks at 4 B each -> 64 B
     mb_info *mb;
-    AVPicture sub_scratch, sub_last;
+    AVPicture sub_scratch = {{0}}, sub_last = {{0}};
 
     //encode codebooks
 ////// 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,16 +1118,16 @@ 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;
+    int num_strips, strip, i, y, nexty, size, temp_size;
     AVPicture last_pict, pict, scratch_pict;
     int64_t best_score = 0, score, score_temp;
 #ifdef CINEPAK_REPORT_SERR
     int64_t best_serr = 0, serr, serr_temp;
 #endif
 
-    int best_nstrips;
+    int best_nstrips = -1, best_size = -1; // mark as uninitialzed
 
     if(s->pix_fmt == AV_PIX_FMT_RGB24) {
         int x;
@@ -1205,28 +1239,30 @@ static int rd_frame(CinepakEncContext *s, AVFrame *frame, int isakeyframe, unsig
             break;
     }
 
+    av_assert0(best_nstrips >= 0 && best_size >= 0);
+
 // 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;
 }
@@ -1247,8 +1283,6 @@ static int cinepak_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
         pkt->flags |= AV_PKT_FLAG_KEY;
     *got_packet = 1;
 
-    avctx->coded_frame = frame;
-
     FFSWAP(AVFrame *, s->last_frame, s->best_frame);
 
     if (++s->curframe >= s->keyint)
@@ -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,
 };