X-Git-Url: http://git.cinelerra-gg.org/git/?a=blobdiff_plain;f=cinelerra-5.1%2Flibzmpeg3%2Fvideo%2Fvideo.C;fp=cinelerra-5.1%2Flibzmpeg3%2Fvideo%2Fvideo.C;h=5df67402680a3aa097a161a0d7644234b4aa759a;hb=30bdb85eb33a8ee7ba675038a86c6be59c43d7bd;hp=0000000000000000000000000000000000000000;hpb=52fcc46226f9df46f9ce9d0566dc568455a7db0b;p=goodguy%2Fhistory.git diff --git a/cinelerra-5.1/libzmpeg3/video/video.C b/cinelerra-5.1/libzmpeg3/video/video.C new file mode 100644 index 00000000..5df67402 --- /dev/null +++ b/cinelerra-5.1/libzmpeg3/video/video.C @@ -0,0 +1,751 @@ +#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); +} +