summaryrefslogtreecommitdiffstats
path: root/libhb/encx264.c
diff options
context:
space:
mode:
authorjbrjake <[email protected]>2007-04-16 16:58:57 +0000
committerjbrjake <[email protected]>2007-04-16 16:58:57 +0000
commit1b38e1511ba75646540f6d2b90b21713614de392 (patch)
treed82a8a528f2c1d6201167d26e5615b5a23b0d145 /libhb/encx264.c
parenta715de4c2ff593e121fbfa394409eff4e493415b (diff)
Much better B-frame muxing frame-reordering. This will preserve the sps/pps info, properly offset the first frame, and flush any remaining frames at the end of the encode.
Patch by: Nyx git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@513 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb/encx264.c')
-rw-r--r--libhb/encx264.c223
1 files changed, 145 insertions, 78 deletions
diff --git a/libhb/encx264.c b/libhb/encx264.c
index a67225b01..e1961b5e9 100644
--- a/libhb/encx264.c
+++ b/libhb/encx264.c
@@ -23,12 +23,22 @@ hb_work_object_t hb_encx264 =
encx264Close
};
+// 16 is probably overkill but it's also the maximum for h.264 reference frames
+#define MAX_INFLIGHT_FRAMES 16
+
struct hb_work_private_s
{
hb_job_t * job;
x264_t * x264;
x264_picture_t pic_in;
+ // Internal queue of DTS start/stop values.
+ int64_t dts_start[MAX_INFLIGHT_FRAMES];
+ int64_t dts_stop[MAX_INFLIGHT_FRAMES];
+
+ int64_t dts_write_index;
+ int64_t dts_read_index;
+
char filename[1024];
};
@@ -215,6 +225,9 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
x264_picture_alloc( &pv->pic_in, X264_CSP_I420,
job->width, job->height );
+ pv->dts_write_index = 0;
+ pv->dts_read_index = 0;
+
return 0;
}
@@ -240,31 +253,57 @@ int encx264Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
x264_nal_t * nal;
int i;
- /* XXX avoid this memcpy ? */
- memcpy( pv->pic_in.img.plane[0], in->data, job->width * job->height );
- 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 (in->data)
{
- memcpy( pv->pic_in.img.plane[1], in->data + job->width * job->height,
- job->width * job->height / 4 );
- memcpy( pv->pic_in.img.plane[2], in->data + 5 * job->width *
- job->height / 4, job->width * job->height / 4 );
- }
+ /* XXX avoid this memcpy ? */
+ memcpy( pv->pic_in.img.plane[0], in->data, job->width * job->height );
+ 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
+ {
+ memcpy( pv->pic_in.img.plane[1], in->data + job->width * job->height,
+ job->width * job->height / 4 );
+ memcpy( pv->pic_in.img.plane[2], in->data + 5 * job->width *
+ job->height / 4, job->width * job->height / 4 );
+ }
- pv->pic_in.i_type = X264_TYPE_AUTO;
- pv->pic_in.i_qpplus1 = 0;
+ pv->pic_in.i_type = X264_TYPE_AUTO;
+ pv->pic_in.i_qpplus1 = 0;
- /* Feed the input DTS to x264 so it can figure out proper output PTS */
- pv->pic_in.i_pts = in->start;
+ // Remember current PTS value, use as DTS later
+ pv->dts_start[pv->dts_write_index & (MAX_INFLIGHT_FRAMES-1)] = in->start;
+ pv->dts_stop[pv->dts_write_index & (MAX_INFLIGHT_FRAMES-1)] = in->stop;
+ pv->dts_write_index++;
- x264_encoder_encode( pv->x264, &nal, &i_nal,
- &pv->pic_in, &pic_out );
+ /* Feed the input DTS to x264 so it can figure out proper output PTS */
+ pv->pic_in.i_pts = in->start;
+ x264_encoder_encode( pv->x264, &nal, &i_nal,
+ &pv->pic_in, &pic_out );
+ }
+ 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
+ {
+ /* 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));
+ }
+ }
+
/* Should be way too large */
buf = hb_buffer_init( 3 * job->width * job->height / 2 );
buf->size = 0;
@@ -272,76 +311,104 @@ int encx264Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
buf->stop = in->stop;
buf->key = 0;
- for( i = 0; i < i_nal; i++ )
+ if (i_nal)
{
- int size, data;
-
- data = buf->alloc - buf->size;
- if( ( size = x264_nal_encode( buf->data + buf->size, &data,
+ /* Should be way too large */
+ buf = hb_buffer_init( 3 * job->width * job->height / 2 );
+ buf->size = 0;
+ buf->start = in->start;
+ buf->stop = in->stop;
+ buf->key = 0;
+
+ int64_t dts_start, dts_stop;
+
+ // Get next DTS value to use
+ dts_start = pv->dts_start[pv->dts_read_index & (MAX_INFLIGHT_FRAMES-1)];
+ dts_stop = pv->dts_stop[pv->dts_read_index & (MAX_INFLIGHT_FRAMES-1)];
+ pv->dts_read_index++;
+
+ for( i = 0; i < i_nal; i++ )
+ {
+ int size, data;
+
+ data = buf->alloc - buf->size;
+ if( ( size = x264_nal_encode( buf->data + buf->size, &data,
1, &nal[i] ) ) < 1 )
- {
- continue;
- }
+ {
+ continue;
+ }
- if( job->mux & HB_MUX_AVI )
- {
- if( nal[i].i_ref_idc == NAL_PRIORITY_HIGHEST )
+ if( job->mux & HB_MUX_AVI )
{
- buf->key = 1;
+ if( nal[i].i_ref_idc == NAL_PRIORITY_HIGHEST )
+ {
+ buf->key = 1;
+ }
+ buf->size += size;
+ continue;
}
- buf->size += size;
- continue;
- }
- /* 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;
-
- /* For IDR (key frames), buf->key = 1,
- and the same for regular I-frames. */
- if( (pic_out.i_type == X264_TYPE_IDR) || (pic_out.i_type == X264_TYPE_I) )
- {
- buf->key = 1;
- }
- /* For B-frames, buf->key = 2 */
- else if( (pic_out.i_type == X264_TYPE_B) )
- {
- buf->key = 2;
- }
- /* This is for b-pyramid, which has reference b-frames
- However, it doesn't seem to ever be used...
- They just show up as buf->key == 2 like
- regular b-frames. */
- else if( (pic_out.i_type == X264_TYPE_BREF) )
- {
- buf->key = 3;
- }
- /* For P-frames, buf->key = 0 */
- else
- {
- buf->key = 0;
- }
+ /* 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;
+
+ /* For IDR (key frames), buf->key = 1,
+ and the same for regular I-frames. */
+ if( (pic_out.i_type == X264_TYPE_IDR) || (pic_out.i_type == X264_TYPE_I) )
+ {
+ buf->key = 1;
+ }
+ /* For B-frames, buf->key = 2 */
+ else if( (pic_out.i_type == X264_TYPE_B) )
+ {
+ buf->key = 2;
+ }
+ /* This is for b-pyramid, which has reference b-frames
+ However, it doesn't seem to ever be used...
+ They just show up as buf->key == 2 like
+ regular b-frames. */
+ else if( (pic_out.i_type == X264_TYPE_BREF) )
+ {
+ buf->key = 3;
+ }
+ /* For P-frames, buf->key = 0 */
+ else
+ {
+ buf->key = 0;
+ }
- /* Store the output presentation time stamp
- from x264 for use by muxmp4 in off-setting
- b-frames with the CTTS atom. */
- buf->encodedPTS = pic_out.i_pts;
+ /* Store the output presentation time stamp
+ from x264 for use by muxmp4 in off-setting
+ b-frames with the CTTS atom. */
+ /* For now, just add 1000000 to the offset so that the
+ value is pretty much guaranteed to be positive. The
+ muxing code will minimize the renderOffsets at the end. */
+
+ buf->renderOffset = pic_out.i_pts - dts_start + 1000000;
+
+ /* Send out the next dts values */
+ buf->start = dts_start;
+ buf->stop = dts_stop;
buf->size += size;
+ }
}
}
+ else
+ buf = NULL;
+
*buf_out = buf;
return HB_WORK_OK;