OSDN Git Service

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