AVPicture pic_tmp_out;
hb_buffer_t * buf_scale;
hb_fifo_t * subtitle_queue;
+ hb_fifo_t * delay_queue;
+ int frames_to_extend;
+ int dropped_frames;
+ int extended_frames;
+ uint64_t last_start[4];
+ uint64_t last_stop[4];
};
int renderInit( hb_work_object_t *, hb_job_t * );
{
hb_buffer_t * sub = *_sub;
hb_title_t * title = job->title;
- int i, j, offset_top, offset_left;
+ int i, j, offset_top, offset_left, margin_top, margin_percent;
uint8_t * lum, * alpha, * out, * sub_chromaU, * sub_chromaV;
+ /*
+ * Percent of height of picture that form a margin that subtitles
+ * should not be displayed within.
+ */
+ margin_percent = 2;
+
if( !sub )
{
return;
}
+
+ /*
+ * If necessary, move the subtitle so it is not in a cropped zone.
+ * When it won't fit, we center it so we lose as much on both ends.
+ * Otherwise we try to leave a 20px or 2% margin around it.
+ */
+ margin_top = ( ( title->height - job->crop[0] - job->crop[1] ) *
+ margin_percent ) / 100;
- /* If necessary, move the subtitle so it is not in a cropped zone.
- When it won't fit, we center it so we loose as much on both ends.
- Otherwise we try to leave a 20px margin around it. */
+ if( margin_top > 20 )
+ {
+ /*
+ * A maximum margin of 20px regardless of height of the picture.
+ */
+ margin_top = 20;
+ }
- if( sub->height > title->height - job->crop[0] - job->crop[1] - 40 )
+ if( sub->height > title->height - job->crop[0] - job->crop[1] -
+ ( margin_top * 2 ) )
+ {
+ /*
+ * The subtitle won't fit in the cropped zone, so center
+ * it vertically so we fit in as much as we can.
+ */
offset_top = job->crop[0] + ( title->height - job->crop[0] -
- job->crop[1] - sub->height ) / 2;
- else if( sub->y < job->crop[0] + 20 )
- offset_top = job->crop[0] + 20;
- else if( sub->y > title->height - job->crop[1] - 20 - sub->height )
- offset_top = title->height - job->crop[1] - 20 - sub->height;
+ job->crop[1] - sub->height ) / 2;
+ }
+ else if( sub->y < job->crop[0] + margin_top )
+ {
+ /*
+ * The subtitle fits in the cropped zone, but is currently positioned
+ * within our top margin, so move it outside of our margin.
+ */
+ offset_top = job->crop[0] + margin_top;
+ }
+ else if( sub->y > title->height - job->crop[1] - margin_top - sub->height )
+ {
+ /*
+ * The subtitle fits in the cropped zone, and is not within the top
+ * margin but is within the bottom margin, so move it to be above
+ * the margin.
+ */
+ offset_top = title->height - job->crop[1] - margin_top - sub->height;
+ }
else
+ {
+ /*
+ * The subtitle is fine where it is.
+ */
offset_top = sub->y;
+ }
if( sub->width > title->width - job->crop[2] - job->crop[3] - 40 )
offset_left = job->crop[2] + ( title->width - job->crop[2] -
hb_job_t * job = pv->job;
hb_title_t * title = job->title;
hb_buffer_t * in = *buf_in, * buf_tmp_in = *buf_in;
+ hb_buffer_t * ivtc_buffer = NULL;
if(!in->data)
{
- /* If the input buffer is end of stream, send out an empty one to the next stage as well. */
+ /* If the input buffer is end of stream, send out an empty one
+ * to the next stage as well. Note that this will result in us
+ * losing the current contents of the delay queue.
+ */
*buf_out = hb_buffer_init(0);
return HB_WORK_OK;
}
/* Setup render buffer */
hb_buffer_t * buf_render = hb_buffer_init( 3 * job->width * job->height / 2 );
-
+
+ /* Cache frame start and stop times, so we can renumber
+ time stamps if dropping frames for VFR. */
+ int i;
+ for( i = 3; i >= 1; i-- )
+ {
+ pv->last_start[i] = pv->last_start[i-1];
+ pv->last_stop[i] = pv->last_stop[i-1];
+ }
+ pv->last_start[0] = in->start;
+ pv->last_stop[0] = in->stop;
+
/* Apply filters */
if( job->filters )
{
}
else if( result == FILTER_DROP )
{
- hb_fifo_get( pv->subtitle_queue );
- buf_tmp_in = NULL;
+ if( job->vfr )
+ {
+ pv->frames_to_extend += 4;
+ pv->dropped_frames++;
+ hb_fifo_get( pv->subtitle_queue );
+ buf_tmp_in = NULL;
+ }
+ else
+ {
+ buf_tmp_in = buf_tmp_out;
+ }
break;
}
}
memcpy( buf_render->data, buf_tmp_in->data, buf_render->size );
hb_buffer_copy_settings( buf_render, buf_tmp_in );
}
+
+ if (*buf_out)
+ {
+ hb_fifo_push( pv->delay_queue, *buf_out );
+ *buf_out = NULL;
+ }
+
+ /*
+ * Keep the last three frames in our queue, this ensures that we have the last
+ * two always in there should we need to rewrite the durations on them.
+ */
+ if( hb_fifo_size( pv->delay_queue ) >= 3 )
+ {
+ *buf_out = hb_fifo_get( pv->delay_queue );
+ }
+
+ if( *buf_out )
+ {
+ if( pv->frames_to_extend )
+ {
+ /*
+ * A frame's been dropped by VFR detelecine.
+ * Gotta make up the lost time. This will also
+ * slow down the video to 23.976fps.
+ * The dropped frame ran for 3003 ticks, so
+ * divvy it up amongst the 4 frames left behind.
+ * This is what the delay_queue is for;
+ * telecined sequences start 2 frames before
+ * the dropped frame, so to slow down the right
+ * ones you need a 2 frame delay between
+ * reading input and writing output.
+ */
+ ivtc_buffer = *buf_out;
+
+ /* The 4th cached frame will be the to use. */
+ ivtc_buffer->start = pv->last_start[3];
+ ivtc_buffer->stop = pv->last_stop[3];
+
+ if (pv->frames_to_extend % 4)
+ ivtc_buffer->stop += 751;
+ else
+ ivtc_buffer->stop += 750;
+
+ /* Set the 3rd cached frame to start when this one stops,
+ and to stop 3003 ticks later -- a normal 29.97fps
+ length frame. If it needs to be extended as well to
+ make up lost time, it'll be handled on the next
+ loop through the renderer. */
+ pv->last_start[2] = ivtc_buffer->stop;
+ pv->last_stop[2] = ivtc_buffer->stop + 3003;
+
+ pv->frames_to_extend--;
+ pv->extended_frames++;
+ }
+
+ }
return HB_WORK_OK;
}
{
hb_work_private_t * pv = w->private_data;
+ hb_log("render: dropped frames: %i (%i ticks)", pv->dropped_frames, (pv->dropped_frames * 3003) );
+ hb_log("render: extended frames: %i (%i ticks)", pv->extended_frames, ( ( pv->extended_frames / 4 ) * 3003 ) );
+ hb_log("render: Lost time: %i frames (%i ticks)", (pv->dropped_frames * 4) - (pv->extended_frames), (pv->dropped_frames * 3003) - ( ( pv->extended_frames / 4 ) * 3003 ) );
+
/* Cleanup subtitle queue */
if( pv->subtitle_queue )
{
hb_fifo_close( &pv->subtitle_queue );
}
+ if( pv->delay_queue )
+ {
+ hb_fifo_close( &pv->delay_queue );
+ }
+
/* Cleanup filters */
/* TODO: Move to work.c? */
if( pv->job->filters )
title->height - (job->crop[0] + job->crop[1]),
PIX_FMT_YUV420P,
job->width, job->height, PIX_FMT_YUV420P,
- SWS_LANCZOS|SWS_ACCURATE_RND, NULL, NULL, NULL);
+ (uint16_t)(SWS_LANCZOS|SWS_ACCURATE_RND), NULL, NULL, NULL);
}
/* Setup FIFO queue for subtitle cache */
pv->subtitle_queue = hb_fifo_init( 8 );
+ pv->delay_queue = hb_fifo_init( 8 );
+ pv->frames_to_extend = 0;
+ pv->dropped_frames = 0;
+ pv->extended_frames = 0;
+ pv->last_start[0] = 0;
+ pv->last_stop[0] = 0;
/* Setup filters */
/* TODO: Move to work.c? */