diff options
-rw-r--r-- | libhb/deca52.c | 9 | ||||
-rw-r--r-- | libhb/decavcodec.c | 16 | ||||
-rw-r--r-- | libhb/decdca.c | 8 | ||||
-rw-r--r-- | libhb/declpcm.c | 11 | ||||
-rw-r--r-- | libhb/decsub.c | 9 | ||||
-rw-r--r-- | libhb/encavcodec.c | 6 | ||||
-rw-r--r-- | libhb/encfaac.c | 94 | ||||
-rw-r--r-- | libhb/enclame.c | 8 | ||||
-rw-r--r-- | libhb/enctheora.c | 26 | ||||
-rw-r--r-- | libhb/encvorbis.c | 8 | ||||
-rw-r--r-- | libhb/encx264.c | 465 | ||||
-rw-r--r-- | libhb/encxvid.c | 8 | ||||
-rw-r--r-- | libhb/muxcommon.c | 104 | ||||
-rw-r--r-- | libhb/muxmp4.c | 52 | ||||
-rw-r--r-- | libhb/reader.c | 14 | ||||
-rw-r--r-- | libhb/sync.c | 53 | ||||
-rw-r--r-- | libhb/work.c | 11 |
17 files changed, 520 insertions, 382 deletions
diff --git a/libhb/deca52.c b/libhb/deca52.c index 1495611da..c0651fad2 100644 --- a/libhb/deca52.c +++ b/libhb/deca52.c @@ -144,11 +144,16 @@ static int deca52Work( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_work_private_t * pv = w->private_data; hb_buffer_t * buf; - if( buf_in && *buf_in ) + if ( (*buf_in)->size <= 0 ) { - pv->sequence = (*buf_in)->sequence; + /* EOF on input stream - send it downstream & say that we're done */ + *buf_out = *buf_in; + *buf_in = NULL; + return HB_WORK_DONE; } + pv->sequence = (*buf_in)->sequence; + hb_list_add( pv->list, *buf_in ); *buf_in = NULL; diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c index c49dcc79c..2ad3993a0 100644 --- a/libhb/decavcodec.c +++ b/libhb/decavcodec.c @@ -164,6 +164,14 @@ static int decavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in, unsigned char *parser_output_buffer; int parser_output_buffer_len; + if ( (*buf_in)->size <= 0 ) + { + /* EOF on input stream - send it downstream & say that we're done */ + *buf_out = *buf_in; + *buf_in = NULL; + return HB_WORK_DONE; + } + *buf_out = NULL; cur = ( in->start < 0 )? pv->pts_next : in->start; @@ -842,6 +850,14 @@ static void decodeAudio( hb_work_private_t *pv, uint8_t *data, int size ) static int decavcodecaiWork( hb_work_object_t *w, hb_buffer_t **buf_in, hb_buffer_t **buf_out ) { + if ( (*buf_in)->size <= 0 ) + { + /* EOF on input stream - send it downstream & say that we're done */ + *buf_out = *buf_in; + *buf_in = NULL; + return HB_WORK_DONE; + } + hb_work_private_t *pv = w->private_data; if ( ! pv->context ) { diff --git a/libhb/decdca.c b/libhb/decdca.c index a326031dd..0c905af10 100644 --- a/libhb/decdca.c +++ b/libhb/decdca.c @@ -113,6 +113,14 @@ static int decdcaWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_work_private_t * pv = w->private_data; hb_buffer_t * buf; + if ( (*buf_in)->size <= 0 ) + { + /* EOF on input stream - send it downstream & say that we're done */ + *buf_out = *buf_in; + *buf_in = NULL; + return HB_WORK_DONE; + } + hb_list_add( pv->list, *buf_in ); *buf_in = NULL; diff --git a/libhb/declpcm.c b/libhb/declpcm.c index 3220d3da5..50a5a128d 100644 --- a/libhb/declpcm.c +++ b/libhb/declpcm.c @@ -133,14 +133,15 @@ 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; - hb_buffer_t *in; + hb_buffer_t *in = *buf_in; hb_buffer_t *buf = NULL; - /* need an input buffer to do anything */ - if( ! buf_in || ! ( in = *buf_in ) ) + if ( in->size <= 0 ) { - *buf_out = buf; - return HB_WORK_OK; + /* EOF on input stream - send it downstream & say that we're done */ + *buf_out = in; + *buf_in = NULL; + return HB_WORK_DONE; } pv->sequence = in->sequence; diff --git a/libhb/decsub.c b/libhb/decsub.c index 4b6b873fc..98ffa1a0d 100644 --- a/libhb/decsub.c +++ b/libhb/decsub.c @@ -51,9 +51,16 @@ int decsubWork( hb_work_object_t * w, hb_buffer_t ** buf_in, { hb_work_private_t * pv = w->private_data; hb_buffer_t * in = *buf_in; - int size_sub, size_rle; + if ( in->size <= 0 ) + { + /* EOF on input stream - send it downstream & say that we're done */ + *buf_out = in; + *buf_in = NULL; + return HB_WORK_DONE; + } + pv->stream_id = in->id; size_sub = ( in->data[0] << 8 ) | in->data[1]; diff --git a/libhb/encavcodec.c b/libhb/encavcodec.c index 2d3f33418..f3837b3e7 100644 --- a/libhb/encavcodec.c +++ b/libhb/encavcodec.c @@ -191,9 +191,11 @@ int encavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in, AVFrame * frame; hb_buffer_t * in = *buf_in, * buf; - if(!in->data) + if ( in->size <= 0 ) { - *buf_out = NULL; + /* EOF on input - send it downstream & say we're done */ + *buf_out = in; + *buf_in = NULL; return HB_WORK_DONE; } diff --git a/libhb/encfaac.c b/libhb/encfaac.c index 7e06a1408..645628a8e 100644 --- a/libhb/encfaac.c +++ b/libhb/encfaac.c @@ -16,12 +16,11 @@ struct hb_work_private_s unsigned long input_samples; unsigned long output_bytes; uint8_t * buf; - + uint8_t * obuf; hb_list_t * list; int64_t pts; - + int64_t framedur; int out_discrete_channels; - }; int encfaacInit( hb_work_object_t *, hb_job_t * ); @@ -57,9 +56,12 @@ int encfaacInit( hb_work_object_t * w, hb_job_t * job ) /* pass the number of channels used into the private work data */ pv->out_discrete_channels = HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown); - pv->faac = faacEncOpen( audio->config.out.samplerate, pv->out_discrete_channels, &pv->input_samples, - &pv->output_bytes ); + pv->faac = faacEncOpen( audio->config.out.samplerate, pv->out_discrete_channels, + &pv->input_samples, &pv->output_bytes ); pv->buf = malloc( pv->input_samples * sizeof( float ) ); + pv->obuf = malloc( pv->output_bytes ); + pv->framedur = 90000LL * pv->input_samples / + ( audio->config.out.samplerate * pv->out_discrete_channels ); cfg = faacEncGetCurrentConfiguration( pv->faac ); cfg->mpegVersion = MPEG4; @@ -113,7 +115,6 @@ int encfaacInit( hb_work_object_t * w, hb_job_t * job ) free( bytes ); pv->list = hb_list_init(); - pv->pts = -1; return 0; } @@ -128,6 +129,7 @@ void encfaacClose( hb_work_object_t * w ) hb_work_private_t * pv = w->private_data; faacEncClose( pv->faac ); free( pv->buf ); + free( pv->obuf ); hb_list_empty( &pv->list ); free( pv ); w->private_data = NULL; @@ -141,9 +143,6 @@ void encfaacClose( hb_work_object_t * w ) static hb_buffer_t * Encode( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; - hb_audio_t * audio = w->audio; - hb_buffer_t * buf; - uint64_t pts, pos; if( hb_list_bytes( pv->list ) < pv->input_samples * sizeof( float ) ) { @@ -151,31 +150,64 @@ static hb_buffer_t * Encode( hb_work_object_t * w ) return NULL; } + uint64_t pts, pos; hb_list_getbytes( pv->list, pv->buf, pv->input_samples * sizeof( float ), &pts, &pos ); + int size = faacEncEncode( pv->faac, (int32_t *)pv->buf, pv->input_samples, + pv->obuf, pv->output_bytes ); - buf = hb_buffer_init( pv->output_bytes ); - buf->start = pts + 90000 * pos / pv->out_discrete_channels / sizeof( float ) / audio->config.out.samplerate; - buf->stop = buf->start + 90000 * pv->input_samples / audio->config.out.samplerate / pv->out_discrete_channels; - buf->size = faacEncEncode( pv->faac, (int32_t *) pv->buf, - pv->input_samples, buf->data, pv->output_bytes ); - buf->frametype = HB_FRAME_AUDIO; - - if( !buf->size ) + // AAC needs four frames before it can start encoding so we'll get nothing + // on the first three calls to the encoder. + if ( size > 0 ) { - /* Encoding was successful but we got no data. Try to encode - more */ - hb_buffer_close( &buf ); - return Encode( w ); + hb_buffer_t * buf = hb_buffer_init( size ); + memcpy( buf->data, pv->obuf, size ); + buf->size = size; + buf->start = pv->pts; + pv->pts += pv->framedur; + buf->stop = pv->pts; + buf->frametype = HB_FRAME_AUDIO; + return buf; } - else if( buf->size < 0 ) + return NULL; +} + +static hb_buffer_t *Flush( hb_work_object_t *w, hb_buffer_t *bufin ) +{ + hb_work_private_t *pv = w->private_data; + + // pad whatever data we have out to four input frames. + int nbytes = hb_list_bytes( pv->list ); + int pad = pv->input_samples * sizeof(float) * 4 - nbytes; + if ( pad > 0 ) { - hb_log( "faacEncEncode failed" ); - hb_buffer_close( &buf ); - return NULL; + hb_buffer_t *tmp = hb_buffer_init( pad ); + memset( tmp->data, 0, pad ); + hb_list_add( pv->list, tmp ); } - return buf; + // There are up to three frames buffered in the encoder plus one + // in our list buffer so four calls to Encode should get them all. + hb_buffer_t *bufout = NULL, *buf = NULL; + while ( hb_list_bytes( pv->list ) >= pv->input_samples * sizeof(float) ) + { + hb_buffer_t *b = Encode( w ); + if ( b ) + { + if ( bufout == NULL ) + { + bufout = b; + } + else + { + buf->next = b; + } + buf = b; + } + } + // add the eof marker to the end of our buf chain + buf->next = bufin; + return bufout; } /*********************************************************************** @@ -189,6 +221,16 @@ int encfaacWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_work_private_t * pv = w->private_data; hb_buffer_t * buf; + if ( (*buf_in)->size <= 0 ) + { + // EOF on input. Finish encoding what we have buffered then send + // it & the eof downstream. + + *buf_out = Flush( w, *buf_in ); + *buf_in = NULL; + return HB_WORK_DONE; + } + hb_list_add( pv->list, *buf_in ); *buf_in = NULL; diff --git a/libhb/enclame.c b/libhb/enclame.c index 24e9542f4..8f4defcc5 100644 --- a/libhb/enclame.c +++ b/libhb/enclame.c @@ -140,6 +140,14 @@ int enclameWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_work_private_t * pv = w->private_data; hb_buffer_t * buf; + if ( (*buf_in)->size <= 0 ) + { + /* EOF on input - send it downstream & say we're done */ + *buf_out = *buf_in; + *buf_in = NULL; + return HB_WORK_DONE; + } + hb_list_add( pv->list, *buf_in ); *buf_in = NULL; diff --git a/libhb/enctheora.c b/libhb/enctheora.c index 59414687c..274887f9f 100644 --- a/libhb/enctheora.c +++ b/libhb/enctheora.c @@ -122,23 +122,22 @@ int enctheoraWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t * in = *buf_in, * buf; yuv_buffer yuv; ogg_packet op; - static int last_p = 0; - memset(&op, 0, sizeof(op)); - memset(&yuv, 0, sizeof(yuv)); - - /* If this is the last empty frame, we're done */ - if(!in->data) + if ( in->size <= 0 ) { - if (!last_p) - { - last_p++; - goto finish; - } - *buf_out = NULL; + // EOF on input - send it downstream & say we're done. + // XXX may need to flush packets via a call to + // theora_encode_packetout(&pv->theora, 1, &op); + // but we don't have a timestamp to put on those packets so we + // drop them for now. + *buf_out = in; + *buf_in = NULL; return HB_WORK_DONE; } + memset(&op, 0, sizeof(op)); + memset(&yuv, 0, sizeof(yuv)); + yuv.y_width = job->width; yuv.y_height = job->height; yuv.y_stride = job->width; @@ -153,8 +152,7 @@ int enctheoraWork( hb_work_object_t * w, hb_buffer_t ** buf_in, theora_encode_YUVin(&pv->theora, &yuv); -finish: - theora_encode_packetout(&pv->theora, last_p, &op); + theora_encode_packetout(&pv->theora, 0, &op); buf = hb_buffer_init( op.bytes + sizeof(op) ); memcpy(buf->data, &op, sizeof(op)); diff --git a/libhb/encvorbis.c b/libhb/encvorbis.c index 5ba073291..47a6077a0 100644 --- a/libhb/encvorbis.c +++ b/libhb/encvorbis.c @@ -252,6 +252,14 @@ int encvorbisWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_work_private_t * pv = w->private_data; hb_buffer_t * buf; + if ( (*buf_in)->size <= 0 ) + { + /* EOF on input - send it downstream & say we're done */ + *buf_out = *buf_in; + *buf_in = NULL; + return HB_WORK_DONE; + } + hb_list_add( pv->list, *buf_in ); *buf_in = NULL; diff --git a/libhb/encx264.c b/libhb/encx264.c index 57a2fe281..caeccb646 100644 --- a/libhb/encx264.c +++ b/libhb/encx264.c @@ -30,16 +30,16 @@ hb_work_object_t hb_encx264 = * to x264_encoder_encode. Since frames are uniquely identified by their * timestamp, we use some bits of the timestamp as an index. The LSB is * chosen so that two successive frames will have different values in the - * bits over any plausible range of frame rates. (Starting with bit 9 allows - * any frame rate slower than 175fps.) The MSB determines the size of the array. + * bits over any plausible range of frame rates. (Starting with bit 8 allows + * any frame rate slower than 352fps.) The MSB determines the size of the array. * It is chosen so that two frames can't use the same slot during the * encoder's max frame delay (set by the standard as 16 frames) and so * that, up to some minimum frame rate, frames are guaranteed to map to - * different slots. (An MSB of 16 which is 2^(16-9+1) = 256 slots guarantees - * no collisions down to a rate of 1.4 fps). + * different slots. (An MSB of 17 which is 2^(17-8+1) = 1024 slots guarantees + * no collisions down to a rate of .7 fps). */ -#define FRAME_INFO_MAX2 (9) // 2^9 = 512; 90000/512 = 175 frames/sec -#define FRAME_INFO_MIN2 (16) // 2^16 = 65536; 90000/65536 = 1.4 frames/sec +#define FRAME_INFO_MAX2 (8) // 2^8 = 256; 90000/256 = 352 frames/sec +#define FRAME_INFO_MIN2 (17) // 2^17 = 128K; 90000/131072 = 1.4 frames/sec #define FRAME_INFO_SIZE (1 << (FRAME_INFO_MIN2 - FRAME_INFO_MAX2 + 1)) #define FRAME_INFO_MASK (FRAME_INFO_SIZE - 1) @@ -377,228 +377,311 @@ static int64_t get_frame_duration( hb_work_private_t * pv, int64_t pts ) return pv->frame_info[i].duration; } -int encx264Work( hb_work_object_t * w, hb_buffer_t ** buf_in, - hb_buffer_t ** buf_out ) +static hb_buffer_t *nal_encode( hb_work_object_t *w, x264_picture_t *pic_out, + int i_nal, x264_nal_t *nal ) { - hb_work_private_t * pv = w->private_data; - hb_job_t * job = pv->job; - hb_buffer_t * in = *buf_in, * buf; - x264_picture_t pic_out; - int i_nal; - x264_nal_t * nal; - int i; - - if( in->data ) + hb_buffer_t *buf = NULL; + hb_work_private_t *pv = w->private_data; + hb_job_t *job = pv->job; + + /* Get next DTS value to use */ + int64_t dts_start = pv->dts_next; + + /* compute the stop time based on the original frame's duration */ + int64_t dts_stop = dts_start + get_frame_duration( pv, pic_out->i_pts ); + pv->dts_next = dts_stop; + + /* Should be way too large */ + buf = hb_buffer_init( 3 * job->width * job->height / 2 ); + buf->size = 0; + buf->frametype = 0; + buf->start = dts_start; + buf->stop = dts_stop; + + /* Store the output presentation time stamp from x264 for use by muxmp4 + in off-setting b-frames with the CTTS atom. */ + buf->renderOffset = pic_out->i_pts - dts_start + pv->init_delay; + if ( buf->renderOffset < 0 ) { - /* - * Point x264 at our current buffers Y(UV) data. - */ - pv->pic_in.img.plane[0] = in->data; - - if( job->grayscale ) - { - /* XXX x264 has currently no option for grayscale encoding */ - memset( pv->pic_in.img.plane[1], 0x80, job->width * job->height / 4 ); - memset( pv->pic_in.img.plane[2], 0x80, job->width * job->height / 4 ); - } - else + if ( dts_start - pic_out->i_pts > pv->max_delay ) { - /* - * Point x264 at our buffers (Y)UV data - */ - pv->pic_in.img.plane[1] = in->data + job->width * job->height; - pv->pic_in.img.plane[2] = in->data + 5 * job->width * - job->height / 4; + pv->max_delay = dts_start - pic_out->i_pts; + hb_log( "encx264: init_delay too small: " + "is %lld need %lld", pv->init_delay, + pv->max_delay ); } + buf->renderOffset = 0; + } - if( pv->dts_next == -1 ) + /* Encode all the NALs we were given into buf. + NOTE: This code assumes one video frame per NAL (but there can + be other stuff like SPS and/or PPS). If there are multiple + frames we only get the duration of the first which will + eventually screw up the muxer & decoder. */ + int i; + for( i = 0; i < i_nal; i++ ) + { + int data = buf->alloc - buf->size; + int size = x264_nal_encode( buf->data + buf->size, &data, 1, &nal[i] ); + if( size < 1 ) { - /* we don't have a start time yet so use the first frame's - * start. All other frame times will be determined by the - * sum of the prior output frame durations in *DTS* order - * (not by the order they arrive here). This timing change is - * essential for VFR with b-frames but a complete nop otherwise. - */ - pv->dts_next = in->start; + continue; } - if( in->new_chap && job->chapter_markers ) + + if( job->mux & HB_MUX_AVI ) { - /* chapters have to start with an IDR frame so request that this - frame be coded as IDR. Since there may be up to 16 frames - currently buffered in the encoder remember the timestamp so - when this frame finally pops out of the encoder we'll mark - its buffer as the start of a chapter. */ - pv->pic_in.i_type = X264_TYPE_IDR; - if( pv->next_chap == 0 ) + if( nal[i].i_ref_idc == NAL_PRIORITY_HIGHEST ) { - pv->next_chap = in->start; - pv->chap_mark = in->new_chap; + buf->frametype = HB_FRAME_KEY; } - /* don't let 'work_loop' put a chapter mark on the wrong buffer */ - in->new_chap = 0; + buf->size += size; + continue; } - else + + /* H.264 in .mp4 or .mkv */ + int naltype = buf->data[buf->size+4] & 0x1f; + if ( naltype == 0x7 || naltype == 0x8 ) { - pv->pic_in.i_type = X264_TYPE_AUTO; + // Sequence Parameter Set & Program Parameter Set go in the + // mp4 header so skip them here + continue; } - pv->pic_in.i_qpplus1 = 0; - /* XXX this is temporary debugging code to check that the upstream - * modules (render & sync) have generated a continuous, self-consistent - * frame stream with the current frame's start time equal to the - * previous frame's stop time. - */ - if( pv->last_stop != in->start ) + /* H.264 in mp4 (stolen from mp4creator) */ + buf->data[buf->size+0] = ( ( size - 4 ) >> 24 ) & 0xFF; + buf->data[buf->size+1] = ( ( size - 4 ) >> 16 ) & 0xFF; + buf->data[buf->size+2] = ( ( size - 4 ) >> 8 ) & 0xFF; + buf->data[buf->size+3] = ( ( size - 4 ) >> 0 ) & 0xFF; + + /* Decide what type of frame we have. */ + switch( pic_out->i_type ) { - hb_log("encx264 input continuity err: last stop %lld start %lld", - pv->last_stop, in->start); + case X264_TYPE_IDR: + buf->frametype = HB_FRAME_IDR; + /* if we have a chapter marker pending and this + frame's presentation time stamp is at or after + the marker's time stamp, use this as the + chapter start. */ + if( pv->next_chap != 0 && pv->next_chap <= pic_out->i_pts ) + { + pv->next_chap = 0; + buf->new_chap = pv->chap_mark; + } + break; + + case X264_TYPE_I: + buf->frametype = HB_FRAME_I; + break; + + case X264_TYPE_P: + buf->frametype = HB_FRAME_P; + break; + + case X264_TYPE_B: + buf->frametype = HB_FRAME_B; + break; + + /* This is for b-pyramid, which has reference b-frames + However, it doesn't seem to ever be used... */ + case X264_TYPE_BREF: + buf->frametype = HB_FRAME_BREF; + break; + + // If it isn't the above, what type of frame is it?? + default: + buf->frametype = 0; + break; } - pv->last_stop = in->stop; - // Remember info about this frame that we need to pass across - // the x264_encoder_encode call (since it reorders frames). - save_frame_info( pv, in ); + /* Since libx264 doesn't tell us when b-frames are + themselves reference frames, figure it out on our own. */ + if( (buf->frametype == HB_FRAME_B) && + (nal[i].i_ref_idc != NAL_PRIORITY_DISPOSABLE) ) + buf->frametype = HB_FRAME_BREF; - /* Feed the input DTS to x264 so it can figure out proper output PTS */ - pv->pic_in.i_pts = in->start; + buf->size += size; + } + // make sure we found at least one video frame + if ( buf->size <= 0 ) + { + // no video: back up the output time stamp then free the buf + pv->dts_next = buf->start; + hb_buffer_close( &buf ); + } + return buf; +} + +static hb_buffer_t *x264_encode( hb_work_object_t *w, hb_buffer_t *in ) +{ + hb_work_private_t *pv = w->private_data; + hb_job_t *job = pv->job; - x264_encoder_encode( pv->x264, &nal, &i_nal, - &pv->pic_in, &pic_out ); + /* Point x264 at our current buffers Y(UV) data. */ + pv->pic_in.img.plane[0] = in->data; + + if( job->grayscale ) + { + /* XXX x264 has currently no option for grayscale encoding */ + memset( pv->pic_in.img.plane[1], 0x80, job->width * job->height / 4 ); + memset( pv->pic_in.img.plane[2], 0x80, job->width * job->height / 4 ); } else { - x264_encoder_encode( pv->x264, &nal, &i_nal, - NULL, &pic_out ); - /* No more delayed B frames */ - if( i_nal == 0 ) - { - *buf_out = NULL; - return HB_WORK_DONE; - } - else + /* Point x264 at our buffers (Y)UV data */ + pv->pic_in.img.plane[1] = in->data + job->width * job->height; + pv->pic_in.img.plane[2] = in->data + 5 * job->width * job->height / 4; + } + + if( pv->dts_next == -1 ) + { + /* we don't have a start time yet so use the first frame's + * start. All other frame times will be determined by the + * sum of the prior output frame durations in *DTS* order + * (not by the order they arrive here). This timing change is + * essential for VFR with b-frames but a complete nop otherwise. + */ + pv->dts_next = in->start; + } + if( in->new_chap && job->chapter_markers ) + { + /* chapters have to start with an IDR frame so request that this + frame be coded as IDR. Since there may be up to 16 frames + currently buffered in the encoder remember the timestamp so + when this frame finally pops out of the encoder we'll mark + its buffer as the start of a chapter. */ + pv->pic_in.i_type = X264_TYPE_IDR; + if( pv->next_chap == 0 ) { - /* Since we output at least one more frame, drop another empty - one onto our input fifo. We'll keep doing this automatically - until we stop getting frames out of the encoder. */ - hb_fifo_push(w->fifo_in, hb_buffer_init(0)); + pv->next_chap = in->start; + pv->chap_mark = in->new_chap; } + /* don't let 'work_loop' put a chapter mark on the wrong buffer */ + in->new_chap = 0; } - - if( i_nal ) + else { - /* Should be way too large */ - buf = hb_buffer_init( 3 * job->width * job->height / 2 ); - buf->size = 0; - buf->frametype = 0; + pv->pic_in.i_type = X264_TYPE_AUTO; + } + pv->pic_in.i_qpplus1 = 0; - /* Get next DTS value to use */ - int64_t dts_start = pv->dts_next; + /* XXX this is temporary debugging code to check that the upstream + * modules (render & sync) have generated a continuous, self-consistent + * frame stream with the current frame's start time equal to the + * previous frame's stop time. + */ + if( pv->last_stop != in->start ) + { + hb_log("encx264 input continuity err: last stop %lld start %lld", + pv->last_stop, in->start); + } + pv->last_stop = in->stop; - /* compute the stop time based on the original frame's duration */ - int64_t dts_stop = dts_start + get_frame_duration( pv, pic_out.i_pts ); - pv->dts_next = dts_stop; + // Remember info about this frame that we need to pass across + // the x264_encoder_encode call (since it reorders frames). + save_frame_info( pv, in ); - for( i = 0; i < i_nal; i++ ) - { - int size, data; + /* Feed the input DTS to x264 so it can figure out proper output PTS */ + pv->pic_in.i_pts = in->start; - data = buf->alloc - buf->size; - if( ( size = x264_nal_encode( buf->data + buf->size, &data, - 1, &nal[i] ) ) < 1 ) - { - continue; - } + x264_picture_t pic_out; + int i_nal; + x264_nal_t *nal; - if( job->mux & HB_MUX_AVI ) - { - if( nal[i].i_ref_idc == NAL_PRIORITY_HIGHEST ) - { - buf->frametype = HB_FRAME_KEY; - } - buf->size += size; - continue; - } + x264_encoder_encode( pv->x264, &nal, &i_nal, &pv->pic_in, &pic_out ); + if ( i_nal > 0 ) + { + return nal_encode( w, &pic_out, i_nal, nal ); + } + return NULL; +} - /* H.264 in .mp4 */ - switch( buf->data[buf->size+4] & 0x1f ) - { - case 0x7: - case 0x8: - /* SPS, PPS */ - break; - - default: - /* H.264 in mp4 (stolen from mp4creator) */ - buf->data[buf->size+0] = ( ( size - 4 ) >> 24 ) & 0xFF; - buf->data[buf->size+1] = ( ( size - 4 ) >> 16 ) & 0xFF; - buf->data[buf->size+2] = ( ( size - 4 ) >> 8 ) & 0xFF; - buf->data[buf->size+3] = ( ( size - 4 ) >> 0 ) & 0xFF; - switch( pic_out.i_type ) - { - /* Decide what type of frame we have. */ - case X264_TYPE_IDR: - buf->frametype = HB_FRAME_IDR; - /* if we have a chapter marker pending and this - frame's presentation time stamp is at or after - the marker's time stamp, use this as the - chapter start. */ - if( pv->next_chap != 0 && pv->next_chap <= pic_out.i_pts ) - { - pv->next_chap = 0; - buf->new_chap = pv->chap_mark; - } - break; - case X264_TYPE_I: - buf->frametype = HB_FRAME_I; - break; - case X264_TYPE_P: - buf->frametype = HB_FRAME_P; - break; - case X264_TYPE_B: - buf->frametype = HB_FRAME_B; - break; - /* This is for b-pyramid, which has reference b-frames - However, it doesn't seem to ever be used... */ - case X264_TYPE_BREF: - buf->frametype = HB_FRAME_BREF; - break; - /* If it isn't the above, what type of frame is it?? */ - default: - buf->frametype = 0; - } +int encx264Work( 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; - /* Since libx264 doesn't tell us when b-frames are - themselves reference frames, figure it out on our own. */ - if( (buf->frametype == HB_FRAME_B) && (nal[i].i_ref_idc != NAL_PRIORITY_DISPOSABLE) ) - buf->frametype = HB_FRAME_BREF; + if( in->size <= 0 ) + { + // EOF on input. Flush any frames still in the decoder then + // send the eof downstream to tell the muxer we're done. + x264_picture_t pic_out; + int i_nal; + x264_nal_t *nal; + hb_buffer_t *last_buf = NULL; + + while (1) + { + x264_encoder_encode( pv->x264, &nal, &i_nal, NULL, &pic_out ); + if ( i_nal <= 0 ) + break; - /* Store the output presentation time stamp - from x264 for use by muxmp4 in off-setting - b-frames with the CTTS atom. */ - buf->renderOffset = pic_out.i_pts - dts_start + pv->init_delay; - if ( buf->renderOffset < 0 ) - { - if ( dts_start - pic_out.i_pts > pv->max_delay ) - { - pv->max_delay = dts_start - pic_out.i_pts; - hb_log( "encx264: init_delay too small: " - "is %lld need %lld", pv->init_delay, - pv->max_delay ); - } - buf->renderOffset = 0; - } - buf->size += size; - } + hb_buffer_t *buf = nal_encode( w, &pic_out, i_nal, nal ); + if ( last_buf == NULL ) + *buf_out = buf; + else + last_buf->next = buf; + last_buf = buf; } - /* Send out the next dts values */ - buf->start = dts_start; - buf->stop = dts_stop; + // Flushed everything - add the eof to the end of the chain. + if ( last_buf == NULL ) + *buf_out = in; + else + last_buf->next = in; + + *buf_in = NULL; + return HB_WORK_DONE; } + // Not EOF - encode the packet & wrap it in a NAL + if ( pv->init_delay && in->stop - in->start > pv->init_delay ) + { + // This frame's duration is larger than the time allotted for b-frame + // reordering. That means that if it's used as a reference the decoder + // won't be able to move it early enough to render it in correct + // sequence & the playback will have odd jumps & twitches. To make + // sure this doesn't happen we pretend this frame is multiple + // frames, each with duration <= init_delay. Since each of these + // new frames contains the same image the visual effect is identical + // to the original but the resulting stream can now be coded without + // error. We take advantage of the fact that x264 buffers frame + // data internally to feed the same image into the encoder multiple + // times, just changing its start & stop times each time. + int64_t orig_stop = in->stop; + int64_t new_stop = in->start; + hb_buffer_t *last_buf = NULL; + + // We want to spread the new frames uniformly over the total time + // so that we don't end up with a very short frame at the end. + // In the number of pieces calculation we add in init_delay-1 to + // round up but not add an extra piece if the frame duration is + // a multiple of init_delay. The final increment of frame_dur is + // to restore the bits that got truncated by the divide on the + // previous line. If we don't do this we end up with an extra tiny + // frame at the end whose duration is npieces-1. + int64_t frame_dur = orig_stop - new_stop; + int64_t npieces = ( frame_dur + pv->init_delay - 1 ) / pv->init_delay; + frame_dur /= npieces; + ++frame_dur; + + while ( in->start < orig_stop ) + { + new_stop += frame_dur; + if ( new_stop > orig_stop ) + new_stop = orig_stop; + in->stop = new_stop; + hb_buffer_t *buf = x264_encode( w, in ); + if ( last_buf == NULL ) + *buf_out = buf; + else + last_buf->next = buf; + last_buf = buf; + in->start = new_stop; + } + } else - buf = NULL; - - *buf_out = buf; - + { + *buf_out = x264_encode( w, in ); + } return HB_WORK_OK; } diff --git a/libhb/encxvid.c b/libhb/encxvid.c index 17723899d..b509baf6f 100644 --- a/libhb/encxvid.c +++ b/libhb/encxvid.c @@ -149,10 +149,11 @@ int encxvidWork( hb_work_object_t * w, hb_buffer_t ** buf_in, xvid_enc_frame_t frame; hb_buffer_t * in = *buf_in, * buf; - /* If this is the last empty frame, we're done */ - if(!in->data) + if ( in->size <= 0 ) { - *buf_out = NULL; + /* EOF on input - send it downstream & say we're done */ + *buf_out = in; + *buf_in = NULL; return HB_WORK_DONE; } @@ -160,7 +161,6 @@ int encxvidWork( hb_work_object_t * w, hb_buffer_t ** buf_in, buf = hb_buffer_init( 3 * job->width * job->height / 2 ); buf->start = in->start; buf->stop = in->stop; - //buf->chap = in->chap; memset( &frame, 0, sizeof( frame ) ); diff --git a/libhb/muxcommon.c b/libhb/muxcommon.c index b3337b7bb..dcbba1583 100644 --- a/libhb/muxcommon.c +++ b/libhb/muxcommon.c @@ -24,7 +24,7 @@ typedef struct hb_mux_data_t * mux_data; uint64_t frames; uint64_t bytes; - + int eof; } hb_track_t; static hb_track_t * GetTrack( hb_list_t * list, hb_job_t *job ) @@ -37,34 +37,59 @@ static hb_track_t * GetTrack( hb_list_t * list, hb_job_t *job ) for( i = 0; i < hb_list_count( list ); i++ ) { track2 = hb_list_item( list, i ); - buf = hb_fifo_see( track2->fifo ); - if( !buf ) + if ( ! track2->eof ) { - // XXX libmkv uses a very simple minded muxer that will fail if the - // audio & video are out of sync. To keep them in sync we require - // that *all* fifos have a buffer then we take the oldest. - // Unfortunately this means we can hang in a deadlock with the - // reader process filling the fifos. With the current libmkv - // there's no way to avoid occasional deadlocks & we can only - // suggest that users evolve to using mp4s. - if ( job->mux == HB_MUX_MKV ) + buf = hb_fifo_see( track2->fifo ); + if( !buf ) { - return NULL; - } + // XXX the libmkv muxer will produce unplayable files if the + // audio & video are far out of sync. To keep them in sync we require + // that *all* fifos have a buffer then we take the oldest. + // Unfortunately this means we can hang in a deadlock with the + // reader process filling the fifos. + if ( job->mux == HB_MUX_MKV ) + { + return NULL; + } - // To make sure we don't camp on one fifo & prevent the others - // from making progress we take the earliest data of all the - // data that's currently available but we don't care if some - // fifos don't have data. - continue; + // To make sure we don't camp on one fifo & prevent the others + // from making progress we take the earliest data of all the + // data that's currently available but we don't care if some + // fifos don't have data. + continue; + } + if ( buf->size <= 0 ) + { + // EOF - mark this track as done + buf = hb_fifo_get( track2->fifo ); + hb_buffer_close( &buf ); + track2->eof = 1; + continue; + } + if( !track || buf->start < pts ) + { + track = track2; + pts = buf->start; + } } - if( !track || buf->start < pts ) + } + return track; +} + +static int AllTracksDone( hb_list_t * list ) +{ + hb_track_t * track; + int i; + + for( i = 0; i < hb_list_count( list ); i++ ) + { + track = hb_list_item( list, i ); + if ( track->eof == 0 ) { - track = track2; - pts = buf->start; + return 0; } } - return track; + return 1; } static void MuxerFunc( void * _mux ) @@ -101,34 +126,6 @@ static void MuxerFunc( void * _mux ) } } - /* Wait for one buffer for each track */ - while( !*job->die && !job->done ) - { - int ready; - - ready = 1; - if( !hb_fifo_size( job->fifo_mpeg4 ) ) - { - ready = 0; - } - for( i = 0; i < hb_list_count( title->list_audio ); i++ ) - { - audio = hb_list_item( title->list_audio, i ); - if( !hb_fifo_size( audio->priv.fifo_out ) ) - { - ready = 0; - break; - } - } - - if( ready ) - { - break; - } - - hb_snooze( 50 ); - } - /* Create file, write headers */ if( job->pass == 0 || job->pass == 2 ) { @@ -153,10 +150,15 @@ static void MuxerFunc( void * _mux ) } int thread_sleep_interval = 50; - while( !*job->die && !job->done ) + while( !*job->die ) { if( !( track = GetTrack( list, job ) ) ) { + if ( AllTracksDone( list ) ) + { + // all our input fifos have signaled EOF + break; + } hb_snooze( thread_sleep_interval ); continue; } diff --git a/libhb/muxmp4.c b/libhb/muxmp4.c index db0c9b716..b61d579c8 100644 --- a/libhb/muxmp4.c +++ b/libhb/muxmp4.c @@ -285,9 +285,6 @@ static int MP4Init( hb_mux_object_t * m ) MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.width", job->width * (width / height)); } - /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */ - MP4TrackId firstAudioTrack = 0; - /* add the audio tracks */ for( i = 0; i < hb_list_count( title->list_audio ); i++ ) { @@ -344,15 +341,10 @@ static int MP4Init( hb_mux_object_t * m ) /* If we ever upgrade mpeg4ip, the line above should be replaced with the line below.*/ // MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (u_int16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown)); - /* store a reference to the first audio track, - so we can use it to feed the chapter text track's sample rate */ if (i == 0) { - firstAudioTrack = mux_data->track; - /* Enable the first audio track */ MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE)); } - else /* Disable the other audio tracks so QuickTime doesn't play them all at once. */ @@ -365,14 +357,17 @@ static int MP4Init( hb_mux_object_t * m ) if (job->chapter_markers) { - /* add a text track for the chapters */ - MP4TrackId textTrack; - textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack); + /* add a text track for the chapters. We add the 'chap' atom to track + one which is usually the video track & should never be disabled. + The Quicktime spec says it doesn't matter which media track the + chap atom is on but it has to be an enabled track. */ + MP4TrackId textTrack; + textTrack = MP4AddChapterTextTrack(m->file, 1); m->chapter_track = textTrack; m->chapter_duration = 0; m->current_chapter = job->chapter_start; - } + } /* Add encoded-by metadata listing version and build date */ char *tool_string; @@ -501,30 +496,21 @@ static int MP4End( hb_mux_object_t * m ) if( m->job->chapter_markers ) { int64_t duration = m->sum_dur - m->chapter_duration; - if ( duration <= 0 ) + /* The final chapter can have a very short duration - if it's less + * than a second just skip it. */ + if ( duration >= m->samplerate ) { - /* The initial & final chapters can have very short durations - * (less than the error in our total duration estimate) so - * the duration calc above can result in a negative number. - * when this happens give the chapter a short duration (1/3 - * of an ntsc frame time). */ - duration = 1000 * m->samplerate / 90000; - } - - struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, duration, - m->current_chapter + 1 ); - if( !MP4WriteSample(m->file, - m->chapter_track, - sample->sample, - sample->length, - sample->duration, - 0, true) ) - { - hb_error("Failed to write to output file, disk full?"); - *job->die = 1; + struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, duration, + m->current_chapter + 1 ); + if( ! MP4WriteSample(m->file, m->chapter_track, sample->sample, + sample->length, sample->duration, 0, true) ) + { + hb_error("Failed to write to output file, disk full?"); + *job->die = 1; + } + free(sample); } - free(sample); } if (job->areBframes) diff --git a/libhb/reader.c b/libhb/reader.c index d789993f7..b06816d3d 100644 --- a/libhb/reader.c +++ b/libhb/reader.c @@ -69,7 +69,7 @@ static void push_buf( const 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 + * Loop until the incoming fifo is ready to receive * this buffer. */ hb_snooze( 50 ); @@ -326,8 +326,16 @@ static void ReaderFunc( void * _r ) } } - /* send an empty buffer upstream to signal we're done */ - hb_fifo_push( r->job->fifo_mpeg2, hb_buffer_init(0) ); + /* send empty buffers upstream to video & audio decoders to signal we're done */ + push_buf( r, r->job->fifo_mpeg2, hb_buffer_init(0) ); + + hb_audio_t *audio; + for( n = 0; + ( audio = hb_list_item( r->job->title->list_audio, n ) ) != NULL; + ++n ) + { + push_buf( r, audio->priv.fifo_in, hb_buffer_init(0) ); + } hb_list_empty( &list ); hb_buffer_close( &ps ); diff --git a/libhb/sync.c b/libhb/sync.c index f51e0e130..7f0ac86c4 100644 --- a/libhb/sync.c +++ b/libhb/sync.c @@ -23,7 +23,6 @@ typedef struct int64_t next_start; /* start time of next output frame */ int64_t next_pts; /* start time of next input frame */ - int64_t start_silence; /* if we're inserting silence, the time we started */ int64_t first_drop; /* PTS of first 'went backwards' frame dropped */ int drop_count; /* count of 'time went backwards' drops */ @@ -72,7 +71,6 @@ struct hb_work_private_s static void InitAudio( hb_work_object_t * w, int i ); static int SyncVideo( hb_work_object_t * w ); static void SyncAudio( hb_work_object_t * w, int i ); -static int NeedSilence( hb_work_object_t * w, hb_audio_t *, int i ); static void InsertSilence( hb_work_object_t * w, int i, int64_t d ); static void UpdateState( hb_work_object_t * w ); @@ -151,13 +149,6 @@ void syncClose( hb_work_object_t * w ) for( i = 0; i < hb_list_count( title->list_audio ); i++ ) { - if ( pv->sync_audio[i].start_silence ) - { - hb_log( "sync: added %d ms of silence to audio %d", - (int)((pv->sync_audio[i].next_pts - - pv->sync_audio[i].start_silence) / 90), i ); - } - audio = hb_list_item( title->list_audio, i ); if( audio->config.out.codec == HB_ACODEC_AC3 ) { @@ -727,6 +718,13 @@ static void SyncAudio( hb_work_object_t * w, int i ) while( !hb_fifo_is_full( fifo ) && ( buf = hb_fifo_see( audio->priv.fifo_raw ) ) ) { + /* if the next buffer is an eof send it downstream */ + if ( buf->size <= 0 ) + { + buf = hb_fifo_get( audio->priv.fifo_raw ); + hb_fifo_push( fifo, buf ); + return; + } if ( (int64_t)( buf->start - sync->next_pts ) < 0 ) { // audio time went backwards. @@ -780,43 +778,6 @@ static void SyncAudio( hb_work_object_t * w, int i ) buf = hb_fifo_get( audio->priv.fifo_raw ); OutputAudioFrame( job, audio, buf, sync, fifo, i ); } - - if( NeedSilence( w, audio, i ) ) - { - InsertSilence( w, i, (90000 * AC3_SAMPLES_PER_FRAME) / - sync->audio->config.in.samplerate ); - } -} - -static int NeedSilence( hb_work_object_t * w, hb_audio_t * audio, int i ) -{ - hb_work_private_t * pv = w->private_data; - hb_job_t * job = pv->job; - hb_sync_audio_t * sync = &pv->sync_audio[i]; - - if( hb_fifo_size( audio->priv.fifo_in ) || - hb_fifo_size( audio->priv.fifo_raw ) || - hb_fifo_size( audio->priv.fifo_sync ) || - hb_fifo_size( audio->priv.fifo_out ) ) - { - /* We have some audio, we are fine */ - return 0; - } - - /* No audio left in fifos */ - - if( hb_thread_has_exited( job->reader ) ) - { - /* We might miss some audio to complete encoding and muxing - the video track */ - if ( sync->start_silence == 0 ) - { - hb_log("sync: reader has exited, adding silence to audio %d", i); - sync->start_silence = sync->next_pts; - } - return 1; - } - return 0; } static void InsertSilence( hb_work_object_t * w, int i, int64_t duration ) diff --git a/libhb/work.c b/libhb/work.c index a6071de4b..243b6b2e7 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -265,7 +265,7 @@ static void do_job( hb_job_t * job, int cpu_count ) } hb_log (" + PixelRatio: %d, width:%d, height: %d",job->pixel_ratio,job->width, job->height); - job->fifo_mpeg2 = hb_fifo_init( 2048 ); + job->fifo_mpeg2 = hb_fifo_init( 128 ); job->fifo_raw = hb_fifo_init( FIFO_CPU_MULT * cpu_count ); job->fifo_sync = hb_fifo_init( FIFO_CPU_MULT * cpu_count ); job->fifo_render = hb_fifo_init( FIFO_CPU_MULT * cpu_count ); @@ -585,9 +585,9 @@ static void do_job( hb_job_t * job, int cpu_count ) audio->priv.config.vorbis.language = audio->config.lang.simple; /* set up the audio work structures */ - audio->priv.fifo_in = hb_fifo_init( 256 ); + audio->priv.fifo_in = hb_fifo_init( 32 ); audio->priv.fifo_raw = hb_fifo_init( FIFO_CPU_MULT * cpu_count ); - audio->priv.fifo_sync = hb_fifo_init( 256 ); + audio->priv.fifo_sync = hb_fifo_init( 32 ); audio->priv.fifo_out = hb_fifo_init( FIFO_CPU_MULT * cpu_count ); @@ -638,7 +638,6 @@ static void do_job( hb_job_t * job, int cpu_count ) job->reader = hb_reader_init( job ); hb_log( " + output: %s", job->file ); - job->muxer = hb_muxer_init( job ); job->done = 0; @@ -658,6 +657,10 @@ static void do_job( hb_job_t * job, int cpu_count ) HB_LOW_PRIORITY ); } + // The muxer requires track information that's set up by the encoder + // init routines so we have to init the muxer last. + job->muxer = hb_muxer_init( job ); + done = 0; w = hb_list_item( job->list_work, 0 ); w->thread_sleep_interval = 50; |