X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=libhb%2Fdecmpeg2.c;h=83c36f6980e05dd80d5ff5a8e4ed661186dbd40e;hb=f9341345b37e0738a140423297c222a7e40eddab;hp=fad85017134bec511501665b4dacd0fd8d10e568;hpb=f429994a1b4f4bc2d44f301121c354c9d3dad6b1;p=handbrake-jp%2Fhandbrake-jp-git.git diff --git a/libhb/decmpeg2.c b/libhb/decmpeg2.c index fad85017..83c36f69 100644 --- a/libhb/decmpeg2.c +++ b/libhb/decmpeg2.c @@ -1,11 +1,11 @@ /* $Id: decmpeg2.c,v 1.12 2005/03/03 16:30:42 titer Exp $ This file is part of the HandBrake source code. - Homepage: . + Homepage: . It may be used under the terms of the GNU General Public License. */ #include "hb.h" - +#include "hbffmpeg.h" #include "mpeg2dec/mpeg2.h" /* Cadence tracking */ @@ -25,65 +25,125 @@ #define BTB_PROG 64 #define TB_PROG 128 #define TBT_PROG 256 -int cadence[12]; -int flag = 0; +static int cadence[12]; +static int flag = 0; /********************************************************************** * hb_libmpeg2_t - ********************************************************************** - * A convenient libmpeg wrapper, used both here and in scan.c *********************************************************************/ -struct hb_libmpeg2_s +typedef struct hb_libmpeg2_s { mpeg2dec_t * libmpeg2; const mpeg2_info_t * info; + hb_job_t * job; int width; int height; int rate; - int aspect_ratio; - int got_iframe; - int look_for_break; + double aspect_ratio; + int got_iframe; /* set when we get our first iframe */ + int look_for_iframe; /* need an iframe to add chap break */ + int look_for_break; /* need gop start to add chap break */ + uint32_t nframes; /* number of frames we've decoded */ int64_t last_pts; -}; +} hb_libmpeg2_t; /********************************************************************** * hb_libmpeg2_init ********************************************************************** - * + * *********************************************************************/ -hb_libmpeg2_t * hb_libmpeg2_init() +static hb_libmpeg2_t * hb_libmpeg2_init() { hb_libmpeg2_t * m = calloc( sizeof( hb_libmpeg2_t ), 1 ); - + m->libmpeg2 = mpeg2_init(); m->info = mpeg2_info( m->libmpeg2 ); m->last_pts = -1; - m->look_for_break = 0; return m; } +static hb_buffer_t *hb_copy_frame( hb_job_t *job, int width, int height, + uint8_t* y, uint8_t *u, uint8_t *v ) +{ + int dst_w = width, dst_h = height; + if ( job ) + { + dst_w = job->title->width; + dst_h = job->title->height; + } + int dst_wh = dst_w * dst_h; + hb_buffer_t *buf = hb_video_buffer_init( dst_w, dst_h ); + + if ( dst_w != width || dst_h != height ) + { + // we're encoding and the frame dimensions don't match the title dimensions - + // rescale & matte Y, U, V into our output buf. + AVPicture in, out; + avpicture_alloc(&in, PIX_FMT_YUV420P, width, height ); + avpicture_alloc(&out, PIX_FMT_YUV420P, dst_w, dst_h ); + + int src_wh = width * height; + memcpy( in.data[0], y, src_wh ); + memcpy( in.data[1], u, src_wh >> 2 ); + memcpy( in.data[2], v, src_wh >> 2 ); + struct SwsContext *context = sws_getContext( width, height, PIX_FMT_YUV420P, + dst_w, dst_h, PIX_FMT_YUV420P, + SWS_LANCZOS|SWS_ACCURATE_RND, + NULL, NULL, NULL ); + sws_scale( context, in.data, in.linesize, 0, height, out.data, out.linesize ); + sws_freeContext( context ); + + u_int8_t *data = buf->data; + memcpy( data, out.data[0], dst_wh ); + data += dst_wh; + // U & V planes are 1/4 the size of Y plane. + dst_wh >>= 2; + memcpy( data, out.data[1], dst_wh ); + data += dst_wh; + memcpy( data, out.data[2], dst_wh ); + + avpicture_free( &out ); + avpicture_free( &in ); + } + else + { + // we're scanning or the frame dimensions match the title's dimensions + // so we can do a straight copy. + u_int8_t *data = buf->data; + memcpy( data, y, dst_wh ); + data += dst_wh; + // U & V planes are 1/4 the size of Y plane. + dst_wh >>= 2; + memcpy( data, u, dst_wh ); + data += dst_wh; + memcpy( data, v, dst_wh ); + } + return buf; +} + /********************************************************************** * hb_libmpeg2_decode ********************************************************************** - * + * *********************************************************************/ -int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es, +static int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es, hb_list_t * list_raw ) { mpeg2_state_t state; hb_buffer_t * buf; - uint8_t * data; - int chap_break = 0; - /* Feed libmpeg2 */ - if( buf_es->start > -1 ) + if ( buf_es->size ) { - mpeg2_tag_picture( m->libmpeg2, buf_es->start >> 32, - buf_es->start & 0xFFFFFFFF ); + /* Feed libmpeg2 */ + if( buf_es->start > -1 ) + { + mpeg2_tag_picture( m->libmpeg2, buf_es->start >> 32, + buf_es->start & 0xFFFFFFFF ); + } + mpeg2_buffer( m->libmpeg2, buf_es->data, + buf_es->data + buf_es->size ); } - mpeg2_buffer( m->libmpeg2, buf_es->data, - buf_es->data + buf_es->size ); for( ;; ) { @@ -100,34 +160,26 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es, m->width = m->info->sequence->width; m->height = m->info->sequence->height; m->rate = m->info->sequence->frame_period; - } - if ( m->aspect_ratio <= 0 ) - { - // We can parse out the aspect ratio from the Sequence Start Header data in buf_es->data - - // Make sure we have the correct data in the buffer - if ((buf_es->data[0] == 0x00) && (buf_es->data[1] == 0x00) && (buf_es->data[2] == 0x01) && (buf_es->data[3] == 0xb3)) - { - unsigned char ar_fr = buf_es->data[7]; // Top 4 bits == aspect ratio flag - bottom 4 bits == rate flags - switch ((ar_fr & 0xf0) >> 4) + if ( m->aspect_ratio <= 0 && m->height && + m->info->sequence->pixel_height ) { - case 2: - m->aspect_ratio = HB_ASPECT_BASE * 4 / 3; // 4:3 - break; - case 3: - m->aspect_ratio = HB_ASPECT_BASE * 16 / 9; // 16:9 - break; - default: - hb_log("hb_libmpeg2_decode - STATE_SEQUENCE unexpected aspect ratio/frame rate 0x%x\n", ar_fr); - break; + /* mpeg2_parse doesn't store the aspect ratio. Instead + * it keeps the pixel width & height that would cause + * the storage width & height to come out in the correct + * aspect ratio. Convert these back to aspect ratio. + */ + double ar_numer = m->width * m->info->sequence->pixel_width; + double ar_denom = m->height * m->info->sequence->pixel_height; + m->aspect_ratio = ar_numer / ar_denom; } - } } } - else if( state == STATE_GOP && m->look_for_break == 2) + else if( state == STATE_GOP && m->look_for_break) { - printf("MPEG2: Group of pictures found, searching for I-Frame\n"); - m->look_for_break = 1; + // we were looking for a gop to add a chapter break - we found it + // so now start looking for an iframe. + m->look_for_iframe = m->look_for_break; + m->look_for_break = 0; } else if( ( state == STATE_SLICE || state == STATE_END ) && m->info->display_fbuf ) @@ -135,48 +187,24 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es, if( ( m->info->display_picture->flags & PIC_MASK_CODING_TYPE ) == PIC_FLAG_CODING_TYPE_I ) { + // we got an iframe so we can start decoding video now m->got_iframe = 1; - - // If we are looking for a break, insert the chapter break on an I-Frame - if( m->look_for_break == 1 ) - { - printf("MPEG2: I-Frame Found\n"); - m->look_for_break = 0; - chap_break = 1; - } } if( m->got_iframe ) { - buf = hb_buffer_init( m->width * m->height * 3 / 2 ); - data = buf->data; - - // Was a good break point found? - if( chap_break ) - { - printf("MPEG2: Chapter Break Inserted\n"); - chap_break = 0; - buf->new_chap = 1; - } - - memcpy( data, m->info->display_fbuf->buf[0], - m->width * m->height ); - data += m->width * m->height; - memcpy( data, m->info->display_fbuf->buf[1], - m->width * m->height / 4 ); - data += m->width * m->height / 4; - memcpy( data, m->info->display_fbuf->buf[2], - m->width * m->height / 4 ); + buf = hb_copy_frame( m->job, m->info->sequence->width, + m->info->sequence->height, + m->info->display_fbuf->buf[0], + m->info->display_fbuf->buf[1], + m->info->display_fbuf->buf[2] ); + buf->sequence = buf_es->sequence; if( m->info->display_picture->flags & PIC_FLAG_TAGS ) { buf->start = ( (uint64_t) m->info->display_picture->tag << 32 ) | ( (uint64_t) m->info->display_picture->tag2 ); - /* - * Add back in again to track PTS of MPEG2 frames - * hb_log("MPEG2: Normal buf->start = %lld", buf->start); - */ } else if( m->last_pts > -1 ) { @@ -192,8 +220,37 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es, } m->last_pts = buf->start; + if( m->look_for_iframe && ( m->info->display_picture->flags & + PIC_MASK_CODING_TYPE ) == PIC_FLAG_CODING_TYPE_I ) + { + // we were waiting for an iframe to insert a chapter mark + // and we have one. + buf->new_chap = m->look_for_iframe; + m->look_for_iframe = 0; + const char *chap_name = ""; + if ( m->job && buf->new_chap > 0 && + hb_list_item( m->job->title->list_chapter, + buf->new_chap - 1 ) ) + { + hb_chapter_t * c = hb_list_item( m->job->title->list_chapter, + buf->new_chap - 1 ); + chap_name = c->title; + } + hb_log( "mpeg2: \"%s\" (%d) at frame %u time %lld", + chap_name, buf->new_chap, m->nframes, buf->start ); + } else if ( m->nframes == 0 && m->job && + hb_list_item( m->job->title->list_chapter, + m->job->chapter_start - 1 ) ) + { + hb_chapter_t * c = hb_list_item( m->job->title->list_chapter, + m->job->chapter_start - 1 ); + hb_log( "mpeg2: \"%s\" (%d) at frame %u time %lld", c->title, + m->job->chapter_start, m->nframes, buf->start ); + } + ++m->nframes; + flag = m->info->display_picture->flags; - + /* Uncomment this block to see frame-by-frame picture flags, as the video encodes. hb_log("***** MPEG 2 Picture Info for PTS %lld *****", buf->start); if( flag & TOP_FIRST ) @@ -211,14 +268,14 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es, if( flag & COMPOSITE_MASK ) hb_log("MPEG2 Flag: Composite mask"); hb_log("fields: %d", m->info->display_picture->nb_fields); -*/ +*/ /* Rotate the cadence tracking. */ int i = 0; for(i=11; i > 0; i--) { cadence[i] = cadence[i-1]; } - + if ( !(flag & PROGRESSIVE) && !(flag & TOP_FIRST) ) { /* Not progressive, not top first... @@ -272,15 +329,15 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es, //hb_log("MPEG2 Flag: Progressive repeat. Top field first, 3 fields displayed."); cadence[0] = TBT_PROG; } - + if ( (cadence[2] <= TB) && (cadence[1] <= TB) && (cadence[0] > TB) && (cadence[11]) ) - hb_log("%fs: Interlaced -> Progressive", (float)buf->start / 90000); + hb_log("%fs: Video -> Film", (float)buf->start / 90000); if ( (cadence[2] > TB) && (cadence[1] <= TB) && (cadence[0] <= TB) && (cadence[11]) ) - hb_log("%fs: Progressive -> Interlaced", (float)buf->start / 90000); + hb_log("%fs: Film -> Video", (float)buf->start / 90000); /* Store picture flags for later use by filters */ buf->flags = m->info->display_picture->flags; - + hb_list_add( list_raw, buf ); } } @@ -293,38 +350,11 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es, } /********************************************************************** - * hb_libmpeg2_info - ********************************************************************** - * - *********************************************************************/ -void hb_libmpeg2_info( hb_libmpeg2_t * m, int * width, int * height, - int * rate, int *aspect_ratio ) -{ - *width = m->width; - *height = m->height; - if (m->info->display_fbuf) - { - if( (m->info->display_picture->flags & PROGRESSIVE) && (m->height == 480) ) - { - /* The frame is progressive and it's NTSC DVD height, so change its FPS to 23.976. - This might not be correct for the title. It's really just for scan.c's benefit. - Scan.c will reset the fps to 29.97, until a simple majority of the preview - frames report at 23.976. - */ - //hb_log("Detecting NTSC Progressive Frame"); - m->rate = 1126125; - } - } - *rate = m->rate; - *aspect_ratio = m->aspect_ratio; -} - -/********************************************************************** * hb_libmpeg2_close ********************************************************************** - * + * *********************************************************************/ -void hb_libmpeg2_close( hb_libmpeg2_t ** _m ) +static void hb_libmpeg2_close( hb_libmpeg2_t ** _m ) { hb_libmpeg2_t * m = *_m; @@ -337,7 +367,7 @@ void hb_libmpeg2_close( hb_libmpeg2_t ** _m ) /********************************************************************** * The decmpeg2 work object ********************************************************************** - * + * *********************************************************************/ struct hb_work_private_s { @@ -348,43 +378,53 @@ struct hb_work_private_s /********************************************************************** * hb_work_decmpeg2_init ********************************************************************** - * + * *********************************************************************/ -int decmpeg2Init( hb_work_object_t * w, hb_job_t * job ) +static int decmpeg2Init( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t * pv; - + pv = calloc( 1, sizeof( hb_work_private_t ) ); w->private_data = pv; - + pv->libmpeg2 = hb_libmpeg2_init(); pv->list = hb_list_init(); + pv->libmpeg2->job = job; + return 0; } /********************************************************************** * Work ********************************************************************** - * + * *********************************************************************/ -int decmpeg2Work( hb_work_object_t * w, hb_buffer_t ** buf_in, +static int decmpeg2Work( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; hb_buffer_t * buf, * last = NULL; + int status = HB_WORK_OK; // The reader found a chapter break, consume it completely, and remove it from the // stream. We need to shift it. if( (*buf_in)->new_chap ) { - printf("MPEG2: Chapter Break Cell Found, searching for GOP\n"); - pv->libmpeg2->look_for_break = 2; + pv->libmpeg2->look_for_break = (*buf_in)->new_chap; (*buf_in)->new_chap = 0; } hb_libmpeg2_decode( pv->libmpeg2, *buf_in, pv->list ); + /* if we got an empty buffer signaling end-of-stream send it downstream */ + if ( (*buf_in)->size == 0 ) + { + hb_list_add( pv->list, *buf_in ); + *buf_in = NULL; + status = HB_WORK_DONE; + } + *buf_out = NULL; while( ( buf = hb_list_item( pv->list, 0 ) ) ) { @@ -401,28 +441,67 @@ int decmpeg2Work( hb_work_object_t * w, hb_buffer_t ** buf_in, } } - return HB_WORK_OK; + return status; } /********************************************************************** * Close ********************************************************************** - * + * *********************************************************************/ -void decmpeg2Close( hb_work_object_t * w ) +static void decmpeg2Close( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; + + // don't log during scan + if ( pv->libmpeg2->job ) + { + hb_log( "mpeg2 done: %d frames", pv->libmpeg2->nframes ); + } hb_list_close( &pv->list ); hb_libmpeg2_close( &pv->libmpeg2 ); free( pv ); } +static int decmpeg2Info( hb_work_object_t *w, hb_work_info_t *info ) +{ + hb_work_private_t *pv = w->private_data; + + memset( info, 0, sizeof(*info) ); + + if ( pv && pv->libmpeg2 && pv->libmpeg2->info && pv->libmpeg2->info->sequence ) + { + hb_libmpeg2_t *m = pv->libmpeg2; + + info->width = m->width; + info->height = m->height; + info->pixel_aspect_width = m->info->sequence->pixel_width; + info->pixel_aspect_height = m->info->sequence->pixel_height; + info->aspect = m->aspect_ratio; + + // if the frame is progressive & NTSC DVD height report it as 23.976 FPS + // so that scan can autodetect NTSC film + info->rate = 27000000; + info->rate_base = ( m->info->display_fbuf && m->info->display_picture && + (m->info->display_picture->flags & PROGRESSIVE) && + (m->height == 480 ) ) ? 1126125 : m->rate; + + info->bitrate = m->info->sequence->byte_rate * 8; + info->profile = m->info->sequence->profile_level_id >> 4; + info->level = m->info->sequence->profile_level_id & 0xf; + info->name = "mpeg2"; + return 1; + } + return 0; +} + hb_work_object_t hb_decmpeg2 = { WORK_DECMPEG2, "MPEG-2 decoder (libmpeg2)", decmpeg2Init, decmpeg2Work, - decmpeg2Close + decmpeg2Close, + decmpeg2Info };