OSDN Git Service

Don't drop subtitles when crossing PTS discontinuities by using buffer sequence numbe...
[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     /*
165      * During the indepth_scan ditch the buffers here before applying filters or attempting to
166      * use the subtitles.
167      */
168     if( job->indepth_scan )
169     {      
170         *buf_out = NULL;
171         return HB_WORK_OK;
172     }
173     
174     /* Push subtitles onto queue just in case we need to delay a frame */
175     if( in->sub )
176     {
177         hb_fifo_push( pv->subtitle_queue, in->sub );
178     }
179     else
180     {
181         hb_fifo_push( pv->subtitle_queue,  hb_buffer_init(0) );
182     }
183     
184     /* Setup render buffer */
185     hb_buffer_t * buf_render = hb_buffer_init( 3 * job->width * job->height / 2 );  
186
187     /* Apply filters */
188     if( job->filters )
189     {
190         int filter_count = hb_list_count( job->filters );
191         int i;
192         
193         for( i = 0; i < filter_count; i++ )
194         {
195             hb_filter_object_t * filter = hb_list_item( job->filters, i );
196             
197             if( !filter )
198             {
199                 continue;
200             }            
201             
202             hb_buffer_t * buf_tmp_out = NULL;
203             
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 );
210             
211             /* 
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 
216              */
217             if( result == FILTER_OK )
218             {
219                 buf_tmp_in = buf_tmp_out;
220             }
221             else if( result == FILTER_DELAY )
222             {
223                 buf_tmp_in = NULL;
224                 break;
225             }            
226             else if( result == FILTER_DROP )
227             {
228                 hb_fifo_get( pv->subtitle_queue );
229                 buf_tmp_in = NULL;
230                 break;
231             }
232         }
233     }   
234         
235     /* Apply subtitles */
236     if( buf_tmp_in )
237     {
238         hb_buffer_t * subtitles = hb_fifo_get( pv->subtitle_queue );        
239         if( subtitles )
240         {
241             ApplySub( job, buf_tmp_in, &subtitles );
242         }
243     }
244     
245     /* Apply crop/scale if specified */
246     if( buf_tmp_in && pv->context )
247     {
248         avpicture_fill( &pv->pic_tmp_in, buf_tmp_in->data, 
249                         PIX_FMT_YUV420P,
250                         title->width, title->height );
251         
252         avpicture_fill( &pv->pic_tmp_out, buf_render->data, 
253                         PIX_FMT_YUV420P,
254                         job->width, job->height );
255
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] );
259
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);
265         
266         hb_buffer_copy_settings( buf_render, buf_tmp_in );
267         
268         buf_tmp_in = buf_render;
269     }  
270
271     /* Set output to render buffer */
272     (*buf_out) = buf_render;
273
274     if( buf_tmp_in == NULL )
275     {
276         /* Teardown and cleanup buffers if we are emitting NULL */
277         if( buf_in && *buf_in )
278         {
279             hb_buffer_close( buf_in );
280             *buf_in = NULL;
281         }        
282         if( buf_out && *buf_out )
283         {
284             hb_buffer_close( buf_out );        
285             *buf_out = NULL;
286         }
287     }
288     else if( buf_tmp_in != buf_render )
289     {    
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 );
293     }
294
295     return HB_WORK_OK;
296 }
297
298 void renderClose( hb_work_object_t * w )
299 {
300     hb_work_private_t * pv = w->private_data;   
301         
302     /* Cleanup subtitle queue */
303     if( pv->subtitle_queue )
304     {
305         hb_fifo_close( &pv->subtitle_queue );
306     }
307     
308     /* Cleanup filters */
309     /* TODO: Move to work.c? */
310     if( pv->job->filters )
311     {
312         int filter_count = hb_list_count( pv->job->filters );
313         int i;
314         
315         for( i = 0; i < filter_count; i++ )
316         {
317             hb_filter_object_t * filter = hb_list_item( pv->job->filters, i );
318             
319             if( !filter ) continue;
320
321             filter->close( filter->private_data );
322         }
323         
324         hb_list_close( &pv->job->filters );
325     }    
326    
327     /* Cleanup render work structure */
328     free( pv );
329     w->private_data = NULL;    
330 }
331
332 int renderInit( hb_work_object_t * w, hb_job_t * job )
333 {   
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;
338
339     /* Get title and title size */
340     hb_title_t * title = job->title;
341
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 )
345     {
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);
351     }   
352     
353     /* Setup FIFO queue for subtitle cache */
354     pv->subtitle_queue = hb_fifo_init( 8 );    
355     
356     /* Setup filters */
357     /* TODO: Move to work.c? */
358     if( job->filters )
359     {
360         int filter_count = hb_list_count( job->filters );
361         int i;
362         
363         for( i = 0; i < filter_count; i++ )
364         {
365             hb_filter_object_t * filter = hb_list_item( job->filters, i );
366
367             if( !filter ) continue;
368             
369             filter->private_data = filter->init( PIX_FMT_YUV420P,
370                                                  title->width,
371                                                  title->height,
372                                                  filter->settings );
373         }
374     }
375     
376     return 0;
377 }