From patchwork Thu Mar 7 14:42:09 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: matthew.w.fearnley@gmail.com X-Patchwork-Id: 12235 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id C77D8448613 for ; Thu, 7 Mar 2019 16:47:59 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id A0BDE68A571; Thu, 7 Mar 2019 16:47:59 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f65.google.com (mail-wr1-f65.google.com [209.85.221.65]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id B7098689CC4 for ; Thu, 7 Mar 2019 16:47:53 +0200 (EET) Received: by mail-wr1-f65.google.com with SMTP id w6so17741233wrs.4 for ; Thu, 07 Mar 2019 06:47:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=WhUjyomQ/qMQ0//H46FSRwHZ4cQVr24FqpgBHkt1a2s=; b=RJ0KkCEbRxJndmx5lT8SHaa5VB5hJ9NetY4gUaiAoVh3/dNWp2+Fx+u6nMGAYGMEXJ 1y2b0qc8bLBgKvOB8iNrgAXD7Vq6h78PBbydY6iO97UzL4qnNHBmB8u3CZ/3B2OJ7KGO j5jv2m3NVhxw3IoDr3FZwCLpyO7GRTuKMrY41obfoFpG/MU8P3eyNJU//+PDCHewd9M6 tfk19600ipTb9pM9Pu3opCsDYdYXy/tGCccvRMeqYxvKLIOQMIFun0WZjtX3TxhOvAvd gvHjWhIrBZXkDt+5rdzxcE0oLUOrOyvvkAoA2Jd9MmoVZr04er2PTHfBiCzFemVbxhfA HN5w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=WhUjyomQ/qMQ0//H46FSRwHZ4cQVr24FqpgBHkt1a2s=; b=KUwa5jZF4HezbGCOTSVgFidyKDBxpwX3sa1CXUYVytv8EXA6o7NJI2e//gahz9b9a2 qJei6spvyJklS1PmXdhvZ7PDF+sPaSwhY5wvDRL63+nkMBbhXkqlIvqc71y8Zh5ZqBiM xS5B5RAeIkDnSznqhXd7QkO00k4xlLIgWKWM0K5Aj6kabbd18aKZIoMztoYFHegZYsuF EmzIraV05XHVEp4msH5PR5hu/O4AO+rRrqxFcq223ZnpLGk4OPe3zakFRfT0v/ePz7sG m+GzIDjYMvq4rDcHu5bUQuu0heCmohXFIP/sQnyQd9pjtveZw7KJrgCryVMVLRjyNhZc CTJg== X-Gm-Message-State: APjAAAVkaflHK//pR6s/3T2wKUcptxAGDUU7l4w5gCZ/syOkRmyx4qKV a4U3OlmSr7QMlb/0W5ZmNkESbYTn X-Google-Smtp-Source: APXvYqy9sB0uPANXZ6WELpJP6NM55oIJiWScr4k4QQokOkKxIcRNO0ZChzRJ1PSP+Km8AOfi6H8R3g== X-Received: by 2002:adf:f691:: with SMTP id v17mr7358409wrp.66.1551969742905; Thu, 07 Mar 2019 06:42:22 -0800 (PST) Received: from aspire7315.localdomain (cpc131498-bagu18-2-0-cust88.know.cable.virginm.net. [86.9.33.89]) by smtp.gmail.com with ESMTPSA id c202sm4099978wme.38.2019.03.07.06.42.21 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 07 Mar 2019 06:42:22 -0800 (PST) From: Matthew Fearnley To: ffmpeg-devel@ffmpeg.org Date: Thu, 7 Mar 2019 14:42:09 +0000 Message-Id: <20190307144209.32046-1-matthew.w.fearnley@gmail.com> X-Mailer: git-send-email 2.17.1 Subject: [FFmpeg-devel] [PATCH] libavcodec/zmbvenc: Add support for RGB formats X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Matthew Fearnley MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" This consists mostly of the following changes: - add newly supported pixel formats (RGB555LE, RGB565LE, BGR0) - select the ZMBV format (c->fmt) and bytes per pixel (c->bypp) based on avctx->pix_fmt - multiply widths/x-values by c->bypp, in places where bytes, not pixels, are expected - disable palette-writing code for non-palette pix_fmts - make a note about histogram[]'s datatype (it could need increasing if ZMBV_BLOCK is increased) - adjust the c->score_tab length to take up to (and including) 4 times the number of pixels in a block - initialise c->score_tab up to c->bypp * the number of pixels Note: the ZmbvFormat enum allows for additional bit depths: - 1,2,4-bit (palette) - 24-bit (RGB) At time of writing the specifics of these (e.g. channel order, bit alignment) are not currently defined, and DOSBox only implements support for 8/15/16/32 bpp. One might expect the 24-bit format - if implemented - to be BGR24, to have the same channel order as BGR0. However, the decoder in zmbv.c has been guessed to use RGB24, so I have chosen to not contradict this, and omitted specific support for this format. --- libavcodec/zmbvenc.c | 104 ++++++++++++++++++++++++++++++++----------- 1 file changed, 77 insertions(+), 27 deletions(-) diff --git a/libavcodec/zmbvenc.c b/libavcodec/zmbvenc.c index c9d50b6adf..98029de5f6 100644 --- a/libavcodec/zmbvenc.c +++ b/libavcodec/zmbvenc.c @@ -34,11 +34,29 @@ #include +/* Frame header flags */ #define ZMBV_KEYFRAME 1 #define ZMBV_DELTAPAL 2 +/* Motion block width/height (maximum allowed value is 255) + * Note: histogram datatype in block_cmp() must be big enough to hold values + * up to (4 * ZMBV_BLOCK * ZMBV_BLOCK) + */ #define ZMBV_BLOCK 16 +/* Keyframe header format values */ +enum ZmbvFormat { + ZMBV_FMT_NONE = 0, + ZMBV_FMT_1BPP = 1, + ZMBV_FMT_2BPP = 2, + ZMBV_FMT_4BPP = 3, + ZMBV_FMT_8BPP = 4, + ZMBV_FMT_15BPP = 5, + ZMBV_FMT_16BPP = 6, + ZMBV_FMT_24BPP = 7, + ZMBV_FMT_32BPP = 8 +}; + /** * Encoder context */ @@ -53,9 +71,11 @@ typedef struct ZmbvEncContext { int pstride; int comp_size; int keyint, curfrm; + int bypp; + enum ZmbvFormat fmt; z_stream zstream; - int score_tab[ZMBV_BLOCK * ZMBV_BLOCK + 1]; + int score_tab[ZMBV_BLOCK * ZMBV_BLOCK * 4 + 1]; } ZmbvEncContext; @@ -69,10 +89,11 @@ static inline int block_cmp(ZmbvEncContext *c, uint8_t *src, int stride, int sum = 0; int i, j; uint16_t histogram[256] = {0}; + int bw_bytes = bw * c->bypp; /* Build frequency histogram of byte values for src[] ^ src2[] */ for(j = 0; j < bh; j++){ - for(i = 0; i < bw; i++){ + for(i = 0; i < bw_bytes; i++){ int t = src[i] ^ src2[i]; histogram[t]++; } @@ -81,7 +102,7 @@ static inline int block_cmp(ZmbvEncContext *c, uint8_t *src, int stride, } /* If not all the xored values were 0, then the blocks are different */ - *xored = (histogram[0] < bw * bh); + *xored = (histogram[0] < bw_bytes * bh); /* Exit early if blocks are equal */ if (!*xored) return 0; @@ -114,7 +135,7 @@ static int zmbv_me(ZmbvEncContext *c, uint8_t *src, int sstride, uint8_t *prev, /* Try previous block's MV (if not 0,0) */ if (mx0 || my0){ - tv = block_cmp(c, src, sstride, prev + mx0 + my0 * pstride, pstride, bw, bh, &txored); + tv = block_cmp(c, src, sstride, prev + mx0 * c->bypp + my0 * pstride, pstride, bw, bh, &txored); if(tv < bv){ bv = tv; *mx = mx0; @@ -129,7 +150,7 @@ static int zmbv_me(ZmbvEncContext *c, uint8_t *src, int sstride, uint8_t *prev, for(dx = -c->lrange; dx <= c->urange; dx++){ if(!dx && !dy) continue; // we already tested this block if(dx == mx0 && dy == my0) continue; // this one too - tv = block_cmp(c, src, sstride, prev + dx + dy * pstride, pstride, bw, bh, &txored); + tv = block_cmp(c, src, sstride, prev + dx * c->bypp + dy * pstride, pstride, bw, bh, &txored); if(tv < bv){ bv = tv; *mx = dx; @@ -165,9 +186,10 @@ FF_DISABLE_DEPRECATION_WARNINGS avctx->coded_frame->key_frame = keyframe; FF_ENABLE_DEPRECATION_WARNINGS #endif - chpal = !keyframe && memcmp(p->data[1], c->pal2, 1024); - palptr = (uint32_t*)p->data[1]; + palptr = (avctx->pix_fmt == AV_PIX_FMT_PAL8) ? (uint32_t *)p->data[1] : NULL; + chpal = !keyframe && palptr && memcmp(palptr, c->pal2, 1024); + src = p->data[0]; prev = c->prev; if(chpal){ @@ -181,19 +203,21 @@ FF_ENABLE_DEPRECATION_WARNINGS c->pal[i * 3 + 1] = tpal[1]; c->pal[i * 3 + 2] = tpal[2]; } - memcpy(c->pal2, p->data[1], 1024); + memcpy(c->pal2, palptr, 1024); } if(keyframe){ - for(i = 0; i < 256; i++){ - AV_WB24(c->pal+(i*3), palptr[i]); + if (palptr){ + for(i = 0; i < 256; i++){ + AV_WB24(c->pal+(i*3), palptr[i]); + } + memcpy(c->work_buf, c->pal, 768); + memcpy(c->pal2, palptr, 1024); + work_size = 768; } - memcpy(c->work_buf, c->pal, 768); - memcpy(c->pal2, p->data[1], 1024); - work_size = 768; for(i = 0; i < avctx->height; i++){ - memcpy(c->work_buf + work_size, src, avctx->width); + memcpy(c->work_buf + work_size, src, avctx->width * c->bypp); src += p->linesize[0]; - work_size += avctx->width; + work_size += avctx->width * c->bypp; } }else{ int x, y, bh2, bw2, xored; @@ -212,16 +236,16 @@ FF_ENABLE_DEPRECATION_WARNINGS for(x = 0; x < avctx->width; x += ZMBV_BLOCK, mv += 2) { bw2 = FFMIN(avctx->width - x, ZMBV_BLOCK); - tsrc = src + x; - tprev = prev + x; + tsrc = src + x * c->bypp; + tprev = prev + x * c->bypp; zmbv_me(c, tsrc, p->linesize[0], tprev, c->pstride, x, y, &mx, &my, &xored); mv[0] = (mx << 1) | !!xored; mv[1] = my << 1; - tprev += mx + my * c->pstride; + tprev += mx * c->bypp + my * c->pstride; if(xored){ for(j = 0; j < bh2; j++){ - for(i = 0; i < bw2; i++) + for(i = 0; i < bw2 * c->bypp; i++) c->work_buf[work_size++] = tsrc[i] ^ tprev[i]; tsrc += p->linesize[0]; tprev += c->pstride; @@ -236,7 +260,7 @@ FF_ENABLE_DEPRECATION_WARNINGS src = p->data[0]; prev = c->prev; for(i = 0; i < avctx->height; i++){ - memcpy(prev, src, avctx->width); + memcpy(prev, src, avctx->width * c->bypp); prev += c->pstride; src += p->linesize[0]; } @@ -267,7 +291,7 @@ FF_ENABLE_DEPRECATION_WARNINGS *buf++ = 0; // hi ver *buf++ = 1; // lo ver *buf++ = 1; // comp - *buf++ = 4; // format - 8bpp + *buf++ = c->fmt; // format *buf++ = ZMBV_BLOCK; // block width *buf++ = ZMBV_BLOCK; // block height } @@ -303,12 +327,34 @@ static av_cold int encode_init(AVCodecContext *avctx) int lvl = 9; int prev_size, prev_offset; + switch (avctx->pix_fmt) { + case AV_PIX_FMT_PAL8: + c->fmt = ZMBV_FMT_8BPP; + c->bypp = 1; + break; + case AV_PIX_FMT_RGB555LE: + c->fmt = ZMBV_FMT_15BPP; + c->bypp = 2; + break; + case AV_PIX_FMT_RGB565LE: + c->fmt = ZMBV_FMT_16BPP; + c->bypp = 2; + break; + case AV_PIX_FMT_BGR0: + c->fmt = ZMBV_FMT_32BPP; + c->bypp = 4; + break; + default: + av_log(avctx, AV_LOG_INFO, "unsupported pixel format\n"); + return AVERROR(EINVAL); + } + /* Entropy-based score tables for comparing blocks. * Suitable for blocks up to (ZMBV_BLOCK * ZMBV_BLOCK) bytes. * Scores are nonnegative, lower is better. */ - for(i = 1; i <= ZMBV_BLOCK * ZMBV_BLOCK; i++) - c->score_tab[i] = -i * log2(i / (double)(ZMBV_BLOCK * ZMBV_BLOCK)) * 256; + for(i = 1; i <= ZMBV_BLOCK * ZMBV_BLOCK * c->bypp; i++) + c->score_tab[i] = -i * log2(i / (double)(ZMBV_BLOCK * ZMBV_BLOCK * c->bypp)) * 256; c->avctx = avctx; @@ -331,7 +377,7 @@ static av_cold int encode_init(AVCodecContext *avctx) // Needed if zlib unused or init aborted before deflateInit memset(&c->zstream, 0, sizeof(z_stream)); - c->comp_size = avctx->width * avctx->height + 1024 + + c->comp_size = avctx->width * c->bypp * avctx->height + 1024 + ((avctx->width + ZMBV_BLOCK - 1) / ZMBV_BLOCK) * ((avctx->height + ZMBV_BLOCK - 1) / ZMBV_BLOCK) * 2 + 4; if (!(c->work_buf = av_malloc(c->comp_size))) { av_log(avctx, AV_LOG_ERROR, "Can't allocate work buffer.\n"); @@ -355,8 +401,8 @@ static av_cold int encode_init(AVCodecContext *avctx) * - The first row should also be padded with `lrange` pixels before, then * aligned up to a multiple of 16 bytes. */ - c->pstride = FFALIGN(avctx->width + c->lrange, 16); - prev_size = FFALIGN(c->lrange, 16) + c->pstride * (c->lrange + avctx->height + c->urange); + c->pstride = FFALIGN((avctx->width + c->lrange) * c->bypp, 16); + prev_size = FFALIGN(c->lrange * c->bypp, 16) + c->pstride * (c->lrange + avctx->height + c->urange); prev_offset = FFALIGN(c->lrange, 16) + c->pstride * c->lrange; if (!(c->prev_buf = av_mallocz(prev_size))) { av_log(avctx, AV_LOG_ERROR, "Can't allocate picture.\n"); @@ -385,5 +431,9 @@ AVCodec ff_zmbv_encoder = { .init = encode_init, .encode2 = encode_frame, .close = encode_end, - .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_PAL8, AV_PIX_FMT_NONE }, + .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_PAL8, + AV_PIX_FMT_RGB555LE, + AV_PIX_FMT_RGB565LE, + AV_PIX_FMT_BGR0, + AV_PIX_FMT_NONE }, };