X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fh.263%2Fmkvideo.c;h=250f4c738a5385e1c2ff9144b3386bf0dcbf1021;hb=95ab8f4e51eac64a2cb420a9c309e0f3cbdf0048;hp=88a482a5486bcdcaca373cf64ca5b7ce1434ea64;hpb=78235556f27a7dc3928cc88aef6fba0a5f4b03d9;p=swftools.git diff --git a/lib/h.263/mkvideo.c b/lib/h.263/mkvideo.c index 88a482a..250f4c7 100644 --- a/lib/h.263/mkvideo.c +++ b/lib/h.263/mkvideo.c @@ -13,8 +13,22 @@ #include "png.h" #include "h263tables.c" +typedef struct _YUV +{ + unsigned char y,u,v; +} YUV; + +typedef struct _VIDEOSTREAM +{ + int width; + int height; + int frame; + int linex; + YUV*oldpic; + YUV*current; +} VIDEOSTREAM; -void swf_SetVideoStreamDefine(TAG*tag, U16 frames, U16 width, U16 height) +void swf_SetVideoStreamDefine(TAG*tag, VIDEOSTREAM*stream, U16 frames, U16 width, U16 height) { width=width&~15; height=height&~15; swf_SetU16(tag, frames); @@ -22,9 +36,20 @@ void swf_SetVideoStreamDefine(TAG*tag, U16 frames, U16 width, U16 height) swf_SetU16(tag, height); swf_SetU8(tag, 1); /* smoothing on */ swf_SetU8(tag, 2); /* codec = h.263 sorenson spark */ + + memset(stream, 0, sizeof(VIDEOSTREAM)); + stream->linex = width; + width&=~15; + height&=~15; + stream->width = width; + stream->height = height; + stream->current = (YUV*)malloc(width*height*sizeof(YUV)); + stream->oldpic = (YUV*)malloc(width*height*sizeof(YUV)); + + memset(stream->oldpic, 0, width*height*sizeof(YUV)); } -struct block_t +typedef struct _block_t { int y1[64]; int y2[64]; @@ -32,9 +57,9 @@ struct block_t int y4[64]; int u[64]; int v[64]; -}; +} block_t; -struct fblock_t +typedef struct _fblock_t { double y1[64]; double y2[64]; @@ -42,109 +67,221 @@ struct fblock_t double y4[64]; double u[64]; double v[64]; -}; +} fblock_t; + +static int zigzagtable[64] = { + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63}; -void zigzag(int*src) -{ - int table[64] = { - 0, 1, 5, 6, 14, 15, 27, 28, - 2, 4, 7, 13, 16, 26, 29, 42, - 3, 8, 12, 17, 25, 30, 41, 43, - 9, 11, 18, 24, 31, 40, 44, 53, - 10, 19, 23, 32, 39, 45, 52, 54, - 20, 22, 33, 38, 46, 51, 55, 60, - 21, 34, 37, 47, 50, 56, 59, 61, - 35, 36, 48, 49, 57, 58, 62, 63}; - int tmp[64]; +static void fzigzag(double*src) +{ + double tmp[64]; int t; for(t=0;t<64;t++) { - tmp[table[t]] = src[t]; + ((int*)&tmp[zigzagtable[t]])[0] = ((int*)&src[t])[0]; + ((int*)&tmp[zigzagtable[t]])[1] = ((int*)&src[t])[1]; } - memcpy(src, tmp, sizeof(int)*64); + memcpy(src, tmp, sizeof(double)*64); } #define PI 3.14159265358979 #define SQRT2 1.414214 #define RSQRT2 (1.0/1.414214) -void dct(double*src) +static double table[8][8] = +{ +{0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548,0.707106781186548}, +{0.980785280403230,0.831469612302545,0.555570233019602,0.195090322016128,-0.195090322016128,-0.555570233019602,-0.831469612302545,-0.980785280403230}, +{0.923879532511287,0.382683432365090,-0.382683432365090,-0.923879532511287,-0.923879532511287,-0.382683432365090,0.382683432365090,0.923879532511287}, +{0.831469612302545,-0.195090322016128,-0.980785280403230,-0.555570233019602,0.555570233019602,0.980785280403230,0.195090322016129,-0.831469612302545}, +{0.707106781186548,-0.707106781186547,-0.707106781186548,0.707106781186547,0.707106781186548,-0.707106781186547,-0.707106781186547,0.707106781186547}, +{0.555570233019602,-0.980785280403230,0.195090322016128,0.831469612302545,-0.831469612302545,-0.195090322016128,0.980785280403231,-0.555570233019602}, +{0.382683432365090,-0.923879532511287,0.923879532511287,-0.382683432365090,-0.382683432365091,0.923879532511287,-0.923879532511286,0.382683432365090}, +{0.195090322016128,-0.555570233019602,0.831469612302545,-0.980785280403231,0.980785280403230,-0.831469612302545,0.555570233019602,-0.195090322016129} +}; + +static void dct(double*src) { double tmp[64]; - int x,y,u,v; - for(y=0;y<8;y++) - for(x=0;x<8;x++) + int x,y,u,v,t; + + for(v=0;v<8;v++) + for(u=0;u<8;u++) { double c = 0; - for(v=0;v<8;v++) - for(u=0;u<8;u++) + for(x=0;x<8;x++) { - double f = 0.25*cos(PI*(2.0*x+1.0)*u/16.0)*cos(PI*(2.0*y+1.0)*v/16.0); - if(!u) f *= RSQRT2; - if(!v) f *= RSQRT2; - c+=f*src[v*8+u]; + c+=table[u][x]*src[v*8+x]; } - tmp[y*8+x] = c; + tmp[v*8+u] = c; + } + for(u=0;u<8;u++) + for(v=0;v<8;v++) + { + double c = 0; + for(y=0;y<8;y++) + { + c+=table[v][y]*tmp[y*8+u]; + } + src[v*8+u] = c*0.25; } - memcpy(src, tmp, sizeof(double)*64); } -void idct(double*src) +static void idct(double*src) { double tmp[64]; int x,y,u,v; - for(v=0;v<8;v++) - for(u=0;u<8;u++) + for(y=0;y<8;y++) + for(x=0;x<8;x++) { double c = 0; - for(y=0;y<8;y++) - for(x=0;x<8;x++) + for(u=0;u<8;u++) { - double f = 0.25*cos(PI*(2.0*x+1.0)*u/16.0)*cos(PI*(2.0*y+1.0)*v/16.0); - if(!u) f *= RSQRT2; - if(!v) f *= RSQRT2; - c+=f*src[y*8+x]; + c+=table[u][x]*src[y*8+u]; } - tmp[v*8+u] = c; + tmp[y*8+x] = c; } - memcpy(src, tmp, sizeof(double)*64); + for(y=0;y<8;y++) + for(x=0;x<8;x++) + { + double c = 0; + for(v=0;v<8;v++) + { + c+=table[v][y]*tmp[v*8+x]; + } + src[y*8+x] = c*0.25; + } +} + +static inline int truncate256(int a) +{ + if(a>255) return 255; + if(a<0) return 0; + return a; } -void getregion(struct fblock_t* bb, RGBA*pic, int bx, int by, int width, int height) +static void getregion(fblock_t* bb, YUV*pic, int bx, int by, int linex) { - RGBA*p1 = &pic[by*width*16+bx*16]; - RGBA*p2 = p1; - int linex = width; + YUV*p1 = &pic[by*linex*16+bx*16]; + YUV*p2 = p1; int y1=0, y2=0, y3=0, y4=0; int u=0,v=0; int x,y; for(y=0;y<8;y++) { for(x=0;x<8;x++) { - double r,g,b; - r = (p2[x*2].r + p2[x*2+1].r + p2[linex+x*2].r + p2[linex+x*2+1].r)/4.0; - g = (p2[x*2].g + p2[x*2+1].g + p2[linex+x*2].g + p2[linex+x*2+1].g)/4.0; - b = (p2[x*2].b + p2[x*2+1].b + p2[linex+x*2].b + p2[linex+x*2+1].b)/4.0; - bb->u[u++] = (r*-0.169 + g*-0.332 + b*0.500 + 128.0); - bb->v[v++] = (r*0.500 + g*-0.419 + b*-0.0813 + 128.0); - - r = p1[x].r; g = p1[x].g; b = p1[x].b; - bb->y1[y1++] = (r*0.299 + g*0.587 + b*0.114); - r = p1[x+8].r; g = p1[x+8].g; b = p1[x+8].b; - bb->y2[y2++] = (r*0.299 + g*0.587 + b*0.114); - r = p1[linex*8+x].r; g = p1[linex*8+x].g; b = p1[linex*8+x].b; - bb->y3[y3++] = (r*0.299 + g*0.587 + b*0.114); - r = p1[linex*8+x+8].r; g = p1[linex*8+x+8].g; b = p1[linex*8+x+8].b; - bb->y4[y4++] = (r*0.299 + g*0.587 + b*0.114); + bb->u[u++] = (p2[x*2].u + p2[x*2+1].u + p2[linex+x*2].u + p2[linex+x*2+1].u)/4; + bb->v[v++] = (p2[x*2].v + p2[x*2+1].v + p2[linex+x*2].v + p2[linex+x*2+1].v)/4; + bb->y1[y1++] = p1[x].y; + bb->y2[y2++] = p1[x+8].y; + bb->y3[y3++] = p1[linex*8+x].y; + bb->y4[y4++] = p1[linex*8+x+8].y; } p1+=linex; p2+=linex*2; } } +static void rgb2yuv(YUV*dest, RGBA*src, int linex, int width, int height) +{ + int x,y; + for(y=0;ylinex*16+bx*16]; + YUV*p2 = &dest[by*s->linex*16+bx*16]; + int y; + for(y=0;y<16;y++) { + memcpy(p1, p2, 16*sizeof(YUV)); + p1+=s->linex;p2+=s->linex; + } +} + +static void yuv2rgb(RGBA*dest, YUV*src, int linex, int width, int height) +{ + int x,y; + for(y=0;y>8)); + dest[y*linex+x].g = truncate256(yy - ((88*(u-128)+183*(v-128))>>8)); + dest[y*linex+x].b = truncate256(yy + ((455 * (u-128))>>8)); + } + } +} +static void copyblock(VIDEOSTREAM*s, YUV*dest, block_t*b, int bx, int by) +{ + YUV*p1 = &dest[(by*16)*s->linex+bx*16]; + YUV*p2 = &dest[(by*16+8)*s->linex+bx*16]; + int x,y; + for(y=0;y<8;y++) { + for(x=0;x<8;x++) { + int u,v,yy; + p1[x+0].u = b->u[(y/2)*8+(x/2)]; + p1[x+0].v = b->v[(y/2)*8+(x/2)]; + p1[x+0].y = b->y1[y*8+x]; + p1[x+8].u = b->u[(y/2)*8+(x/2)+4]; + p1[x+8].v = b->v[(y/2)*8+(x/2)+4]; + p1[x+8].y = b->y2[y*8+x]; + p2[x+0].u = b->u[(y/2+4)*8+(x/2)]; + p2[x+0].v = b->v[(y/2+4)*8+(x/2)]; + p2[x+0].y = b->y3[y*8+x]; + p2[x+8].u = b->u[(y/2+4)*8+(x/2)+4]; + p2[x+8].v = b->v[(y/2+4)*8+(x/2)+4]; + p2[x+8].y = b->y4[y*8+x]; + } + p1+=s->linex; + p2+=s->linex; + } +} -int valtodc(int val) +static int compareregions(VIDEOSTREAM*s, int bx, int by) +{ + int linex = s->width; + YUV*p1 = &s->current[by*linex*16+bx*16]; + YUV*p2 = &s->oldpic[by*linex*16+bx*16]; + int diff = 0; + int x,y; + for(y=0;y<16;y++) { + for(x=0;x<16;x++) { + YUV*m = &p1[x]; + YUV*n = &p2[x]; + int y = m->y - n->y; + int u = m->u - n->u; + int v = m->v - n->v; + diff += y*y+(u*u+v*v)/4; + } + p1+=linex; + p2+=linex; + } + return diff/256; +} + +static int valtodc(int val) { assert(val>=0); /* table 12/h.263 */ + + //val+=4; //round val/=8; /* TODO: what to do for zero values? skip the block? */ if(val==0) @@ -155,8 +292,20 @@ int valtodc(int val) return 254; return val; } +static int dctoval(int dc) +{ + int val; + assert(dc>0); + assert(dc!=128); + assert(dc<256); + /* table 12/h.263 */ + val = dc*8; + if(val == 255*8) + val = 128*8; + return val; +} -void codehuffman(TAG*tag, struct huffcode*table, int index) +static int codehuffman(TAG*tag, struct huffcode*table, int index) { /* TODO: !optimize! */ int i=0; @@ -167,48 +316,120 @@ void codehuffman(TAG*tag, struct huffcode*table, int index) swf_SetBits(tag, 1, 1); i++; } + return i; } -int see=0; - -void quantize(double*src, int*dest, int quant, int has_dc) +static void quantize8x8(double*src, int*dest, int has_dc, int quant) { int t,pos=0; if(has_dc) { - dest[0] = (int)src[0]; /*DC*/ - dest[0] = valtodc(dest[0]); + dest[0] = valtodc((int)src[0]); /*DC*/ pos++; } for(t=pos;t<64;t++) { dest[t] = (int)src[t]; - //val = (quant*(2*level+1)-1)+quant&1 - if(quant&1) { - dest[t] = (dest[t]/quant - 1)/2; - } else { - dest[t] = ((dest[t]+1)/quant - 1)/2; + /* exact: if(quant&1){dest[t] = (dest[t]/quant - 1)/2;}else{dest[t] = ((dest[t]+1)/quant - 1)/2;} */ + //if(quant&1){dest[t] = (dest[t]/quant - 1)/2;}else{dest[t] = ((dest[t]+1)/quant - 1)/2;} + dest[t] = dest[t]/(quant*2); + } +} + +static void dequantize8x8(int*b, int has_dc, int quant) +{ + int t,pos=0; + if(has_dc) { + b[0] = dctoval(b[0]); //DC + pos++; + } + for(t=pos;t<64;t++) { + if(b[t]) { + int sign = 0; + if(b[t]<0) { + b[t] = -b[t]; + sign = 1; + } + + if(quant&1) { + b[t] = quant*(2*b[t]+1); //-7,8,24,40 + } else { + b[t] = quant*(2*b[t]+1)-1; //-8,7,23,39 + } + + if(sign) + b[t] = -b[t]; } + + /* paragraph 6.2.2, "clipping of reconstruction levels": */ + if(b[t]>2047) b[t]=2047; + if(b[t]<-2048) b[t]=-2048; } } -int hascoef(int*b, int has_dc) +static int hascoef(int*b, int has_dc) { int t; int pos=0; - int range=2; if(has_dc) pos++; for(t=pos;t<64;t++) { - if(b[t]<=-range || b[t]>=range) + if(b[t]) return 1; } return 0; } -void encode8x8(TAG*tag, int*bb, int has_dc, int has_tcoef) +static int coefbits8x8(int*bb, int has_dc) { int t; int pos=0; + int bits=0; + int last; + + if(has_dc) { + bits+=8; + pos++; + } + for(last=63;last>=pos;last--) { + if(bb[last]) + break; + } + if(last < pos) + return bits; + while(1) { + int run=0, level=0, islast=0,t; + while(!bb[pos] && pos=pos;last--) { + /* TODO: we could leave out small coefficients + after a certain point (32?) */ if(bb[last]) break; } - assert(bb[last]); /* blocks without coefficients should not be included - in the cbpy/cbpc patterns */ + in the cbpy/cbpc patterns: */ + assert(bb[last]); + while(1) { int run=0; int level=0; @@ -238,11 +462,13 @@ void encode8x8(TAG*tag, int*bb, int has_dc, int has_tcoef) if(pos==last) islast=1; level=bb[pos]; + assert(level); if(level<0) { level = -level; sign = 1; } for(t=0;t127) level = 127; + assert(level>=-127); + assert(level<=127); swf_SetBits(tag, islast, 1); swf_SetBits(tag, run, 6); - swf_SetBits(tag, level, 8); //fixme + swf_SetBits(tag, level, 8); //FIXME: fixme?? } if(islast) break; pos++; } + } +} - //codehuffman(tag, rle, 58); - //swf_SetBits(tag, 1, 1); //sign +static void dodct(fblock_t*fb) +{ + int t; + dct(fb->y1); dct(fb->y2); dct(fb->y3); dct(fb->y4); + dct(fb->u); dct(fb->v); + fzigzag(fb->y1); + fzigzag(fb->y2); + fzigzag(fb->y3); + fzigzag(fb->y4); + fzigzag(fb->u); + fzigzag(fb->v); +} + +static void doidct(block_t*b) +{ + fblock_t fb; + int t; + for(t=0;t<64;t++) { + fb.y1[t] = b->y1[zigzagtable[t]]; + fb.y2[t] = b->y2[zigzagtable[t]]; + fb.y3[t] = b->y3[zigzagtable[t]]; + fb.y4[t] = b->y4[zigzagtable[t]]; + fb.u[t] = b->u[zigzagtable[t]]; + fb.v[t] = b->v[zigzagtable[t]]; + } + idct(fb.y1); idct(fb.y2); idct(fb.y3); idct(fb.y4); + idct(fb.u); idct(fb.v); + for(t=0;t<64;t++) { + b->y1[t] = fb.y1[t]; + b->y2[t] = fb.y2[t]; + b->y3[t] = fb.y3[t]; + b->y4[t] = fb.y4[t]; + b->u[t] = fb.u[t]; + b->v[t] = fb.v[t]; + } +} +static void truncateblock(block_t*b) +{ + int t; + for(t=0;t<64;t++) { + b->y1[t] = truncate256(b->y1[t]); + b->y2[t] = truncate256(b->y2[t]); + b->y3[t] = truncate256(b->y3[t]); + b->y4[t] = truncate256(b->y4[t]); + b->u[t] = truncate256(b->u[t]); + b->v[t] = truncate256(b->v[t]); } - see++; } -void encode_blockI(TAG*tag, RGBA*pic, int bx, int by, int width, int height, int*quant) +static void quantize(fblock_t*fb, block_t*b, int has_dc, int quant) +{ + quantize8x8(fb->y1, b->y1, has_dc, quant); + quantize8x8(fb->y2, b->y2, has_dc, quant); + quantize8x8(fb->y3, b->y3, has_dc, quant); + quantize8x8(fb->y4, b->y4, has_dc, quant); + quantize8x8(fb->u, b->u, has_dc, quant); + quantize8x8(fb->v, b->v, has_dc, quant); +} +static void dequantize(block_t*b, int has_dc, int quant) { - struct fblock_t b; - int dquant=0; /* TODO: should we take advantage of the dquant feature?*/ - //int cbpcbits = 3; - //int cbpybits = 15; - int cbpcbits = 0; - int cbpybits = 0; - int y1[64],y2[64],y3[64],y4[64],u[64],v[64]; + dequantize8x8(b->y1, has_dc, quant); + dequantize8x8(b->y2, has_dc, quant); + dequantize8x8(b->y3, has_dc, quant); + dequantize8x8(b->y4, has_dc, quant); + dequantize8x8(b->u, has_dc, quant); + dequantize8x8(b->v, has_dc, quant); +} + +static void getblockpatterns(block_t*b, int*cbpybits,int*cbpcbits, int has_dc) +{ + *cbpybits = 0; + *cbpcbits = 0; - getregion(&b, pic, bx, by, width, height); - dct(b.y1); quantize(b.y1,y1,1,*quant); zigzag(y1); cbpybits|=hascoef(y1, 1)*8; - dct(b.y2); quantize(b.y2,y2,1,*quant); zigzag(y2); cbpybits|=hascoef(y2, 1)*4; - dct(b.y3); quantize(b.y3,y3,1,*quant); zigzag(y3); cbpybits|=hascoef(y3, 1)*2; - dct(b.y4); quantize(b.y4,y4,1,*quant); zigzag(y4); cbpybits|=hascoef(y4, 1)*1; - dct(b.u); quantize(b.u,u,1,*quant); zigzag(u); cbpcbits|=hascoef(u, 1)*2; - dct(b.v); quantize(b.v,v,1,*quant); zigzag(v); cbpcbits|=hascoef(v, 1)*1; + *cbpybits|=hascoef(b->y1, has_dc)*8; + *cbpybits|=hascoef(b->y2, has_dc)*4; + *cbpybits|=hascoef(b->y3, has_dc)*2; + *cbpybits|=hascoef(b->y4, has_dc)*1; + + *cbpcbits|=hascoef(b->u, has_dc)*2; + *cbpcbits|=hascoef(b->v, has_dc)*1; +} + +static void setQuant(TAG*tag, int dquant) +{ + int code = 0; + /* 00 01 10 11 + -1 -2 +1 +2 + */ + if(dquant == -1) { + swf_SetBits(tag, 0x0, 2); + } else if(dquant == -2) { + swf_SetBits(tag, 0x1, 2); + } else if(dquant == +1) { + swf_SetBits(tag, 0x2, 2); + } else if(dquant == +2) { + swf_SetBits(tag, 0x3, 2); + } else { + assert(0*strlen("invalid dquant")); + } +} + +static void change_quant(int quant, int*dquant) +{ + /* TODO */ + *dquant = 0; +} + +static void encode_blockI(TAG*tag, VIDEOSTREAM*s, int bx, int by, int*quant) +{ + fblock_t fb; + block_t b; + int dquant=0; + int cbpcbits = 0, cbpybits=0; + + getregion(&fb, s->current, bx, by, s->width); + dodct(&fb); + + change_quant(*quant, &dquant); + *quant+=dquant; + quantize(&fb, &b, 1, *quant); + + //decode_blockI(s, &b, bx, by); + + getblockpatterns(&b, &cbpybits, &cbpcbits, 1); if(dquant) { codehuffman(tag, mcbpc_intra, 4+cbpcbits); } else { codehuffman(tag, mcbpc_intra, 0+cbpcbits); } - //swf_SetBits(tag, 1, 1); /*cbpc-00 mb_type=3 (no bquant), cbc=0*/ - /* if this was an intra frame, we'd need to code (cbpybits^15) */ codehuffman(tag, cbpy, cbpybits); if(dquant) { - /* 00 01 10 11 - -1 -2 +1 +2 - */ - swf_SetBits(tag, 0x3, 2); + setQuant(tag, dquant); } /* luminance */ - encode8x8(tag, y1, 1, cbpybits&8); - encode8x8(tag, y2, 1, cbpybits&4); - encode8x8(tag, y3, 1, cbpybits&2); - encode8x8(tag, y4, 1, cbpybits&1); - /*swf_SetBits(tag, 0x1, 8); - swf_SetBits(tag, 0xfe, 8); - swf_SetBits(tag, 0x1, 8); - swf_SetBits(tag, 0x1, 8);*/ + encode8x8(tag, b.y1, 1, cbpybits&8); + encode8x8(tag, b.y2, 1, cbpybits&4); + encode8x8(tag, b.y3, 1, cbpybits&2); + encode8x8(tag, b.y4, 1, cbpybits&1); /* chrominance */ - encode8x8(tag, u, 1, cbpcbits&2); - encode8x8(tag, v, 1, cbpcbits&1); - /*swf_SetBits(tag, 0xfe, 8); - swf_SetBits(tag, 0xfe, 8);*/ + encode8x8(tag, b.u, 1, cbpcbits&2); + encode8x8(tag, b.v, 1, cbpcbits&1); + + /* reconstruct */ + dequantize(&b, 1, *quant); + doidct(&b); + truncateblock(&b); + copyblock(s, s->current, &b, bx, by); } -void swf_SetVideoStreamIFrame(TAG*tag, RGBA*pic, U16 width, U16 height, int frame) +static void yuvdiff(fblock_t*a, fblock_t*b) { - U32 i32; - int bx, by, bbx, bby; - int quant = 9; + int t; + for(t=0;t<64;t++) { + a->y1[t] = (a->y1[t] - b->y1[t]); + a->y2[t] = (a->y2[t] - b->y2[t]); + a->y3[t] = (a->y3[t] - b->y3[t]); + a->y4[t] = (a->y4[t] - b->y4[t]); + a->u[t] = (a->u[t] - b->u[t]); + a->v[t] = (a->v[t] - b->v[t]); + } +} - width=width&~15; height=height&~15; +static int encode_blockP(TAG*tag, VIDEOSTREAM*s, int bx, int by, int*quant) +{ + fblock_t fb; + block_t b; + int dquant=0; + int has_mvd=0; + int has_mvd24=0; + int has_dc=1; + int mode = 0; + int cbpcbits = 0, cbpybits=0; + int diff; + + block_t b_i; + int bits_i; + + fblock_t fbold_v00; + block_t b_v00; + int bits_v00; + + diff = compareregions(s, bx, by); + if(diff < 24 /*TODO: should be a parameter- good values are between 32 and 48 */) { + swf_SetBits(tag, 1,1); /* cod=1, block skipped */ + copyregion(s, s->current, s->oldpic, bx, by); + return; + } + + getregion(&fb, s->current, bx, by, s->width); + + { /* consider I-block */ + fblock_t fb_i; + int y,c; + memcpy(&fb_i, &fb, sizeof(fblock_t)); + dodct(&fb_i); + quantize(&fb_i, &b_i, 1, *quant); + getblockpatterns(&b_i, &y, &c, 1); + bits_i = 1; //cod + bits_i += mcbpc_inter[3*4+c].len; + bits_i += cbpy[y].len; + bits_i += coefbits8x8(b_i.y1, 1); + bits_i += coefbits8x8(b_i.y2, 1); + bits_i += coefbits8x8(b_i.y3, 1); + bits_i += coefbits8x8(b_i.y4, 1); + bits_i += coefbits8x8(b_i.u, 1); + bits_i += coefbits8x8(b_i.v, 1); + } + { /* consider mvd(0,0)-block */ + fblock_t fbdiff; + int y,c; + memcpy(&fbdiff, &fb, sizeof(fblock_t)); + getregion(&fbold_v00, s->oldpic, bx, by, s->linex); + yuvdiff(&fbdiff, &fbold_v00); + dodct(&fbdiff); + quantize(&fbdiff, &b_v00, 0, *quant); + getblockpatterns(&b_v00, &y, &c, 0); + bits_v00 = 1; //cod + bits_v00 += mcbpc_inter[0*4+c].len; + bits_v00 += cbpy[y^15].len; + bits_v00 += mvd[32].len; // (0,0) + bits_v00 += mvd[32].len; + bits_v00 += coefbits8x8(b_v00.y1, 0); + bits_v00 += coefbits8x8(b_v00.y2, 0); + bits_v00 += coefbits8x8(b_v00.y3, 0); + bits_v00 += coefbits8x8(b_v00.y4, 0); + bits_v00 += coefbits8x8(b_v00.u, 0); + bits_v00 += coefbits8x8(b_v00.v, 0); + } + + if(bits_i > bits_v00) + { + /* mvd (0,0) block (mode=0) */ + int t; + mode = 0; // mvd w/o mvd24 + has_dc = 0; + memcpy(&b, &b_v00, sizeof(block_t)); + + getblockpatterns(&b, &cbpybits, &cbpcbits, has_dc); + swf_SetBits(tag,0,1); // COD + codehuffman(tag, mcbpc_inter, mode*4+cbpcbits); + codehuffman(tag, cbpy, cbpybits^15); + + /* 0,0 */ + codehuffman(tag, mvd, 32); + codehuffman(tag, mvd, 32); + + /* luminance */ + encode8x8(tag, b.y1, has_dc, cbpybits&8); + encode8x8(tag, b.y2, has_dc, cbpybits&4); + encode8x8(tag, b.y3, has_dc, cbpybits&2); + encode8x8(tag, b.y4, has_dc, cbpybits&1); + + /* chrominance */ + encode8x8(tag, b.u, has_dc, cbpcbits&2); + encode8x8(tag, b.v, has_dc, cbpcbits&1); + + /* -- reconstruction -- */ + dequantize(&b, 0, *quant); + doidct(&b); + for(t=0;t<64;t++) { + b.y1[t] = truncate256(b.y1[t] + (int)fbold_v00.y1[t]); + b.y2[t] = truncate256(b.y2[t] + (int)fbold_v00.y2[t]); + b.y3[t] = truncate256(b.y3[t] + (int)fbold_v00.y3[t]); + b.y4[t] = truncate256(b.y4[t] + (int)fbold_v00.y4[t]); + b.u[t] = truncate256(b.u[t] + (int)fbold_v00.u[t]); + b.v[t] = truncate256(b.v[t] + (int)fbold_v00.v[t]); + } + copyblock(s, s->current, &b, bx, by); + return bits_v00; + } else { + /* i block (mode=3) */ + mode = 3; + has_dc = 1; + memcpy(&b, &b_i, sizeof(block_t)); + //dodct(&fb); + //quantize(&fb, &b, has_dc, *quant); + getblockpatterns(&b, &cbpybits, &cbpcbits, has_dc); + swf_SetBits(tag,0,1); // COD + codehuffman(tag, mcbpc_inter, mode*4+cbpcbits); + codehuffman(tag, cbpy, cbpybits); + + /* luminance */ + encode8x8(tag, b.y1, has_dc, cbpybits&8); + encode8x8(tag, b.y2, has_dc, cbpybits&4); + encode8x8(tag, b.y3, has_dc, cbpybits&2); + encode8x8(tag, b.y4, has_dc, cbpybits&1); + + /* chrominance */ + encode8x8(tag, b.u, has_dc, cbpcbits&2); + encode8x8(tag, b.v, has_dc, cbpcbits&1); + + /* -- reconstruction -- */ + dequantize(&b, 1, *quant); + doidct(&b); + truncateblock(&b); + copyblock(s, s->current, &b, bx, by); + return bits_i; + } + + exit(1); +#if 0 + dodct(&fb); + quantize(&fb, &b, has_dc, *quant); + getblockpatterns(&b, &cbpybits, &cbpcbits, has_dc); + + if(!dquant && has_mvd && !has_mvd24 && !has_dc) mode = 0; + else if(dquant && has_mvd && !has_mvd24 && !has_dc) mode = 1; + else if(!dquant && has_mvd && has_mvd24 && !has_dc) mode = 2; + else if(!dquant && !has_mvd && !has_mvd24 && has_dc) mode = 3; + else if(dquant && !has_mvd && !has_mvd24 && has_dc) mode = 4; + else exit(1); + + swf_SetBits(tag,0,1); /* cod - 1 if we're not going to code this block*/ + + codehuffman(tag, mcbpc_inter, mode*4+cbpcbits); + codehuffman(tag, cbpy, (mode==3 || mode==4)?cbpybits:cbpybits^15); + + if(dquant) { + setQuant(tag, dquant); + } + + if(has_mvd) { + /* 0,0 */ + codehuffman(tag, mvd, 32); + codehuffman(tag, mvd, 32); + } + if(has_mvd24) { + } + + /* luminance */ + encode8x8(tag, b.y1, has_dc, cbpybits&8); + encode8x8(tag, b.y2, has_dc, cbpybits&4); + encode8x8(tag, b.y3, has_dc, cbpybits&2); + encode8x8(tag, b.y4, has_dc, cbpybits&1); + + /* chrominance */ + encode8x8(tag, b.u, has_dc, cbpcbits&2); + encode8x8(tag, b.v, has_dc, cbpcbits&1); +#endif +} + +#define TYPE_IFRAME 0 +#define TYPE_PFRAME 1 + +static void writeHeader(TAG*tag, int width, int height, int frame, int quant, int type) +{ + U32 i32; swf_SetU16(tag, frame); swf_SetBits(tag, 1, 17); /* picture start code*/ swf_SetBits(tag, 0, 5); /* version=0, version 1 would optimize rle behaviour*/ @@ -361,48 +877,105 @@ void swf_SetVideoStreamIFrame(TAG*tag, RGBA*pic, U16 width, U16 height, int fram } } - swf_SetBits(tag, 0, 2); /* I-Frame */ + swf_SetBits(tag, type, 2); /* I-Frame or P-Frame */ swf_SetBits(tag, 0, 1); /* No deblock filter */ + assert(quant>0); swf_SetBits(tag, quant, 5); /* quantizer (1-31), may be updated later on*/ swf_SetBits(tag, 0, 1); /* No extra info */ +} - bbx = (width+15)/16; - bby = (height+15)/16; +void swf_SetVideoStreamIFrame(TAG*tag, VIDEOSTREAM*s, RGBA*pic) +{ + int bx, by, bbx, bby; + int quant = 31; + + writeHeader(tag, s->width, s->height, s->frame, quant, TYPE_IFRAME); + + bbx = (s->width+15)/16; //TODO: move bbx,bby into VIDEOSTREAM + bby = (s->height+15)/16; + + rgb2yuv(s->current, pic, s->linex, s->width, s->height); + + for(by=0;byframe++; + memcpy(s->oldpic, s->current, s->width*s->height*sizeof(YUV)); +} + +void swf_SetVideoStreamPFrame(TAG*tag, VIDEOSTREAM*s, RGBA*pic) +{ + int bx, by, bbx, bby; + int quant = 31; + + writeHeader(tag, s->width, s->height, s->frame, quant, TYPE_PFRAME); + + bbx = (s->width+15)/16; + bby = (s->height+15)/16; + + rgb2yuv(s->current, pic, s->linex, s->width, s->height); for(by=0;byframe++; + memcpy(s->oldpic, s->current, s->width*s->height*sizeof(YUV)); + + { + int t; + FILE*fi = fopen("test.ppm", "wb"); + yuv2rgb(pic, s->current, s->linex, s->width, s->height); + fprintf(fi, "P6\n%d %d\n255\n", s->width, s->height); + for(t=0;twidth*s->height;t++) + { + fwrite(&pic[t].r, 1, 1, fi); + fwrite(&pic[t].g, 1, 1, fi); + fwrite(&pic[t].b, 1, 1, fi); + } + fclose(fi); + } } -int main() +int main(int argn, char*argv[]) { int fi; int t; SWF swf; TAG * tag; - RGBA* pic, rgb; + RGBA* pic, *pic2, rgb; SWFPLACEOBJECT obj; int width = 0; int height = 0; - int frames = 1; + int frames = 2; + int framerate = 1; unsigned char*data; - char* fname = "/home/kramm/pics/lena.png"; + char* fname = "/home/kramm/pics/peppers.png"; + VIDEOSTREAM stream; + double d = 1.0; + + memset(&stream, 0, sizeof(stream)); - getimage(fname, &width, &height, &data); + getPNG(fname, &width, &height, &data); pic = (RGBA*)malloc(width*height*sizeof(RGBA)); + pic2 = (RGBA*)malloc(width*height*sizeof(RGBA)); memcpy(pic, data, width*height*sizeof(RGBA)); free(data); + printf("Compressing %s, size %dx%d\n", fname, width, height); memset(&swf,0,sizeof(SWF)); memset(&obj,0,sizeof(obj)); swf.fileVersion = 6; - swf.frameRate = 29*256; + swf.frameRate = framerate*256; swf.movieSize.xmax = 20*width; swf.movieSize.ymax = 20*height; @@ -413,13 +986,26 @@ int main() tag = swf_InsertTag(tag, ST_DEFINEVIDEOSTREAM); swf_SetU16(tag, 33); - swf_SetVideoStreamDefine(tag, frames, width, height); + swf_SetVideoStreamDefine(tag, &stream, frames, width, height); for(t=0;t