X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fzmbvenc.c;h=68618f77e9412141d9dcc57f2e5bf6db3d88b826;hb=4e8d22478bc854949a9adbda97348f2997e1dc8d;hp=4d9147657d25918d4793c6205c2a687e046e1a0b;hpb=73d193d1d0ff62a029a905d1404c0fd357f4c880;p=ffmpeg diff --git a/libavcodec/zmbvenc.c b/libavcodec/zmbvenc.c index 4d9147657d2..68618f77e94 100644 --- a/libavcodec/zmbvenc.c +++ b/libavcodec/zmbvenc.c @@ -34,34 +34,53 @@ #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 */ typedef struct ZmbvEncContext { AVCodecContext *avctx; - int range; + int lrange, urange; uint8_t *comp_buf, *work_buf; uint8_t pal[768]; uint32_t pal2[256]; //for quick comparisons - uint8_t *prev; + uint8_t *prev, *prev_buf; int pstride; int comp_size; int keyint, curfrm; + int bypp; + enum ZmbvFormat fmt; z_stream zstream; - int score_tab[256]; + int score_tab[ZMBV_BLOCK * ZMBV_BLOCK * 4 + 1]; } ZmbvEncContext; /** Block comparing function * XXX should be optimized and moved to DSPContext - * TODO handle out of edge ME */ static inline int block_cmp(ZmbvEncContext *c, uint8_t *src, int stride, uint8_t *src2, int stride2, int bw, int bh, @@ -69,20 +88,27 @@ static inline int block_cmp(ZmbvEncContext *c, uint8_t *src, int stride, { int sum = 0; int i, j; - uint8_t histogram[256] = {0}; + uint16_t histogram[256] = {0}; + int bw_bytes = bw * c->bypp; - *xored = 0; + /* 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]++; - *xored |= t; } src += stride; src2 += stride2; } - for(i = 1; i < 256; i++) + /* If not all the xored values were 0, then the blocks are different */ + *xored = (histogram[0] < bw_bytes * bh); + + /* Exit early if blocks are equal */ + if (!*xored) return 0; + + /* Sum the entropy of all values */ + for(i = 0; i < 256; i++) sum += c->score_tab[histogram[i]]; return sum; @@ -94,23 +120,42 @@ static inline int block_cmp(ZmbvEncContext *c, uint8_t *src, int stride, static int zmbv_me(ZmbvEncContext *c, uint8_t *src, int sstride, uint8_t *prev, int pstride, int x, int y, int *mx, int *my, int *xored) { - int dx, dy, tx, ty, tv, bv, bw, bh; + int dx, dy, txored, tv, bv, bw, bh; + int mx0, my0; - *mx = *my = 0; + mx0 = *mx; + my0 = *my; bw = FFMIN(ZMBV_BLOCK, c->avctx->width - x); bh = FFMIN(ZMBV_BLOCK, c->avctx->height - y); + + /* Try (0,0) */ bv = block_cmp(c, src, sstride, prev, pstride, bw, bh, xored); + *mx = *my = 0; if(!bv) return 0; - for(ty = FFMAX(y - c->range, 0); ty < FFMIN(y + c->range, c->avctx->height - bh); ty++){ - for(tx = FFMAX(x - c->range, 0); tx < FFMIN(x + c->range, c->avctx->width - bw); tx++){ - if(tx == x && ty == y) continue; // we already tested this block - dx = tx - x; - dy = ty - y; - tv = block_cmp(c, src, sstride, prev + dx + dy * pstride, pstride, bw, bh, xored); + + /* Try previous block's MV (if not 0,0) */ + if (mx0 || my0){ + tv = block_cmp(c, src, sstride, prev + mx0 * c->bypp + my0 * pstride, pstride, bw, bh, &txored); + if(tv < bv){ + bv = tv; + *mx = mx0; + *my = my0; + *xored = txored; + if(!bv) return 0; + } + } + + /* Try other MVs from top-to-bottom, left-to-right */ + for(dy = -c->lrange; dy <= c->urange; dy++){ + 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 * c->bypp + dy * pstride, pstride, bw, bh, &txored); if(tv < bv){ bv = tv; *mx = dx; *my = dy; + *xored = txored; if(!bv) return 0; } } @@ -135,15 +180,10 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, c->curfrm++; if(c->curfrm == c->keyint) c->curfrm = 0; -#if FF_API_CODED_FRAME -FF_DISABLE_DEPRECATION_WARNINGS - avctx->coded_frame->pict_type = keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; - 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){ @@ -157,25 +197,27 @@ 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; uint8_t *tsrc, *tprev; uint8_t *mv; - int mx, my; + int mx = 0, my = 0; bw = (avctx->width + ZMBV_BLOCK - 1) / ZMBV_BLOCK; bh = (avctx->height + ZMBV_BLOCK - 1) / ZMBV_BLOCK; @@ -188,16 +230,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; + mv[0] = (mx * 2) | !!xored; + mv[1] = my * 2; + 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; @@ -212,7 +254,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]; } @@ -243,13 +285,13 @@ 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 + pkt->flags |= AV_PKT_FLAG_KEY; } memcpy(buf, c->comp_buf, c->zstream.total_out); - pkt->flags |= AV_PKT_FLAG_KEY*keyframe; *got_packet = 1; return 0; @@ -263,7 +305,7 @@ static av_cold int encode_end(AVCodecContext *avctx) av_freep(&c->work_buf); deflateEnd(&c->zstream); - av_freep(&c->prev); + av_freep(&c->prev_buf); return 0; } @@ -277,17 +319,54 @@ static av_cold int encode_init(AVCodecContext *avctx) int zret; // Zlib return code int i; 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; +#ifdef ZMBV_ENABLE_24BPP + case AV_PIX_FMT_BGR24: + c->fmt = ZMBV_FMT_24BPP; + c->bypp = 3; + break; +#endif //ZMBV_ENABLE_24BPP + 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); + } - for(i=1; i<256; i++) - c->score_tab[i] = -i * log2(i / (double)(ZMBV_BLOCK * ZMBV_BLOCK)) * 256; + /* 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 * c->bypp; i++) + c->score_tab[i] = -i * log2(i / (double)(ZMBV_BLOCK * ZMBV_BLOCK * c->bypp)) * 256; c->avctx = avctx; c->curfrm = 0; c->keyint = avctx->keyint_min; - c->range = 8; - if(avctx->me_range > 0) - c->range = FFMIN(avctx->me_range, 127); + + /* Motion estimation range: maximum distance is -64..63 */ + c->lrange = c->urange = 8; + if(avctx->me_range > 0){ + c->lrange = FFMIN(avctx->me_range, 64); + c->urange = FFMIN(avctx->me_range, 63); + } if(avctx->compression_level >= 0) lvl = avctx->compression_level; @@ -298,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"); @@ -313,11 +392,23 @@ static av_cold int encode_init(AVCodecContext *avctx) av_log(avctx, AV_LOG_ERROR, "Can't allocate compression buffer.\n"); return AVERROR(ENOMEM); } - c->pstride = FFALIGN(avctx->width, 16); - if (!(c->prev = av_malloc(c->pstride * avctx->height))) { + + /* Allocate prev buffer - pad around the image to allow out-of-edge ME: + * - The image should be padded with `lrange` rows before and `urange` rows + * after. + * - The stride should be padded with `lrange` pixels, then rounded up to a + * multiple of 16 bytes. + * - 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) * c->bypp, 16); + prev_size = FFALIGN(c->lrange * c->bypp, 16) + c->pstride * (c->lrange + avctx->height + c->urange); + prev_offset = FFALIGN(c->lrange * c->bypp, 16) + c->pstride * c->lrange; + if (!(c->prev_buf = av_mallocz(prev_size))) { av_log(avctx, AV_LOG_ERROR, "Can't allocate picture.\n"); return AVERROR(ENOMEM); } + c->prev = c->prev_buf + prev_offset; c->zstream.zalloc = Z_NULL; c->zstream.zfree = Z_NULL; @@ -331,7 +422,7 @@ static av_cold int encode_init(AVCodecContext *avctx) return 0; } -AVCodec ff_zmbv_encoder = { +const AVCodec ff_zmbv_encoder = { .name = "zmbv", .long_name = NULL_IF_CONFIG_SMALL("Zip Motion Blocks Video"), .type = AVMEDIA_TYPE_VIDEO, @@ -340,5 +431,12 @@ 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, +#ifdef ZMBV_ENABLE_24BPP + AV_PIX_FMT_BGR24, +#endif //ZMBV_ENABLE_24BPP + AV_PIX_FMT_BGR0, + AV_PIX_FMT_NONE }, };