OSDN Git Service

Patch from huevos_rancheros to restore 2-pass functionality, which broke when the...
[handbrake-jp/handbrake-jp-git.git] / libhb / render.c
1 /* $Id: render.c,v 1.17 2005/04/14 17:37:54 titer Exp $
2
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. */
6
7 #include "hb.h"
8
9 #include "ffmpeg/avcodec.h"
10
11 struct hb_work_private_s
12 {
13     hb_job_t * job;
14
15     ImgReSampleContext * context;
16     AVPicture            pic_tmp_in;
17     AVPicture            pic_tmp_out;        
18     hb_buffer_t        * buf_scale;
19     hb_fifo_t          * subtitle_queue;
20 };
21
22 int  renderInit( hb_work_object_t *, hb_job_t * );
23 int  renderWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
24 void renderClose( hb_work_object_t * );
25
26 hb_work_object_t hb_render =
27 {   
28     WORK_RENDER,
29     "Renderer",
30     renderInit,
31     renderWork,
32     renderClose
33 };
34
35 static void ApplySub( hb_job_t * job, hb_buffer_t * buf,
36                       hb_buffer_t ** _sub )
37 {
38     hb_buffer_t * sub = *_sub;
39     hb_title_t * title = job->title;
40     int i, j, offset_top, offset_left;
41     uint8_t * lum, * alpha, * out;
42
43     if( !sub )
44     {
45         return;
46     }
47
48     /* If necessary, move the subtitle so it is not in a cropped zone.
49        When it won't fit, we center it so we loose as much on both ends.
50        Otherwise we try to leave a 20px margin around it. */
51
52     if( sub->height > title->height - job->crop[0] - job->crop[1] - 40 )
53         offset_top = job->crop[0] + ( title->height - job->crop[0] -
54                 job->crop[1] - sub->height ) / 2;
55     else if( sub->y < job->crop[0] + 20 )
56         offset_top = job->crop[0] + 20;
57     else if( sub->y > title->height - job->crop[1] - 20 - sub->height )
58         offset_top = title->height - job->crop[1] - 20 - sub->height;
59     else
60         offset_top = sub->y;
61
62     if( sub->width > title->width - job->crop[2] - job->crop[3] - 40 )
63         offset_left = job->crop[2] + ( title->width - job->crop[2] -
64                 job->crop[3] - sub->width ) / 2;
65     else if( sub->x < job->crop[2] + 20 )
66         offset_left = job->crop[2] + 20;
67     else if( sub->x > title->width - job->crop[3] - 20 - sub->width )
68         offset_left = title->width - job->crop[3] - 20 - sub->width;
69     else
70         offset_left = sub->x;
71
72     lum   = sub->data;
73     alpha = lum + sub->width * sub->height;
74     out   = buf->data + offset_top * title->width + offset_left;
75
76     for( i = 0; i < sub->height; i++ )
77     {
78         if( offset_top + i >= 0 && offset_top + i < title->height )
79         {
80             for( j = 0; j < sub->width; j++ )
81             {
82                 if( offset_left + j >= 0 && offset_left + j < title->width )
83                 {
84                     out[j] = ( (uint16_t) out[j] * ( 16 - (uint16_t) alpha[j] ) +
85                                (uint16_t) lum[j] * (uint16_t) alpha[j] ) >> 4;
86                 }
87             }
88         }
89
90         lum   += sub->width;
91         alpha += sub->width;
92         out   += title->width;
93     }
94
95     hb_buffer_close( _sub );
96 }
97
98 int renderWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
99                 hb_buffer_t ** buf_out )
100 {
101     hb_work_private_t * pv = w->private_data;
102     hb_job_t   * job   = pv->job;
103     hb_title_t * title = job->title;
104     hb_buffer_t * in = *buf_in, * buf_tmp_in = *buf_in;
105
106     int title_size = 3 * title->width * title->height / 2;
107     int job_size = 3 * job->width * job->height / 2;
108     
109     if(!in->data)
110     {
111         /* If the input buffer is end of stream, send out an empty one to the next stage as well. */
112        *buf_out = hb_buffer_init(0);
113        return HB_WORK_OK;
114     }
115     
116     /* Push subtitles onto queue just in case we need to delay a frame */
117     if( in->sub )
118     {
119         hb_fifo_push( pv->subtitle_queue, in->sub );
120     }
121     else
122     {
123         hb_fifo_push( pv->subtitle_queue,  hb_buffer_init(0) );
124     }
125     
126     /* Setup render buffer */
127     hb_buffer_t * buf_render = hb_buffer_init( job_size );  
128
129     /* Apply filters */
130     if( job->filters )
131     {
132         int filter_count = hb_list_count( job->filters );
133         int i;
134         
135         for( i = 0; i < filter_count; i++ )
136         {
137             hb_filter_object_t * filter = hb_list_item( job->filters, i );
138             
139             if( !filter )
140             {
141                 continue;
142             }            
143             
144             hb_buffer_t * buf_tmp_out = NULL;
145             
146             int result = filter->work( buf_tmp_in,
147                                        &buf_tmp_out, 
148                                        PIX_FMT_YUV420P, 
149                                        title->width, 
150                                        title->height, 
151                                        filter->private_data );
152             
153             /* 
154              * FILTER_OK:      set temp buffer to filter buffer, continue 
155              * FILTER_DELAY:   set temp buffer to NULL, abort 
156              * FILTER_DROP:    set temp buffer to NULL, pop subtitle, abort 
157              * FILTER_FAILED:  leave temp buffer alone, continue 
158              */
159             if( result == FILTER_OK )
160             {
161                 buf_tmp_in = buf_tmp_out;
162             }
163             else if( result == FILTER_DELAY )
164             {
165                 buf_tmp_in = NULL;
166                 break;
167             }            
168             else if( result == FILTER_DROP )
169             {
170                 hb_fifo_get( pv->subtitle_queue );
171                 buf_tmp_in = NULL;
172                 break;
173             }
174         }
175     }   
176         
177     /* Apply subtitles */
178     if( buf_tmp_in )
179     {
180         hb_buffer_t * subtitles = hb_fifo_get( pv->subtitle_queue );        
181         if( subtitles )
182         {
183             ApplySub( job, buf_tmp_in, &subtitles );
184         }
185     }
186     
187     /* Apply crop/scale if specified */
188     if( buf_tmp_in && pv->context )
189     {
190         avpicture_fill( &pv->pic_tmp_in, buf_tmp_in->data, 
191                         PIX_FMT_YUV420P,
192                         title->width, title->height );
193         
194         avpicture_fill( &pv->pic_tmp_out, buf_render->data, 
195                         PIX_FMT_YUV420P,
196                         job->width, job->height );
197         
198         img_resample( pv->context, &pv->pic_tmp_out, &pv->pic_tmp_in );
199         
200         hb_buffer_copy_settings( buf_render, buf_tmp_in );
201         
202         buf_tmp_in = buf_render;
203     }  
204
205     /* Set output to render buffer */
206     (*buf_out) = buf_render;
207
208     if( buf_tmp_in == NULL )
209     {
210         /* Teardown and cleanup buffers if we are emitting NULL */
211         if( buf_in && *buf_in )
212         {
213             hb_buffer_close( buf_in );
214             *buf_in = NULL;
215         }        
216         if( buf_out && *buf_out )
217         {
218             hb_buffer_close( buf_out );        
219             *buf_out = NULL;
220         }
221     }
222     else if( buf_tmp_in != buf_render )
223     {    
224         /* Copy temporary results and settings into render buffer */
225         memcpy( buf_render->data, buf_tmp_in->data, buf_render->size );
226         hb_buffer_copy_settings( buf_render, buf_tmp_in );
227     }
228
229     return HB_WORK_OK;
230 }
231
232 void renderClose( hb_work_object_t * w )
233 {
234     hb_work_private_t * pv = w->private_data;   
235         
236     /* Cleanup subtitle queue */
237     if( pv->subtitle_queue )
238     {
239         hb_fifo_close( &pv->subtitle_queue );
240     }
241     
242     /* Cleanup filters */
243     /* TODO: Move to work.c? */
244     if( pv->job->filters )
245     {
246         int filter_count = hb_list_count( pv->job->filters );
247         int i;
248         
249         for( i = 0; i < filter_count; i++ )
250         {
251             hb_filter_object_t * filter = hb_list_item( pv->job->filters, i );
252             
253             if( !filter ) continue;
254
255             filter->close( filter->private_data );
256         }
257         
258         hb_list_close( &pv->job->filters );
259     }    
260    
261     /* Cleanup render work structure */
262     free( pv );
263     w->private_data = NULL;    
264 }
265
266 int renderInit( hb_work_object_t * w, hb_job_t * job )
267 {   
268     /* Allocate new private work object */
269     hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
270     pv->job = job;
271     w->private_data = pv;
272
273     /* Get title and title size */
274     hb_title_t * title = job->title;    
275     int title_size = 3 * title->width * title->height / 2;    
276     
277     /* If crop or scale is specified, setup rescale context */
278     if( job->crop[0] || job->crop[1] || job->crop[2] || job->crop[3] ||
279         job->width != title->width || job->height != title->height )
280     {
281         pv->context = img_resample_full_init(
282             job->width, job->height, title->width, title->height,
283             job->crop[0], job->crop[1], job->crop[2], job->crop[3],
284             0, 0, 0, 0 );
285     }   
286     
287     /* Setup FIFO queue for subtitle cache */
288     pv->subtitle_queue = hb_fifo_init( 8 );    
289     
290     /* Setup filters */
291     /* TODO: Move to work.c? */
292     if( job->filters )
293     {
294         int filter_count = hb_list_count( job->filters );
295         int i;
296         
297         for( i = 0; i < filter_count; i++ )
298         {
299             hb_filter_object_t * filter = hb_list_item( job->filters, i );
300
301             if( !filter ) continue;
302             
303             filter->private_data = filter->init( PIX_FMT_YUV420P,
304                                                  title->width,
305                                                  title->height,
306                                                  filter->settings );
307         }
308     }
309     
310     return 0;
311 }