Index: libhb/reader.c =================================================================== --- libhb/reader.c (revision 1573) +++ libhb/reader.c (working copy) @@ -69,7 +69,7 @@ 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,9 +326,17 @@ } } - /* 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 ); if (r->dvd) Index: libhb/deca52.c =================================================================== --- libhb/deca52.c (revision 1573) +++ libhb/deca52.c (working copy) @@ -144,11 +144,16 @@ 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; Index: libhb/enclame.c =================================================================== --- libhb/enclame.c (revision 1573) +++ libhb/enclame.c (working copy) @@ -140,6 +140,14 @@ 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; Index: libhb/decsub.c =================================================================== --- libhb/decsub.c (revision 1573) +++ libhb/decsub.c (working copy) @@ -51,9 +51,16 @@ { 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]; Index: libhb/encx264.c =================================================================== --- libhb/encx264.c (revision 1573) +++ libhb/encx264.c (working copy) @@ -30,16 +30,16 @@ * 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 @@ 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; + hb_buffer_t *buf = NULL; + hb_work_private_t *pv = w->private_data; + hb_job_t *job = pv->job; - if( in->data ) + /* 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 ) + if ( dts_start - pic_out->i_pts > pv->max_delay ) { - /* 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 ); + 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 ); } - else + buf->renderOffset = 0; + } + + /* 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 ) { - /* - * 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; + continue; } - if( pv->dts_next == -1 ) + if( job->mux & HB_MUX_AVI ) { - /* 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 ) + 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; +} - x264_encoder_encode( pv->x264, &nal, &i_nal, - &pv->pic_in, &pic_out ); +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; + + /* 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 ) + /* 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 ) { - *buf_out = NULL; - return HB_WORK_DONE; + pv->next_chap = in->start; + pv->chap_mark = in->new_chap; } - else - { - /* 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)); - } + /* don't let 'work_loop' put a chapter mark on the wrong buffer */ + in->new_chap = 0; } + else + { + pv->pic_in.i_type = X264_TYPE_AUTO; + } + pv->pic_in.i_qpplus1 = 0; - if( i_nal ) + /* 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 ) { - /* Should be way too large */ - buf = hb_buffer_init( 3 * job->width * job->height / 2 ); - buf->size = 0; - buf->frametype = 0; + hb_log("encx264 input continuity err: last stop %lld start %lld", + pv->last_stop, in->start); + } + pv->last_stop = in->stop; - /* Get next DTS value to use */ - int64_t dts_start = pv->dts_next; + // 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 ); - /* 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; + /* Feed the input DTS to x264 so it can figure out proper output PTS */ + pv->pic_in.i_pts = in->start; - for( i = 0; i < i_nal; i++ ) - { - int size, data; + x264_picture_t pic_out; + int i_nal; + x264_nal_t *nal; - data = buf->alloc - buf->size; - if( ( size = x264_nal_encode( buf->data + buf->size, &data, - 1, &nal[i] ) ) < 1 ) - { - 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; +} - if( job->mux & HB_MUX_AVI ) - { - if( nal[i].i_ref_idc == NAL_PRIORITY_HIGHEST ) - { - buf->frametype = HB_FRAME_KEY; - } - buf->size += size; - continue; - } +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; - /* H.264 in .mp4 */ - switch( buf->data[buf->size+4] & 0x1f ) - { - case 0x7: - case 0x8: - /* SPS, PPS */ - break; + 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; - 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; - } + while (1) + { + x264_encoder_encode( pv->x264, &nal, &i_nal, NULL, &pic_out ); + if ( i_nal <= 0 ) + break; - /* 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; + 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; + } + // Flushed everything - add the eof to the end of the chain. + if ( last_buf == NULL ) + *buf_out = in; + else + last_buf->next = in; - /* 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; - } - } - /* Send out the next dts values */ - buf->start = dts_start; - buf->stop = dts_stop; + *buf_in = NULL; + return HB_WORK_DONE; } - else - buf = NULL; + // 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; - *buf_out = buf; + // 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_out = x264_encode( w, in ); + } return HB_WORK_OK; } Index: libhb/encavcodec.c =================================================================== --- libhb/encavcodec.c (revision 1573) +++ libhb/encavcodec.c (working copy) @@ -191,9 +191,11 @@ 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; } Index: libhb/muxmp4.c =================================================================== --- libhb/muxmp4.c (revision 1573) +++ libhb/muxmp4.c (working copy) @@ -285,9 +285,6 @@ 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 @@ /* 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 @@ 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 @@ 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) Index: libhb/encxvid.c =================================================================== --- libhb/encxvid.c (revision 1573) +++ libhb/encxvid.c (working copy) @@ -149,10 +149,11 @@ 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 @@ 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 ) ); Index: libhb/decdca.c =================================================================== --- libhb/decdca.c (revision 1573) +++ libhb/decdca.c (working copy) @@ -113,6 +113,14 @@ 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; Index: libhb/declpcm.c =================================================================== --- libhb/declpcm.c (revision 1573) +++ libhb/declpcm.c (working copy) @@ -133,14 +133,15 @@ 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; Index: libhb/enctheora.c =================================================================== --- libhb/enctheora.c (revision 1573) +++ libhb/enctheora.c (working copy) @@ -122,23 +122,22 @@ 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 @@ 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)); Index: libhb/sync.c =================================================================== --- libhb/sync.c (revision 1573) +++ libhb/sync.c (working copy) @@ -23,7 +23,6 @@ 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 @@ 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 @@ 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 @@ 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,45 +778,8 @@ 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 ) { hb_work_private_t * pv = w->private_data; Index: libhb/encfaac.c =================================================================== --- libhb/encfaac.c (revision 1573) +++ libhb/encfaac.c (working copy) @@ -16,12 +16,11 @@ 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 @@ /* 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 @@ free( bytes ); pv->list = hb_list_init(); - pv->pts = -1; return 0; } @@ -128,6 +129,7 @@ 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 @@ 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 @@ 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; + // 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 ) + { + 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; + } + return NULL; +} - if( !buf->size ) +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 ) { - /* Encoding was successful but we got no data. Try to encode - more */ - hb_buffer_close( &buf ); - return Encode( w ); + hb_buffer_t *tmp = hb_buffer_init( pad ); + memset( tmp->data, 0, pad ); + hb_list_add( pv->list, tmp ); } - else if( buf->size < 0 ) + + // 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_log( "faacEncEncode failed" ); - hb_buffer_close( &buf ); - return NULL; + hb_buffer_t *b = Encode( w ); + if ( b ) + { + if ( bufout == NULL ) + { + bufout = b; + } + else + { + buf->next = b; + } + buf = b; + } } - - return buf; + // add the eof marker to the end of our buf chain + buf->next = bufin; + return bufout; } /*********************************************************************** @@ -189,6 +221,16 @@ 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; Index: libhb/decavcodec.c =================================================================== --- libhb/decavcodec.c (revision 1573) +++ libhb/decavcodec.c (working copy) @@ -164,6 +164,14 @@ 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 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 ) { Index: libhb/muxcommon.c =================================================================== --- libhb/muxcommon.c (revision 1573) +++ libhb/muxcommon.c (working copy) @@ -24,7 +24,7 @@ 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 ) @@ -40,8 +40,8 @@ buf = hb_fifo_see( track2->fifo ); if( !buf ) { - // 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 + // 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. With the current libmkv @@ -58,6 +58,14 @@ // 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; @@ -67,6 +75,22 @@ 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 ) + { + return 0; + } + } + return 1; +} + static void MuxerFunc( void * _mux ) { hb_mux_t * mux = _mux; @@ -101,34 +125,6 @@ } } - /* 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 +149,15 @@ } 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; } Index: libhb/work.c =================================================================== --- libhb/work.c (revision 1573) +++ libhb/work.c (working copy) @@ -265,7 +265,7 @@ } 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 @@ 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 @@ 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 @@ 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; Index: libhb/encvorbis.c =================================================================== --- libhb/encvorbis.c (revision 1573) +++ libhb/encvorbis.c (working copy) @@ -252,6 +252,14 @@ 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; Index: contrib/Jamfile =================================================================== --- contrib/Jamfile (revision 1573) +++ contrib/Jamfile (working copy) @@ -387,6 +387,7 @@ cd `dirname $(>)` && CONTRIB=`pwd` && rm -rf libtheora && (gzip -dc libtheora.tar.gz | tar xf - ) && cd libtheora && ./configure --prefix=$CONTRIB --with-ogg=$CONTRIB --with-vorbis=$CONTRIB --cache-file=$CONTRIB/config.cache --disable-shared \ + --disable-asm \ --disable-oggtest --disable-vorbistest --disable-sdltest --disable-examples && $(MAKE) && $(MAKE) install && $(STRIP) $CONTRIB/lib/libtheora.a