Index: libhb/reader.c =================================================================== --- libhb/reader.c (revision 1456) +++ libhb/reader.c (working copy) @@ -47,6 +47,19 @@ HB_NORMAL_PRIORITY ); } +static void push_buf( hb_reader_t *r, hb_fifo_t *fifo, hb_buffer_t *buf ) +{ + while( !*r->die && !r->job->done && hb_fifo_is_full( fifo ) ) + { + /* + * Loop until the incoming fifo is reaqdy to receive + * this buffer. + */ + hb_snooze( 50 ); + } + hb_fifo_push( fifo, buf ); +} + /*********************************************************************** * ReaderFunc *********************************************************************** @@ -57,7 +70,6 @@ hb_reader_t * r = _r; hb_fifo_t ** fifos; hb_buffer_t * buf; - hb_buffer_t * buf_old; hb_list_t * list; int n; int chapter = -1; @@ -172,7 +184,7 @@ if ( buf->id == 0xE0 && buf->start != -1 ) { r->saw_video = 1; - r->demux.scr_offset = buf->start; + r->demux.scr_offset = buf->renderOffset; hb_log( "reader: first SCR %llu scr_offset %llu", r->demux.last_scr, r->demux.scr_offset ); } @@ -190,34 +202,21 @@ everything after this sees a continuous clock with 0 being the time of the first video packet. */ buf->start -= r->demux.scr_offset; + buf->renderOffset -= r->demux.scr_offset; } buf->sequence = r->sequence++; - for( n = 0; fifos[n] != NULL; n++) + /* if there are mutiple output fifos, send a copy of the + * buffer down all but the first (we have to not ship the + * original buffer or we'll race with the thread that's + * consuming the buffer & inject garbage into the data stream). */ + for( n = 1; fifos[n] != NULL; n++) { - if( n != 0 ) - { - /* - * Replace the buffer with a new copy of itself for when - * it is being sent down multiple fifos. - */ - buf_old = buf; - buf = hb_buffer_init(buf_old->size); - memcpy( buf->data, buf_old->data, buf->size ); - hb_buffer_copy_settings( buf, buf_old ); - } - - while( !*r->die && !r->job->done && - hb_fifo_is_full( fifos[n] ) ) - { - /* - * Loop until the incoming fifo is reaqdy to receive - * this buffer. - */ - hb_snooze( 50 ); - } - - hb_fifo_push( fifos[n], buf ); + hb_buffer_t *buf_copy = hb_buffer_init( buf->size ); + hb_buffer_copy_settings( buf_copy, buf ); + memcpy( buf_copy->data, buf->data, buf->size ); + push_buf( r, fifos[n], buf_copy ); } + push_buf( r, fifos[0], buf ); } else { Index: libhb/deca52.c =================================================================== --- libhb/deca52.c (revision 1456) +++ libhb/deca52.c (working copy) @@ -38,9 +38,11 @@ }; -int deca52Init( hb_work_object_t *, hb_job_t * ); -int deca52Work( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); -void deca52Close( hb_work_object_t * ); +static int deca52Init( hb_work_object_t *, hb_job_t * ); +static int deca52Work( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +static void deca52Close( hb_work_object_t * ); +static int deca52BSInfo( hb_work_object_t * , const hb_buffer_t *, + hb_work_info_t * ); hb_work_object_t hb_deca52 = { @@ -48,7 +50,9 @@ "AC3 decoder", deca52Init, deca52Work, - deca52Close + deca52Close, + 0, + deca52BSInfo }; /*********************************************************************** @@ -85,7 +89,7 @@ *********************************************************************** * Allocate the work object, initialize liba52 **********************************************************************/ -int deca52Init( hb_work_object_t * w, hb_job_t * job ) +static int deca52Init( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); hb_audio_t * audio = w->audio; @@ -119,7 +123,7 @@ *********************************************************************** * Free memory **********************************************************************/ -void deca52Close( hb_work_object_t * w ) +static void deca52Close( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; a52_free( pv->state ); @@ -134,7 +138,7 @@ * Add the given buffer to the data we already have, and decode as much * as we can **********************************************************************/ -int deca52Work( hb_work_object_t * w, hb_buffer_t ** buf_in, +static int deca52Work( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; @@ -276,3 +280,82 @@ return buf; } +static int deca52BSInfo( hb_work_object_t *w, const hb_buffer_t *b, + hb_work_info_t *info ) +{ + int i, rate, bitrate, flags; + + memset( info, 0, sizeof(*info) ); + + /* since AC3 frames don't line up with MPEG ES frames scan the + * entire frame for an AC3 sync pattern. */ + for ( i = 0; i < b->size - 7; ++i ) + { + if( a52_syncinfo( &b->data[i], &flags, &rate, &bitrate ) != 0 ) + { + break; + } + } + if ( i >= b->size - 7 ) + { + /* didn't find AC3 sync */ + return 0; + } + + info->name = "AC-3"; + info->rate = rate; + info->rate_base = 1; + info->bitrate = bitrate; + info->flags = flags; + if ( (flags & A52_CHANNEL_MASK) == A52_DOLBY ) + { + info->flags |= AUDIO_F_DOLBY; + } + + switch( flags & A52_CHANNEL_MASK ) + { + /* mono sources */ + case A52_MONO: + case A52_CHANNEL1: + case A52_CHANNEL2: + info->channel_layout = HB_INPUT_CH_LAYOUT_MONO; + break; + /* stereo input */ + case A52_CHANNEL: + case A52_STEREO: + info->channel_layout = HB_INPUT_CH_LAYOUT_STEREO; + break; + /* dolby (DPL1 aka Dolby Surround = 4.0 matrix-encoded) input */ + case A52_DOLBY: + info->channel_layout = HB_INPUT_CH_LAYOUT_DOLBY; + break; + /* 3F/2R input */ + case A52_3F2R: + info->channel_layout = HB_INPUT_CH_LAYOUT_3F2R; + break; + /* 3F/1R input */ + case A52_3F1R: + info->channel_layout = HB_INPUT_CH_LAYOUT_3F1R; + break; + /* other inputs */ + case A52_3F: + info->channel_layout = HB_INPUT_CH_LAYOUT_3F; + break; + case A52_2F1R: + info->channel_layout = HB_INPUT_CH_LAYOUT_2F1R; + break; + case A52_2F2R: + info->channel_layout = HB_INPUT_CH_LAYOUT_2F2R; + break; + /* unknown */ + default: + info->channel_layout = HB_INPUT_CH_LAYOUT_STEREO; + } + + if (flags & A52_LFE) + { + info->channel_layout |= HB_INPUT_CH_LAYOUT_HAS_LFE; + } + + return 1; +} Index: libhb/decmpeg2.c =================================================================== --- libhb/decmpeg2.c (revision 1456) +++ libhb/decmpeg2.c (working copy) @@ -25,15 +25,13 @@ #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; @@ -47,14 +45,14 @@ 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 ); @@ -70,7 +68,7 @@ ********************************************************************** * *********************************************************************/ -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; @@ -312,7 +310,7 @@ ********************************************************************** * *********************************************************************/ -void hb_libmpeg2_info( hb_libmpeg2_t * m, int * width, int * height, +static void hb_libmpeg2_info( hb_libmpeg2_t * m, int * width, int * height, int * rate, int *aspect_ratio ) { *width = m->width; @@ -334,19 +332,12 @@ *aspect_ratio = m->aspect_ratio; } -int hb_libmpeg2_clear_aspect_ratio( hb_libmpeg2_t * m ) -{ - int ar = m->aspect_ratio; - m->aspect_ratio = 0; - return ar; -} - /********************************************************************** * 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; @@ -372,7 +363,7 @@ ********************************************************************** * *********************************************************************/ -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; @@ -392,7 +383,7 @@ ********************************************************************** * *********************************************************************/ -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; @@ -441,21 +432,51 @@ ********************************************************************** * *********************************************************************/ -void decmpeg2Close( hb_work_object_t * w ) +static void decmpeg2Close( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; - hb_log( "mpeg2 done: %d frames", pv->libmpeg2->nframes ); + + // 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; + + if ( pv && pv->libmpeg2 ) + { + int aspect; + hb_libmpeg2_t *m = pv->libmpeg2; + + hb_libmpeg2_info( m, &info->width, &info->height, &info->rate_base, + &aspect ); + + info->aspect = (double)aspect; + info->rate = 27000000; + + 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 }; Index: libhb/hb.c =================================================================== --- libhb/hb.c (revision 1456) +++ libhb/hb.c (working copy) @@ -124,7 +124,6 @@ /* libavcodec */ avcodec_init(); avcodec_register_all(); - av_register_codec_parser( &mpegaudio_parser); /* Start library thread */ hb_log( "hb_init: starting libhb thread" ); @@ -220,6 +219,7 @@ hb_register( &hb_deca52 ); hb_register( &hb_decdca ); hb_register( &hb_decavcodec ); + hb_register( &hb_decavcodecv ); hb_register( &hb_declpcm ); hb_register( &hb_encfaac ); hb_register( &hb_enclame ); Index: libhb/demuxmpeg.c =================================================================== --- libhb/demuxmpeg.c (revision 1456) +++ libhb/demuxmpeg.c (working copy) @@ -6,7 +6,7 @@ #include "hb.h" -/* Basic MPEG demuxer, only works with DVDs (2048 bytes packets) */ +/* Basic MPEG demuxer */ int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es, hb_psdemux_t* state ) { @@ -91,7 +91,7 @@ int pes_header_d_length; int pes_header_end; int has_pts; - int64_t pts = -1; + int64_t pts = -1, dts = -1; pos += 3; /* packet_start_code_prefix */ id = d[pos]; @@ -109,7 +109,7 @@ continue; } - has_pts = ( ( d[pos+1] >> 6 ) & 0x2 ) ? 1 : 0; + has_pts = d[pos+1] >> 6; pos += 2; /* Required headers */ pes_header_d_length = d[pos]; @@ -118,11 +118,23 @@ if( has_pts ) { - pts = ( ( ( (uint64_t) d[pos] >> 1 ) & 0x7 ) << 30 ) + + pts = ( (uint64_t)(d[pos] & 0xe ) << 29 ) + ( d[pos+1] << 22 ) + ( ( d[pos+2] >> 1 ) << 15 ) + ( d[pos+3] << 7 ) + ( d[pos+4] >> 1 ); + if ( has_pts & 1 ) + { + dts = ( (uint64_t)(d[pos+5] & 0xe ) << 29 ) + + ( d[pos+6] << 22 ) + + ( ( d[pos+7] >> 1 ) << 15 ) + + ( d[pos+8] << 7 ) + + ( d[pos+9] >> 1 ); + } + else + { + dts = pts; + } } pos = pes_header_end; @@ -153,6 +165,7 @@ buf_es->id = id; buf_es->start = pts; + buf_es->renderOffset = dts; buf_es->stop = -1; if (id == 0xE0) { // Consume a chapter break, and apply it to the ES. Index: libhb/stream.c =================================================================== --- libhb/stream.c (revision 1456) +++ libhb/stream.c (working copy) @@ -7,11 +7,76 @@ #include "hb.h" #include "lang.h" #include "a52dec/a52.h" +#include "ffmpeg/avcodec.h" #include #define min(a, b) a < b ? a : b +/* + * This table defines how ISO MPEG stream type codes map to HandBrake + * codecs. It is indexed by the 8 bit stream type and contains the codec + * worker object id and a parameter for that worker proc (ignored except + * for the ffmpeg-based codecs in which case it is the ffmpeg codec id). + * + * Entries with a worker proc id of 0 or a kind of 'U' indicate that HB + * doesn't handle the stream type. + */ +typedef struct { + enum { U, A, V } kind; /* unknown / audio / video */ + int codec; /* HB worker object id of codec */ + int codec_param; /* param for codec (usually ffmpeg codec id) */ + const char* name; /* description of type */ +} stream2codec_t; + +#define st(id, kind, codec, codec_param, name) \ + [id] = { kind, codec, codec_param, name } + +static const stream2codec_t st2codec[256] = { + st(0x01, V, WORK_DECMPEG2, 0, "MPEG1"), + st(0x02, V, WORK_DECMPEG2, 0, "MPEG2"), + st(0x03, A, HB_ACODEC_MPGA, CODEC_ID_MP2, "MPEG1"), + st(0x04, A, HB_ACODEC_MPGA, CODEC_ID_MP2, "MPEG2"), + st(0x05, U, 0, 0, "ISO 13818-1 private section"), + st(0x06, U, 0, 0, "ISO 13818-1 PES private data"), + st(0x07, U, 0, 0, "ISO 13522 MHEG"), + st(0x08, U, 0, 0, "ISO 13818-1 DSM-CC"), + st(0x09, U, 0, 0, "ISO 13818-1 auxiliary"), + st(0x0a, U, 0, 0, "ISO 13818-6 encap"), + st(0x0b, U, 0, 0, "ISO 13818-6 DSM-CC U-N msgs"), + st(0x0c, U, 0, 0, "ISO 13818-6 Stream descriptors"), + st(0x0d, U, 0, 0, "ISO 13818-6 Sections"), + st(0x0e, U, 0, 0, "ISO 13818-1 auxiliary"), + st(0x0f, A, HB_ACODEC_MPGA, CODEC_ID_AAC, "ISO 13818-7 AAC Audio"), + st(0x10, V, WORK_DECAVCODECV, CODEC_ID_MPEG4, "MPEG4"), + st(0x11, A, HB_ACODEC_MPGA, CODEC_ID_AAC, "MPEG4 LATM AAC"), + st(0x12, U, 0, 0, "MPEG4 generic"), + + st(0x14, U, 0, 0, "ISO 13818-6 DSM-CC download"), + + st(0x1b, V, WORK_DECAVCODECV, CODEC_ID_H264, "H.264"), + + st(0x80, U, 0, 0, "DigiCipher II Video"), + st(0x81, A, HB_ACODEC_AC3, 0, "AC-3"), + st(0x82, A, HB_ACODEC_DCA, 0, "HDMV DTS"), + st(0x83, A, HB_ACODEC_LPCM, 0, "LPCM"), + st(0x84, U, 0, 0, "SDDS"), + st(0x85, U, 0, 0, "ATSC Program ID"), + st(0x86, U, 0, 0, "SCTE 35 splice info"), + st(0x87, A, 0, 0, "E-AC-3"), + + st(0x8a, A, HB_ACODEC_DCA, 0, "DTS"), + + st(0x91, A, HB_ACODEC_AC3, 0, "AC-3"), + st(0x92, U, 0, 0, "Subtitle"), + + st(0x94, U, 0, 0, "SDDS"), + st(0xa0, U, 0, 0, "MSCODEC"), + + st(0xea, V, WORK_DECAVCODECV, CODEC_ID_VC1, "VC-1"), +}; +#undef st + typedef enum { hb_stream_type_unknown = 0, transport, @@ -557,6 +622,10 @@ stream->ts_audio_pids[stream->ts_number_audio_pids++] = stream->pmt_info.PCR_PID; } + + // set up the video codec to use for this title + aTitle->video_codec = st2codec[stream->ts_stream_type[0]].codec; + aTitle->video_codec_param = st2codec[stream->ts_stream_type[0]].codec_param; } else { @@ -992,65 +1061,52 @@ buf = hb_ts_stream_getPEStype(stream, stream->ts_audio_pids[aud_pid_index]); /* check that we found a PES header */ + uint8_t stype = 0; if (buf && buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01) { - if (buf[3] == 0xbd) + // 0xbd is the normal container for AC3/DCA/PCM/etc. 0xfd indicates an + // extended stream id (ISO 13818-1(2007)). If we cared about the + // real id we'd have to look inside the PES extension to find it. + // But since we remap stream id's when we generate PS packets from + // the TS packets we can just ignore the actual id. + if ( buf[3] == 0xbd || buf[3] == 0xfd ) { audio->id = 0x80bd | (aud_pid_index << 8); - audio->config.in.codec = HB_ACODEC_AC3; - hb_log("transport stream pid 0x%x (type 0x%x) is AC-3 audio id 0x%x", - stream->ts_audio_pids[aud_pid_index], - stream->ts_stream_type[1 + aud_pid_index], - audio->id); - stream->ts_stream_type[1 + aud_pid_index] = 0x81; - stream->ts_streamid[1 + aud_pid_index] = 0xbd; - } - else if (buf[3] == 0xfd) - { - /* XXX Extended stream id (ISO 13818-1(2000) Amd 2) - we have to look - * inside the PES extension to find out the real stream id then - * figure out what the heck it means. For now we just use the stream - * type if one was specified. */ - const char *atype = 0; - switch (stream->ts_stream_type[1 + aud_pid_index]) + stype = stream->ts_stream_type[1 + aud_pid_index]; + if ( st2codec[stype].kind == U ) { - case 0x81: // AC-3 - atype = "AC-3"; - audio->config.in.codec = HB_ACODEC_AC3; - break; - case 0x82: // HDMV DTS - case 0x8a: // DTS - atype = "DTS"; - audio->config.in.codec = HB_ACODEC_DCA; - break; - case 0x83: // LPCM - atype = "PCM"; - audio->config.in.codec = HB_ACODEC_LPCM; - break; + // XXX assume unknown stream types are AC-3 (if they're not + // audio we'll find that out during the scan but if they're + // some other type of audio we'll end up ignoring them). + stype = 0x81; + stream->ts_stream_type[1 + aud_pid_index] = 0x81; } - audio->id = 0x80bd | (aud_pid_index << 8); stream->ts_streamid[1 + aud_pid_index] = 0xbd; - hb_log("transport stream pid 0x%x (type 0x%x) is %s audio id 0x%x", - stream->ts_audio_pids[aud_pid_index], - stream->ts_stream_type[1 + aud_pid_index], atype, audio->id); } else if ((buf[3] & 0xe0) == 0xc0) { - audio->id = buf[3] | aud_pid_index; - audio->config.in.codec = HB_ACODEC_MPGA; - hb_log("transport stream pid 0x%x (type 0x%x) is MPEG audio id 0x%x", - stream->ts_audio_pids[aud_pid_index], - stream->ts_stream_type[1 + aud_pid_index], - audio->id); - stream->ts_stream_type[1 + aud_pid_index] = 0x03; - stream->ts_streamid[1 + aud_pid_index] = buf[3]; + audio->id = 0xc0 | aud_pid_index; + stype = stream->ts_stream_type[1 + aud_pid_index]; + if ( st2codec[stype].kind == U ) + { + // XXX assume unknown stream types are MPEG audio + stype = 0x03; + stream->ts_stream_type[1 + aud_pid_index] = 0x03; + } } } - fseeko(stream->file_handle, cur_pos, SEEK_SET); - if ( audio->config.in.codec ) + // if we found an audio stream type & HB has a codec that can decode it + // finish configuring the audio so we'll add it to the title's list. + if ( st2codec[stype].kind == A && st2codec[stype].codec ) { + stream->ts_streamid[1 + aud_pid_index] = audio->id; + audio->config.in.codec = st2codec[stype].codec; + audio->config.in.codec_param = st2codec[stype].codec_param; set_audio_description( audio, lang_for_code( stream->a52_info[aud_pid_index].lang_code ) ); + hb_log("transport stream pid 0x%x (type 0x%x) is %s audio id 0x%x", + stream->ts_audio_pids[aud_pid_index], + stype, st2codec[stype].name, audio->id); } else { @@ -1058,6 +1114,7 @@ stream->ts_audio_pids[aud_pid_index], stream->ts_stream_type[1 + aud_pid_index]); } + fseeko(stream->file_handle, cur_pos, SEEK_SET); return audio; } @@ -1294,54 +1351,9 @@ } } -static const char * stream_type_name (uint8_t stream_type) +static const char *stream_type_name (uint8_t stream_type) { - switch( stream_type ) - { - case 0x01: return("ISO 11172 (MPEG1) Video"); - case 0x02: return("ISO 13818-2 (MPEG2) Video"); - case 0x03: return("ISO 11172 (MPEG1) Audio"); - case 0x04: return("ISO 13818-3 (MPEG2) Audio"); - case 0x05: return("ISO 13818-1 private section"); - case 0x06: return("ISO 13818-1 PES private data"); - case 0x07: return("ISO 13522 MHEG"); - case 0x08: return("ISO 13818-1 DSM-CC"); - case 0x09: return("ISO 13818-1 auxiliary"); - case 0x0a: return("ISO 13818-6 multi-protocol encap"); - case 0x0b: return("ISO 13818-6 DSM-CC U-N msgs"); - case 0x0c: return("ISO 13818-6 Stream descriptors"); - case 0x0d: return("ISO 13818-6 Sections"); - case 0x0e: return("ISO 13818-1 auxiliary"); - case 0x0f: return("ISO 13818-7 AAC Audio"); - case 0x10: return("MPEG4 Video"); - case 0x11: return("MPEG4 Audio"); - case 0x12: return("MPEG4 generic"); - - case 0x14: return("ISO 13818-6 DSM-CC download"); - - case 0x1b: return("H.264 Video"); - - case 0x80: return("DigiCipher II Video"); - case 0x81: return("A52/AC-3 Audio"); - case 0x82: return("HDMV DTS Audio"); - case 0x83: return("LPCM Audio"); - case 0x84: return("SDDS Audio"); - case 0x85: return("ATSC Program ID"); - case 0x86: return("SCTE 35 splice info"); - case 0x87: return("ATSC E-AC-3"); - - case 0x8a: return("DTS Audio"); - - case 0x91: return("A52b/AC-3 Audio"); - case 0x92: return("Subtitle"); - - case 0x94: return("SDDS Audio"); - case 0xa0: return("MSCODEC Video"); - - case 0xea: return("VC-1 Video"); - - default: return("Unknown"); - } + return st2codec[stream_type].name? st2codec[stream_type].name : "Unknown"; } int decode_program_map(hb_stream_t* stream) @@ -1393,8 +1405,7 @@ } - if (stream->ts_number_video_pids == 0 && - ( stream_type == 0x02 || stream_type == 0x10 || stream_type == 0x1B ) ) + if (stream->ts_number_video_pids == 0 && st2codec[stream_type].kind == V ) { stream->ts_video_pids[0] = elementary_PID; stream->ts_stream_type[0] = stream_type; @@ -1765,7 +1776,7 @@ int len; // we always ship a PACK header plus all the data in our demux buf. - // AC3 audio also always needs it substream header. + // AC3 audio also always needs its substream header. len = 14 + stream->ts_pos[curstream]; if ( stream->ts_stream_type[curstream] == 0x81) { @@ -1876,37 +1887,82 @@ stream->ts_pos[curstream] = 0; } -static int isIframe( const uint8_t *buf, int adapt_len ) +static int isIframe( hb_stream_t *stream, const uint8_t *buf, int adapt_len ) { - // Look for the Group of Pictures packet + // For mpeg2: look for a gop start or i-frame picture start + // for h.264: look for idr nal type or a slice header for an i-frame + // for vc1: ??? int i; uint32_t strid = 0; - for (i = 4 + adapt_len; i < 188; i++) + + if ( stream->ts_stream_type[0] <= 2 ) { - strid = (strid << 8) | buf[i]; - switch ( strid ) + // This section of the code handles MPEG-1 and MPEG-2 video streams + for (i = 13 + adapt_len; i < 188; i++) { - case 0x000001B8: // group_start_code (GOP header) - case 0x000001B3: // sequence_header code - return 1; + strid = (strid << 8) | buf[i]; + if ( ( strid >> 8 ) == 1 ) + { + // we found a start code + uint8_t id = strid; + switch ( id ) + { + case 0xB8: // group_start_code (GOP header) + case 0xB3: // sequence_header code + return 1; - case 0x00000100: // picture_start_code - // picture_header, let's see if it's an I-frame - if (i<185) + case 0x00: // picture_start_code + // picture_header, let's see if it's an I-frame + if (i<185) + { + // check if picture_coding_type == 1 + if ((buf[i+2] & (0x7 << 3)) == (1 << 3)) + { + // found an I-frame picture + return 1; + } + } + break; + } + } + } + // didn't find an I-frame + return 0; + } + if ( stream->ts_stream_type[0] == 0x1b ) + { + // we have an h.264 stream + for (i = 13 + adapt_len; i < 188; i++) + { + strid = (strid << 8) | buf[i]; + if ( ( strid >> 8 ) == 1 ) + { + // we found a start code - remove the ref_idc from the nal type + uint8_t nal_type = strid & 0x1f; + if ( nal_type == 0x05 ) + // h.264 IDR picture start + return 1; + + if ( nal_type == 0x01 ) { - // check if picture_coding_type == 1 - if ((buf[i+2] & (0x7 << 3)) == (1 << 3)) + // h.264 slice: has to be start MB 0 & type I (2, 4, 7 or 9) + uint8_t id = buf[i+1]; + if ( ( id >> 4 ) == 0x0b || ( id >> 2 ) == 0x25 || + id == 0x88 || id == 0x8a ) { - // found an I-frame picture return 1; } } - break; + } } + // didn't find an I-frame + return 0; } - // didn't find an I frame - return 0; + + // we don't understand the stream type so just say "yes" otherwise + // we'll discard all the video. + return 1; } /*********************************************************************** @@ -2034,7 +2090,7 @@ { // video skips to an iframe after a bad packet to minimize // screen corruption - if ( curstream == 0 && !isIframe( buf, adapt_len ) ) + if ( curstream == 0 && !isIframe( stream, buf, adapt_len ) ) { continue; } @@ -2047,8 +2103,7 @@ { if ( !stream->ts_foundfirst[0] ) { - if ( stream->ts_stream_type[0] == 2 && - !isIframe( buf, adapt_len ) ) + if ( !isIframe( stream, buf, adapt_len ) ) { // didn't find an I frame continue; Index: libhb/hb.h =================================================================== --- libhb/hb.h (revision 1456) +++ libhb/hb.h (working copy) @@ -29,6 +29,7 @@ hb_register( &hb_deca52 ); \ hb_register( &hb_decdca ); \ hb_register( &hb_decavcodec ); \ +hb_register( &hb_decavcodecv ); \ hb_register( &hb_declpcm ); \ hb_register( &hb_encfaac ); \ hb_register( &hb_enclame ); \ @@ -45,6 +46,7 @@ hb_register( &hb_deca52 ); \ hb_register( &hb_decdca ); \ hb_register( &hb_decavcodec ); \ +hb_register( &hb_decavcodecv ); \ hb_register( &hb_declpcm ); \ hb_register( &hb_encfaac ); \ Index: libhb/decdca.c =================================================================== --- libhb/decdca.c (revision 1456) +++ libhb/decdca.c (working copy) @@ -35,9 +35,11 @@ }; -int decdcaInit( hb_work_object_t *, hb_job_t * ); -int decdcaWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); -void decdcaClose( hb_work_object_t * ); +static int decdcaInit( hb_work_object_t *, hb_job_t * ); +static int decdcaWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +static void decdcaClose( hb_work_object_t * ); +static int decdcaBSInfo( hb_work_object_t *, const hb_buffer_t *, + hb_work_info_t * ); hb_work_object_t hb_decdca = { @@ -45,7 +47,9 @@ "DCA decoder", decdcaInit, decdcaWork, - decdcaClose + decdcaClose, + 0, + decdcaBSInfo }; /*********************************************************************** @@ -58,7 +62,7 @@ *********************************************************************** * Allocate the work object, initialize libdca **********************************************************************/ -int decdcaInit( hb_work_object_t * w, hb_job_t * job ) +static int decdcaInit( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); hb_audio_t * audio = w->audio; @@ -88,7 +92,7 @@ *********************************************************************** * Free memory **********************************************************************/ -void decdcaClose( hb_work_object_t * w ) +static void decdcaClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; dca_free( pv->state ); @@ -103,7 +107,7 @@ * Add the given buffer to the data we already have, and decode as much * as we can **********************************************************************/ -int decdcaWork( hb_work_object_t * w, hb_buffer_t ** buf_in, +static int decdcaWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; @@ -214,3 +218,85 @@ return buf; } + +static int decdcaBSInfo( hb_work_object_t *w, const hb_buffer_t *b, + hb_work_info_t *info ) +{ + int i, flags, rate, bitrate, frame_length; + dca_state_t * state = dca_init( 0 ); + + memset( info, 0, sizeof(*info) ); + + /* since DCA frames don't line up with MPEG ES frames scan the + * entire frame for an DCA sync pattern. */ + for ( i = 0; i < b->size - 7; ++i ) + { + if( dca_syncinfo( state, &b->data[i], &flags, &rate, &bitrate, + &frame_length ) ) + { + break; + } + } + if ( i >= b->size - 7 ) + { + /* didn't find DCA sync */ + return 0; + } + + info->name = "DCA"; + info->rate = rate; + info->rate_base = 1; + info->bitrate = bitrate; + info->flags = flags; + + if ( ( flags & DCA_CHANNEL_MASK) == DCA_DOLBY ) + { + info->flags |= AUDIO_F_DOLBY; + } + + switch( flags & DCA_CHANNEL_MASK ) + { + /* mono sources */ + case DCA_MONO: + info->channel_layout = HB_INPUT_CH_LAYOUT_MONO; + break; + /* stereo input */ + case DCA_CHANNEL: + case DCA_STEREO: + case DCA_STEREO_SUMDIFF: + case DCA_STEREO_TOTAL: + info->channel_layout = HB_INPUT_CH_LAYOUT_STEREO; + break; + /* 3F/2R input */ + case DCA_3F2R: + info->channel_layout = HB_INPUT_CH_LAYOUT_3F2R; + break; + /* 3F/1R input */ + case DCA_3F1R: + info->channel_layout = HB_INPUT_CH_LAYOUT_3F1R; + break; + /* other inputs */ + case DCA_3F: + info->channel_layout = HB_INPUT_CH_LAYOUT_3F; + break; + case DCA_2F1R: + info->channel_layout = HB_INPUT_CH_LAYOUT_2F1R; + break; + case DCA_2F2R: + info->channel_layout = HB_INPUT_CH_LAYOUT_2F2R; + break; + case DCA_4F2R: + info->channel_layout = HB_INPUT_CH_LAYOUT_4F2R; + break; + /* unknown */ + default: + info->channel_layout = HB_INPUT_CH_LAYOUT_STEREO; + } + + if (flags & DCA_LFE) + { + info->channel_layout |= HB_INPUT_CH_LAYOUT_HAS_LFE; + } + + return 1; +} Index: libhb/declpcm.c =================================================================== --- libhb/declpcm.c (revision 1456) +++ libhb/declpcm.c (working copy) @@ -27,9 +27,11 @@ }; static hb_buffer_t * Decode( hb_work_object_t * w ); -int declpcmInit( hb_work_object_t *, hb_job_t * ); -int declpcmWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); -void declpcmClose( hb_work_object_t * ); +static int declpcmInit( hb_work_object_t *, hb_job_t * ); +static int declpcmWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +static void declpcmClose( hb_work_object_t * ); +static int declpcmBSInfo( hb_work_object_t *, const hb_buffer_t *, + hb_work_info_t * ); hb_work_object_t hb_declpcm = { @@ -37,11 +39,19 @@ "LPCM decoder", declpcmInit, declpcmWork, - declpcmClose + declpcmClose, + 0, + declpcmBSInfo }; static const int hdr2samplerate[] = { 48000, 96000, 44100, 32000 }; static const int hdr2samplesize[] = { 16, 20, 24, 16 }; +static const int hdr2layout[] = { + HB_INPUT_CH_LAYOUT_MONO, HB_INPUT_CH_LAYOUT_STEREO, + HB_INPUT_CH_LAYOUT_2F1R, HB_INPUT_CH_LAYOUT_2F2R, + HB_INPUT_CH_LAYOUT_3F2R, HB_INPUT_CH_LAYOUT_4F2R, + HB_INPUT_CH_LAYOUT_STEREO, HB_INPUT_CH_LAYOUT_STEREO, +}; static void lpcmInfo( hb_work_object_t *w, hb_buffer_t *in ) { @@ -103,7 +113,7 @@ pv->next_pts = in->start; } -int declpcmInit( hb_work_object_t * w, hb_job_t * job ) +static int declpcmInit( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); w->private_data = pv; @@ -119,7 +129,7 @@ * to DVD PES boundaries, this routine has to reconstruct then extract the audio * frames. Because of the arbitrary alignment, it can output zero, one or two buf's. */ -int declpcmWork( hb_work_object_t * w, hb_buffer_t ** buf_in, +static int declpcmWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; @@ -225,7 +235,7 @@ return out; } -void declpcmClose( hb_work_object_t * w ) +static void declpcmClose( hb_work_object_t * w ) { if ( w->private_data ) { @@ -233,3 +243,24 @@ w->private_data = 0; } } + +static int declpcmBSInfo( hb_work_object_t *w, const hb_buffer_t *b, + hb_work_info_t *info ) +{ + int nchannels = ( b->data[4] & 7 ) + 1; + int sample_size = hdr2samplesize[b->data[4] >> 6]; + + int rate = hdr2samplerate[ ( b->data[4] >> 4 ) & 0x3 ]; + int bitrate = rate * sample_size * nchannels; + + memset( info, 0, sizeof(*info) ); + + info->name = "LPCM"; + info->rate = rate; + info->rate_base = 1; + info->bitrate = bitrate; + info->flags = ( b->data[3] << 16 ) | ( b->data[4] << 8 ) | b->data[5]; + info->channel_layout = hdr2layout[nchannels - 1]; + + return 1; +} Index: libhb/decavcodec.c =================================================================== --- libhb/decavcodec.c (revision 1456) +++ libhb/decavcodec.c (working copy) @@ -8,9 +8,11 @@ #include "ffmpeg/avcodec.h" -int decavcodecInit( hb_work_object_t *, hb_job_t * ); -int decavcodecWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); -void decavcodecClose( hb_work_object_t * ); +static int decavcodecInit( hb_work_object_t *, hb_job_t * ); +static int decavcodecWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +static void decavcodecClose( hb_work_object_t * ); +static int decavcodecInfo( hb_work_object_t *, hb_work_info_t * ); +static int decavcodecBSInfo( hb_work_object_t *, const hb_buffer_t *, hb_work_info_t * ); hb_work_object_t hb_decavcodec = { @@ -18,25 +20,32 @@ "MPGA decoder (libavcodec)", decavcodecInit, decavcodecWork, - decavcodecClose + decavcodecClose, + decavcodecInfo, + decavcodecBSInfo }; struct hb_work_private_s { - hb_job_t * job; - - AVCodecContext * context; - int64_t pts_last; + hb_job_t *job; + AVCodecContext *context; AVCodecParserContext *parser; + hb_list_t *list; + int64_t pts_next; // next pts we expect to generate + int64_t pts; // (video) pts passing from parser to decoder + int64_t chap_time; // time of next chap mark (if new_chap != 0) + int new_chap; + int nframes; }; + /*********************************************************************** * hb_work_decavcodec_init *********************************************************************** * **********************************************************************/ -int decavcodecInit( hb_work_object_t * w, hb_job_t * job ) +static int decavcodecInit( hb_work_object_t * w, hb_job_t * job ) { AVCodec * codec; @@ -45,12 +54,15 @@ pv->job = job; - codec = avcodec_find_decoder( CODEC_ID_MP2 ); - pv->parser = av_parser_init(CODEC_ID_MP2); + int codec_id = w->codec_param; + /*XXX*/ + if ( codec_id == 0 ) + codec_id = CODEC_ID_MP2; + codec = avcodec_find_decoder( codec_id ); + pv->parser = av_parser_init( codec_id ); pv->context = avcodec_alloc_context(); avcodec_open( pv->context, codec ); - pv->pts_last = -1; return 0; } @@ -60,11 +72,15 @@ *********************************************************************** * **********************************************************************/ -void decavcodecClose( hb_work_object_t * w ) +static void decavcodecClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; av_parser_close(pv->parser); avcodec_close( pv->context ); + if ( pv->list ) + { + hb_list_close( &pv->list ); + } } /*********************************************************************** @@ -72,7 +88,7 @@ *********************************************************************** * **********************************************************************/ -int decavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in, +static int decavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; @@ -85,28 +101,24 @@ *buf_out = NULL; - if( in->start < 0 || - ( pv->pts_last > 0 && - in->start > pv->pts_last && - in->start - pv->pts_last < 5000 ) ) /* Hacky */ - { - cur = pv->pts_last; - } - else - { - cur = in->start; - } + cur = ( in->start < 0 )? pv->pts_next : in->start; pos = 0; while( pos < in->size ) { - len = av_parser_parse(pv->parser, pv->context,&parser_output_buffer,&parser_output_buffer_len,in->data + pos,in->size - pos,cur,cur); - + len = av_parser_parse( pv->parser, pv->context, + &parser_output_buffer, &parser_output_buffer_len, + in->data + pos, in->size - pos, cur, cur ); out_size = 0; uncompressed_len = 0; if (parser_output_buffer_len) - uncompressed_len = avcodec_decode_audio( pv->context, buffer, &out_size, - parser_output_buffer, parser_output_buffer_len ); + { + out_size = sizeof(buffer); + uncompressed_len = avcodec_decode_audio2( pv->context, buffer, + &out_size, + parser_output_buffer, + parser_output_buffer_len ); + } if( out_size ) { short * s16; @@ -152,8 +164,331 @@ pos += len; } - pv->pts_last = cur; + pv->pts_next = cur; return HB_WORK_OK; } +static int decavcodecInfo( 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->context ) + { + AVCodecContext *context = pv->context; + info->bitrate = context->bit_rate; + info->rate = context->time_base.num; + info->rate_base = context->time_base.den; + info->profile = context->profile; + info->level = context->level; + return 1; + } + return 0; +} + +static int decavcodecBSInfo( hb_work_object_t *w, const hb_buffer_t *buf, + hb_work_info_t *info ) +{ + hb_work_private_t *pv = w->private_data; + + memset( info, 0, sizeof(*info) ); + + if ( pv && pv->context ) + { + return decavcodecInfo( w, info ); + } + // XXX + // We should parse the bitstream to find its parameters but for right + // now we just return dummy values if there's a codec that will handle it. + AVCodec *codec = avcodec_find_decoder( w->codec_param? w->codec_param : + CODEC_ID_MP2 ); + if ( codec ) + { + static char codec_name[64]; + + info->name = strncpy( codec_name, codec->name, sizeof(codec_name)-1 ); + info->bitrate = 384000; + info->rate = 48000; + info->rate_base = 1; + info->channel_layout = HB_INPUT_CH_LAYOUT_STEREO; + return 1; + } + return -1; +} + +/* ------------------------------------------------------------- + * General purpose video decoder using libavcodec + */ + +static uint8_t *copy_plane( uint8_t *dst, uint8_t* src, int dstride, int sstride, + int h ) +{ + if ( dstride == sstride ) + { + memcpy( dst, src, dstride * h ); + return dst + dstride * h; + } + int lbytes = dstride <= sstride? dstride : sstride; + while ( --h >= 0 ) + { + memcpy( dst, src, lbytes ); + src += sstride; + dst += dstride; + } + return dst; +} + +/* Note: assumes frame format is PIX_FMT_YUV420P */ +static hb_buffer_t *copy_frame( AVCodecContext *context, AVFrame *frame ) +{ + int w = context->width, h = context->height; + hb_buffer_t *buf = hb_buffer_init( w * h * 3 / 2 ); + uint8_t *dst = buf->data; + + dst = copy_plane( dst, frame->data[0], w, frame->linesize[0], h ); + w >>= 1; h >>= 1; + dst = copy_plane( dst, frame->data[1], w, frame->linesize[1], h ); + dst = copy_plane( dst, frame->data[2], w, frame->linesize[2], h ); + + return buf; +} + +static int get_frame_buf( AVCodecContext *context, AVFrame *frame ) +{ + hb_work_private_t *pv = context->opaque; + int64_t pts = pv->pts; + pv->pts = -1; + frame->pts = pts; + + return avcodec_default_get_buffer( context, frame ); +} + +static void log_chapter( hb_work_private_t *pv, int chap_num, int64_t pts ) +{ + hb_chapter_t *c = hb_list_item( pv->job->title->list_chapter, chap_num - 1 ); + hb_log( "%s: \"%s\" (%d) at frame %u time %lld", pv->context->codec->name, + c->title, chap_num, pv->nframes, pts ); +} + +static int decodeFrame( hb_work_private_t *pv, uint8_t *data, int size ) +{ + int got_picture; + AVFrame frame; + + int len = avcodec_decode_video( pv->context, &frame, &got_picture, data, size ); + if ( len < 0 ) + { + hb_log( "avcodec_decode_video err %d", len ); + return 0; + } + if( got_picture ) + { + // ffmpeg makes it hard to attach a pts to a frame. if the MPEG ES + // packet had a pts we handed it to av_parser_parse (if the packet had + // no pts we set it to -1 but before the parse we can't distinguish between + // the start of a video frame with no pts & an intermediate packet of + // some frame which never has a pts). we hope that when parse returns + // the frame to us the pts we originally handed it will be in parser->pts. + // we put this pts into pv->pts so that when a avcodec_decode_video + // finally gets around to allocating an AVFrame to hold the decoded + // frame we can stuff that pts into the frame. if all of these relays + // worked at this point frame.pts should hold the frame's pts from the + // original data stream or -1 if it didn't have one. in the latter case + // we generate the next pts in sequence for it. + int64_t pts = frame.pts; + if ( pts < 0 ) + { + pts = pv->pts_next; + } + double frame_dur = (double) pv->context->time_base.num / + (double) pv->context->time_base.den * 90000.; + frame_dur += frame.repeat_pict * frame_dur * 0.5; + pv->pts_next = (double)pts + frame_dur; + + hb_buffer_t *buf = copy_frame( pv->context, &frame ); + buf->start = pts; + + if ( pv->new_chap && buf->start >= pv->chap_time ) + { + buf->new_chap = pv->new_chap; + pv->new_chap = 0; + pv->chap_time = 0; + if ( pv->job ) + { + log_chapter( pv, buf->new_chap, buf->start ); + } + } + else if ( pv->job && pv->nframes == 0 ) + { + log_chapter( pv, pv->job->chapter_start, buf->start ); + } + hb_list_add( pv->list, buf ); + ++pv->nframes; + } + return got_picture; +} + +static void decodeVideo( hb_work_private_t *pv, uint8_t *data, int size, + int64_t pts, int64_t dts ) +{ + /* + * The following loop is a do..while because we need to handle both + * data & the flush at the end (signaled by size=0). At the end there's + * generally a frame in the parser & one or more frames in the decoder + * (depending on the bframes setting). + */ + int pos = 0; + do { + uint8_t *pout; + int pout_len; + int len = av_parser_parse( pv->parser, pv->context, &pout, &pout_len, + data + pos, size - pos, pts, dts ); + pos += len; + + if ( pout_len > 0 ) + { + pv->pts = pv->parser->pts; + decodeFrame( pv, pout, pout_len ); + } + } while ( pos < size ); + + /* the stuff above flushed the parser, now flush the decoder */ + while ( size == 0 && decodeFrame( pv, NULL, 0 ) ) + { + } +} + +static hb_buffer_t *link_buf_list( hb_work_private_t *pv ) +{ + hb_buffer_t *head = hb_list_item( pv->list, 0 ); + + if ( head ) + { + hb_list_rem( pv->list, head ); + + hb_buffer_t *last = head, *buf; + + while ( ( buf = hb_list_item( pv->list, 0 ) ) != NULL ) + { + hb_list_rem( pv->list, buf ); + last->next = buf; + last = buf; + } + } + return head; +} + + +static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job ) +{ + + hb_work_private_t *pv = calloc( 1, sizeof( hb_work_private_t ) ); + w->private_data = pv; + pv->job = job; + pv->list = hb_list_init(); + + int codec_id = w->codec_param; + pv->parser = av_parser_init( codec_id ); + pv->context = avcodec_alloc_context2( CODEC_TYPE_VIDEO ); + + /* we have to wrap ffmpeg's get_buffer to be able to set the pts (?!) */ + pv->context->opaque = pv; + pv->context->get_buffer = get_frame_buf; + + AVCodec *codec = avcodec_find_decoder( codec_id ); + avcodec_open( pv->context, codec ); + + return 0; +} + +static int decavcodecvWork( 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 *in = *buf_in; + int64_t pts = -1; + int64_t dts = pts; + + *buf_in = NULL; + + /* if we got an empty buffer signaling end-of-stream send it downstream */ + if ( in->size == 0 ) + { + decodeVideo( pv, in->data, in->size, pts, dts ); + hb_list_add( pv->list, in ); + *buf_out = link_buf_list( pv ); + hb_log( "%s done: %d frames", pv->context->codec->name, pv->nframes ); + return HB_WORK_DONE; + } + + if( in->start >= 0 ) + { + pts = in->start; + dts = in->renderOffset; + } + if ( in->new_chap ) + { + pv->new_chap = in->new_chap; + pv->chap_time = pts >= 0? pts : pv->pts_next; + } + decodeVideo( pv, in->data, in->size, pts, dts ); + hb_buffer_close( &in ); + *buf_out = link_buf_list( pv ); + return HB_WORK_OK; +} + +static int decavcodecvInfo( 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->context ) + { + AVCodecContext *context = pv->context; + info->bitrate = context->bit_rate; + info->width = context->width; + info->height = context->height; + + /* ffmpeg gives the frame rate in frames per second while HB wants + * it in units of the 27MHz MPEG clock. */ + info->rate = 27000000; + info->rate_base = (int64_t)context->time_base.num * 27000000LL / + context->time_base.den; + + /* ffmpeg returns the Pixel Aspect Ratio (PAR). Handbrake wants the + * Display Aspect Ratio so we convert by scaling by the Storage + * Aspect Ratio (w/h). We do the calc in floating point to get the + * rounding right. We round in the second decimal digit because we + * scale the (integer) aspect by 9 to preserve the 1st digit. */ + info->aspect = ( (double)context->sample_aspect_ratio.num * + (double)context->width / + (double)context->sample_aspect_ratio.den / + (double)context->height + 0.05 ) * HB_ASPECT_BASE; + + info->profile = context->profile; + info->level = context->level; + info->name = context->codec->name; + return 1; + } + return 0; +} + +static int decavcodecvBSInfo( hb_work_object_t *w, const hb_buffer_t *buf, + hb_work_info_t *info ) +{ + return 0; +} + +hb_work_object_t hb_decavcodecv = +{ + WORK_DECAVCODECV, + "Video decoder (libavcodec)", + decavcodecvInit, + decavcodecvWork, + decavcodecClose, + decavcodecvInfo, + decavcodecvBSInfo +}; Index: libhb/internal.h =================================================================== --- libhb/internal.h (revision 1456) +++ libhb/internal.h (working copy) @@ -97,24 +97,11 @@ volatile int * die, int * error, hb_job_t ** job ); hb_thread_t * hb_reader_init( hb_job_t * ); hb_thread_t * hb_muxer_init( hb_job_t * ); +hb_work_object_t * hb_get_work( int ); +hb_work_object_t * hb_codec_decoder( int ); +hb_work_object_t * hb_codec_encoder( int ); /*********************************************************************** - * libmpeg2 wrapper - *********************************************************************** - * It is exported here because it is used at several places - **********************************************************************/ -typedef struct hb_libmpeg2_s hb_libmpeg2_t; - -hb_libmpeg2_t * hb_libmpeg2_init(); -int hb_libmpeg2_decode( hb_libmpeg2_t *, - hb_buffer_t * es_buf, - hb_list_t * raw_list ); -void hb_libmpeg2_info( hb_libmpeg2_t * m, int * width, - int * height, int * rate, int * aspect_ratio ); -void hb_libmpeg2_close( hb_libmpeg2_t ** ); -int hb_libmpeg2_clear_aspect_ratio( hb_libmpeg2_t * ); - -/*********************************************************************** * mpegdemux.c **********************************************************************/ typedef struct { @@ -213,6 +200,7 @@ WORK_DECA52, WORK_DECDCA, WORK_DECAVCODEC, + WORK_DECAVCODECV, WORK_DECLPCM, WORK_ENCFAAC, WORK_ENCLAME, Index: libhb/work.c =================================================================== --- libhb/work.c (revision 1456) +++ libhb/work.c (working copy) @@ -70,19 +70,44 @@ free( work ); } -static hb_work_object_t * getWork( int id ) +hb_work_object_t * hb_get_work( int id ) { hb_work_object_t * w; for( w = hb_objects; w; w = w->next ) { if( w->id == id ) { - return w; + hb_work_object_t *wc = malloc( sizeof(*w) ); + *wc = *w; + return wc; } } return NULL; } +hb_work_object_t * hb_codec_decoder( int codec ) +{ + switch( codec ) + { + case HB_ACODEC_AC3: return hb_get_work( WORK_DECA52 ); + case HB_ACODEC_DCA: return hb_get_work( WORK_DECDCA ); + case HB_ACODEC_MPGA: return hb_get_work( WORK_DECAVCODEC ); + case HB_ACODEC_LPCM: return hb_get_work( WORK_DECLPCM ); + } + return NULL; +} + +hb_work_object_t * hb_codec_encoder( int codec ) +{ + switch( codec ) + { + case HB_ACODEC_FAAC: return hb_get_work( WORK_ENCFAAC ); + case HB_ACODEC_LAME: return hb_get_work( WORK_ENCLAME ); + case HB_ACODEC_VORBIS: return hb_get_work( WORK_ENCVORBIS ); + } + return NULL; +} + /** * Job initialization rountine. * Initializes fifos. @@ -99,10 +124,6 @@ hb_title_t * title; int i, j; hb_work_object_t * w; - - /* FIXME: This feels really hackish, anything better? */ - hb_work_object_t * audio_w = NULL; - hb_work_object_t * sub_w = NULL; hb_work_object_t * final_w = NULL; hb_audio_t * audio; @@ -234,17 +255,19 @@ job->fifo_mpeg4 = hb_fifo_init( FIFO_CPU_MULT * cpu_count ); /* Synchronization */ - hb_list_add( job->list_work, ( w = getWork( WORK_SYNC ) ) ); + hb_list_add( job->list_work, ( w = hb_get_work( WORK_SYNC ) ) ); w->fifo_in = NULL; w->fifo_out = NULL; /* Video decoder */ - hb_list_add( job->list_work, ( w = getWork( WORK_DECMPEG2 ) ) ); + int vcodec = title->video_codec? title->video_codec : WORK_DECMPEG2; + hb_list_add( job->list_work, ( w = hb_get_work( vcodec ) ) ); + w->codec_param = title->video_codec_param; w->fifo_in = job->fifo_mpeg2; w->fifo_out = job->fifo_raw; /* Video renderer */ - hb_list_add( job->list_work, ( w = getWork( WORK_RENDER ) ) ); + hb_list_add( job->list_work, ( w = hb_get_work( WORK_RENDER ) ) ); w->fifo_in = job->fifo_sync; w->fifo_out = job->fifo_render; if ( job->indepth_scan ) @@ -262,21 +285,21 @@ { case HB_VCODEC_FFMPEG: hb_log( " + encoder FFmpeg" ); - w = getWork( WORK_ENCAVCODEC ); + w = hb_get_work( WORK_ENCAVCODEC ); break; case HB_VCODEC_XVID: hb_log( " + encoder XviD" ); - w = getWork( WORK_ENCXVID ); + w = hb_get_work( WORK_ENCXVID ); break; case HB_VCODEC_X264: hb_log( " + encoder x264" ); if( job->x264opts != NULL && *job->x264opts != '\0' ) hb_log( " + x264 options: %s", job->x264opts); - w = getWork( WORK_ENCX264 ); + w = hb_get_work( WORK_ENCX264 ); break; case HB_VCODEC_THEORA: hb_log( " + encoder Theora" ); - w = getWork( WORK_ENCTHEORA ); + w = hb_get_work( WORK_ENCTHEORA ); break; } w->fifo_in = job->fifo_render; @@ -338,20 +361,10 @@ * Don't add threads for subtitles when we are scanning, unless * looking for forced subtitles. */ - if( sub_w != NULL ) - { - /* - * Need to copy the prior subtitle structure so that we - * don't overwrite the fifos. - */ - sub_w = calloc( sizeof( hb_work_object_t ), 1 ); - sub_w = memcpy( sub_w, w, sizeof( hb_work_object_t )); - } else { - w = sub_w = getWork( WORK_DECSUB ); - } - hb_list_add( job->list_work, sub_w ); - sub_w->fifo_in = subtitle->fifo_in; - sub_w->fifo_out = subtitle->fifo_raw; + w = hb_get_work( WORK_DECSUB ); + w->fifo_in = subtitle->fifo_in; + w->fifo_out = subtitle->fifo_raw; + hb_list_add( job->list_work, w ); } } } @@ -541,79 +554,42 @@ /* * Audio Decoder Thread */ - switch( audio->config.in.codec ) + if ( ( w = hb_codec_decoder( audio->config.in.codec ) ) == NULL ) { - case HB_ACODEC_AC3: - w = getWork( WORK_DECA52 ); - break; - case HB_ACODEC_DCA: - w = getWork( WORK_DECDCA ); - break; - case HB_ACODEC_MPGA: - w = getWork( WORK_DECAVCODEC ); - break; - case HB_ACODEC_LPCM: - w = getWork( WORK_DECLPCM ); - break; - default: - /* Invalid input codec */ - hb_error("Invalid input codec: %d", audio->config.in.codec); - *job->die = 1; - goto cleanup; + hb_error("Invalid input codec: %d", audio->config.in.codec); + *job->die = 1; + goto cleanup; } - w->fifo_in = audio->priv.fifo_in; - w->fifo_out = audio->priv.fifo_raw; - w->config = &audio->priv.config; - w->audio = audio; + w->fifo_in = audio->priv.fifo_in; + w->fifo_out = audio->priv.fifo_raw; + w->config = &audio->priv.config; + w->audio = audio; + w->codec_param = audio->config.in.codec_param; - /* FIXME: This feels really hackish, anything better? */ - audio_w = calloc( sizeof( hb_work_object_t ), 1 ); - audio_w = memcpy( audio_w, w, sizeof( hb_work_object_t )); + hb_list_add( job->list_work, w ); - hb_list_add( job->list_work, audio_w ); - /* * Audio Encoder Thread */ - switch( audio->config.out.codec ) + if( audio->config.out.codec != HB_ACODEC_AC3 ) { - case HB_ACODEC_FAAC: - w = getWork( WORK_ENCFAAC ); - break; - case HB_ACODEC_LAME: - w = getWork( WORK_ENCLAME ); - break; - case HB_ACODEC_VORBIS: - w = getWork( WORK_ENCVORBIS ); - break; - case HB_ACODEC_AC3: - break; - case HB_ACODEC_DCA: /* These are all invalid output codecs. */ - default: + /* + * Add the encoder thread if not doing AC-3 pass through + */ + if ( ( w = hb_codec_encoder( audio->config.out.codec ) ) == NULL ) + { hb_error("Invalid audio codec: %#x", audio->config.out.codec); w = NULL; *job->die = 1; goto cleanup; - } + } + w->fifo_in = audio->priv.fifo_sync; + w->fifo_out = audio->priv.fifo_out; + w->config = &audio->priv.config; + w->audio = audio; - if( audio->config.out.codec != HB_ACODEC_AC3 ) - { - /* - * Add the encoder thread if not doing AC-3 pass through - */ - w->fifo_in = audio->priv.fifo_sync; - w->fifo_out = audio->priv.fifo_out; - w->config = &audio->priv.config; - w->audio = audio; - - /* FIXME: This feels really hackish, anything better? */ - audio_w = calloc( sizeof( hb_work_object_t ), 1 ); - audio_w = memcpy( audio_w, w, sizeof( hb_work_object_t )); - - hb_list_add( job->list_work, audio_w ); + hb_list_add( job->list_work, w ); } - - } /* Init read & write threads */ @@ -655,6 +631,7 @@ } hb_list_rem( job->list_work, w ); w->close( w ); + free( w ); job->done = 1; cleanup: @@ -662,23 +639,12 @@ while( ( w = hb_list_item( job->list_work, 0 ) ) ) { hb_list_rem( job->list_work, w ); - if( w != NULL && w->thread != NULL ) + if( w->thread != NULL ) { hb_thread_close( &w->thread ); w->close( w ); } - - /* FIXME: This feels really hackish, anything better? */ - if ( w->id == WORK_DECA52 || - w->id == WORK_DECDCA || - w->id == WORK_DECLPCM || - w->id == WORK_ENCFAAC || - w->id == WORK_ENCLAME || - w->id == WORK_ENCVORBIS ) - { - free( w ); - w = NULL; - } + free( w ); } hb_list_close( &job->list_work ); @@ -831,7 +797,7 @@ } /** - * Performs the work objects specific work function. + * Performs the work object's specific work function. * Loops calling work function for associated work object. Sleeps when fifo is full. * Monitors work done indicator. * Exits loop when work indiactor is set. Index: libhb/fifo.c =================================================================== --- libhb/fifo.c (revision 1456) +++ libhb/fifo.c (working copy) @@ -14,53 +14,63 @@ struct hb_fifo_s { hb_lock_t * lock; - int capacity; - int size; - int buffer_size; + uint32_t capacity; + uint32_t size; + uint32_t buffer_size; hb_buffer_t * first; hb_buffer_t * last; }; -#define MAX_BUFFER_POOLS 15 -#define BUFFER_POOL_MAX_ELEMENTS 2048 +/* we round the requested buffer size up to the next power of 2 so there can + * be at most 32 possible pools when the size is a 32 bit int. To avoid a lot + * of slow & error-prone run-time checking we allow for all 32. */ +#define MAX_BUFFER_POOLS 32 +/* the buffer pool only exists to avoid the two malloc and two free calls that + * it would otherwise take to allocate & free a buffer. but we don't want to + * tie up a lot of memory in the pool because this allocator isn't as general + * as malloc so memory tied up here puts more pressure on the malloc pool. + * A pool of 16 elements will avoid 94% of the malloc/free calls without wasting + * too much memory. */ +#define BUFFER_POOL_MAX_ELEMENTS 32 struct hb_buffer_pools_s { - int entries; - int allocated; - hb_fifo_t *pool[MAX_BUFFER_POOLS]; + int64_t allocated; hb_lock_t *lock; -}; + hb_fifo_t *pool[MAX_BUFFER_POOLS]; +} buffers; -struct hb_buffer_pools_s buffers; void hb_buffer_pool_init( void ) { - hb_fifo_t *buffer_pool; - int size = 512; - int max_size = 32768;; - - buffers.entries = 0; buffers.lock = hb_lock_init(); buffers.allocated = 0; - while(size <= max_size) { - buffer_pool = buffers.pool[buffers.entries++] = hb_fifo_init(BUFFER_POOL_MAX_ELEMENTS); - buffer_pool->buffer_size = size; - size *= 2; + /* we allocate pools for sizes 2^10 through 2^25. requests larger than + * 2^25 will get passed through to malloc. */ + int i; + for ( i = 10; i < 26; ++i ) + { + buffers.pool[i] = hb_fifo_init(BUFFER_POOL_MAX_ELEMENTS); + buffers.pool[i]->buffer_size = 1 << i; } + /* requests smaller than 2^10 are satisfied from the 2^10 pool. */ + for ( i = 1; i < 10; ++i ) + { + buffers.pool[i] = buffers.pool[10]; + } } void hb_buffer_pool_free( void ) { int i; int count; - int freed = 0; + int64_t freed = 0; hb_buffer_t *b; hb_lock(buffers.lock); - for( i = 0; i < buffers.entries; i++) + for( i = 10; i < 26; ++i) { count = 0; while( ( b = hb_fifo_get(buffers.pool[i]) ) ) @@ -69,70 +79,42 @@ if( b->data ) { free( b->data ); - b->data = NULL; } free( b ); count++; } - hb_log("Freed %d buffers of size %d", count, buffers.pool[i]->buffer_size); + if ( count ) + { + hb_log("Freed %d buffers of size %d", count, + buffers.pool[i]->buffer_size); + } } - hb_log("Allocated %d bytes of buffers on this pass and Freed %d bytes, %d bytes leaked", - buffers.allocated, freed, buffers.allocated - freed); + hb_log("Allocated %lld bytes of buffers on this pass and Freed %lld bytes, " + "%lld bytes leaked", buffers.allocated, freed, buffers.allocated - freed); buffers.allocated = 0; hb_unlock(buffers.lock); } - -hb_buffer_t * hb_buffer_init( int size ) +static hb_fifo_t *size_to_pool( int size ) { - hb_buffer_t * b; int i; - hb_fifo_t *buffer_pool = NULL; - uint8_t *data; - int b_alloc; - int resize = 0; - - if( size > 0 ) + for ( i = 0; i < 30; ++i ) { - /* - * The buffer pools are allocated in increasing size - */ - for( i = 0; i < buffers.entries; i++ ) + if ( size <= (1 << i) ) { - if( buffers.pool[i]->buffer_size >= size ) - { - /* - * This pool is big enough, but are there any buffers in it? - */ - if( hb_fifo_size( buffers.pool[i] ) ) - { - /* - * We've found a matching buffer pool, with buffers. - */ - buffer_pool = buffers.pool[i]; - resize = buffers.pool[i]->buffer_size; - } else { - /* - * Buffer pool is empty, - */ - if( resize ) { - /* - * This is the second time through, so break - * out of here to avoid using too large a - * buffer for a small job. - */ - break; - } - resize = buffers.pool[i]->buffer_size; - } - } + return buffers.pool[i]; } } - /* - * Don't reuse the 0 size buffers, not much gain. - */ - if( size != 0 && buffer_pool ) + return NULL; +} + +hb_buffer_t * hb_buffer_init( int size ) +{ + hb_buffer_t * b; + hb_fifo_t *buffer_pool = size_to_pool( size ); + + if( buffer_pool ) { b = hb_fifo_get( buffer_pool ); @@ -141,15 +123,10 @@ /* * Zero the contents of the buffer, would be nice if we * didn't have to do this. - * - hb_log("Reused buffer size %d for size %d from pool %d depth %d", - b->alloc, size, smallest_pool->buffer_size, - hb_fifo_size(smallest_pool)); - */ - data = b->data; - b_alloc = b->alloc; + */ + uint8_t *data = b->data; memset( b, 0, sizeof(hb_buffer_t) ); - b->alloc = b_alloc; + b->alloc = buffer_pool->buffer_size; b->size = size; b->data = data; return( b ); @@ -166,152 +143,66 @@ } b->size = size; + b->alloc = buffer_pool? buffer_pool->buffer_size : size; - if( resize ) + if (size) { - size = resize; - } - b->alloc = size; - - /* - hb_log("Allocating new buffer of size %d for size %d", - b->alloc, - b->size); - */ - - if (!size) - return b; #if defined( SYS_DARWIN ) || defined( SYS_FREEBSD ) - b->data = malloc( b->alloc ); + b->data = malloc( b->alloc ); #elif defined( SYS_CYGWIN ) - /* FIXME */ - b->data = malloc( b->alloc + 17 ); + /* FIXME */ + b->data = malloc( b->alloc + 17 ); #else - b->data = memalign( 16, b->alloc ); + b->data = memalign( 16, b->alloc ); #endif - - if( !b->data ) - { - hb_log( "out of memory" ); - free( b ); - return NULL; + if( !b->data ) + { + hb_log( "out of memory" ); + free( b ); + return NULL; + } + hb_lock(buffers.lock); + buffers.allocated += b->alloc; + hb_unlock(buffers.lock); } - - buffers.allocated += b->alloc; - return b; } void hb_buffer_realloc( hb_buffer_t * b, int size ) { - /* No more alignment, but we don't care */ - if( size < 2048 ) { - size = 2048; + if ( size > b->alloc ) + { + uint32_t orig = b->alloc; + size = size_to_pool( size )->buffer_size; + b->data = realloc( b->data, size ); + b->alloc = size; + + hb_lock(buffers.lock); + buffers.allocated += size - orig; + hb_unlock(buffers.lock); } - b->data = realloc( b->data, size ); - buffers.allocated -= b->alloc; - b->alloc = size; - buffers.allocated += b->alloc; } void hb_buffer_close( hb_buffer_t ** _b ) { hb_buffer_t * b = *_b; - hb_fifo_t *buffer_pool = NULL; - int i; + hb_fifo_t *buffer_pool = size_to_pool( b->alloc ); - /* - * Put the buffer into our free list in the matching buffer pool, if there is one. - */ - if( b->alloc != 0 ) + if( buffer_pool && b->data && !hb_fifo_is_full( buffer_pool ) ) { - for( i = 0; i < buffers.entries; i++ ) - { - if( b->alloc == buffers.pool[i]->buffer_size ) - { - buffer_pool = buffers.pool[i]; - break; - } - } + hb_fifo_push( buffer_pool, b ); + return; } - - if( buffer_pool ) + /* either the pool is full or this size doesn't use a pool - free the buf */ + if( b->data ) { - if( !hb_fifo_is_full( buffer_pool ) ) - { - if(b->data) - { - /* - hb_log("Putting a buffer of size %d on pool %d, depth %d", - b->alloc, - buffer_pool->buffer_size, - hb_fifo_size(buffer_pool)); - */ - hb_fifo_push( buffer_pool, b ); - } else { - free(b); - } - } else { - /* - * Got a load of these size ones already, free this buffer. - * - hb_log("Buffer pool for size %d full, freeing buffer", b->alloc); - */ - if( b->data ) - { - free( b->data ); - } - buffers.allocated -= b->alloc; - free( b ); - } - } else { - /* - * Need a new buffer pool for this size. - */ + free( b->data ); hb_lock(buffers.lock); - if ( b->alloc != 0 && buffers.entries < MAX_BUFFER_POOLS) - { - buffer_pool = buffers.pool[buffers.entries++] = hb_fifo_init(BUFFER_POOL_MAX_ELEMENTS); - buffer_pool->buffer_size = b->alloc; - hb_fifo_push( buffer_pool, b ); - /* - hb_log("*** Allocated a new buffer pool for size %d [%d]", b->alloc, - buffers.entries ); - */ - } else { - if( b->alloc != 0 ) - { - for( i = buffers.entries-1; i >= 0; i-- ) - { - if( hb_fifo_size(buffers.pool[i]) == 0 ) - { - /* - * Reuse this pool as it is empty. - */ - buffers.pool[i]->buffer_size = b->alloc; - hb_fifo_push( buffers.pool[i], b ); - b = NULL; - break; - } - } - } - - if( b ) - { - if( b->data ) - { - free( b->data ); - b->data = NULL; - buffers.allocated -= b->alloc; - } - free( b ); - } - } + buffers.allocated -= b->alloc; hb_unlock(buffers.lock); } - + free( b ); *_b = NULL; - } void hb_buffer_copy_settings( hb_buffer_t * dst, const hb_buffer_t * src ) Index: libhb/scan.c =================================================================== --- libhb/scan.c (revision 1456) +++ libhb/scan.c (working copy) @@ -237,16 +237,15 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title ) { int i, npreviews = 0; - hb_buffer_t * buf_ps, * buf_es, * buf_raw; - hb_list_t * list_es, * list_raw; - hb_libmpeg2_t * mpeg2; + hb_buffer_t * buf_ps, * buf_es; + hb_list_t * list_es; int progressive_count = 0; int interlaced_preview_count = 0; - int last_ar = 0, ar16_count = 0, ar4_count = 0; + double last_ar = 0; + int ar16_count = 0, ar4_count = 0; buf_ps = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE ); list_es = hb_list_init(); - list_raw = hb_list_init(); hb_log( "scan: decoding previews for title %d", title->index ); @@ -263,7 +262,7 @@ { if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / 11.0 ) ) { - goto error; + continue; } } else if (data->stream) @@ -273,13 +272,17 @@ * file and we need it to decode any previews. */ if (!hb_stream_seek(data->stream, (float) i / 11.0 ) ) { - goto error; + continue; } } hb_log( "scan: preview %d", i + 1 ); - mpeg2 = hb_libmpeg2_init(); + int vcodec = title->video_codec? title->video_codec : WORK_DECMPEG2; + hb_work_object_t *vid_decoder = hb_get_work( vcodec ); + vid_decoder->codec_param = title->video_codec_param; + vid_decoder->init( vid_decoder, NULL ); + hb_buffer_t * vid_buf = NULL; for( j = 0; j < 10240 ; j++ ) { @@ -304,78 +307,65 @@ while( ( buf_es = hb_list_item( list_es, 0 ) ) ) { hb_list_rem( list_es, buf_es ); - if( buf_es->id == 0xE0 && !hb_list_count( list_raw ) ) + if( buf_es->id == 0xE0 && vid_buf == NULL ) { - hb_libmpeg2_decode( mpeg2, buf_es, list_raw ); - int ar = hb_libmpeg2_clear_aspect_ratio( mpeg2 ); - if ( ar != 0 ) - { - if ( ar != last_ar && last_ar != 0 ) - { - hb_log( "aspect ratio changed from %d to %d", - last_ar, ar ); - } - switch ( ar ) - { - case HB_ASPECT_BASE * 4 / 3: - ++ar4_count; - break; - case HB_ASPECT_BASE * 16 / 9: - ++ar16_count; - break; - default: - hb_log( "unknown aspect ratio %d", ar ); - /* if the aspect is closer to 4:3 use that - * otherwise use 16:9 */ - if ( ar < HB_ASPECT_BASE * 14 / 9 ) - { - ++ar4_count; - } - else - { - ++ar16_count; - } - break; - } - } - last_ar = ar; + vid_decoder->work( vid_decoder, &buf_es, &vid_buf ); } else if( ! AllAudioOK( title ) ) { LookForAudio( title, buf_es ); } - hb_buffer_close( &buf_es ); - - if( hb_list_count( list_raw ) && AllAudioOK( title ) ) - { - /* We got a picture */ - break; - } + if ( buf_es ) + hb_buffer_close( &buf_es ); } - if( hb_list_count( list_raw ) && AllAudioOK( title ) ) - { + if( vid_buf && AllAudioOK( title ) ) break; - } } - if( !hb_list_count( list_raw ) ) + if( ! vid_buf ) { hb_log( "scan: could not get a decoded picture" ); continue; } /* Get size and rate infos */ - title->rate = 27000000; - int ar; - hb_libmpeg2_info( mpeg2, &title->width, &title->height, - &title->rate_base, &ar ); - /* if we found mostly 4:3 previews use that as the aspect ratio otherwise - use 16:9 */ - title->aspect = ar4_count > ar16_count ? - HB_ASPECT_BASE * 4 / 3 : HB_ASPECT_BASE * 16 / 9; + hb_work_info_t vid_info; + vid_decoder->info( vid_decoder, &vid_info ); + vid_decoder->close( vid_decoder ); + free( vid_decoder ); + title->width = vid_info.width; + title->height = vid_info.height; + title->rate = vid_info.rate; + title->rate_base = vid_info.rate_base; + if ( vid_info.aspect != 0 ) + { + if ( vid_info.aspect != last_ar && last_ar != 0 ) + { + hb_log( "aspect ratio changed from %g to %g", + last_ar, vid_info.aspect ); + } + switch ( (int)vid_info.aspect ) + { + case HB_ASPECT_BASE * 4 / 3: + ++ar4_count; + break; + case HB_ASPECT_BASE * 16 / 9: + ++ar16_count; + break; + default: + hb_log( "unknown aspect ratio %g", vid_info.aspect ); + /* if the aspect is closer to 4:3 use that + * otherwise use 16:9 */ + vid_info.aspect < HB_ASPECT_BASE * 14 / 9 ? ++ar4_count : + ++ar16_count; + break; + } + last_ar = vid_info.aspect; + } + if( title->rate_base == 1126125 ) { /* Frame FPS is 23.976 (meaning it's progressive), so @@ -421,18 +411,14 @@ title->crop[2] = title->crop[3] = title->width / 2; } - hb_libmpeg2_close( &mpeg2 ); - while( ( buf_es = hb_list_item( list_es, 0 ) ) ) { hb_list_rem( list_es, buf_es ); hb_buffer_close( &buf_es ); } - buf_raw = hb_list_item( list_raw, 0 ); - /* Check preview for interlacing artifacts */ - if( hb_detect_comb( buf_raw, title->width, title->height, 10, 30, 9 ) ) + if( hb_detect_comb( vid_buf, title->width, title->height, 10, 30, 9 ) ) { hb_log("Interlacing detected in preview frame %i", i); interlaced_preview_count++; @@ -444,7 +430,7 @@ file_preview = fopen( filename, "w" ); if( file_preview ) { - fwrite( buf_raw->data, title->width * title->height * 3 / 2, + fwrite( vid_buf->data, title->width * title->height * 3 / 2, 1, file_preview ); fclose( file_preview ); } @@ -453,14 +439,14 @@ hb_log( "scan: fopen failed (%s)", filename ); } -#define Y buf_raw->data +#define Y vid_buf->data #define DARK 64 /* Detect black borders */ for( j = 0; j < title->width; j++ ) { - for( k = 0; k < title->crop[0]; k++ ) + for( k = 2; k < title->crop[0]; k++ ) if( Y[ k * title->width + j ] > DARK ) { title->crop[0] = k; @@ -493,13 +479,15 @@ ++npreviews; skip_preview: - while( ( buf_raw = hb_list_item( list_raw, 0 ) ) ) - { - hb_list_rem( list_raw, buf_raw ); - hb_buffer_close( &buf_raw ); - } + if ( vid_buf ) + hb_buffer_close( &vid_buf ); } + /* if we found mostly 4:3 previews use that as the aspect ratio otherwise + use 16:9 */ + title->aspect = ar4_count > ar16_count ? + HB_ASPECT_BASE * 4 / 3 : HB_ASPECT_BASE * 16 / 9; + title->crop[0] = EVEN( title->crop[0] ); title->crop[1] = EVEN( title->crop[1] ); title->crop[2] = EVEN( title->crop[2] ); @@ -523,12 +511,6 @@ title->detected_interlacing = 0; } - goto cleanup; - -error: - npreviews = 0; - -cleanup: hb_buffer_close( &buf_ps ); while( ( buf_es = hb_list_item( list_es, 0 ) ) ) { @@ -536,236 +518,12 @@ hb_buffer_close( &buf_es ); } hb_list_close( &list_es ); - while( ( buf_raw = hb_list_item( list_raw, 0 ) ) ) - { - hb_list_rem( list_raw, buf_raw ); - hb_buffer_close( &buf_raw ); - } - hb_list_close( &list_raw ); if (data->dvd) hb_dvd_stop( data->dvd ); return npreviews; } -static void update_audio_description( const char *codec, hb_audio_t *audio, - int is_dolby ) -{ - hb_log( "scan: %s, rate=%dHz, bitrate=%d", codec, audio->config.in.samplerate, - audio->config.in.bitrate ); - - /* XXX */ - if ( is_dolby ) - { - strcat( audio->config.lang.description, " (Dolby Surround)" ); - return; - } - - char *desc = audio->config.lang.description + - strlen( audio->config.lang.description ); - sprintf( desc, " (%d.%d ch)", - HB_INPUT_CH_LAYOUT_GET_DISCRETE_FRONT_COUNT(audio->config.in.channel_layout) + - HB_INPUT_CH_LAYOUT_GET_DISCRETE_REAR_COUNT(audio->config.in.channel_layout), - HB_INPUT_CH_LAYOUT_GET_DISCRETE_LFE_COUNT(audio->config.in.channel_layout)); -} - -static int hb_setup_a52_audio( hb_audio_t *audio, hb_buffer_t *b ) -{ - int i, rate, bitrate, flags; - - /* since AC3 frames don't line up with MPEG ES frames scan the - * entire frame for an AC3 sync pattern. */ - for ( i = 0; i < b->size - 7; ++i ) - { - if( a52_syncinfo( &b->data[i], &flags, &rate, &bitrate ) != 0 ) - { - break; - } - } - if ( i >= b->size - 7 ) - { - /* didn't find AC3 sync */ - return 0; - } - - audio->config.in.samplerate = rate; - audio->config.in.bitrate = bitrate; - - switch( flags & A52_CHANNEL_MASK ) - { - /* mono sources */ - case A52_MONO: - case A52_CHANNEL1: - case A52_CHANNEL2: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_MONO; - break; - /* stereo input */ - case A52_CHANNEL: - case A52_STEREO: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; - break; - /* dolby (DPL1 aka Dolby Surround = 4.0 matrix-encoded) input */ - case A52_DOLBY: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_DOLBY; - break; - /* 3F/2R input */ - case A52_3F2R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F2R; - break; - /* 3F/1R input */ - case A52_3F1R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F1R; - break; - /* other inputs */ - case A52_3F: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F; - break; - case A52_2F1R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F1R; - break; - case A52_2F2R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F2R; - break; - /* unknown */ - default: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; - } - - if (flags & A52_LFE) - { - audio->config.in.channel_layout |= HB_INPUT_CH_LAYOUT_HAS_LFE; - } - - /* store the AC3 flags for future reference - * This enables us to find out if we had a stereo or Dolby source later on - * Store the ac3 flags in the public ac3flags property too, so we can access - * it from the GUI - */ - audio->config.flags.ac3 = audio->priv.config.a52.ac3flags = flags; - update_audio_description( "AC3", audio, (flags & A52_CHANNEL_MASK) == A52_DOLBY ); - return 1; -} - -static int hb_setup_dca_audio( hb_audio_t *audio, hb_buffer_t *b ) -{ - int i, flags, rate, bitrate, frame_length; - dca_state_t * state = dca_init( 0 ); - - /* since DCA frames don't line up with MPEG ES frames scan the - * entire frame for an DCA sync pattern. */ - for ( i = 0; i < b->size - 7; ++i ) - { - if( dca_syncinfo( state, &b->data[i], &flags, &rate, &bitrate, - &frame_length ) ) - { - break; - } - } - if ( i >= b->size - 7 ) - { - /* didn't find DCA sync */ - return 0; - } - - audio->config.in.samplerate = rate; - audio->config.in.bitrate = bitrate; - switch( flags & DCA_CHANNEL_MASK ) - { - /* mono sources */ - case DCA_MONO: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_MONO; - break; - /* stereo input */ - case DCA_CHANNEL: - case DCA_STEREO: - case DCA_STEREO_SUMDIFF: - case DCA_STEREO_TOTAL: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; - break; - /* 3F/2R input */ - case DCA_3F2R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F2R; - break; - /* 3F/1R input */ - case DCA_3F1R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F1R; - break; - /* other inputs */ - case DCA_3F: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F; - break; - case DCA_2F1R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F1R; - break; - case DCA_2F2R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F2R; - break; - case DCA_4F2R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_4F2R; - break; - /* unknown */ - default: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; - } - - if (flags & DCA_LFE) - { - audio->config.in.channel_layout |= HB_INPUT_CH_LAYOUT_HAS_LFE; - } - - /* store the DCA flags for future reference - * This enables us to find out if we had a stereo or Dolby source later on - * store the dca flags in the public dcaflags property too, so we can access - * it from the GUI - */ - audio->config.flags.dca = audio->priv.config.dca.dcaflags = flags; - update_audio_description( "DCA", audio, (flags & DCA_CHANNEL_MASK) == DCA_DOLBY ); - return 1; -} - -static int hb_setup_pcm_audio( hb_audio_t *audio, hb_buffer_t *b ) -{ - // LPCM doesn't have a sync pattern like AC3 or DCA but every - // LPCM elementary stream packet starts with a 7 byte header - // giving the characteristics of the stream. - // See libhb/declpcm.c for a description of the LPCM header. - - static const int hdr2samplerate[] = { 48000, 96000, 44100, 32000 }; - static const int hdr2samplesize[] = { 16, 20, 24, 16 }; - static const int hdr2layout[] = { - HB_INPUT_CH_LAYOUT_MONO, HB_INPUT_CH_LAYOUT_STEREO, - HB_INPUT_CH_LAYOUT_2F1R, HB_INPUT_CH_LAYOUT_2F2R, - HB_INPUT_CH_LAYOUT_3F2R, HB_INPUT_CH_LAYOUT_4F2R, - HB_INPUT_CH_LAYOUT_STEREO, HB_INPUT_CH_LAYOUT_STEREO, - }; - - int nchannels = ( b->data[4] & 7 ) + 1; - int sample_size = hdr2samplesize[b->data[4] >> 6]; - - int rate = hdr2samplerate[ ( b->data[4] >> 4 ) & 0x3 ]; - int bitrate = rate * sample_size * nchannels; - - audio->config.in.samplerate = rate; - audio->config.in.bitrate = bitrate; - audio->config.in.channel_layout = hdr2layout[nchannels - 1]; - update_audio_description( "LPCM", audio, 0 ); - return 1; -} - -static int hb_setup_mpg_audio( hb_audio_t *audio, hb_buffer_t *b ) -{ - /* XXX - * This is a placeholder to get the audio sample rate set. - * It should be replaced by something that extracts the correct info from - * the mpeg audio bitstream. - */ - audio->config.in.samplerate = 48000; - audio->config.in.bitrate = 384000; - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; - update_audio_description( "MPGA", audio, 0 ); - return 1; -} - /* * This routine is called for every frame from a non-video elementary stream. * These are a mix of audio & subtitle streams, some of which we want & some @@ -804,29 +562,64 @@ return; } - switch ( audio->config.in.codec ) + hb_work_object_t *w = hb_codec_decoder( audio->config.in.codec ); + + if ( w == NULL || w->bsinfo == NULL ) { - case HB_ACODEC_AC3: - hb_setup_a52_audio( audio, b ); - break; + hb_log( "Internal error in scan: unhandled audio type %d for id 0x%x", + audio->config.in.codec, audio->id ); + goto drop_audio; + } - case HB_ACODEC_DCA: - hb_setup_dca_audio( audio, b ); - break; + hb_work_info_t info; + w->audio = audio; + w->codec_param = audio->config.in.codec_param; + int ret = w->bsinfo( w, b, &info ); + if ( ret < 0 ) + { + hb_log( "no info on audio type %d/%d for id 0x%x", + audio->config.in.codec, audio->config.in.codec_param, audio->id ); + goto drop_audio; + } + audio->config.in.samplerate = info.rate; + audio->config.in.bitrate = info.bitrate; + audio->config.in.channel_layout = info.channel_layout; + audio->config.flags.ac3 = info.flags; - case HB_ACODEC_LPCM: - hb_setup_pcm_audio( audio, b ); - break; + // update the audio description string based on the info we found + if ( audio->config.flags.ac3 & AUDIO_F_DOLBY ) + { + strcat( audio->config.lang.description, " (Dolby Surround)" ); + } + else + { + int layout = audio->config.in.channel_layout; + char *desc = audio->config.lang.description + + strlen( audio->config.lang.description ); + sprintf( desc, " (%d.%d ch)", + HB_INPUT_CH_LAYOUT_GET_DISCRETE_FRONT_COUNT(layout) + + HB_INPUT_CH_LAYOUT_GET_DISCRETE_REAR_COUNT(layout), + HB_INPUT_CH_LAYOUT_GET_DISCRETE_LFE_COUNT(layout) ); + } - case HB_ACODEC_MPGA: - hb_setup_mpg_audio( audio, b ); - break; + hb_log( "scan: audio 0x%x: %s, rate=%dHz, bitrate=%d %s", audio->id, + info.name, audio->config.in.samplerate, audio->config.in.bitrate, + audio->config.lang.description ); + + free( w ); + return; - default: - hb_log( "Internal error in scan: unhandled audio type %d for 0x%x", - audio->config.in.codec, audio->id ); - break; - } + // We get here if there's no hope of finding info on an audio bitstream, + // either because we don't have a decoder (or a decoder with a bitstream + // info proc) or because the decoder's info proc said that the stream + // wasn't something it could handle. Delete the item from the title's + // audio list so we won't keep reading packets while trying to get its + // bitstream info. + drop_audio: + if ( w ) + free( w ); + + hb_list_rem( title->list_audio, audio ); } /* Index: libhb/common.h =================================================================== --- libhb/common.h (revision 1456) +++ libhb/common.h (working copy) @@ -251,6 +251,7 @@ #define HB_ACODEC_MPGA 0x001000 #define HB_ACODEC_LPCM 0x002000 #define HB_ACODEC_DCA 0x004000 +#define HB_ACODEC_AAC 0x008000 /* Audio Mixdown */ /* define some masks, used to extract the various information from the HB_AMIXDOWN_XXXX values */ @@ -329,8 +330,9 @@ /* Input */ struct { - int track; /* Input track number */ - PRIVATE uint32_t codec; /* Input audio codec */ + int track; /* Input track number */ + PRIVATE uint32_t codec; /* Input audio codec */ + PRIVATE uint32_t codec_param; /* per-codec config info */ PRIVATE int samplerate; /* Input sample rate (Hz) */ PRIVATE int bitrate; /* Input bitrate (kbps) */ PRIVATE int channel_layout; @@ -345,6 +347,7 @@ PRIVATE int ac3; /* flags.ac3 is only set when the source audio format is HB_ACODEC_AC3 */ PRIVATE int dca; /* flags.dca is only set when the source audio format is HB_ACODEC_DCA */ } flags; +#define AUDIO_F_DOLBY (1 << 31) /* set if source uses Dolby Surround */ struct { @@ -438,6 +441,8 @@ int rate_base; int crop[4]; int detected_interlacing; + int video_codec; /* worker object id of video codec */ + int video_codec_param; /* codec specific config */ uint32_t palette[16]; @@ -501,6 +506,27 @@ } param; }; +typedef struct hb_work_info_s +{ + const char *name; + int profile; + int level; + int bitrate; + int rate; + int rate_base; + int flags; + union { + struct { // info only valid for video decoders + int width; + int height; + double aspect; + }; + struct { // info only valid for audio decoders + int channel_layout; + }; + }; +} hb_work_info_t; + struct hb_work_object_s { int id; @@ -511,6 +537,18 @@ int (* work) ( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); void (* close) ( hb_work_object_t * ); + /* the info entry point is used by scan to get bitstream information + * during a decode (i.e., it should only be called after at least one + * call to the 'work' entry point). currently it's only called for + * video streams & can be null for other work objects. */ + int (* info) ( hb_work_object_t *, hb_work_info_t * ); + /* the bitstream info entry point is used by scan to get bitstream + * information from a buffer. it doesn't have to be called during a + * decode (it can be called even if init & work haven't been). + * currently it's only called for audio streams & can be null for + * other work objects. */ + int (* bsinfo) ( hb_work_object_t *, const hb_buffer_t *, + hb_work_info_t * ); hb_fifo_t * fifo_in; hb_fifo_t * fifo_out; @@ -524,6 +562,7 @@ hb_thread_t * thread; volatile int * done; int status; + int codec_param; hb_work_object_t * next; int thread_sleep_interval; @@ -541,6 +580,7 @@ extern hb_work_object_t hb_deca52; extern hb_work_object_t hb_decdca; extern hb_work_object_t hb_decavcodec; +extern hb_work_object_t hb_decavcodecv; extern hb_work_object_t hb_declpcm; extern hb_work_object_t hb_encfaac; extern hb_work_object_t hb_enclame; Index: contrib/Jamfile =================================================================== --- contrib/Jamfile (revision 1456) +++ contrib/Jamfile (working copy) @@ -78,10 +78,10 @@ cd `dirname $(>)` && CONTRIB=`pwd` && rm -rf ffmpeg && (gzip -dc ffmpeg.tar.gz | tar xf -) && cd ffmpeg && $(FFMPEG_PATCH) && - ./configure --prefix=$CONTRIB --enable-gpl --enable-pthreads --enable-swscaler --disable-audio-beos --disable-shared --enable-static \ - --disable-decoders --enable-decoder=mp2 --disable-parsers --enable-parser=mpegaudio \ - --disable-encoders --enable-encoder=mpeg4 --enable-encoder=ac3 --enable-encoder=snow \ - --disable-muxers --enable-muxer=ipod --disable-demuxers --disable-protocols --disable-bsfs && + ./configure --prefix=$CONTRIB --enable-gpl --enable-pthreads --enable-swscaler \ + --disable-shared --enable-static --disable-encoders \ + --enable-encoder=mpeg4 --enable-encoder=ac3 --enable-encoder=snow \ + --disable-muxers --enable-muxer=ipod --disable-protocols --disable-bsfs && $(MAKE) && $(MAKE) install && $(STRIP) $CONTRIB/lib/libavcodec.a }