1 /* $Id: render.c,v 1.17 2005/04/14 17:37:54 titer Exp $
3 This file is part of the HandBrake source code.
4 Homepage: <http://handbrake.m0k.org/>.
5 It may be used under the terms of the GNU General Public License. */
9 #include "ffmpeg/avcodec.h"
10 #include "ffmpeg/swscale.h"
12 struct hb_work_private_s
16 struct SwsContext * context;
18 AVPicture pic_tmp_crop;
19 AVPicture pic_tmp_out;
20 hb_buffer_t * buf_scale;
21 hb_fifo_t * subtitle_queue;
22 hb_fifo_t * delay_queue;
26 uint64_t last_start[4];
27 uint64_t last_stop[4];
30 int renderInit( hb_work_object_t *, hb_job_t * );
31 int renderWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
32 void renderClose( hb_work_object_t * );
34 hb_work_object_t hb_render =
46 * Utility function that finds where the U is in the YUV sub-picture
48 * The Y data is at the top, followed by U and V, but the U and V
49 * are half the width of the Y, i.e. each chroma element covers 2x2
52 static uint8_t *getU(uint8_t *data, int width, int height, int x, int y)
54 return(&data[(((y/2) * (width/2)) + (x/2)) + (width*height)]);
57 static uint8_t *getV(uint8_t *data, int width, int height, int x, int y)
59 return(&data[(((y/2) * (width/2)) + (x/2)) + (width*height) +
63 static void ApplySub( hb_job_t * job, hb_buffer_t * buf,
66 hb_buffer_t * sub = *_sub;
67 hb_title_t * title = job->title;
68 int i, j, offset_top, offset_left;
69 uint8_t * lum, * alpha, * out, * sub_chromaU, * sub_chromaV;
76 /* If necessary, move the subtitle so it is not in a cropped zone.
77 When it won't fit, we center it so we loose as much on both ends.
78 Otherwise we try to leave a 20px margin around it. */
80 if( sub->height > title->height - job->crop[0] - job->crop[1] - 40 )
81 offset_top = job->crop[0] + ( title->height - job->crop[0] -
82 job->crop[1] - sub->height ) / 2;
83 else if( sub->y < job->crop[0] + 20 )
84 offset_top = job->crop[0] + 20;
85 else if( sub->y > title->height - job->crop[1] - 20 - sub->height )
86 offset_top = title->height - job->crop[1] - 20 - sub->height;
90 if( sub->width > title->width - job->crop[2] - job->crop[3] - 40 )
91 offset_left = job->crop[2] + ( title->width - job->crop[2] -
92 job->crop[3] - sub->width ) / 2;
93 else if( sub->x < job->crop[2] + 20 )
94 offset_left = job->crop[2] + 20;
95 else if( sub->x > title->width - job->crop[3] - 20 - sub->width )
96 offset_left = title->width - job->crop[3] - 20 - sub->width;
101 alpha = lum + sub->width * sub->height;
102 sub_chromaU = alpha + sub->width * sub->height;
103 sub_chromaV = sub_chromaU + sub->width * sub->height;
105 out = buf->data + offset_top * title->width + offset_left;
107 for( i = 0; i < sub->height; i++ )
109 if( offset_top + i >= 0 && offset_top + i < title->height )
111 for( j = 0; j < sub->width; j++ )
113 if( offset_left + j >= 0 && offset_left + j < title->width )
115 uint8_t *chromaU, *chromaV;
118 * Merge the luminance and alpha with the picture
120 out[j] = ( (uint16_t) out[j] * ( 16 - (uint16_t) alpha[j] ) +
121 (uint16_t) lum[j] * (uint16_t) alpha[j] ) >> 4;
123 * Set the chroma (colour) based on whether there is
124 * any alpha at all. Don't try to blend with the picture.
126 chromaU = getU(buf->data, title->width, title->height,
127 offset_left+j, offset_top+i);
129 chromaV = getV(buf->data, title->width, title->height,
130 offset_left+j, offset_top+i);
135 * Add the chroma from the sub-picture, as this is
136 * not a transparent element.
138 *chromaU = sub_chromaU[j];
139 *chromaV = sub_chromaV[j];
147 sub_chromaU += sub->width;
148 sub_chromaV += sub->width;
152 hb_buffer_close( _sub );
155 int renderWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
156 hb_buffer_t ** buf_out )
158 hb_work_private_t * pv = w->private_data;
159 hb_job_t * job = pv->job;
160 hb_title_t * title = job->title;
161 hb_buffer_t * in = *buf_in, * buf_tmp_in = *buf_in;
162 hb_buffer_t * ivtc_buffer = NULL;
166 /* If the input buffer is end of stream, send out an empty one
167 * to the next stage as well. Note that this will result in us
168 * losing the current contents of the delay queue.
170 *buf_out = hb_buffer_init(0);
175 * During the indepth_scan ditch the buffers here before applying filters or attempting to
178 if( job->indepth_scan )
184 /* Push subtitles onto queue just in case we need to delay a frame */
187 hb_fifo_push( pv->subtitle_queue, in->sub );
191 hb_fifo_push( pv->subtitle_queue, hb_buffer_init(0) );
194 /* Setup render buffer */
195 hb_buffer_t * buf_render = hb_buffer_init( 3 * job->width * job->height / 2 );
197 /* Cache frame start and stop times, so we can renumber
198 time stamps if dropping frames for VFR. */
200 for( i = 3; i >= 1; i-- )
202 pv->last_start[i] = pv->last_start[i-1];
203 pv->last_stop[i] = pv->last_stop[i-1];
205 pv->last_start[0] = in->start;
206 pv->last_stop[0] = in->stop;
211 int filter_count = hb_list_count( job->filters );
214 for( i = 0; i < filter_count; i++ )
216 hb_filter_object_t * filter = hb_list_item( job->filters, i );
223 hb_buffer_t * buf_tmp_out = NULL;
225 int result = filter->work( buf_tmp_in,
230 filter->private_data );
233 * FILTER_OK: set temp buffer to filter buffer, continue
234 * FILTER_DELAY: set temp buffer to NULL, abort
235 * FILTER_DROP: set temp buffer to NULL, pop subtitle, abort
236 * FILTER_FAILED: leave temp buffer alone, continue
238 if( result == FILTER_OK )
240 buf_tmp_in = buf_tmp_out;
242 else if( result == FILTER_DELAY )
247 else if( result == FILTER_DROP )
251 pv->frames_to_extend += 4;
252 pv->dropped_frames++;
253 hb_fifo_get( pv->subtitle_queue );
258 buf_tmp_in = buf_tmp_out;
265 /* Apply subtitles */
268 hb_buffer_t * subtitles = hb_fifo_get( pv->subtitle_queue );
271 ApplySub( job, buf_tmp_in, &subtitles );
275 /* Apply crop/scale if specified */
276 if( buf_tmp_in && pv->context )
278 avpicture_fill( &pv->pic_tmp_in, buf_tmp_in->data,
280 title->width, title->height );
282 avpicture_fill( &pv->pic_tmp_out, buf_render->data,
284 job->width, job->height );
286 // Crop; this alters the pointer to the data to point to the correct place for cropped frame
287 av_picture_crop( &pv->pic_tmp_crop, &pv->pic_tmp_in, PIX_FMT_YUV420P,
288 job->crop[0], job->crop[2] );
290 // Scale pic_crop into pic_render according to the context set up in renderInit
291 sws_scale(pv->context,
292 pv->pic_tmp_crop.data, pv->pic_tmp_crop.linesize,
293 0, title->height - (job->crop[0] + job->crop[1]),
294 pv->pic_tmp_out.data, pv->pic_tmp_out.linesize);
296 hb_buffer_copy_settings( buf_render, buf_tmp_in );
298 buf_tmp_in = buf_render;
301 /* Set output to render buffer */
302 (*buf_out) = buf_render;
304 if( buf_tmp_in == NULL )
306 /* Teardown and cleanup buffers if we are emitting NULL */
307 if( buf_in && *buf_in )
309 hb_buffer_close( buf_in );
312 if( buf_out && *buf_out )
314 hb_buffer_close( buf_out );
318 else if( buf_tmp_in != buf_render )
320 /* Copy temporary results and settings into render buffer */
321 memcpy( buf_render->data, buf_tmp_in->data, buf_render->size );
322 hb_buffer_copy_settings( buf_render, buf_tmp_in );
327 hb_fifo_push( pv->delay_queue, *buf_out );
332 * Keep the last three frames in our queue, this ensures that we have the last
333 * two always in there should we need to rewrite the durations on them.
335 if( hb_fifo_size( pv->delay_queue ) >= 3 )
337 *buf_out = hb_fifo_get( pv->delay_queue );
342 if( pv->frames_to_extend )
345 * A frame's been dropped by VFR detelecine.
346 * Gotta make up the lost time. This will also
347 * slow down the video to 23.976fps.
348 * The dropped frame ran for 3003 ticks, so
349 * divvy it up amongst the 4 frames left behind.
350 * This is what the delay_queue is for;
351 * telecined sequences start 2 frames before
352 * the dropped frame, so to slow down the right
353 * ones you need a 2 frame delay between
354 * reading input and writing output.
356 ivtc_buffer = *buf_out;
358 /* The 4th cached frame will be the to use. */
359 ivtc_buffer->start = pv->last_start[3];
360 ivtc_buffer->stop = pv->last_stop[3];
362 if (pv->frames_to_extend % 4)
363 ivtc_buffer->stop += 751;
365 ivtc_buffer->stop += 750;
367 /* Set the 3rd cached frame to start when this one stops,
368 and to stop 3003 ticks later -- a normal 29.97fps
369 length frame. If it needs to be extended as well to
370 make up lost time, it'll be handled on the next
371 loop through the renderer. */
372 pv->last_start[2] = ivtc_buffer->stop;
373 pv->last_stop[2] = ivtc_buffer->stop + 3003;
375 pv->frames_to_extend--;
376 pv->extended_frames++;
384 void renderClose( hb_work_object_t * w )
386 hb_work_private_t * pv = w->private_data;
388 hb_log("render: dropped frames: %i (%i ticks)", pv->dropped_frames, (pv->dropped_frames * 3003) );
389 hb_log("render: extended frames: %i (%i ticks)", pv->extended_frames, ( ( pv->extended_frames / 4 ) * 3003 ) );
390 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 ) );
392 /* Cleanup subtitle queue */
393 if( pv->subtitle_queue )
395 hb_fifo_close( &pv->subtitle_queue );
398 if( pv->delay_queue )
400 hb_fifo_close( &pv->delay_queue );
403 /* Cleanup filters */
404 /* TODO: Move to work.c? */
405 if( pv->job->filters )
407 int filter_count = hb_list_count( pv->job->filters );
410 for( i = 0; i < filter_count; i++ )
412 hb_filter_object_t * filter = hb_list_item( pv->job->filters, i );
414 if( !filter ) continue;
416 filter->close( filter->private_data );
419 hb_list_close( &pv->job->filters );
422 /* Cleanup render work structure */
424 w->private_data = NULL;
427 int renderInit( hb_work_object_t * w, hb_job_t * job )
429 /* Allocate new private work object */
430 hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
432 w->private_data = pv;
434 /* Get title and title size */
435 hb_title_t * title = job->title;
437 /* If crop or scale is specified, setup rescale context */
438 if( job->crop[0] || job->crop[1] || job->crop[2] || job->crop[3] ||
439 job->width != title->width || job->height != title->height )
441 pv->context = sws_getContext(title->width - (job->crop[2] + job->crop[3]),
442 title->height - (job->crop[0] + job->crop[1]),
444 job->width, job->height, PIX_FMT_YUV420P,
445 (uint16_t)(SWS_LANCZOS|SWS_ACCURATE_RND), NULL, NULL, NULL);
448 /* Setup FIFO queue for subtitle cache */
449 pv->subtitle_queue = hb_fifo_init( 8 );
450 pv->delay_queue = hb_fifo_init( 8 );
451 pv->frames_to_extend = 0;
452 pv->dropped_frames = 0;
453 pv->extended_frames = 0;
454 pv->last_start[0] = 0;
455 pv->last_stop[0] = 0;
458 /* TODO: Move to work.c? */
461 int filter_count = hb_list_count( job->filters );
464 for( i = 0; i < filter_count; i++ )
466 hb_filter_object_t * filter = hb_list_item( job->filters, i );
468 if( !filter ) continue;
470 filter->private_data = filter->init( PIX_FMT_YUV420P,