OSDN Git Service

MacGui: Fix main window closing to it doesnt crash HB and allow you to get it back...
[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 /*
38  * getU() & getV()
39  *
40  * Utility function that finds where the U is in the YUV sub-picture
41  *
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.
45  */
46 static uint8_t *getU(uint8_t *data, int width, int height, int x, int y)
47 {
48     return(&data[(((y/2) * (width/2)) + (x/2)) + (width*height)]);
49 }
50
51 static uint8_t *getV(uint8_t *data, int width, int height, int x, int y)
52 {
53     return(&data[(((y/2) * (width/2)) + (x/2)) + (width*height) + 
54                  (width*height)/4]);
55 }
56
57 static void ApplySub( hb_job_t * job, hb_buffer_t * buf,
58                       hb_buffer_t ** _sub )
59 {
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;
64
65     if( !sub )
66     {
67         return;
68     }
69
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. */
73
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;
83
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;
93
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;
98
99     out   = buf->data + offset_top * title->width + offset_left;
100
101     for( i = 0; i < sub->height; i++ )
102     {
103         if( offset_top + i >= 0 && offset_top + i < title->height )
104         {
105             for( j = 0; j < sub->width; j++ )
106             {
107                 if( offset_left + j >= 0 && offset_left + j < title->width )
108                 {
109                     uint8_t *chromaU, *chromaV;
110
111                     /*
112                      * Merge the luminance and alpha with the picture
113                      */
114                     out[j] = ( (uint16_t) out[j] * ( 16 - (uint16_t) alpha[j] ) +
115                                (uint16_t) lum[j] * (uint16_t) alpha[j] ) >> 4;   
116                     /*
117                      * Set the chroma (colour) based on whether there is
118                      * any alpha at all. Don't try to blend with the picture.
119                      */
120                     chromaU = getU(buf->data, title->width, title->height,
121                                    offset_left+j, offset_top+i);
122                     
123                     chromaV = getV(buf->data, title->width, title->height,
124                                    offset_left+j, offset_top+i);
125                     
126                     if( alpha[j] > 0 )
127                     {
128                         /*
129                          * Add the chroma from the sub-picture, as this is 
130                          * not a transparent element.
131                          */
132                         *chromaU = sub_chromaU[j];
133                         *chromaV = sub_chromaV[j];
134                     } 
135                 }
136             }
137         }
138
139         lum   += sub->width;
140         alpha += sub->width;
141         sub_chromaU += sub->width;
142         sub_chromaV += sub->width;
143         out   += title->width;
144     }
145
146     hb_buffer_close( _sub );
147 }
148
149 int renderWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
150                 hb_buffer_t ** buf_out )
151 {
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;
156     
157     if(!in->data)
158     {
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;
162     }
163     
164     /* Push subtitles onto queue just in case we need to delay a frame */
165     if( in->sub )
166     {
167         hb_fifo_push( pv->subtitle_queue, in->sub );
168     }
169     else
170     {
171         hb_fifo_push( pv->subtitle_queue,  hb_buffer_init(0) );
172     }
173     
174     /* Setup render buffer */
175     hb_buffer_t * buf_render = hb_buffer_init( 3 * job->width * job->height / 2 );  
176
177     /* Apply filters */
178     if( job->filters )
179     {
180         int filter_count = hb_list_count( job->filters );
181         int i;
182         
183         for( i = 0; i < filter_count; i++ )
184         {
185             hb_filter_object_t * filter = hb_list_item( job->filters, i );
186             
187             if( !filter )
188             {
189                 continue;
190             }            
191             
192             hb_buffer_t * buf_tmp_out = NULL;
193             
194             int result = filter->work( buf_tmp_in,
195                                        &buf_tmp_out, 
196                                        PIX_FMT_YUV420P, 
197                                        title->width, 
198                                        title->height, 
199                                        filter->private_data );
200             
201             /* 
202              * FILTER_OK:      set temp buffer to filter buffer, continue 
203              * FILTER_DELAY:   set temp buffer to NULL, abort 
204              * FILTER_DROP:    set temp buffer to NULL, pop subtitle, abort 
205              * FILTER_FAILED:  leave temp buffer alone, continue 
206              */
207             if( result == FILTER_OK )
208             {
209                 buf_tmp_in = buf_tmp_out;
210             }
211             else if( result == FILTER_DELAY )
212             {
213                 buf_tmp_in = NULL;
214                 break;
215             }            
216             else if( result == FILTER_DROP )
217             {
218                 hb_fifo_get( pv->subtitle_queue );
219                 buf_tmp_in = NULL;
220                 break;
221             }
222         }
223     }   
224         
225     /* Apply subtitles */
226     if( buf_tmp_in )
227     {
228         hb_buffer_t * subtitles = hb_fifo_get( pv->subtitle_queue );        
229         if( subtitles )
230         {
231             ApplySub( job, buf_tmp_in, &subtitles );
232         }
233     }
234     
235     /* Apply crop/scale if specified */
236     if( buf_tmp_in && pv->context )
237     {
238         avpicture_fill( &pv->pic_tmp_in, buf_tmp_in->data, 
239                         PIX_FMT_YUV420P,
240                         title->width, title->height );
241         
242         avpicture_fill( &pv->pic_tmp_out, buf_render->data, 
243                         PIX_FMT_YUV420P,
244                         job->width, job->height );
245
246         // Crop; this alters the pointer to the data to point to the correct place for cropped frame
247         av_picture_crop( &pv->pic_tmp_crop, &pv->pic_tmp_in, PIX_FMT_YUV420P,
248                          job->crop[0], job->crop[2] );
249
250         // Scale pic_crop into pic_render according to the context set up in renderInit
251         sws_scale(pv->context,
252                   pv->pic_tmp_crop.data, pv->pic_tmp_crop.linesize,
253                   0, title->height - (job->crop[0] + job->crop[1]),
254                   pv->pic_tmp_out.data,  pv->pic_tmp_out.linesize);
255         
256         hb_buffer_copy_settings( buf_render, buf_tmp_in );
257         
258         buf_tmp_in = buf_render;
259     }  
260
261     /* Set output to render buffer */
262     (*buf_out) = buf_render;
263
264     if( buf_tmp_in == NULL )
265     {
266         /* Teardown and cleanup buffers if we are emitting NULL */
267         if( buf_in && *buf_in )
268         {
269             hb_buffer_close( buf_in );
270             *buf_in = NULL;
271         }        
272         if( buf_out && *buf_out )
273         {
274             hb_buffer_close( buf_out );        
275             *buf_out = NULL;
276         }
277     }
278     else if( buf_tmp_in != buf_render )
279     {    
280         /* Copy temporary results and settings into render buffer */
281         memcpy( buf_render->data, buf_tmp_in->data, buf_render->size );
282         hb_buffer_copy_settings( buf_render, buf_tmp_in );
283     }
284
285     return HB_WORK_OK;
286 }
287
288 void renderClose( hb_work_object_t * w )
289 {
290     hb_work_private_t * pv = w->private_data;   
291         
292     /* Cleanup subtitle queue */
293     if( pv->subtitle_queue )
294     {
295         hb_fifo_close( &pv->subtitle_queue );
296     }
297     
298     /* Cleanup filters */
299     /* TODO: Move to work.c? */
300     if( pv->job->filters )
301     {
302         int filter_count = hb_list_count( pv->job->filters );
303         int i;
304         
305         for( i = 0; i < filter_count; i++ )
306         {
307             hb_filter_object_t * filter = hb_list_item( pv->job->filters, i );
308             
309             if( !filter ) continue;
310
311             filter->close( filter->private_data );
312         }
313         
314         hb_list_close( &pv->job->filters );
315     }    
316    
317     /* Cleanup render work structure */
318     free( pv );
319     w->private_data = NULL;    
320 }
321
322 int renderInit( hb_work_object_t * w, hb_job_t * job )
323 {   
324     /* Allocate new private work object */
325     hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
326     pv->job = job;
327     w->private_data = pv;
328
329     /* Get title and title size */
330     hb_title_t * title = job->title;
331
332     /* If crop or scale is specified, setup rescale context */
333     if( job->crop[0] || job->crop[1] || job->crop[2] || job->crop[3] ||
334         job->width != title->width || job->height != title->height )
335     {
336         pv->context = sws_getContext(title->width  - (job->crop[2] + job->crop[3]),
337                                      title->height - (job->crop[0] + job->crop[1]),
338                                      PIX_FMT_YUV420P,
339                                      job->width, job->height, PIX_FMT_YUV420P,
340                                      SWS_LANCZOS, NULL, NULL, NULL);
341     }   
342     
343     /* Setup FIFO queue for subtitle cache */
344     pv->subtitle_queue = hb_fifo_init( 8 );    
345     
346     /* Setup filters */
347     /* TODO: Move to work.c? */
348     if( job->filters )
349     {
350         int filter_count = hb_list_count( job->filters );
351         int i;
352         
353         for( i = 0; i < filter_count; i++ )
354         {
355             hb_filter_object_t * filter = hb_list_item( job->filters, i );
356
357             if( !filter ) continue;
358             
359             filter->private_data = filter->init( PIX_FMT_YUV420P,
360                                                  title->width,
361                                                  title->height,
362                                                  filter->settings );
363         }
364     }
365     
366     return 0;
367 }