#include #include #include #include #include "h261vlc.h" #include "video_common.h" #include "framefunc.h" #include "dct.h" void bounds_error(int line, char *file, char *function) { fprintf(stderr, "%s:%d -- %s\n", file, line, function); } inline int min(int x, int y) { return ((x < y) ? x : y); } inline int max(int x, int y) { return (x > y) ? x : y; } inline int uc_abs(int x) { return (x < 0) ? -x : x; } void motion_compensate(Frame *res, Frame *src, int xvector, int yvector, int blk_row, int blk_col) { u_char *ry, *ru, *rv, *sy, *su, *sv; int rows, cols, x, y; int tmp; int x_dst, y_dst; #ifdef BOUNDS_CHECK if(!res->sizecmp(src)) { /* Sizes do not match */ fprintf(stderr, "BOUNDS CHECK %s : %d %s\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); exit(0); } #endif rows = res->GetRows(); cols = res->GetCols(); y = blk_row * YBLKELEMS; x = blk_col * YBLKELEMS; x_dst = x + xvector; y_dst = y + yvector; #ifdef BOUNDS_CHECK if( x_dst < 0 || x_dst+16 > cols || y_dst < 0 || y_dst+16 > rows) { fprintf(stderr, "BOUNDS CHECK %s : %d %s r %d c %d\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, blk_row, blk_col); return; } #endif ry = res->Getydata() + y * cols + x; sy = src->Getydata() + y_dst * cols + x_dst; for(int j=0; j < 16; j++) { for(int i=0; i < 16; i++) *ry++ = *sy++; /* Skip to beginning of next row */ ry += cols - 16; sy += cols - 16; } /* That took care of y components now u and v */ ru = res->Getudata() + ((y *cols) >> 2) + (x >> 1); su = src->Getudata() + ((y_dst* cols)>> 2) + (x_dst >>1); rv = res->Getvdata() + (res->Getudata() - ru); sv = src->Getvdata() + (src->Getudata() - su); for(int j=0; j < 8; j++) { for(int i=0; i < 8; i++) { *ru++ = *su++; *rv++ = *sv++; } /* Skip to beginning of next row */ tmp = (cols >> 1) - 8; ru += tmp; su += tmp; rv += tmp; sv += tmp; /* Release tmp */ } } void motion_vectors(Frame *reference, Frame *source, int& xvector, int& yvector, int blk_row, int blk_col) { u_char *F = reference->Getydata(); u_char *G = source->Getydata(); int max_displacement = 15; int block_rows, block_cols, rows, cols; /* Per macro block variables */ int minYdisplacement, maxYdisplacement; int minXdisplacement, maxXdisplacement; u_char *Foffset; u_char *Goffset; int mad = INT_MAX; int dxmin = 0, dymin = 0; int x,y; #ifdef BOUNDS_CHECK if(!reference->sizecmp(source)) { /* Sizes do not match */ printf("Sizes problem\n"); exit(0); } #endif rows = reference->GetRows(); cols = reference->GetCols(); block_rows = rows/YBLKELEMS; block_cols = cols/YBLKELEMS; y = blk_row; x = blk_col; /* Max displacement region to search for this block */ minYdisplacement = min(y * YBLKELEMS , max_displacement); maxYdisplacement = min((block_rows-1-y) * YBLKELEMS, max_displacement); minXdisplacement = min(x * YBLKELEMS, max_displacement); maxXdisplacement = min((block_cols-1-x) * YBLKELEMS, max_displacement); Goffset = G + ( (y * YBLKELEMS) * cols + (x * YBLKELEMS)); /* Goffset sets us at the left top corner of the source and Foffset puts us at the left top corner of the reference. Each pixel traces out a matrix. */ for(int m=-minYdisplacement; m <= maxYdisplacement; m++) { for(int n=-minXdisplacement; n<= maxXdisplacement; n++) { int tmp = 0; u_char *Gtmp = Goffset, *Ftmp; Foffset = F + ((y * YBLKELEMS +m) * cols + x * YBLKELEMS +n); Ftmp = Foffset; /* Now for each of these displacement vals, we have to do comparisons */ for(int j=0; j < YBLKELEMS; j++) { for(int i=0; i < YBLKELEMS; i++) { tmp += uc_abs(*Gtmp - *Ftmp); Gtmp++; Ftmp++; } /* New addition */ if( tmp > mad) break; /* Skip to beginning of next row */ Gtmp += cols - YBLKELEMS; Ftmp += cols - YBLKELEMS; } /* End of macro block comparison for a single dx dy */ if ( tmp < mad) { mad = tmp; dxmin = n; dymin = m; } else if(tmp == mad) { if( (m*m + n*n) < (dxmin*dxmin + dymin*dymin) ) { dxmin = n; dymin = m; } } } /* Going through different dx for comparison */ } /* End of motion estimation search for this particular macroblock */ /* Set return value */ xvector = dxmin; yvector = dymin; } inline int get_MAD(u_char *Ftmp, u_char *Gtmp, int cols, int mad) { int tmp = 0; for(int j=0; j < YBLKELEMS; j++) { for(int i=0; i < YBLKELEMS; i++) { tmp += uc_abs(*Gtmp - *Ftmp); Gtmp++; Ftmp++; } if( tmp > mad) break; /* Skip to beginning of next row */ Gtmp += cols - YBLKELEMS; Ftmp += cols - YBLKELEMS; } /* End of macro block comparison for a single dx dy */ return tmp; } inline int bound_check(int max_blkrows, int max_blkcols, int blk_row, int blk_col, int xdisp, int ydisp) { if(!blk_row && ydisp < 0) /* Row 0 */ return 0; if(!blk_col && xdisp < 0) /* Col 0 */ return 0; if(!(max_blkrows-blk_row-1) && ydisp > 0) return 0; if(!(max_blkcols-blk_col-1) && xdisp > 0) return 0; if( xdisp < -15 || xdisp > 15 || ydisp < -15 || ydisp > 15) return 0; return 1; } inline void step_search(u_char *F, u_char *Goffset, int cols, int block_rows, int block_cols, int blk_row, int blk_col, int x_center, int y_center, int stepsz, int& curr_mad, int& dxmin, int& dymin) { u_char *Foffset; int mad = INT_MAX; for(int dx=-stepsz; dx <=stepsz; dx+=stepsz) { for(int dy=-stepsz; dy <=stepsz; dy+=stepsz) { if( (dx||dy) && bound_check(block_rows, block_cols, blk_row, blk_col, x_center+dx, y_center+dy)) { Foffset = F + ((blk_row * YBLKELEMS + y_center+dy) * cols + blk_col * YBLKELEMS + x_center+dx); mad = get_MAD(Foffset, Goffset, cols, curr_mad); if(mad < curr_mad) { dxmin = dx; dymin = dy; curr_mad = mad; } } } } } void mv_3step(Frame *reference, Frame *source, int& xvector, int& yvector, int blk_row, int blk_col) { u_char *F = reference->Getydata(); u_char *G = source->Getydata(); int block_rows, block_cols, rows, cols; u_char *Foffset; u_char *Goffset; int start_x = 0, start_y = 0; int dxmin=0, dymin=0; int curr_mad = INT_MAX; rows = reference->GetRows(); cols = reference->GetCols(); block_rows = rows/YBLKELEMS; block_cols = cols/YBLKELEMS; Goffset = G + ( (blk_row * YBLKELEMS) * cols + (blk_col * YBLKELEMS)); Foffset = F + ( (blk_row * YBLKELEMS) * cols + (blk_col * YBLKELEMS)); /* Get MAD for 0,0 */ curr_mad = get_MAD(Foffset, Goffset, cols, curr_mad); /* Search at [0,10], [0,-10], [-10,0], [10,0], [-10,-10], [-10,10], [10,-10], [10,10] */ /* Step 1 */ step_search(F, Goffset, cols, block_rows, block_cols, blk_row, blk_col, start_x, start_y, 10, curr_mad, dxmin, dymin); start_x += dxmin; start_y += dymin; dxmin = dymin = 0; /* Step 2 +-4 */ step_search(F, Goffset, cols, block_rows, block_cols, blk_row, blk_col, start_x, start_y, 4, curr_mad, dxmin, dymin); start_x += dxmin; start_y += dymin; dxmin = dymin = 0; /* Step 3 +-2 */ step_search(F, Goffset, cols, block_rows, block_cols, blk_row, blk_col, start_x, start_y, 2, curr_mad, dxmin, dymin); start_x += dxmin; start_y += dymin; dxmin = dymin = 0; /* Step 4 +-1 */ step_search(F, Goffset, cols, block_rows, block_cols, blk_row, blk_col, start_x, start_y, 1, curr_mad, dxmin, dymin); start_x += dxmin; start_y += dymin; dxmin = dymin = 0; xvector = start_x; yvector = start_y; } static const int zigzag[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 }; void write_block(short *fr, bit_io *output, int framewidth, int quant) { short val; char level; u_char run=0; int row, col; /* Do dct and quantization */ dct(fr, framewidth); qnt(fr, framewidth, 8, quant*2); for(int i=0; i < 64; i++) { row = zigzag[i] /8; col = zigzag[i] %8; val = fr[row * framewidth + col]; /* For each element */ if(!val) run++; else { /* 6bits of escape */ output->write_bits(tcoeff[1]); /* 6 bits of run */ run = run << 2; output->write_bits( &run, 6); /* 8bits of level */ level = char(val); output->write_bits((u_char *)&level, 8); /* write run,level combo */ run = 0; } } output->write_bits(tcoeff[0]); /* Do iquant and idct */ iqnt(fr, framewidth, 8, quant*2); idct(fr, framewidth); } int write_picture(PicInfo *p, Frame *xn, Frame *xpn1, Frame *xmcn1, int startgb, int startmb) { u_char tmp; // fprintf(stderr, "writing at %d %d\n", startgb, startmb); if( !(startgb || startmb)) { p->output.write_bits(psc); /* PSC */ /* tr */ tmp = ( u_char(p->framenum)) << 3; p->output.write_bits(&tmp,5); if(p->frameformat.type == CIF_TYPE) tmp = 6 << 2; /* type is 000110 */ else if(p->frameformat.type == QCIF_TYPE) tmp = 2 << 2; #ifdef BOUNDS_CHECK else bounds_error(__LINE__, __FILE__, __PRETTY_FUNCTION__); #endif p->output.write_bits(&tmp,6); /* PTYPE */ p->output.write_bit(0); /* PEI */ /* Skip spare */ } for(int i=startgb; i < p->frameformat.numgbs; i++) { if(p->frameformat.type == CIF_TYPE) write_gob(p, xn, xpn1, xmcn1, i, startmb); else write_gob(p, xn, xpn1, xmcn1, i*2, startmb); startgb = startmb = 0; } return 0; } void write_gob(PicInfo *p, Frame *xn, Frame *xpn1, Frame *xmcn1, int gobnum, int startmb) { u_char tmp; int gquant = p->gquant[gobnum]; if(!startmb) { p->output.write_bits(gbsc); /* GBSC */ /* gn */ tmp = (u_char(gobnum+1)) << 4; p->output.write_bits(&tmp, 4); /* gquant */ tmp = u_char(gquant) << 3; p->output.write_bits(&tmp, 5); /* GEI */ p->output.write_bit(0); } for(int i=startmb; i < MBS_IN_GOB; i++) write_mb(p, xn, xpn1, xmcn1, gobnum, i); } void write_mb(PicInfo *p, Frame *xn, Frame *xpn1, Frame *xmcn1, int gobnum, int mbnum) throw (PicException_t) { int x_motion_vec; int y_motion_vec; int row, col, mbcol, mbindex; int gquant; /* row, col give the index of the mb in the current frame */ short *fry, *fru, *frv; int yoffset, uvoffset; int framewidth = p->frameformat.cols; mbcol = mbnum % MBCOLS_IN_GOB; row = (gobnum /2) * MBROWS_IN_GOB + mbnum/MBCOLS_IN_GOB; col = (gobnum %2) * MBCOLS_IN_GOB + mbcol; mbindex = row * (framewidth / YBLKELEMS) + col; yoffset = row * YBLKELEMS * framewidth + col * YBLKELEMS; uvoffset = row * UVBLKELEMS * framewidth/2 + col * UVBLKELEMS; gquant = p->gquant[gobnum]; /* For now we are transmitting all the macroblocks */ p->output.write_bits(mba_addr[1]); /* MBA */ /* Conditional */ // output->write_bits(); /* MQUANT */ if( INTRA_FRAME & p->mbinfo[mbindex]) { /* Is it intra */ p->output.write_bits(mtype[0]); char2shortframe(p->pixdata, xn, yoffset, uvoffset); } else { /* Not intra */ p->output.write_bits(mtype[5]); // motion_vectors(xpn1, xn, x_motion_vec, y_motion_vec, row, col); mv_3step(xpn1, xn, x_motion_vec, y_motion_vec, row, col); motion_compensate(xmcn1, xpn1, x_motion_vec, y_motion_vec, row, col); p->xvecs[mbindex] = x_motion_vec; p->yvecs[mbindex] = y_motion_vec; subtract(p->pixdata, xn, xmcn1, yoffset, uvoffset); /* Absolute motion vector in three cases 1. for mbs 0, 11, 22 (mbcol = 0) 2. discontinuity in macroblocks 3. MTYPE of previous is not MC */ if(mbcol && !(INTRA_FRAME & p->mbinfo[mbindex-1])) { x_motion_vec -= p->xvecs[mbindex-1]; y_motion_vec -= p->yvecs[mbindex-1]; } if(x_motion_vec < 0) x_motion_vec += 32; if(y_motion_vec < 0) y_motion_vec += 32; #ifdef BOUNDS_CHECK if(x_motion_vec > 31 || y_motion_vec > 31) bounds_error(__LINE__, __FILE__, __PRETTY_FUNCTION__); #endif } /* Write mquant if applicable */ if(!(INTRA_FRAME & p->mbinfo[mbindex])) { p->output.write_bits(mvd[x_motion_vec]); /* MVD */ p->output.write_bits(mvd[y_motion_vec]); } /* Conditional */ p->output.write_bits(cbp[63]); /* CBP */ fry = p->pixdata->Getydata() + yoffset; fru = p->pixdata->Getudata() + uvoffset; frv = p->pixdata->Getvdata() + uvoffset; write_block(fry , &p->output, framewidth, gquant); write_block(fry+8, &p->output, framewidth, gquant); write_block(fry+8*framewidth, &p->output, framewidth, gquant); write_block(fry+8*framewidth+8, &p->output, framewidth, gquant); write_block(fru, &p->output, framewidth/2, gquant); write_block(frv, &p->output, framewidth/2, gquant); if( INTRA_FRAME & p->mbinfo[mbindex]) { /* Is it intra */ short2charframe(xpn1, p->pixdata, yoffset, uvoffset); } else { /* Not intra */ add(xpn1, p->pixdata, xmcn1, yoffset, uvoffset); } /* If packet is about full throw an exception */ if( p->output.GetBufWbytes() > 1024) { // fprintf(stderr, "---- Throwing exception at %d %d\n", gobnum, mbnum); throw(PicException_t(END_OF_PKT, gobnum, mbnum)); } } void read_mb(PicInfo *p, Frame *xn, Frame *xpn1, Frame *xmcn1, int gobnum, int mbnum) { int x_motion_vec; int y_motion_vec; int row, col, mbcol, mbindex, tmp; int mbnumrcvd, mtypercvd; int is_iframe = 0; int gquant; short *fry, *fru, *frv; int yoffset, uvoffset; int framewidth = p->frameformat.cols; mbcol = mbnum % MBCOLS_IN_GOB; row = (gobnum /2) * MBROWS_IN_GOB + mbnum/MBCOLS_IN_GOB; col = (gobnum %2) * MBCOLS_IN_GOB + mbcol; mbindex = row * (framewidth / YBLKELEMS) + col; yoffset = row * YBLKELEMS * framewidth + col * YBLKELEMS; uvoffset = row * UVBLKELEMS * framewidth/2 + col * UVBLKELEMS; gquant = p->gquant[gobnum]; if(tree_read(&p->input, &mba_addr_root, mbnumrcvd) < 0) { fprintf(stderr, "tree_read mba_addr at %d %d rbits=%d\n", gobnum, mbnum, p->input.GetRbufBits()); throw(PicException_t(STREAM_ERROR)); } #ifdef EXTRA_INFO if(mbnumrcvd != 1) { fprintf(stderr, "mbaaddr mismatch rcvd %d rbits=%d\n", mbnumrcvd, p->input.GetRbufBits()); fprintf(stderr, "gobnum=%d mbnum=%d\n", gobnum, mbnum); } #endif if(tree_read(&p->input, &mtype_root, mtypercvd) < 0) fprintf(stderr, "tree_read of mtype failed\n"); if(mtypercvd == 0) { is_iframe = 1; p->mbinfo[mbindex] |= INTRA_FRAME; } else { is_iframe = 0; p->mbinfo[mbindex] &= !INTRA_FRAME; } #ifdef EXTRA_INFO if(mtypercvd != 0 && mtypercvd != 5) fprintf(stderr, "mtypereceived is %d\n", mtypercvd); #endif if(!is_iframe) { if(tree_read(&p->input, &mvd_root, x_motion_vec) < 0) fprintf(stderr, "tree_read of xmotionvec failed\n"); if(tree_read(&p->input, &mvd_root, y_motion_vec) < 0) fprintf(stderr, "tree_read of xmotionvec failed\n"); if(mbcol && !(INTRA_FRAME & p->mbinfo[mbindex-1])) {/* For anything other than 0 */ x_motion_vec += p->xvecs[mbindex-1]; y_motion_vec += p->yvecs[mbindex-1]; } if(x_motion_vec > 15) x_motion_vec -= 32; if(y_motion_vec > 15) y_motion_vec -= 32; #ifdef BOUNDS_CHECK if(x_motion_vec > 15 || y_motion_vec > 15) bounds_error(__LINE__, __FILE__, __PRETTY_FUNCTION__); #endif p->xvecs[mbindex] = x_motion_vec; p->yvecs[mbindex] = y_motion_vec; } if( tree_read(&p->input, &cbp_root, tmp) < 0) fprintf(stderr, "tree_read of cbp failed\n"); if(tmp != 63) fprintf(stderr, "rcvd cbp index of %d\n", tmp); fry = p->pixdata->Getydata() + yoffset; fru = p->pixdata->Getudata() + uvoffset; frv = p->pixdata->Getvdata() + uvoffset; read_block(fry , &p->input, framewidth, gquant); read_block(fry+8, &p->input, framewidth, gquant); read_block(fry+8*framewidth, &p->input, framewidth, gquant); read_block(fry+8*framewidth+8, &p->input, framewidth, gquant); read_block(fru, &p->input, framewidth/2, gquant); read_block(frv, &p->input, framewidth/2, gquant); if( INTRA_FRAME & p->mbinfo[mbindex]) { /* Is it intra */ short2charframe(xn, p->pixdata, yoffset, uvoffset); } else { /* Not intra */ motion_compensate(xmcn1, xn, x_motion_vec, y_motion_vec, row, col); add(xn, p->pixdata, xmcn1, yoffset, uvoffset); } } void read_gob(PicInfo *p, Frame *xn, Frame *xpn1, Frame *xmcn1, int gobnum, int startmb) throw (PicException_t) { int tmp = 0, gobnumrcvd = 0; if(!startmb) { if( !p->input.GetRbufBits()) throw(PicException_t(END_OF_PKT, gobnum, startmb)); tmp = p->input.read_bits(gbsc.codelen); if(tmp != GBSC_VAL) { fprintf(stderr, "No GBSC start code\n"); throw(PicException_t(STREAM_ERROR)); } /* gn */ gobnumrcvd = p->input.read_bits(4)-1; if(gobnumrcvd != gobnum) { throw(PicException_t(STREAM_ERROR)); #ifdef EXTRA_INFO fprintf(stderr, "gobnum mismatch expected %d recvd %d\n", gobnum, gobnumrcvd); fprintf(stderr, "received group %d\n", tmp); #endif } p->gquant[gobnum] = p->input.read_bits(5); tmp = p->input.read_bit(); while(tmp) { p->input.read_bits(8); tmp = p->input.read_bit(); fprintf(stderr, "why am I receiving gei\n"); } } for(int i=startmb; i < MBS_IN_GOB; i++) { try { read_mb(p, xn, xpn1, xmcn1, gobnum, i); } catch (PicException_t& X) { if(X.type == READ_EXPT) throw(PicException_t(END_OF_PKT, gobnum, i)); else throw; } } } int read_picture(PicInfo *p, Frame *xn, Frame *xpn1, Frame *xmcn1, int startgb, int startmb) { int tmp = 0; if( !(startgb || startmb)) { try { tmp = p->input.read_bits(psc.codelen); if( tmp != PSC_VAL) { fprintf(stderr, "No PSC start code startgb=%d startmb=%d\n", startgb, startmb); fprintf(stderr, "tmp = %d = %x\n", tmp, tmp); exit(0); } p->framenum = p->input.read_bits(5); tmp = p->input.read_bits(6); if( !( (tmp == 6 && p->frameformat.type == CIF_TYPE) || (tmp == 2 && p->frameformat.type == QCIF_TYPE) ) ) bounds_error(__LINE__, __FILE__, __PRETTY_FUNCTION__); tmp = p->input.read_bit(); while(tmp) { p->input.read_bits(8); tmp = p->input.read_bit(); } } catch (PicException_t& X) { fprintf(stderr, "caught exception line %d\n", __LINE__); } } for(int i=startgb; i < p->frameformat.numgbs; i++) { if(p->frameformat.type == CIF_TYPE) read_gob(p, xn, xpn1, xmcn1, i, startmb); else read_gob(p, xn, xpn1, xmcn1, i << 1, startmb); startgb = startmb = 0; } return 0; } void read_block(short *fr, bit_io *input, int framewidth, int gquant) { int symbol; int index; int row, col; int run_till=0, tmp; char level=0; int pels_read = 0; while(pels_read < 64) { if(tree_read(input, &tcoeff_root, symbol) < 0) fprintf(stderr, "tree read of tcoeff failed\n"); if(symbol == 0) { /* Fill rest of the elements with zeros */ run_till = 64; } else if(symbol == 1) { /* We have escape + run + level */ run_till = pels_read + input->read_bits(6); tmp = input->read_bits(8); level = (char)tmp; } else fprintf(stderr, "invalid tcoeff\n"); for(; pels_read < run_till; pels_read++) { index = zigzag[pels_read]; row = index/8; col = index%8; fr[row * framewidth + col] = 0; } #ifdef BOUNDS_CHECK if(pels_read > 64) bounds_error(__LINE__, __FILE__, __PRETTY_FUNCTION__); #endif if(pels_read < 64) { index = zigzag[pels_read]; row = index/8; col = index%8; fr[row*framewidth + col] = level; pels_read++; } } if(symbol) { /* Expect another eob if eob not reached yet */ if(tree_read(input, &tcoeff_root, symbol) < 0) fprintf(stderr, "tree read of tcoeff_eob failed\n"); if(symbol) fprintf(stderr, "tcoeff_eob did not yield eob\n"); } /* Doint iquant and idct */ iqnt(fr, framewidth, 8, gquant*2); idct(fr, framewidth); }