#include "../libzmpeg3.h" /* zig-zag scan */ uint8_t zvideo_t:: zig_zag_scan_nommx[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, }; /* alternate scan */ uint8_t zvideo_t:: alternate_scan_nommx[64] = { 0, 8, 16, 24, 1, 9, 2, 10, 17, 25, 32, 40, 48, 56, 57, 49, 41, 33, 26, 18, 3, 11, 4, 12, 19, 27, 34, 42, 50, 58, 35, 43, 51, 59, 20, 28, 5, 13, 6, 14, 21, 29, 36, 44, 52, 60, 37, 45, 53, 61, 22, 30, 7, 15, 23, 31, 38, 46, 54, 62, 39, 47, 55, 63, }; /* default intra quantization matrix */ uint8_t zvideo_t:: default_intra_quantizer_matrix[64] = { 8, 16, 19, 22, 26, 27, 29, 34, 16, 16, 22, 24, 27, 29, 34, 37, 19, 22, 26, 27, 29, 34, 34, 38, 22, 22, 26, 27, 29, 34, 37, 40, 22, 26, 27, 29, 32, 35, 40, 48, 26, 27, 29, 32, 35, 40, 48, 58, 26, 27, 29, 34, 38, 46, 56, 69, 27, 29, 35, 38, 46, 56, 69, 83, }; double zvideo_t:: frame_rate_table[16] = { 0.0, /* Pad */ (double)24000.0/1001.0, /* Official frame rates */ (double)24.0, (double)25.0, (double)30000.0/1001.0, (double)30.0, (double)50.0, (double)60000.0/1001.0, (double)60.0, 1.0, 5.0, 10.0, 12.0, 15.0, /* Unofficial economy rates */ 0.0, 0.0, }; static int blk_cnt_tab[3] = { 6, 8, 12 }; int zvideo_t:: init_decoder() { int i, j, sz, cc; long size[4], padding[2]; /* Size of Y, U, and V buffers */ if( !mpeg2 ) { /* force MPEG-1 parameters */ prog_seq = 1; prog_frame = 1; pict_struct = pics_FRAME_PICTURE; frame_pred_dct = 1; chroma_format = cfmt_420; matrix_coefficients = 5; } /* Get dimensions rounded to nearest multiple of coded macroblocks */ mb_width = (horizontal_size + 15) / 16; mb_height = (mpeg2 && !prog_seq) ? (2 * ((vertical_size + 31) / 32)) : ((vertical_size + 15) / 16); coded_picture_width = 16 * mb_width; coded_picture_height = 16 * mb_height; chrom_width = (chroma_format == cfmt_444) ? coded_picture_width : (coded_picture_width >> 1); chrom_height = (chroma_format != cfmt_420) ? coded_picture_height : (coded_picture_height >> 1); blk_cnt = blk_cnt_tab[chroma_format - 1]; /* Get sizes of YUV buffers */ padding[0] = 16*coded_picture_width + 16; size[0] = coded_picture_width*coded_picture_height + padding[0]; padding[1] = 16*chrom_width + 16; size[1] = chrom_width*chrom_height + padding[1]; size[3] = (size[2] = (llw * llh)) / 4; /* Allocate contiguous fragments for YUV buffers for hardware YUV decoding */ sz = size[0] + 2*size[1]; yuv_buffer[0] = new uint8_t[sz]; memset(yuv_buffer[0],0,sz); yuv_buffer[1] = new uint8_t[sz]; memset(yuv_buffer[1],0,sz); yuv_buffer[2] = new uint8_t[sz]; memset(yuv_buffer[2],0,sz); if( scalable_mode == slice_decoder_t::sc_SPAT ) { sz = size[2] + 2 * size[3]; yuv_buffer[3] = new uint8_t[sz]; memset(yuv_buffer[3],0,sz); yuv_buffer[4] = new uint8_t[sz]; memset(yuv_buffer[4],0,sz); } tdat = new uint8_t[4 * mb_width * mb_height]; memset(tdat, 0, 4 * mb_width * mb_height); slice_decoder_t::init_tables(); /* Direct pointers to areas of contiguous fragments in YVU order per Microsoft */ for( cc=0; cc<3; ++cc ) { llframe0[cc] = 0; llframe1[cc] = 0; newframe[cc] = 0; } refframe[0] = yuv_buffer[0]; oldrefframe[0] = yuv_buffer[1]; auxframe[0] = yuv_buffer[2]; sz = size[0]; refframe[2] = yuv_buffer[0] + sz; oldrefframe[2] = yuv_buffer[1] + sz; auxframe[2] = yuv_buffer[2] + sz; sz += size[1]; refframe[1] = yuv_buffer[0] + sz; oldrefframe[1] = yuv_buffer[1] + sz; auxframe[1] = yuv_buffer[2] + sz; if( scalable_mode == slice_decoder_t::sc_SPAT ) { /* this assumes lower layer is 4:2:0 */ sz = padding[0]; llframe0[0] = yuv_buffer[3] + sz; llframe1[0] = yuv_buffer[4] + sz; sz = padding[1] + size[2]; llframe0[2] = yuv_buffer[3] + sz; llframe1[2] = yuv_buffer[4] + sz; sz += size[3]; llframe0[1] = yuv_buffer[3] + sz; llframe1[1] = yuv_buffer[4] + sz; } /* Initialize the YUV tables for software YUV decoding */ cr_to_r = new int[256]; crb_to_g = new int[256*256]; cb_to_b = new int[256]; int *cr_to_r_ptr = cr_to_r + 128; int *crb_to_g_ptr = crb_to_g + 128*256 + 128; int *cb_to_b_ptr = cb_to_b + 128; for( i=-128; i<128; ++i ) { cr_to_r_ptr[i] = (int)(1.371*i * 65536.); for( j=-128; j<128; ++j ) crb_to_g_ptr[i*256 + j] = (int)((-0.698*i + -0.336*j) * 65536.); cb_to_b_ptr[i] = (int)((1.732*i) * 65536.); } decoder_initted = 1; return 0; } int zvideo_t:: delete_decoder() { if( yuv_buffer[0] ) delete [] yuv_buffer[0]; if( yuv_buffer[1] ) delete [] yuv_buffer[1]; if( yuv_buffer[2] ) delete [] yuv_buffer[2]; if( subtitle_frame[0] ) delete [] subtitle_frame[0]; if( subtitle_frame[1] ) delete [] subtitle_frame[1]; if( subtitle_frame[2] ) delete [] subtitle_frame[2]; if( llframe0[0] ) { delete [] yuv_buffer[3]; delete [] yuv_buffer[4]; } delete [] tdat; delete [] cr_to_r; delete [] crb_to_g; delete [] cb_to_b; return 0; } void zvideo_t:: init_scantables() { zigzag_scan_table = zig_zag_scan_nommx; alternate_scan_table = alternate_scan_nommx; } void zvideo_t:: init_video() { int result; init_decoder(); track->width = horizontal_size; track->height = vertical_size; track->frame_rate = frame_rate; demuxer_t *demux = vstream->demuxer; /* Try to get the length of the file from GOP's */ if( !track->frame_offsets ) { if( src->is_video_stream() ) { /* Load the first GOP */ rewind_video(0); result = vstream->next_code(GOP_START_CODE); if( !result ) vstream->get_bits(32); if( !result ) result = get_gop_header(); first_frame = gop_to_frame(&gop_timecode); /* GOPs are supposed to have 16 frames */ frames_per_gop = 16; /* Read the last GOP in the file by seeking backward. */ demux->seek_byte(demux->movie_size()); demux->start_reverse(); result = demux->prev_code(GOP_START_CODE); demux->start_forward(); vstream->reset(); vstream->get_bits(8); if(!result) result = get_gop_header(); last_frame = gop_to_frame(&gop_timecode); //zmsgs("3 %p\n", this); /* Count number of frames to end */ while( !result ) { result = vstream->next_code(PICTURE_START_CODE); if( !result ) { vstream->get_byte_noptr(); ++last_frame; } } track->total_frames = last_frame-first_frame+1; //zmsgs("mpeg3video_new 3 %ld\n", track->total_frames); } else { /* Try to get the length of the file from the multiplexing. */ /* Need a table of contents */ /* first_frame = 0; * track->total_frames = last_frame = * (long)(demux->length() * frame_rate); * first_frame = 0; */ } } else { /* Get length from table of contents */ track->total_frames = track->total_frame_offsets-1; } maxframe = track->total_frames; rewind_video(0); } int zvideo_t:: video_pts_padding() { int result = 0; if( track->video_time >= 0 && src->pts_padding > 0 ) { int pts_framenum = track->video_time*track->frame_rate + 0.5; int padding = pts_framenum - track->pts_position; if( padding > 0 ) { if( track->vskip <= 0 ) { int limit = 3; if( padding > limit ) { zmsgs("video start padding pid %02x @ %12d (%12.6f) %d\n", track->pid, framenum, track->get_video_time(), padding); track->vskip = 1; result = 1; } } else if( !(++track->vskip & 1) || padding > track->frame_rate ) result = 1; if( result ) ++track->pts_position; } else if( track->vskip > 0 ) { zmsgs("video end padding pid %02x @ %12d (%12.6f) %d\n", track->pid, framenum, track->get_video_time(), (1+track->vskip)/2); track->vskip = 0; } } return result; } int zvideo_t:: video_pts_skipping() { int result = 0; if( track->video_time >= 0 && src->pts_padding > 0 ) { int pts_framenum = track->video_time*track->frame_rate + 0.5; int skipping = track->pts_position - pts_framenum; if( skipping > 0 ) { if( track->vskip >= 0 ) { int limit = 3; if( skipping > limit ) { zmsgs("video start skipping pid %02x @ %12d (%12.6f) %d frames\n", track->pid, framenum, track->get_video_time(), skipping); track->vskip = -1; result = 1; } } else if( !(--track->vskip & 1) || skipping > track->frame_rate ) result = 1; if( result ) --track->pts_position; } else if( track->vskip < 0 ) { zmsgs("video end skipping pid %02x @ %12d (%12.6f) %d frames\n", track->pid, framenum, track->get_video_time(), (1-track->vskip)/2); track->vskip = 0; } } return result; } int zvideo_t:: eof() { while( vstream->eof() ) { demuxer_t *demux = vstream->demuxer; if( demux->seek_phys() ) return 1; } return 0; } /* The way they do field based encoding, */ /* the I frames have the top field but both the I frame and */ /* subsequent P frame are interlaced to make the keyframe. */ int zvideo_t:: read_picture() { int result = 0; int field = 0; got_top = got_bottom = 0; secondfield = 0; do { if( (result=eof()) != 0 ) break; if( (result=get_header()) != 0 ) break; if( pict_struct != pics_FRAME_PICTURE ) secondfield = field; /* if dropping frames then skip B frames, */ /* Don't skip the B frames which generate original */ /* repeated frames at the end of the skip sequence */ if( !skip_bframes || pict_type != pic_type_B || repeat_fields > 2*skip_bframes ) { if( (result=get_picture()) != 0 ) break; } ++field; if( pict_struct == pics_FRAME_PICTURE ) { got_top = got_bottom = field; break; } if( pict_struct == pics_TOP_FIELD ) got_top = field; else if( pict_struct == pics_BOTTOM_FIELD ) got_bottom = field; } while( !secondfield ); if( pict_type != pic_type_B ) ++ref_frames; return result; } int zvideo_t:: read_frame_backend(int zskip_bframes) { int result = 0; //if( src->seekable ) { // int app_pos = track->apparent_position(); // double vtime = track->get_video_time(); // int pts_frm = vtime * track->frame_rate + 0.5; // zmsgs(" apr_pos %f: %d/%d + %d: pts_frame %d + %d\n", vtime, // app_pos, framenum, app_pos - framenum, pts_frm, pts_frm-framenum); //} if( !mpeg2 ) current_field = repeat_fields = 0; /* Repeat if current_field is more than 1 field from repeat_fields */ if( !repeat_fields || current_field+2 >= repeat_fields ) { if( (repeat_fields -= current_field) < 0 ) repeat_fields = 0; track->update_video_time(); // if pts lags framenum, skip to next picture // only skip once (double speed) to catch up while( !(result=find_header()) ) { if( !video_pts_skipping() ) break; vstream->refill(); } // if framenum lags pts, repeat picture if( !result && !video_pts_padding() ) { track->update_frame_pts(); skip_bframes = zskip_bframes; //static const char *pstruct[] = { "nil", "top", "bot", "fld" }; //zmsgs("video %d PID %02x frame %d vtime %12.5f %c/%s %d 0x%010lx/0x%010lx %d/%d\n", // result, track->pid, framenum, track->get_video_time(), "XIPBD"[pict_type], // pstruct[pict_struct], skip_bframes, vstream->demuxer->last_packet_start, // vstream->demuxer->absolute_position()-vstream->demuxer->zdata.length(), // repeat_fields, current_field); result = read_picture(); #if 0 { char fn[512]; snprintf(&fn[0],sizeof(fn),"/tmp/dat/f%05d.pnm",framenum); int fd = open(&fn[0],O_CREAT+O_TRUNC+O_WRONLY,0666); write(fd,&fn, snprintf(&fn[0],sizeof(fn), "P5\n%d %d\n255\n", coded_picture_width, coded_picture_height)); write(fd,output_src[0],coded_picture_width*coded_picture_height); close(fd); } #endif } } if( !result ) { if( mpeg2 ) current_field = !repeat_fields ? 0 : current_field+2; decode_subtitle(); last_number = framenum++; } return result; } int* zvideo_t:: get_scaletable(int in_l, int out_l) { int *result = new int[out_l]; double scale = (double)in_l / out_l; for( int i=0; ihour; int minute = gop_timecode->minute; int second = gop_timecode->second; int frame = gop_timecode->frame; long result = ((long)hour*3600 + minute*60 + second)*fps + frame; return result; } /* ======================================================================= */ /* ENTRY POINTS */ /* ======================================================================= */ zvideo_t:: video_t(zmpeg3_t *zsrc, zvtrack_t *ztrack) { src = zsrc; track = ztrack; vstream = new bits_t(zsrc, track->demuxer); //zmsgs("%d\n", vstream->eof()); last_number = -1; /* First frame is all green */ framenum = -1; byte_seek = -1; frame_seek = -1; subtitle_track = -1; init_scantables(); init_output(); allocate_slice_buffers(); slice_wait.lock(); } zvideo_t *zmpeg3_t:: new_video_t(zvtrack_t *ztrack) { int result = 0; video_t *new_video = new video_t(this,ztrack); /* Get encoding parameters from stream */ if( seekable ) { result = new_video->get_header(); if( !result ) new_video->init_video(); else { /* No header found */ #ifdef TODO zerr("no header found.\n"); delete new_video; new_video = 0; #endif } } return new_video; } zvideo_t:: ~video_t() { delete_slice_buffers(); if( decoder_initted ) delete_decoder(); delete vstream; delete cc; if( x_table ) { delete [] x_table; delete [] y_table; } } int zvideo_t:: set_cpus(int cpus) { return 0; } int zvideo_t:: set_mmx(int use_mmx) { init_scantables(); return 0; } /* Read all the way up to and including the next picture start code */ int zvideo_t:: read_raw(uint8_t *output, long *size, long max_size) { uint32_t zcode = 0; long sz = 0; while( zcode!=PICTURE_START_CODE && zcode!=SEQUENCE_END_CODE && sz < max_size && !eof() ) { uint8_t byte = vstream->get_byte_noptr(); *output++ = byte; zcode = (zcode << 8) | byte; ++sz; } *size = sz; return eof(); } int zvideo_t::read_frame( uint8_t **output_rows, int in_x, int in_y, int in_w, int in_h, int out_w, int out_h, int color_model) { uint8_t *y, *u, *v; int frame_number, result; result = 0; want_yvu = 0; this->output_rows = output_rows; this->color_model = color_model; /* Get scaling tables */ if( this->out_w != out_w || this->out_h != out_h || this->in_w != in_w || this->in_h != in_h || this->in_x != in_x || this->in_y != in_y) { if(x_table) { delete [] x_table; x_table = 0; delete [] y_table; y_table = 0; } } this->out_w = out_w; this->out_h = out_h; this->in_w = in_w; this->in_h = in_h; this->in_x = in_x; this->in_y = in_y; if( !x_table ) { x_table = get_scaletable(in_w, out_w); y_table = get_scaletable(in_h, out_h); } //zmsgs("mpeg3video_read_frame 1 %d\n", framenum); /* Recover from cache */ frame_number = frame_seek >= 0 ? frame_seek : framenum; cache_t *cache = track->frame_cache; if( frame_seek != last_number && cache->get_frame(frame_number, &y, &u, &v) ) { //zmsgs("1 %d\n", frame_number); /* Transfer with cropping */ if( y ) present_frame(y, u, v); /* Advance either framenum or frame_seek */ if( frame_number == framenum ) framenum = ++frame_number; else if( frame_number == frame_seek ) frame_seek = ++frame_number; } else { /* Only decode if it's a different frame */ if( frame_seek < 0 || last_number < 0 || frame_seek != last_number) { if( !result ) result = seek(); if( !result ) result = read_frame_backend(0); } else { framenum = frame_seek + 1; last_number = frame_seek; frame_seek = -1; } if( output_src[0] ) present_frame(output_src[0], output_src[1], output_src[2]); } return result; } int zvideo_t:: read_yuvframe(char *y_output, char *u_output, char *v_output, int in_x, int in_y, int in_w, int in_h) { uint8_t *y = 0, *u = 0, *v = 0; int result = 0; //zmsgs("1 %d\n", framenum); want_yvu = 1; this->y_output = y_output; this->u_output = u_output; this->v_output = v_output; this->in_x = in_x; this->in_y = in_y; this->in_w = in_w; this->in_h = in_h; /* Recover from cache if framenum exists */ int frame_number = frame_seek >= 0 ? frame_seek : framenum; cache_t *cache = track->frame_cache; if( cache->get_frame(frame_number, &y, &u, &v) ) { //zmsgs("1 %d\n", frame_number); /* Transfer with cropping */ if( y ) present_frame(y, u, v); /* Advance either framenum or frame_seek */ if( frame_number == framenum ) framenum = ++frame_number; else if( frame_number == frame_seek ) frame_seek = ++frame_number; } else { if( !result ) result = seek(); if( !result ) result = read_frame_backend(0); if( !result && output_src[0] ) present_frame(output_src[0], output_src[1], output_src[2]); } want_yvu = 0; byte_seek = -1; return result; } int zvideo_t:: read_yuvframe_ptr(char **y_output, char **u_output, char **v_output) { uint8_t *y, *u, *v; int frame_number, result; int debug = 0; result = 0; want_yvu = 1; *y_output = *u_output = *v_output = 0; frame_number = frame_seek >= 0 ? frame_seek : framenum; cache_t *cache = track->frame_cache; if( debug ) zmsgs("%d\n", __LINE__); if( cache->get_frame(frame_number, &y, &u, &v) ) { if( debug ) zmsgs("%d\n", __LINE__); *y_output = (char*)y; *u_output = (char*)u; *v_output = (char*)v; /* Advance either framenum or frame_seek */ if( frame_number == framenum ) framenum = ++frame_number; else if( frame_number == frame_seek ) frame_seek = ++frame_number; if( debug ) zmsgs("%d\n", __LINE__); } /* Only decode if it's a different frame */ else if( frame_seek < 0 || last_number < 0 || frame_seek != last_number) { if( debug ) zmsgs("%d\n", __LINE__); if( !result ) result = seek(); if( debug ) zmsgs("%d\n", __LINE__); if( !result ) result = read_frame_backend(0); if( debug ) zmsgs("%d\n", __LINE__); if( output_src[0] ) { *y_output = (char*)output_src[0]; *u_output = (char*)output_src[1]; *v_output = (char*)output_src[2]; } if( debug ) zmsgs("%d\n", __LINE__); } else { if( debug ) zmsgs("%d\n", __LINE__); framenum = frame_seek + 1; last_number = frame_seek; frame_seek = -1; if( output_src[0 ]) { *y_output = (char*)output_src[0]; *u_output = (char*)output_src[1]; *v_output = (char*)output_src[2]; } if( debug ) zmsgs("%d\n", __LINE__); } if( debug ) zmsgs("%d\n", __LINE__); want_yvu = 0; /* Caching not used if byte seek */ byte_seek = -1; return result; } int zvideo_t:: colormodel() { switch( chroma_format ) { case cfmt_422: return cmdl_YUV422P; break; case cfmt_420: return cmdl_YUV420P; break; } return cmdl_YUV420P; } zcc_t *zvideo_t:: get_cc() { if( !cc ) cc = new cc_t(this); return cc; } void zvideo_t:: reset_subtitles() { for( int i=0; itotal_stracks; ++i ) { strack_t *strack = src->strack[i]; if( strack->video != this ) continue; strack->del_all_subtitles(); } } int zvideo_t:: show_subtitle(int strk) { int ret = subtitle_track; if( subtitle_track != strk ) { reset_subtitles(); subtitle_track = strk; if( cc ) { if( strk >= 0 ) cc->reset(); else { delete cc; cc = 0; } } } return ret; } void zvideo_t:: dump() { zmsg("\n"); zmsg(" *** sequence extension 1\n"); zmsgs("prog_seq=%d\n", prog_seq); zmsg(" *** picture header 1\n"); zmsgs("pict_type=%d field_sequence=%d\n", pict_type, field_sequence); zmsg(" *** picture coding extension 1\n"); zmsgs("field_sequence=%d repeatfirst=%d prog_frame=%d pict_struct=%d\n", field_sequence, repeatfirst, prog_frame, pict_struct); }