diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 27c9a11..5d84167 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -35,6 +35,7 @@ OBJS = allcodecs.o \ resample2.o \ simple_idct.o \ utils.o \ + zmbv_dosbox.o # parts needed for many different codecs OBJS-$(CONFIG_AANDCTTABLES) += aandcttab.o @@ -511,7 +512,7 @@ OBJS-$(CONFIG_ZEROCODEC_DECODER) += zerocodec.o OBJS-$(CONFIG_ZLIB_DECODER) += lcldec.o OBJS-$(CONFIG_ZLIB_ENCODER) += lclenc.o OBJS-$(CONFIG_ZMBV_DECODER) += zmbv.o -OBJS-$(CONFIG_ZMBV_ENCODER) += zmbvenc.o +OBJS-$(CONFIG_ZMBV_ENCODER) += zmbvenc.o zmbv_dosbox.o # (AD)PCM decoders/encoders OBJS-$(CONFIG_PCM_ALAW_DECODER) += pcm.o diff --git a/libavcodec/zmbv_dosbox.cpp b/libavcodec/zmbv_dosbox.cpp new file mode 100644 index 0000000..4707a65 --- /dev/null +++ b/libavcodec/zmbv_dosbox.cpp @@ -0,0 +1,826 @@ +/* + * Copyright (C) 2002-2009 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "zmbv_dosbox.h" + +#define DBZV_VERSION_HIGH 0 +#define DBZV_VERSION_LOW 1 + +#define COMPRESSION_NONE 0 +#define COMPRESSION_ZLIB 1 + +#define MAX_VECTOR (128+256) + +#define Mask_KeyFrame 0x01 +#define Mask_DeltaPalette 0x02 + +zmbv_format_t ZMBV_Codec::BPPFormat(int bpp) { + switch (bpp) { + case 1: return ZMBV_FORMAT_1BPP; + case 2: return ZMBV_FORMAT_2BPP; + case 4: return ZMBV_FORMAT_4BPP; + case 8: return ZMBV_FORMAT_8BPP; + case 15: return ZMBV_FORMAT_15BPP; + case 16: return ZMBV_FORMAT_16BPP; + case 24: return ZMBV_FORMAT_24BPP; + case 32: return ZMBV_FORMAT_32BPP; + default: break; + } + return ZMBV_FORMAT_NONE; +} +int ZMBV_Codec::NeededSize( int _width, int _height, zmbv_format_t _format) { + int f; + switch (_format) { + case ZMBV_FORMAT_8BPP:f = 1;break; + case ZMBV_FORMAT_15BPP:f = 2;break; + case ZMBV_FORMAT_16BPP:f = 2;break; + case ZMBV_FORMAT_24BPP:f = 3;break; + case ZMBV_FORMAT_32BPP:f = 4;break; + default: + return -1; + } + f = f*_width*_height + 2*(1+(_width/8)) * (1+(_height/8))+1024; + return f + f/1000; +} + +bool ZMBV_Codec::SetupBuffers(zmbv_format_t _format, int blockwidth, int blockheight) { + FreeBuffers(); + palsize = 0; + switch (_format) { + case ZMBV_FORMAT_8BPP: + pixelsize = 1; + palsize = 256; + break; + case ZMBV_FORMAT_15BPP: + pixelsize = 2; + break; + case ZMBV_FORMAT_16BPP: + pixelsize = 2; + break; + case ZMBV_FORMAT_24BPP: + pixelsize = 3; + break; + case ZMBV_FORMAT_32BPP: + pixelsize = 4; + break; + default: + return false; + }; + bufsize = (height+2*MAX_VECTOR)*pitch*pixelsize+2048; + + buf1 = (unsigned char*) malloc(bufsize); + buf2 = (unsigned char*) malloc(bufsize); + work = (unsigned char*) malloc(bufsize); + bw = blockwidth; + bh = blockheight; + + int xblocks = (width/blockwidth); + int xleft = width % blockwidth; + if (xleft) xblocks++; + int yblocks = (height/blockheight); + int yleft = height % blockheight; + if (yleft) yblocks++; + blockcount=yblocks*xblocks; + blocks = (FrameBlock*) malloc(blockcount * sizeof(FrameBlock)); + + if (!buf1 || !buf2 || !work || !blocks) { + FreeBuffers(); + return false; + } + int y,x,i; + i=0; + for (y=0;y 8 && ((y+64)%16) != 0) continue; + if( abs(y) > 8 && ((x+64)%16) != 0) continue; + if(x + bw >= MAX_VECTOR) continue; + if(y + bh >= MAX_VECTOR) continue; + if(-x >= MAX_VECTOR) continue; + if(-y >= MAX_VECTOR) continue; + + /* + // SPECIAL CASE FOR 4240x2400 VIDEO: + if(abs(x) >= 4 || abs(y) >= 4) + { + if(abs(y)%4 != 0 && abs(y)%5 != 0 && abs(y)%12 != 0) continue; + if(abs(x)%4 != 0 && abs(x)%5 != 0) continue; + } + // END SPECIAL CASE + */ + + { CodecVector tmp = {x,y,0}; VectorTable.push_back(tmp); } + } + + std::sort(VectorTable.begin(), VectorTable.end(), NearestVectorsFirst); + fprintf(stderr, "ZMBV: %zu motion vectors will be used for testing\n", VectorTable.size()); +} + +template +INLINE int ZMBV_Codec::PossibleBlock(int vx,int vy,FrameBlock * block) { + int ret=0; + P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx; + P * pnew=((P*)newframe)+block->start; + + const int xskip = (bw+3)/4; + const int yskip = (bh+3)/4; + for (int y=0;yheight_clamped; y+=yskip) { + for (int x=0;xwidth_clamped; x+=xskip) { + ret += (pold[x] != pnew[x]); + /* + int test=0-((pold[x]-pnew[x])&0x00ffffff); + ret-=(test>>31); + */ + } + pold+=pitch*yskip; + pnew+=pitch*yskip; + } + return ret; +} + +template +INLINE int ZMBV_Codec::CompareBlock(int vx,int vy,FrameBlock * block) { + int ret=0; + P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx; + P * pnew=((P*)newframe)+block->start;; + for (int y=0;yheight_clamped;y++) { + unsigned nx = block->width_clamped * sizeof(P); + const unsigned char* a = (const unsigned char*)pold; + const unsigned char* b = (const unsigned char*)pnew; + for(unsigned x=0; x= 0x04) + if(d >= 0x10) ret += 5; + //else if(d >= 0x08) ret += 4; + else ret += 3; + else + if(d >= 0x02) ret += 2; + else ret += (d > 0); + /*if(d > 0x10) ret += 5; + while(d > 0) { ret += 1; d >>= 1; }*/ + } + pold+=pitch; + pnew+=pitch; + } + return ret; +} + +template +INLINE void ZMBV_Codec::AddXorBlock(int vx,int vy,FrameBlock * block) { + P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx; + P * pnew=((P*)newframe)+block->start; + for (int y=0;yheight_clamped;y++) { + for (int x=0;xwidth_clamped;x++) { + *((P*)&work[workUsed])=pnew[x] ^ pold[x]; + workUsed+=sizeof(P); + } + pold+=pitch; + pnew+=pitch; + } +} + +template +void ZMBV_Codec::AddXorFrame(void) { + //int written=0; + //int lastvector=0; + signed char * vectors=(signed char*)&work[workUsed]; + /* Align the following xor data on 4 byte boundary*/ + workUsed=(workUsed + blockcount*2 +3) & ~3; + //int totalx=0; + //int totaly=0; + + #pragma omp parallel for ordered schedule(dynamic,1) + for (int b=0;b(0,0, block); + int possibles = 64; + int VectorCount = (int) VectorTable.size(); + + for (int v=0;v(vx, vy, block) < 4) { + possibles--; +// if (!possibles) Msg("Ran out of possibles, at %d of %d best %d\n",v,VectorCount,bestchange); + int testchange=CompareBlock

(vx,vy, block); + if (testchange(bestvx, bestvy, block); + if(bestvx || bestvy) ++n_offseted; else ++n_raw; + } + else + { + ++n_copied; + } + } + } +} + +bool ZMBV_Codec::SetupCompress( int _width, int _height ) { + width = _width; + height = _height; + pitch = _width + 2*MAX_VECTOR; + format = ZMBV_FORMAT_NONE; + if (deflateInit2 (&zstream, 9,Z_DEFLATED, 15, 9,Z_FILTERED) != Z_OK) + return false; + + blockadjust = 12; + + return true; +} + +bool ZMBV_Codec::SetupDecompress( int _width, int _height) { + width = _width; + height = _height; + pitch = _width + 2*MAX_VECTOR; + format = ZMBV_FORMAT_NONE; + if (inflateInit (&zstream) != Z_OK) + return false; + return true; +} + +bool ZMBV_Codec::PrepareCompressFrame + (int flags, zmbv_format_t _format, char * pal, void *writeBuf, int writeSize, + int bw_base, + int bh_base) +{ + int i; + unsigned char *firstByte; + + int good = int(n_copied + n_offseted); + int bad = int(n_raw); + bool adjusted = false; + + while(bw_base * blockadjust >= 256 + || bh_base * blockadjust >= 256) + { + adjusted = true; + blockadjust /= 2; + } + if(bw_base * blockadjust != bw + || bh_base * blockadjust != bh) + { + adjusted = true; + } + + if(good*3 > bad*2) // More than 1.5x good compared to bad + { + // Decrease block size, we might be able to compress better + if(bad == 0 && blockadjust >= 8) { adjusted = true; blockadjust /= 8; } + else if(bad == 0 && blockadjust >= 4) { adjusted = true; blockadjust /= 4; } + else if(blockadjust > 4) { adjusted = true; blockadjust /= 2; } + else if(blockadjust > 1) { adjusted = true; --blockadjust; } + } + else if(good*2 < bad*3) // More than 1.5x bad compared to good + { + // Increase block size, we're creating too large data + if(n_copied == 0 && n_offseted < n_raw/16 && blockadjust < 8 && + bw_base * (blockadjust*4) < 256) { adjusted = true; blockadjust*=4; } + else if(blockadjust < 8 && + bw_base * (blockadjust*2) < 256) { adjusted = true; blockadjust*=2; } + else + if(bw_base * (blockadjust+2) < 256) { adjusted = true; blockadjust += 2; } + else + if(bw_base * (blockadjust+1) < 256) { adjusted = true; ++blockadjust; } + } + + if (_format != format || adjusted) { + int w = bw_base * blockadjust; + int h = bh_base * blockadjust; + + if (!SetupBuffers( _format, w,h)) + return false; + flags|=1; //Force a keyframe + } + + /* replace oldframe with new frame */ + unsigned char *copyFrame = newframe; + newframe = oldframe; + oldframe = copyFrame; + + compress.linesDone = 0; + compress.writeSize = writeSize; + compress.writeDone = 1; + compress.writeBuf = (unsigned char *)writeBuf; + /* Set a pointer to the first byte which will contain info about this frame */ + firstByte = compress.writeBuf; + *firstByte = 0; + //Reset the work buffer + workUsed = 0;workPos = 0; + if (flags & 1) { + /* Make a keyframe */ + *firstByte |= Mask_KeyFrame; + KeyframeHeader * header = (KeyframeHeader *)(compress.writeBuf + compress.writeDone); + header->high_version = DBZV_VERSION_HIGH; + header->low_version = DBZV_VERSION_LOW; + header->compression = COMPRESSION_ZLIB; + header->format = format; + header->blockwidth = bw; + header->blockheight = bh; + compress.writeDone += sizeof(KeyframeHeader); + /* Copy the new frame directly over */ + if (palsize) { + if (pal) + memcpy(&palette, pal, sizeof(palette)); + else + memset(&palette,0, sizeof(palette)); + /* keyframes get the full palette */ + for (i=0;i(); + break; + case ZMBV_FORMAT_15BPP: + case ZMBV_FORMAT_16BPP: + AddXorFrame(); + break; + case ZMBV_FORMAT_24BPP: + AddXorFrame(); + break; + case ZMBV_FORMAT_32BPP: + AddXorFrame(); + break; + } + } + /* Create the actual frame with compression */ + zstream.next_in = (Bytef *)work; + zstream.avail_in = workUsed; + zstream.total_in = 0; + + zstream.next_out = (Bytef *)(compress.writeBuf + compress.writeDone); + zstream.avail_out = compress.writeSize - compress.writeDone; + zstream.total_out = 0; + int res = deflate(&zstream, Z_SYNC_FLUSH); + + int res2 = compress.writeDone + zstream.total_out; + fprintf(stderr, "ZMBV frame (%u-byte pixels, %ux%u, %s, %u raw, %u copied, %u offseted): %u bytes -> %u ;\n", + pixelsize, + bw,bh, + (firstByte & Mask_KeyFrame) ? "keyframe" : "", + n_raw, + n_copied, + n_offseted, + (unsigned)workUsed, (unsigned)res2); + return res2; +} + +template +INLINE void ZMBV_Codec::UnXorBlock(int vx,int vy,FrameBlock * block) { + P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx; + P * pnew=((P*)newframe)+block->start; + for (int y=0;yheight_clamped;y++) { + for (int x=0;xwidth_clamped;x++) { + pnew[x]=pold[x]^*((P*)&work[workPos]); + workPos+=sizeof(P); + } + pold+=pitch; + pnew+=pitch; + } +} + +template +INLINE void ZMBV_Codec::CopyBlock(int vx,int vy,FrameBlock * block) { + P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx; + P * pnew=((P*)newframe)+block->start; + for (int y=0;yheight_clamped;y++) { + for (int x=0;xwidth_clamped;x++) { + pnew[x]=pold[x]; + } + pold+=pitch; + pnew+=pitch; + } +} + +template +void ZMBV_Codec::UnXorFrame(void) { + signed char * vectors=(signed char *)&work[workPos]; + workPos=(workPos + blockcount*2 + 3) & ~3; + for (int b=0;b> 1; + int vy = vectors[b*2+1] >> 1; + if (delta) UnXorBlock

(vx,vy,block); + else CopyBlock

(vx,vy,block); + } +} + +bool ZMBV_Codec::DecompressFrame(void * framedata, int size) { + unsigned char *data=(unsigned char *)framedata; + unsigned char tag;int i; + + tag = *data++; + if (--size<=0) + return false; + if (tag & Mask_KeyFrame) { + KeyframeHeader * header = (KeyframeHeader *)data; + size -= sizeof(KeyframeHeader);data += sizeof(KeyframeHeader); + if (size<=0) + return false; + if (header->low_version != DBZV_VERSION_LOW || header->high_version != DBZV_VERSION_HIGH) + return false; + if (format != (zmbv_format_t)header->format && !SetupBuffers((zmbv_format_t)header->format, header->blockwidth, header->blockheight)) + return false; + inflateReset(&zstream); + } + zstream.next_in = (Bytef *)data; + zstream.avail_in = size; + zstream.total_in = 0; + + zstream.next_out = (Bytef *)work; + zstream.avail_out = bufsize; + zstream.total_out = 0; + int res = inflate(&zstream, Z_FINISH); + workUsed= zstream.total_out; + workPos = 0; + if (tag & Mask_KeyFrame) { + if (palsize) { + for (i=0;i(); + break; + case ZMBV_FORMAT_15BPP: + case ZMBV_FORMAT_16BPP: + UnXorFrame(); + break; + case ZMBV_FORMAT_32BPP: + UnXorFrame(); + break; + } + } + return true; +} + +void ZMBV_Codec::Output_UpsideDown_24(void *output) { + int i; + unsigned char *r; + unsigned char *w = (unsigned char *)output; + int pad = width & 3; + + for (i=height-1;i>=0;i--) { + r = newframe + pixelsize*(MAX_VECTOR+(i+MAX_VECTOR)*pitch); + switch (format) { + case ZMBV_FORMAT_8BPP: + for (int j=0;j> 2); + *w++ = (unsigned char)(((c & 0x03e0) * 0x21) >> 7); + *w++ = (unsigned char)(((c & 0x7c00) * 0x21) >> 12); + } + break; + case ZMBV_FORMAT_16BPP: + for (int j=0;j> 2); + *w++ = (unsigned char)(((c & 0x07e0) * 0x41) >> 9); + *w++ = (unsigned char)(((c & 0xf800) * 0x21) >> 13); + } + break; + case ZMBV_FORMAT_24BPP: + for (int j=0;jSetupCompress(_width, _height); +} + +int zmbv_SetupDecompress(zmbv_codec c, int _width, int _height) +{ + return ((ZMBV_Codec*)c)->SetupDecompress(_width, _height); +} + +zmbv_format_t zmbv_BPPFormat( int bpp ) +{ + return ZMBV_Codec::BPPFormat(bpp); +} + +int zmbv_NeededSize(int _width, int _height, zmbv_format_t _format) +{ + return ZMBV_Codec::NeededSize(_width,_height,_format); +} + +void zmbv_CompressLines(zmbv_codec c, int lineCount, void *lineData[]) +{ + ((ZMBV_Codec*)c)->CompressLines(lineCount,lineData); +} + +int zmbv_PrepareCompressFrame(zmbv_codec c, int flags, zmbv_format_t _format, char * pal, void *writeBuf, int writeSize) +{ + return ((ZMBV_Codec*)c)->PrepareCompressFrame(flags,_format,pal,writeBuf,writeSize, 16,16); +} + +int zmbv_FinishCompressFrame(zmbv_codec c) +{ + return ((ZMBV_Codec*)c)->FinishCompressFrame(); +} + +int zmbv_DecompressFrame(zmbv_codec c, void * framedata, int size) +{ + return ((ZMBV_Codec*)c)->DecompressFrame(framedata,size); +} + +void zmbv_Output_UpsideDown_24(zmbv_codec c, void * output) +{ + ((ZMBV_Codec*)c)->Output_UpsideDown_24(output); +} + +void zmbv_erase(zmbv_codec c) +{ + ZMBV_Codec* v = (ZMBV_Codec*) c; + v->~ZMBV_Codec(); + free(v); +} diff --git a/libavcodec/zmbv_dosbox.h b/libavcodec/zmbv_dosbox.h new file mode 100644 index 0000000..fa92e92 --- /dev/null +++ b/libavcodec/zmbv_dosbox.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2002-2009 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef DOSBOX_DOSBOX_H +#ifdef _MSC_VER +#define INLINE __forceinline +#else +#define INLINE inline +#endif +#endif + +#define CODEC_4CC "ZMBV" + +#include + +typedef enum { + ZMBV_FORMAT_NONE = 0x00, + ZMBV_FORMAT_1BPP = 0x01, + ZMBV_FORMAT_2BPP = 0x02, + ZMBV_FORMAT_4BPP = 0x03, + ZMBV_FORMAT_8BPP = 0x04, + ZMBV_FORMAT_15BPP = 0x05, + ZMBV_FORMAT_16BPP = 0x06, + ZMBV_FORMAT_24BPP = 0x07, + ZMBV_FORMAT_32BPP = 0x08 +} zmbv_format_t; + +//void Msg(const char fmt[], ...); + +#ifdef __cplusplus + +#include + +class ZMBV_Codec { +private: + struct FrameBlock { + int start; + int width_clamped,height_clamped; + int xbegin, ybegin; + }; +public: + struct CodecVector { + int x,y; + int slot; + }; +private: + struct KeyframeHeader { + unsigned char high_version; + unsigned char low_version; + unsigned char compression; + unsigned char format; + unsigned char blockwidth,blockheight; + }; + + struct { + int linesDone; + int writeSize; + int writeDone; + unsigned char *writeBuf; + } compress; + + std::vector VectorTable; + + unsigned char *oldframe, *newframe; + unsigned char *buf1, *buf2, *work; + int bufsize; + + int blockcount; + FrameBlock * blocks; + + int workUsed, workPos; + + int palsize; + char palette[256*4]; + int height, width, pitch; + zmbv_format_t format; + int pixelsize; + + int bw,bh, blockadjust; + + unsigned n_copied, n_offseted, n_raw; + + z_stream zstream; + + // methods + void FreeBuffers(void); + void CreateVectorTable(void); + bool SetupBuffers(zmbv_format_t format, int blockwidth, int blockheight); + + template + void AddXorFrame(void); + template + void UnXorFrame(void); + template + INLINE int PossibleBlock(int vx,int vy,FrameBlock * block); + template + INLINE int CompareBlock(int vx,int vy,FrameBlock * block); + template + INLINE void AddXorBlock(int vx,int vy,FrameBlock * block); + template + INLINE void UnXorBlock(int vx,int vy,FrameBlock * block); + template + INLINE void CopyBlock(int vx, int vy,FrameBlock * block); +public: + ZMBV_Codec(); + bool SetupCompress( int _width, int _height); + bool SetupDecompress( int _width, int _height); + static zmbv_format_t BPPFormat( int bpp ); + + static int NeededSize( int _width, int _height, zmbv_format_t _format); + + void CompressLines(int lineCount, void *lineData[]); + bool PrepareCompressFrame(int flags, zmbv_format_t _format, char * pal, void *writeBuf, int writeSize, + int bw_base, int bh_base); + int FinishCompressFrame( void ); + bool DecompressFrame(void * framedata, int size); + void Output_UpsideDown_24(void * output); +}; + +extern "C" { +#endif + +typedef void* zmbv_codec; +zmbv_codec zmbv_create(void); +int zmbv_SetupCompress(zmbv_codec, int _width, int _height); +int zmbv_SetupDecompress(zmbv_codec, int _width, int _height); +zmbv_format_t zmbv_BPPFormat( int bpp ); +int zmbv_NeededSize(int _width, int _height, zmbv_format_t _format); +void zmbv_CompressLines(zmbv_codec, int lineCount, void *lineData[]); +int zmbv_PrepareCompressFrame(zmbv_codec, int flags, zmbv_format_t _format, char * pal, void *writeBuf, int writeSize); +int zmbv_FinishCompressFrame(zmbv_codec); +int zmbv_DecompressFrame(zmbv_codec, void * framedata, int size); +void zmbv_Output_UpsideDown_24(zmbv_codec, void * output); +void zmbv_erase(zmbv_codec); + +#ifdef __cplusplus +} +#endif diff --git a/libavcodec/zmbvenc.c b/libavcodec/zmbvenc.c index fb782a4..7ab1e2a 100644 --- a/libavcodec/zmbvenc.c +++ b/libavcodec/zmbvenc.c @@ -32,7 +32,7 @@ #include "avcodec.h" #include "internal.h" -#include +#include "zmbv_dosbox.h" #define ZMBV_KEYFRAME 1 #define ZMBV_DELTAPAL 2 @@ -46,206 +46,50 @@ typedef struct ZmbvEncContext { AVCodecContext *avctx; AVFrame pic; - int range; - uint8_t *comp_buf, *work_buf; - uint8_t pal[768]; - uint32_t pal2[256]; //for quick comparisons - uint8_t *prev; - int pstride; - int comp_size; - int keyint, curfrm; - z_stream zstream; -} ZmbvEncContext; - -static int score_tab[256]; - -/** Block comparing function - * XXX should be optimized and moved to DSPContext - * TODO handle out of edge ME - */ -static inline int block_cmp(uint8_t *src, int stride, uint8_t *src2, int stride2, - int bw, int bh, int *xored) -{ - int sum = 0; - int i, j; - uint8_t histogram[256] = {0}; - - *xored = 0; - for(j = 0; j < bh; j++){ - for(i = 0; i < bw; i++){ - int t = src[i] ^ src2[i]; - histogram[t]++; - *xored |= t; - } - src += stride; - src2 += stride2; - } + size_t comp_size; + char* comp_buf; - for(i = 1; i < 256; i++) - sum += score_tab[histogram[i]]; + size_t img_size; + unsigned char* img_buf; - return sum; -} + void** lines; -/** Motion estimation function - * TODO make better ME decisions - */ -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; - - *mx = *my = 0; - bw = FFMIN(ZMBV_BLOCK, c->avctx->width - x); - bh = FFMIN(ZMBV_BLOCK, c->avctx->height - y); - bv = block_cmp(src, sstride, prev, pstride, bw, bh, xored); - 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(src, sstride, prev + dx + dy*pstride, pstride, bw, bh, xored); - if(tv < bv){ - bv = tv; - *mx = dx; - *my = dy; - if(!bv) return 0; - } - } - } - return bv; -} + int keyint, curfrm; + zmbv_codec codec; + zmbv_format_t format; +} ZmbvEncContext; static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *pict, int *got_packet) { ZmbvEncContext * const c = avctx->priv_data; AVFrame * const p = &c->pic; - uint8_t *src, *prev, *buf; - uint32_t *palptr; - int keyframe, chpal; - int fl; - int work_size = 0, pkt_size; - int bw, bh; - int i, j, ret; - - keyframe = !c->curfrm; + + int pkt_size, ret; + int keyframe = !c->curfrm; + char* palette = avctx->pix_fmt == PIX_FMT_PAL8 ? (char*) p->data[1] : NULL; + c->curfrm++; if(c->curfrm == c->keyint) c->curfrm = 0; + *p = *pict; p->pict_type= keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; p->key_frame= keyframe; - chpal = !keyframe && memcmp(p->data[1], c->pal2, 1024); - - palptr = (uint32_t*)p->data[1]; - src = p->data[0]; - prev = c->prev; - if(chpal){ - uint8_t tpal[3]; - for(i = 0; i < 256; i++){ - AV_WB24(tpal, palptr[i]); - c->work_buf[work_size++] = tpal[0] ^ c->pal[i * 3 + 0]; - c->work_buf[work_size++] = tpal[1] ^ c->pal[i * 3 + 1]; - c->work_buf[work_size++] = tpal[2] ^ c->pal[i * 3 + 2]; - c->pal[i * 3 + 0] = tpal[0]; - c->pal[i * 3 + 1] = tpal[1]; - c->pal[i * 3 + 2] = tpal[2]; - } - memcpy(c->pal2, p->data[1], 1024); - } - if(keyframe){ - for(i = 0; i < 256; i++){ - AV_WB24(c->pal+(i*3), palptr[i]); - } - 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); - src += p->linesize[0]; - work_size += avctx->width; - } - }else{ - int x, y, bh2, bw2, xored; - uint8_t *tsrc, *tprev; - uint8_t *mv; - int mx, my; - - bw = (avctx->width + ZMBV_BLOCK - 1) / ZMBV_BLOCK; - bh = (avctx->height + ZMBV_BLOCK - 1) / ZMBV_BLOCK; - mv = c->work_buf + work_size; - memset(c->work_buf + work_size, 0, (bw * bh * 2 + 3) & ~3); - work_size += (bw * bh * 2 + 3) & ~3; - /* for now just XOR'ing */ - for(y = 0; y < avctx->height; y += ZMBV_BLOCK) { - bh2 = FFMIN(avctx->height - y, ZMBV_BLOCK); - for(x = 0; x < avctx->width; x += ZMBV_BLOCK, mv += 2) { - bw2 = FFMIN(avctx->width - x, ZMBV_BLOCK); - - tsrc = src + x; - tprev = prev + x; - - 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; - if(xored){ - for(j = 0; j < bh2; j++){ - for(i = 0; i < bw2; i++) - c->work_buf[work_size++] = tsrc[i] ^ tprev[i]; - tsrc += p->linesize[0]; - tprev += c->pstride; - } - } - } - src += p->linesize[0] * ZMBV_BLOCK; - prev += c->pstride * ZMBV_BLOCK; - } - } - /* save the previous frame */ - src = p->data[0]; - prev = c->prev; - for(i = 0; i < avctx->height; i++){ - memcpy(prev, src, avctx->width); - prev += c->pstride; - src += p->linesize[0]; - } - if (keyframe) - deflateReset(&c->zstream); + avpicture_layout(p->data, avctx->pix_fmt, avctx->width, avctx->height, + c->img_buf, c->img_size); - c->zstream.next_in = c->work_buf; - c->zstream.avail_in = work_size; - c->zstream.total_in = 0; - - c->zstream.next_out = c->comp_buf; - c->zstream.avail_out = c->comp_size; - c->zstream.total_out = 0; - if(deflate(&c->zstream, Z_SYNC_FLUSH) != Z_OK){ - av_log(avctx, AV_LOG_ERROR, "Error compressing data\n"); - return -1; - } - - pkt_size = c->zstream.total_out + 1 + 6*keyframe; + zmbv_PrepareCompressFrame(c->codec, keyframe, c->format, palette, c->comp_buf, c->comp_size); + zmbv_CompressLines(c->codec, avctx->height, c->lines); + + pkt_size = zmbv_FinishCompressFrame(c->codec); if ((ret = ff_alloc_packet2(avctx, pkt, pkt_size)) < 0) return ret; - buf = pkt->data; - - fl = (keyframe ? ZMBV_KEYFRAME : 0) | (chpal ? ZMBV_DELTAPAL : 0); - *buf++ = fl; - if (keyframe) { - *buf++ = 0; // hi ver - *buf++ = 1; // lo ver - *buf++ = 1; // comp - *buf++ = 4; // format - 8bpp - *buf++ = ZMBV_BLOCK; // block width - *buf++ = ZMBV_BLOCK; // block height - } - memcpy(buf, c->comp_buf, c->zstream.total_out); - pkt->flags |= AV_PKT_FLAG_KEY*keyframe; + memcpy(pkt->data, c->comp_buf, pkt_size); + + pkt->flags |= AV_PKT_FLAG_KEY * keyframe; *got_packet = 1; return 0; @@ -258,61 +102,60 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, static av_cold int encode_init(AVCodecContext *avctx) { ZmbvEncContext * const c = avctx->priv_data; - int zret; // Zlib return code - int i; - int lvl = 9; - for(i=1; i<256; i++) - score_tab[i]= -i * log(i/(double)(ZMBV_BLOCK*ZMBV_BLOCK)) * (256/M_LN2); + unsigned bpp = 0, bytesperpixel = 0; + int y; 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); - - if(avctx->compression_level >= 0) - lvl = avctx->compression_level; - if(lvl < 0 || lvl > 9){ - av_log(avctx, AV_LOG_ERROR, "Compression level should be 0-9, not %i\n", lvl); - return AVERROR(EINVAL); + c->keyint = avctx->gop_size; + + switch(avctx->pix_fmt) + { + case PIX_FMT_PAL8: bpp = 8; break; + case PIX_FMT_RGB555LE: bpp = 15; break; + case PIX_FMT_RGB565LE: bpp = 16; break; + case PIX_FMT_RGB24: bpp = 24; break; + case PIX_FMT_RGBA: bpp = 32; break; + case PIX_FMT_RGB32: bpp = 32; break; + default: + av_log(avctx, AV_LOG_ERROR, "Unknown pixel format %u, try \"format=bgr32\"\n", + (unsigned) avctx->pix_fmt); + return -1; } - // Needed if zlib unused or init aborted before deflateInit - memset(&c->zstream, 0, sizeof(z_stream)); - c->comp_size = avctx->width * 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)) == NULL) { - av_log(avctx, AV_LOG_ERROR, "Can't allocate work buffer.\n"); - return AVERROR(ENOMEM); - } - /* Conservative upper bound taken from zlib v1.2.1 source via lcl.c */ - c->comp_size = c->comp_size + ((c->comp_size + 7) >> 3) + - ((c->comp_size + 63) >> 6) + 11; + c->format = zmbv_BPPFormat(bpp); + c->comp_size = zmbv_NeededSize(avctx->width, avctx->height, c->format); - /* Allocate compression buffer */ if ((c->comp_buf = av_malloc(c->comp_size)) == NULL) { - av_log(avctx, AV_LOG_ERROR, "Can't allocate compression buffer.\n"); + av_log(avctx, AV_LOG_ERROR, "Can't allocate work buffer.\n"); return AVERROR(ENOMEM); } - c->pstride = FFALIGN(avctx->width, 16); - if ((c->prev = av_malloc(c->pstride * avctx->height)) == NULL) { - av_log(avctx, AV_LOG_ERROR, "Can't allocate picture.\n"); - return AVERROR(ENOMEM); + + bytesperpixel = ((bpp + 7) / 8); + c->img_size = avctx->width * avctx->height * bytesperpixel; + if (avctx->pix_fmt == PIX_FMT_PAL8) + c->img_size += 256 * 4; + + if ((c->img_buf = av_malloc(c->img_size)) == NULL) { + av_log(avctx, AV_LOG_ERROR, "Can't allocate image buffer.\n"); + return -1; } - - c->zstream.zalloc = Z_NULL; - c->zstream.zfree = Z_NULL; - c->zstream.opaque = Z_NULL; - zret = deflateInit(&c->zstream, lvl); - if (zret != Z_OK) { - av_log(avctx, AV_LOG_ERROR, "Inflate init error: %d\n", zret); + + if ((c->lines = (void**)av_malloc(avctx->height * sizeof(void*) )) == NULL) { + av_log(avctx, AV_LOG_ERROR, "Can't allocate line buffer.\n"); return -1; } - avctx->coded_frame = &c->pic; + for(y = 0; y < avctx->height; ++y) + { + c->lines[y] = &c->img_buf[y * avctx->width * bytesperpixel]; + } + + c->codec = zmbv_create(); + zmbv_SetupCompress(c->codec, avctx->width, avctx->height); + + avctx->coded_frame = (AVFrame*)&c->pic; return 0; } @@ -327,10 +170,8 @@ static av_cold int encode_end(AVCodecContext *avctx) ZmbvEncContext * const c = avctx->priv_data; av_freep(&c->comp_buf); - av_freep(&c->work_buf); - - deflateEnd(&c->zstream); - av_freep(&c->prev); + av_freep(&c->img_buf); + zmbv_erase(c->codec); return 0; } @@ -343,6 +184,14 @@ 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 PixelFormat[]){ + PIX_FMT_PAL8, + PIX_FMT_RGB555LE, + PIX_FMT_RGB565LE, + PIX_FMT_RGB24, + PIX_FMT_RGB32, + PIX_FMT_RGBA, + PIX_FMT_NONE + }, .long_name = NULL_IF_CONFIG_SMALL("Zip Motion Blocks Video"), };