Modifies CLI help message for audio track selection, to make it clearer how to specif...
[HandBrake.git] / libhb / render.c
blobedbd18a80ebc87eebbcf3e42821e7c4aa7bc5455
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. */
7 #include "hb.h"
9 #include "ffmpeg/avcodec.h"
10 #include "ffmpeg/swscale.h"
12 struct hb_work_private_s
14 hb_job_t * job;
16 struct SwsContext * context;
17 AVPicture pic_tmp_in;
18 AVPicture pic_tmp_crop;
19 AVPicture pic_tmp_out;
20 hb_buffer_t * buf_scale;
21 hb_fifo_t * subtitle_queue;
24 int renderInit( hb_work_object_t *, hb_job_t * );
25 int renderWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
26 void renderClose( hb_work_object_t * );
28 hb_work_object_t hb_render =
30 WORK_RENDER,
31 "Renderer",
32 renderInit,
33 renderWork,
34 renderClose
38 * getU() & getV()
40 * Utility function that finds where the U is in the YUV sub-picture
42 * The Y data is at the top, followed by U and V, but the U and V
43 * are half the width of the Y, i.e. each chroma element covers 2x2
44 * of the Y's.
46 static uint8_t *getU(uint8_t *data, int width, int height, int x, int y)
48 return(&data[(((y/2) * (width/2)) + (x/2)) + (width*height)]);
51 static uint8_t *getV(uint8_t *data, int width, int height, int x, int y)
53 return(&data[(((y/2) * (width/2)) + (x/2)) + (width*height) +
54 (width*height)/4]);
57 static void ApplySub( hb_job_t * job, hb_buffer_t * buf,
58 hb_buffer_t ** _sub )
60 hb_buffer_t * sub = *_sub;
61 hb_title_t * title = job->title;
62 int i, j, offset_top, offset_left;
63 uint8_t * lum, * alpha, * out, * sub_chromaU, * sub_chromaV;
65 if( !sub )
67 return;
70 /* If necessary, move the subtitle so it is not in a cropped zone.
71 When it won't fit, we center it so we loose as much on both ends.
72 Otherwise we try to leave a 20px margin around it. */
74 if( sub->height > title->height - job->crop[0] - job->crop[1] - 40 )
75 offset_top = job->crop[0] + ( title->height - job->crop[0] -
76 job->crop[1] - sub->height ) / 2;
77 else if( sub->y < job->crop[0] + 20 )
78 offset_top = job->crop[0] + 20;
79 else if( sub->y > title->height - job->crop[1] - 20 - sub->height )
80 offset_top = title->height - job->crop[1] - 20 - sub->height;
81 else
82 offset_top = sub->y;
84 if( sub->width > title->width - job->crop[2] - job->crop[3] - 40 )
85 offset_left = job->crop[2] + ( title->width - job->crop[2] -
86 job->crop[3] - sub->width ) / 2;
87 else if( sub->x < job->crop[2] + 20 )
88 offset_left = job->crop[2] + 20;
89 else if( sub->x > title->width - job->crop[3] - 20 - sub->width )
90 offset_left = title->width - job->crop[3] - 20 - sub->width;
91 else
92 offset_left = sub->x;
94 lum = sub->data;
95 alpha = lum + sub->width * sub->height;
96 sub_chromaU = alpha + sub->width * sub->height;
97 sub_chromaV = sub_chromaU + sub->width * sub->height;
99 out = buf->data + offset_top * title->width + offset_left;
101 for( i = 0; i < sub->height; i++ )
103 if( offset_top + i >= 0 && offset_top + i < title->height )
105 for( j = 0; j < sub->width; j++ )
107 if( offset_left + j >= 0 && offset_left + j < title->width )
109 uint8_t *chromaU, *chromaV;
112 * Merge the luminance and alpha with the picture
114 out[j] = ( (uint16_t) out[j] * ( 16 - (uint16_t) alpha[j] ) +
115 (uint16_t) lum[j] * (uint16_t) alpha[j] ) >> 4;
117 * Set the chroma (colour) based on whether there is
118 * any alpha at all. Don't try to blend with the picture.
120 chromaU = getU(buf->data, title->width, title->height,
121 offset_left+j, offset_top+i);
123 chromaV = getV(buf->data, title->width, title->height,
124 offset_left+j, offset_top+i);
126 if( alpha[j] > 0 )
129 * Add the chroma from the sub-picture, as this is
130 * not a transparent element.
132 *chromaU = sub_chromaU[j];
133 *chromaV = sub_chromaV[j];
139 lum += sub->width;
140 alpha += sub->width;
141 sub_chromaU += sub->width;
142 sub_chromaV += sub->width;
143 out += title->width;
146 hb_buffer_close( _sub );
149 int renderWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
150 hb_buffer_t ** buf_out )
152 hb_work_private_t * pv = w->private_data;
153 hb_job_t * job = pv->job;
154 hb_title_t * title = job->title;
155 hb_buffer_t * in = *buf_in, * buf_tmp_in = *buf_in;
157 if(!in->data)
159 /* If the input buffer is end of stream, send out an empty one to the next stage as well. */
160 *buf_out = hb_buffer_init(0);
161 return HB_WORK_OK;
165 * During the indepth_scan ditch the buffers here before applying filters or attempting to
166 * use the subtitles.
168 if( job->indepth_scan )
170 *buf_out = NULL;
171 return HB_WORK_OK;
174 /* Push subtitles onto queue just in case we need to delay a frame */
175 if( in->sub )
177 hb_fifo_push( pv->subtitle_queue, in->sub );
179 else
181 hb_fifo_push( pv->subtitle_queue, hb_buffer_init(0) );
184 /* Setup render buffer */
185 hb_buffer_t * buf_render = hb_buffer_init( 3 * job->width * job->height / 2 );
187 /* Apply filters */
188 if( job->filters )
190 int filter_count = hb_list_count( job->filters );
191 int i;
193 for( i = 0; i < filter_count; i++ )
195 hb_filter_object_t * filter = hb_list_item( job->filters, i );
197 if( !filter )
199 continue;
202 hb_buffer_t * buf_tmp_out = NULL;
204 int result = filter->work( buf_tmp_in,
205 &buf_tmp_out,
206 PIX_FMT_YUV420P,
207 title->width,
208 title->height,
209 filter->private_data );
212 * FILTER_OK: set temp buffer to filter buffer, continue
213 * FILTER_DELAY: set temp buffer to NULL, abort
214 * FILTER_DROP: set temp buffer to NULL, pop subtitle, abort
215 * FILTER_FAILED: leave temp buffer alone, continue
217 if( result == FILTER_OK )
219 buf_tmp_in = buf_tmp_out;
221 else if( result == FILTER_DELAY )
223 buf_tmp_in = NULL;
224 break;
226 else if( result == FILTER_DROP )
228 hb_fifo_get( pv->subtitle_queue );
229 buf_tmp_in = NULL;
230 break;
235 /* Apply subtitles */
236 if( buf_tmp_in )
238 hb_buffer_t * subtitles = hb_fifo_get( pv->subtitle_queue );
239 if( subtitles )
241 ApplySub( job, buf_tmp_in, &subtitles );
245 /* Apply crop/scale if specified */
246 if( buf_tmp_in && pv->context )
248 avpicture_fill( &pv->pic_tmp_in, buf_tmp_in->data,
249 PIX_FMT_YUV420P,
250 title->width, title->height );
252 avpicture_fill( &pv->pic_tmp_out, buf_render->data,
253 PIX_FMT_YUV420P,
254 job->width, job->height );
256 // Crop; this alters the pointer to the data to point to the correct place for cropped frame
257 av_picture_crop( &pv->pic_tmp_crop, &pv->pic_tmp_in, PIX_FMT_YUV420P,
258 job->crop[0], job->crop[2] );
260 // Scale pic_crop into pic_render according to the context set up in renderInit
261 sws_scale(pv->context,
262 pv->pic_tmp_crop.data, pv->pic_tmp_crop.linesize,
263 0, title->height - (job->crop[0] + job->crop[1]),
264 pv->pic_tmp_out.data, pv->pic_tmp_out.linesize);
266 hb_buffer_copy_settings( buf_render, buf_tmp_in );
268 buf_tmp_in = buf_render;
271 /* Set output to render buffer */
272 (*buf_out) = buf_render;
274 if( buf_tmp_in == NULL )
276 /* Teardown and cleanup buffers if we are emitting NULL */
277 if( buf_in && *buf_in )
279 hb_buffer_close( buf_in );
280 *buf_in = NULL;
282 if( buf_out && *buf_out )
284 hb_buffer_close( buf_out );
285 *buf_out = NULL;
288 else if( buf_tmp_in != buf_render )
290 /* Copy temporary results and settings into render buffer */
291 memcpy( buf_render->data, buf_tmp_in->data, buf_render->size );
292 hb_buffer_copy_settings( buf_render, buf_tmp_in );
295 return HB_WORK_OK;
298 void renderClose( hb_work_object_t * w )
300 hb_work_private_t * pv = w->private_data;
302 /* Cleanup subtitle queue */
303 if( pv->subtitle_queue )
305 hb_fifo_close( &pv->subtitle_queue );
308 /* Cleanup filters */
309 /* TODO: Move to work.c? */
310 if( pv->job->filters )
312 int filter_count = hb_list_count( pv->job->filters );
313 int i;
315 for( i = 0; i < filter_count; i++ )
317 hb_filter_object_t * filter = hb_list_item( pv->job->filters, i );
319 if( !filter ) continue;
321 filter->close( filter->private_data );
324 hb_list_close( &pv->job->filters );
327 /* Cleanup render work structure */
328 free( pv );
329 w->private_data = NULL;
332 int renderInit( hb_work_object_t * w, hb_job_t * job )
334 /* Allocate new private work object */
335 hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
336 pv->job = job;
337 w->private_data = pv;
339 /* Get title and title size */
340 hb_title_t * title = job->title;
342 /* If crop or scale is specified, setup rescale context */
343 if( job->crop[0] || job->crop[1] || job->crop[2] || job->crop[3] ||
344 job->width != title->width || job->height != title->height )
346 pv->context = sws_getContext(title->width - (job->crop[2] + job->crop[3]),
347 title->height - (job->crop[0] + job->crop[1]),
348 PIX_FMT_YUV420P,
349 job->width, job->height, PIX_FMT_YUV420P,
350 (uint16_t)(SWS_LANCZOS|SWS_ACCURATE_RND), NULL, NULL, NULL);
353 /* Setup FIFO queue for subtitle cache */
354 pv->subtitle_queue = hb_fifo_init( 8 );
356 /* Setup filters */
357 /* TODO: Move to work.c? */
358 if( job->filters )
360 int filter_count = hb_list_count( job->filters );
361 int i;
363 for( i = 0; i < filter_count; i++ )
365 hb_filter_object_t * filter = hb_list_item( job->filters, i );
367 if( !filter ) continue;
369 filter->private_data = filter->init( PIX_FMT_YUV420P,
370 title->width,
371 title->height,
372 filter->settings );
376 return 0;