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
;
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
=
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
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
) +
57 static void ApplySub( hb_job_t
* job
, hb_buffer_t
* buf
,
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
;
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
;
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
;
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
);
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
];
141 sub_chromaU
+= sub
->width
;
142 sub_chromaV
+= sub
->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
;
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);
165 * During the indepth_scan ditch the buffers here before applying filters or attempting to
168 if( job
->indepth_scan
)
174 /* Push subtitles onto queue just in case we need to delay a frame */
177 hb_fifo_push( pv
->subtitle_queue
, in
->sub
);
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 );
190 int filter_count
= hb_list_count( job
->filters
);
193 for( i
= 0; i
< filter_count
; i
++ )
195 hb_filter_object_t
* filter
= hb_list_item( job
->filters
, i
);
202 hb_buffer_t
* buf_tmp_out
= NULL
;
204 int result
= filter
->work( buf_tmp_in
,
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
)
226 else if( result
== FILTER_DROP
)
228 hb_fifo_get( pv
->subtitle_queue
);
235 /* Apply subtitles */
238 hb_buffer_t
* subtitles
= hb_fifo_get( pv
->subtitle_queue
);
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
,
250 title
->width
, title
->height
);
252 avpicture_fill( &pv
->pic_tmp_out
, buf_render
->data
,
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
);
282 if( buf_out
&& *buf_out
)
284 hb_buffer_close( buf_out
);
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
);
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
);
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 */
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
) );
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]),
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 );
357 /* TODO: Move to work.c? */
360 int filter_count
= hb_list_count( job
->filters
);
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
,