You've already forked FFmpeg
							
							
				mirror of
				https://github.com/FFmpeg/FFmpeg.git
				synced 2025-10-30 23:18:11 +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:
		
				
					committed by
					
						 Michael Niedermayer
						Michael Niedermayer
					
				
			
			
				
	
			
			
			
						parent
						
							c1333a762c
						
					
				
				
					commit
					bf23642dcc
				
			| @@ -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, | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user