OSDN Git Service

Fixed jam error caused by my previous commit.
[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     hb_fifo_t          * delay_queue;
23     int                  frames_to_extend;
24     int                  dropped_frames;
25     int                  extended_frames;
26     uint64_t             last_start[4];
27     uint64_t             last_stop[4];
28 };
29
30 int  renderInit( hb_work_object_t *, hb_job_t * );
31 int  renderWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
32 void renderClose( hb_work_object_t * );
33
34 hb_work_object_t hb_render =
35 {   
36     WORK_RENDER,
37     "Renderer",
38     renderInit,
39     renderWork,
40     renderClose
41 };
42
43 /*
44  * getU() & getV()
45  *
46  * Utility function that finds where the U is in the YUV sub-picture
47  *
48  * The Y data is at the top, followed by U and V, but the U and V
49  * are half the width of the Y, i.e. each chroma element covers 2x2
50  * of the Y's.
51  */
52 static uint8_t *getU(uint8_t *data, int width, int height, int x, int y)
53 {
54     return(&data[(((y/2) * (width/2)) + (x/2)) + (width*height)]);
55 }
56
57 static uint8_t *getV(uint8_t *data, int width, int height, int x, int y)
58 {
59     return(&data[(((y/2) * (width/2)) + (x/2)) + (width*height) + 
60                  (width*height)/4]);
61 }
62
63 static void ApplySub( hb_job_t * job, hb_buffer_t * buf,
64                       hb_buffer_t ** _sub )
65 {
66     hb_buffer_t * sub = *_sub;
67     hb_title_t * title = job->title;
68     int i, j, offset_top, offset_left;
69     uint8_t * lum, * alpha, * out, * sub_chromaU, * sub_chromaV;
70
71     if( !sub )
72     {
73         return;
74     }
75
76     /* If necessary, move the subtitle so it is not in a cropped zone.
77        When it won't fit, we center it so we loose as much on both ends.
78        Otherwise we try to leave a 20px margin around it. */
79
80     if( sub->height > title->height - job->crop[0] - job->crop[1] - 40 )
81         offset_top = job->crop[0] + ( title->height - job->crop[0] -
82                 job->crop[1] - sub->height ) / 2;
83     else if( sub->y < job->crop[0] + 20 )
84         offset_top = job->crop[0] + 20;
85     else if( sub->y > title->height - job->crop[1] - 20 - sub->height )
86         offset_top = title->height - job->crop[1] - 20 - sub->height;
87     else
88         offset_top = sub->y;
89
90     if( sub->width > title->width - job->crop[2] - job->crop[3] - 40 )
91         offset_left = job->crop[2] + ( title->width - job->crop[2] -
92                 job->crop[3] - sub->width ) / 2;
93     else if( sub->x < job->crop[2] + 20 )
94         offset_left = job->crop[2] + 20;
95     else if( sub->x > title->width - job->crop[3] - 20 - sub->width )
96         offset_left = title->width - job->crop[3] - 20 - sub->width;
97     else
98         offset_left = sub->x;
99
100     lum   = sub->data;
101     alpha = lum + sub->width * sub->height;
102     sub_chromaU = alpha + sub->width * sub->height;
103     sub_chromaV = sub_chromaU + sub->width * sub->height;
104
105     out   = buf->data + offset_top * title->width + offset_left;
106
107     for( i = 0; i < sub->height; i++ )
108     {
109         if( offset_top + i >= 0 && offset_top + i < title->height )
110         {
111             for( j = 0; j < sub->width; j++ )
112             {
113                 if( offset_left + j >= 0 && offset_left + j < title->width )
114                 {
115                     uint8_t *chromaU, *chromaV;
116
117                     /*
118                      * Merge the luminance and alpha with the picture
119                      */
120                     out[j] = ( (uint16_t) out[j] * ( 16 - (uint16_t) alpha[j] ) +
121                                (uint16_t) lum[j] * (uint16_t) alpha[j] ) >> 4;   
122                     /*
123                      * Set the chroma (colour) based on whether there is
124                      * any alpha at all. Don't try to blend with the picture.
125                      */
126                     chromaU = getU(buf->data, title->width, title->height,
127                                    offset_left+j, offset_top+i);
128                     
129                     chromaV = getV(buf->data, title->width, title->height,
130                                    offset_left+j, offset_top+i);
131                     
132                     if( alpha[j] > 0 )
133                     {
134                         /*
135                          * Add the chroma from the sub-picture, as this is 
136                          * not a transparent element.
137                          */
138                         *chromaU = sub_chromaU[j];
139                         *chromaV = sub_chromaV[j];
140                     } 
141                 }
142             }
143         }
144
145         lum   += sub->width;
146         alpha += sub->width;
147         sub_chromaU += sub->width;
148         sub_chromaV += sub->width;
149         out   += title->width;
150     }
151
152     hb_buffer_close( _sub );
153 }
154
155 int renderWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
156                 hb_buffer_t ** buf_out )
157 {
158     hb_work_private_t * pv = w->private_data;
159     hb_job_t   * job   = pv->job;
160     hb_title_t * title = job->title;
161     hb_buffer_t * in = *buf_in, * buf_tmp_in = *buf_in;
162     hb_buffer_t * ivtc_buffer = NULL;
163     
164     if(!in->data)
165     {
166         /* If the input buffer is end of stream, send out an empty one
167          * to the next stage as well. Note that this will result in us
168          * losing the current contents of the delay queue.
169          */
170        *buf_out = hb_buffer_init(0);
171        return HB_WORK_OK;
172     }
173
174     /*
175      * During the indepth_scan ditch the buffers here before applying filters or attempting to
176      * use the subtitles.
177      */
178     if( job->indepth_scan )
179     {      
180         *buf_out = NULL;
181         return HB_WORK_OK;
182     }
183     
184     /* Push subtitles onto queue just in case we need to delay a frame */
185     if( in->sub )
186     {
187         hb_fifo_push( pv->subtitle_queue, in->sub );
188     }
189     else
190     {
191         hb_fifo_push( pv->subtitle_queue,  hb_buffer_init(0) );
192     }
193     
194     /* Setup render buffer */
195     hb_buffer_t * buf_render = hb_buffer_init( 3 * job->width * job->height / 2 );  
196     
197     /* Cache frame start and stop times, so we can renumber
198        time stamps if dropping frames for VFR.              */ 
199     int i;
200     for( i = 3; i >= 1; i-- )
201     {
202         pv->last_start[i] = pv->last_start[i-1];
203         pv->last_stop[i] = pv->last_stop[i-1];
204     }
205     pv->last_start[0] = in->start;
206     pv->last_stop[0] = in->stop;
207     
208     /* Apply filters */
209     if( job->filters )
210     {
211         int filter_count = hb_list_count( job->filters );
212         int i;
213         
214         for( i = 0; i < filter_count; i++ )
215         {
216             hb_filter_object_t * filter = hb_list_item( job->filters, i );
217             
218             if( !filter )
219             {
220                 continue;
221             }            
222             
223             hb_buffer_t * buf_tmp_out = NULL;
224             
225             int result = filter->work( buf_tmp_in,
226                                        &buf_tmp_out, 
227                                        PIX_FMT_YUV420P, 
228                                        title->width, 
229                                        title->height, 
230                                        filter->private_data );
231             
232             /* 
233              * FILTER_OK:      set temp buffer to filter buffer, continue 
234              * FILTER_DELAY:   set temp buffer to NULL, abort 
235              * FILTER_DROP:    set temp buffer to NULL, pop subtitle, abort 
236              * FILTER_FAILED:  leave temp buffer alone, continue 
237              */
238             if( result == FILTER_OK )
239             {
240                 buf_tmp_in = buf_tmp_out;
241             }
242             else if( result == FILTER_DELAY )
243             {
244                 buf_tmp_in = NULL;
245                 break;
246             }            
247             else if( result == FILTER_DROP )
248             {
249                 if( job->vfr )
250                 {
251                     pv->frames_to_extend += 4;
252                     pv->dropped_frames++;
253                     hb_fifo_get( pv->subtitle_queue );
254                     buf_tmp_in = NULL;
255                 }
256                 else
257                 {
258                     buf_tmp_in = buf_tmp_out;
259                 }
260                 break;
261             }
262         }
263     }   
264         
265     /* Apply subtitles */
266     if( buf_tmp_in )
267     {
268         hb_buffer_t * subtitles = hb_fifo_get( pv->subtitle_queue );        
269         if( subtitles )
270         {
271             ApplySub( job, buf_tmp_in, &subtitles );
272         }
273     }
274     
275     /* Apply crop/scale if specified */
276     if( buf_tmp_in && pv->context )
277     {
278         avpicture_fill( &pv->pic_tmp_in, buf_tmp_in->data, 
279                         PIX_FMT_YUV420P,
280                         title->width, title->height );
281         
282         avpicture_fill( &pv->pic_tmp_out, buf_render->data, 
283                         PIX_FMT_YUV420P,
284                         job->width, job->height );
285
286         // Crop; this alters the pointer to the data to point to the correct place for cropped frame
287         av_picture_crop( &pv->pic_tmp_crop, &pv->pic_tmp_in, PIX_FMT_YUV420P,
288                          job->crop[0], job->crop[2] );
289
290         // Scale pic_crop into pic_render according to the context set up in renderInit
291         sws_scale(pv->context,
292                   pv->pic_tmp_crop.data, pv->pic_tmp_crop.linesize,
293                   0, title->height - (job->crop[0] + job->crop[1]),
294                   pv->pic_tmp_out.data,  pv->pic_tmp_out.linesize);
295         
296         hb_buffer_copy_settings( buf_render, buf_tmp_in );
297         
298         buf_tmp_in = buf_render;
299     }  
300
301     /* Set output to render buffer */
302     (*buf_out) = buf_render;
303
304     if( buf_tmp_in == NULL )
305     {
306         /* Teardown and cleanup buffers if we are emitting NULL */
307         if( buf_in && *buf_in )
308         {
309             hb_buffer_close( buf_in );
310             *buf_in = NULL;
311         }        
312         if( buf_out && *buf_out )
313         {
314             hb_buffer_close( buf_out );        
315             *buf_out = NULL;
316         }
317     }
318     else if( buf_tmp_in != buf_render )
319     {    
320         /* Copy temporary results and settings into render buffer */
321         memcpy( buf_render->data, buf_tmp_in->data, buf_render->size );
322         hb_buffer_copy_settings( buf_render, buf_tmp_in );
323     }
324     
325     if (*buf_out)
326     {
327         hb_fifo_push( pv->delay_queue, *buf_out );
328         *buf_out = NULL;        
329     }
330
331     /*
332      * Keep the last three frames in our queue, this ensures that we have the last
333      * two always in there should we need to rewrite the durations on them.
334      */
335     if( hb_fifo_size( pv->delay_queue ) >= 3 )
336     {
337         *buf_out = hb_fifo_get( pv->delay_queue );
338     } 
339
340     if( *buf_out )
341     {
342         if( pv->frames_to_extend )
343         {
344             /*
345              * A frame's been dropped by VFR detelecine.
346              * Gotta make up the lost time. This will also
347              * slow down the video to 23.976fps.
348              * The dropped frame ran for 3003 ticks, so
349              * divvy it up amongst the 4 frames left behind.
350              * This is what the delay_queue is for;
351              * telecined sequences start 2 frames before
352              * the dropped frame, so to slow down the right
353              * ones you need a 2 frame delay between
354              * reading input and writing output.
355              */
356             ivtc_buffer = *buf_out;
357             
358             /* The 4th cached frame will be the to use. */
359             ivtc_buffer->start = pv->last_start[3];
360             ivtc_buffer->stop = pv->last_stop[3];
361
362             if (pv->frames_to_extend % 4)
363                 ivtc_buffer->stop += 751;
364             else
365                 ivtc_buffer->stop += 750;
366             
367             /* Set the 3rd cached frame to start when this one stops,
368                and to stop 3003 ticks later -- a normal 29.97fps
369                length frame. If it needs to be extended as well to
370                make up lost time, it'll be handled on the next
371                loop through the renderer.                            */
372             pv->last_start[2] = ivtc_buffer->stop;
373             pv->last_stop[2] = ivtc_buffer->stop + 3003;
374             
375             pv->frames_to_extend--;
376             pv->extended_frames++;
377         }
378
379     }
380
381     return HB_WORK_OK;
382 }
383
384 void renderClose( hb_work_object_t * w )
385 {
386     hb_work_private_t * pv = w->private_data;   
387         
388     hb_log("render: dropped frames: %i (%i ticks)", pv->dropped_frames, (pv->dropped_frames * 3003) );
389     hb_log("render: extended frames: %i (%i ticks)", pv->extended_frames, ( ( pv->extended_frames / 4 ) * 3003 ) );
390     hb_log("render: Lost time: %i frames (%i ticks)", (pv->dropped_frames * 4) - (pv->extended_frames), (pv->dropped_frames * 3003) - ( ( pv->extended_frames / 4 ) * 3003 ) );
391
392     /* Cleanup subtitle queue */
393     if( pv->subtitle_queue )
394     {
395         hb_fifo_close( &pv->subtitle_queue );
396     }
397     
398     if( pv->delay_queue )
399     {
400         hb_fifo_close( &pv->delay_queue );
401     }
402    
403     /* Cleanup filters */
404     /* TODO: Move to work.c? */
405     if( pv->job->filters )
406     {
407         int filter_count = hb_list_count( pv->job->filters );
408         int i;
409         
410         for( i = 0; i < filter_count; i++ )
411         {
412             hb_filter_object_t * filter = hb_list_item( pv->job->filters, i );
413             
414             if( !filter ) continue;
415
416             filter->close( filter->private_data );
417         }
418         
419         hb_list_close( &pv->job->filters );
420     }    
421    
422     /* Cleanup render work structure */
423     free( pv );
424     w->private_data = NULL;    
425 }
426
427 int renderInit( hb_work_object_t * w, hb_job_t * job )
428 {   
429     /* Allocate new private work object */
430     hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
431     pv->job = job;
432     w->private_data = pv;
433
434     /* Get title and title size */
435     hb_title_t * title = job->title;
436
437     /* If crop or scale is specified, setup rescale context */
438     if( job->crop[0] || job->crop[1] || job->crop[2] || job->crop[3] ||
439         job->width != title->width || job->height != title->height )
440     {
441         pv->context = sws_getContext(title->width  - (job->crop[2] + job->crop[3]),
442                                      title->height - (job->crop[0] + job->crop[1]),
443                                      PIX_FMT_YUV420P,
444                                      job->width, job->height, PIX_FMT_YUV420P,
445                                      (uint16_t)(SWS_LANCZOS|SWS_ACCURATE_RND), NULL, NULL, NULL);
446     }   
447     
448     /* Setup FIFO queue for subtitle cache */
449     pv->subtitle_queue = hb_fifo_init( 8 );    
450     pv->delay_queue = hb_fifo_init( 8 );
451     pv->frames_to_extend = 0;
452     pv->dropped_frames = 0;
453     pv->extended_frames = 0;
454     pv->last_start[0] = 0;
455     pv->last_stop[0] = 0;
456     
457     /* Setup filters */
458     /* TODO: Move to work.c? */
459     if( job->filters )
460     {
461         int filter_count = hb_list_count( job->filters );
462         int i;
463         
464         for( i = 0; i < filter_count; i++ )
465         {
466             hb_filter_object_t * filter = hb_list_item( job->filters, i );
467
468             if( !filter ) continue;
469             
470             filter->private_data = filter->init( PIX_FMT_YUV420P,
471                                                  title->width,
472                                                  title->height,
473                                                  filter->settings );
474         }
475     }
476     
477     return 0;
478 }