OSDN Git Service

Another minor step on the way back to Solaris compiles - not linking as yet.
[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.fr/>.
5    It may be used under the terms of the GNU General Public License. */
6
7 #include "hb.h"
8 #include "hbffmpeg.h"
9
10 struct hb_work_private_s
11 {
12     hb_job_t * job;
13
14     struct SwsContext  * context;
15     AVPicture            pic_tmp_in;
16     AVPicture            pic_tmp_crop;
17     AVPicture            pic_tmp_out;
18     hb_buffer_t        * buf_scale;
19     hb_fifo_t          * subtitle_queue;
20     hb_fifo_t          * delay_queue;
21     int                  dropped_frames;
22     int                  extended_frames;
23     uint64_t             last_start[4];
24     uint64_t             last_stop[4];
25     uint64_t             lost_time[4];
26     uint64_t             total_lost_time;
27     uint64_t             total_gained_time;
28     int64_t              chapter_time;
29     int                  chapter_val;
30 };
31
32 int  renderInit( hb_work_object_t *, hb_job_t * );
33 int  renderWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
34 void renderClose( hb_work_object_t * );
35
36 hb_work_object_t hb_render =
37 {
38     WORK_RENDER,
39     "Renderer",
40     renderInit,
41     renderWork,
42     renderClose
43 };
44
45 /*
46  * getU() & getV()
47  *
48  * Utility function that finds where the U is in the YUV sub-picture
49  *
50  * The Y data is at the top, followed by U and V, but the U and V
51  * are half the width of the Y, i.e. each chroma element covers 2x2
52  * of the Y's.
53  */
54 static uint8_t *getU(uint8_t *data, int width, int height, int x, int y)
55 {
56     return(&data[(y>>1) * ((width+1)>>1) + (x>>1) + width*height]);
57 }
58
59 static uint8_t *getV(uint8_t *data, int width, int height, int x, int y)
60 {
61     int w2 = (width+1) >> 1, h2 = (height+1) >> 1;
62     return(&data[(y>>1) * w2 + (x>>1) + width*height + w2*h2]);
63 }
64
65 static void ApplySub( hb_job_t * job, hb_buffer_t * buf,
66                       hb_buffer_t ** _sub )
67 {
68     hb_buffer_t * sub = *_sub;
69     hb_title_t * title = job->title;
70     int i, j, offset_top, offset_left, margin_top, margin_percent;
71     uint8_t * lum, * alpha, * out, * sub_chromaU, * sub_chromaV;
72
73     /*
74      * Percent of height of picture that form a margin that subtitles
75      * should not be displayed within.
76      */
77     margin_percent = 2;
78
79     if( !sub )
80     {
81         return;
82     }
83
84     /*
85      * If necessary, move the subtitle so it is not in a cropped zone.
86      * When it won't fit, we center it so we lose as much on both ends.
87      * Otherwise we try to leave a 20px or 2% margin around it.
88      */
89     margin_top = ( ( title->height - job->crop[0] - job->crop[1] ) *
90                    margin_percent ) / 100;
91
92     if( margin_top > 20 )
93     {
94         /*
95          * A maximum margin of 20px regardless of height of the picture.
96          */
97         margin_top = 20;
98     }
99
100     if( sub->height > title->height - job->crop[0] - job->crop[1] -
101         ( margin_top * 2 ) )
102     {
103         /*
104          * The subtitle won't fit in the cropped zone, so center
105          * it vertically so we fit in as much as we can.
106          */
107         offset_top = job->crop[0] + ( title->height - job->crop[0] -
108                                       job->crop[1] - sub->height ) / 2;
109     }
110     else if( sub->y < job->crop[0] + margin_top )
111     {
112         /*
113          * The subtitle fits in the cropped zone, but is currently positioned
114          * within our top margin, so move it outside of our margin.
115          */
116         offset_top = job->crop[0] + margin_top;
117     }
118     else if( sub->y > title->height - job->crop[1] - margin_top - sub->height )
119     {
120         /*
121          * The subtitle fits in the cropped zone, and is not within the top
122          * margin but is within the bottom margin, so move it to be above
123          * the margin.
124          */
125         offset_top = title->height - job->crop[1] - margin_top - sub->height;
126     }
127     else
128     {
129         /*
130          * The subtitle is fine where it is.
131          */
132         offset_top = sub->y;
133     }
134
135     if( sub->width > title->width - job->crop[2] - job->crop[3] - 40 )
136         offset_left = job->crop[2] + ( title->width - job->crop[2] -
137                 job->crop[3] - sub->width ) / 2;
138     else if( sub->x < job->crop[2] + 20 )
139         offset_left = job->crop[2] + 20;
140     else if( sub->x > title->width - job->crop[3] - 20 - sub->width )
141         offset_left = title->width - job->crop[3] - 20 - sub->width;
142     else
143         offset_left = sub->x;
144
145     lum   = sub->data;
146     alpha = lum + sub->width * sub->height;
147     sub_chromaU = alpha + sub->width * sub->height;
148     sub_chromaV = sub_chromaU + sub->width * sub->height;
149
150     out   = buf->data + offset_top * title->width + offset_left;
151
152     for( i = 0; i < sub->height; i++ )
153     {
154         if( offset_top + i >= 0 && offset_top + i < title->height )
155         {
156             for( j = 0; j < sub->width; j++ )
157             {
158                 if( offset_left + j >= 0 && offset_left + j < title->width )
159                 {
160                     uint8_t *chromaU, *chromaV;
161
162                     /*
163                      * Merge the luminance and alpha with the picture
164                      */
165                     out[j] = ( (uint16_t) out[j] * ( 16 - (uint16_t) alpha[j] ) +
166                                (uint16_t) lum[j] * (uint16_t) alpha[j] ) >> 4;
167                     /*
168                      * Set the chroma (colour) based on whether there is
169                      * any alpha at all. Don't try to blend with the picture.
170                      */
171                     chromaU = getU(buf->data, title->width, title->height,
172                                    offset_left+j, offset_top+i);
173
174                     chromaV = getV(buf->data, title->width, title->height,
175                                    offset_left+j, offset_top+i);
176
177                     if( alpha[j] > 0 )
178                     {
179                         /*
180                          * Add the chroma from the sub-picture, as this is
181                          * not a transparent element.
182                          */
183                         *chromaU = sub_chromaU[j];
184                         *chromaV = sub_chromaV[j];
185                     }
186                 }
187             }
188         }
189
190         lum   += sub->width;
191         alpha += sub->width;
192         sub_chromaU += sub->width;
193         sub_chromaV += sub->width;
194         out   += title->width;
195     }
196
197     hb_buffer_close( _sub );
198 }
199
200 int renderWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
201                 hb_buffer_t ** buf_out )
202 {
203     hb_work_private_t * pv = w->private_data;
204     hb_job_t   * job   = pv->job;
205     hb_title_t * title = job->title;
206     hb_buffer_t * in = *buf_in, * buf_tmp_in = *buf_in;
207     hb_buffer_t * ivtc_buffer = NULL;
208
209     if( in->size <= 0 )
210     {
211         /* If the input buffer is end of stream, send out an empty one
212          * to the next stage as well. Note that this will result in us
213          * losing the current contents of the delay queue.
214          */
215         *buf_out = in;
216         *buf_in = NULL;
217         return HB_WORK_DONE;
218     }
219
220     /*
221      * During the indepth_scan ditch the buffers here before applying filters or attempting to
222      * use the subtitles.
223      */
224     if( job->indepth_scan )
225     {
226         *buf_out = NULL;
227         return HB_WORK_OK;
228     }
229
230     /* Push subtitles onto queue just in case we need to delay a frame */
231     if( in->sub )
232     {
233         hb_fifo_push( pv->subtitle_queue, in->sub );
234     }
235     else
236     {
237         hb_fifo_push( pv->subtitle_queue,  hb_buffer_init(0) );
238     }
239
240     /* If there's a chapter mark remember it in case we delay or drop its frame */
241     if( in->new_chap && job->vfr )
242     {
243         pv->chapter_time = in->start;
244         pv->chapter_val = in->new_chap;
245         in->new_chap = 0;
246     }
247
248     /* Setup render buffer */
249     hb_buffer_t * buf_render = hb_video_buffer_init( job->width, job->height );
250
251     /* Apply filters */
252     if( job->filters )
253     {
254         int filter_count = hb_list_count( job->filters );
255         int i;
256
257         for( i = 0; i < filter_count; i++ )
258         {
259             hb_filter_object_t * filter = hb_list_item( job->filters, i );
260
261             if( !filter )
262             {
263                 continue;
264             }
265
266             hb_buffer_t * buf_tmp_out = NULL;
267
268             int result = filter->work( buf_tmp_in,
269                                        &buf_tmp_out,
270                                        PIX_FMT_YUV420P,
271                                        title->width,
272                                        title->height,
273                                        filter->private_data );
274
275             /*
276              * FILTER_OK:      set temp buffer to filter buffer, continue
277              * FILTER_DELAY:   set temp buffer to NULL, abort
278              * FILTER_DROP:    set temp buffer to NULL, pop subtitle, abort
279              * FILTER_FAILED:  leave temp buffer alone, continue
280              */
281             if( result == FILTER_OK )
282             {
283                 buf_tmp_in = buf_tmp_out;
284             }
285             else if( result == FILTER_DELAY )
286             {
287                 buf_tmp_in = NULL;
288                 break;
289             }
290             else if( result == FILTER_DROP )
291             {
292                 if( job->vfr )
293                 {
294                     /* We need to compensate for the time lost by dropping this frame.
295                        Spread its duration out in quarters, because usually dropped frames
296                        maintain a 1-out-of-5 pattern and this spreads it out amongst the remaining ones.
297                        Store these in the lost_time array, which has 4 slots in it.
298                        Because not every frame duration divides evenly by 4, and we can't lose the
299                        remainder, we have to go through an awkward process to preserve it in the 4th array index. */
300                     uint64_t temp_duration = buf_tmp_out->stop - buf_tmp_out->start;
301                     pv->lost_time[0] += (temp_duration / 4);
302                     pv->lost_time[1] += (temp_duration / 4);
303                     pv->lost_time[2] += (temp_duration / 4);
304                     pv->lost_time[3] += ( temp_duration - (temp_duration / 4) - (temp_duration / 4) - (temp_duration / 4) );
305
306                     pv->total_lost_time += temp_duration;
307                     pv->dropped_frames++;
308
309                     /* Pop the frame's subtitle and dispose of it. */
310                     hb_buffer_t * subtitles = hb_fifo_get( pv->subtitle_queue );
311                     hb_buffer_close( &subtitles );
312                     buf_tmp_in = NULL;
313                     break;
314                 }
315                 else
316                 {
317                     buf_tmp_in = buf_tmp_out;
318                 }
319             }
320         }
321     }
322
323     if( buf_tmp_in )
324     {
325         /* Cache frame start and stop times, so we can renumber
326            time stamps if dropping frames for VFR.              */
327         int i;
328         for( i = 3; i >= 1; i-- )
329         {
330             pv->last_start[i] = pv->last_start[i-1];
331             pv->last_stop[i] = pv->last_stop[i-1];
332         }
333
334         /* In order to make sure we have continuous time stamps, store
335            the current frame's duration as starting when the last one stopped. */
336         pv->last_start[0] = pv->last_stop[1];
337         pv->last_stop[0] = pv->last_start[0] + (in->stop - in->start);
338     }
339
340     /* Apply subtitles */
341     if( buf_tmp_in )
342     {
343         hb_buffer_t * subtitles = hb_fifo_get( pv->subtitle_queue );
344         if( subtitles )
345         {
346             ApplySub( job, buf_tmp_in, &subtitles );
347         }
348     }
349
350     /* Apply crop/scale if specified */
351     if( buf_tmp_in && pv->context )
352     {
353         avpicture_fill( &pv->pic_tmp_in, buf_tmp_in->data,
354                         PIX_FMT_YUV420P,
355                         title->width, title->height );
356
357         avpicture_fill( &pv->pic_tmp_out, buf_render->data,
358                         PIX_FMT_YUV420P,
359                         job->width, job->height );
360
361         // Crop; this alters the pointer to the data to point to the correct place for cropped frame
362         av_picture_crop( &pv->pic_tmp_crop, &pv->pic_tmp_in, PIX_FMT_YUV420P,
363                          job->crop[0], job->crop[2] );
364
365         // Scale pic_crop into pic_render according to the context set up in renderInit
366         sws_scale(pv->context,
367                   pv->pic_tmp_crop.data, pv->pic_tmp_crop.linesize,
368                   0, title->height - (job->crop[0] + job->crop[1]),
369                   pv->pic_tmp_out.data,  pv->pic_tmp_out.linesize);
370
371         hb_buffer_copy_settings( buf_render, buf_tmp_in );
372
373         buf_tmp_in = buf_render;
374     }
375
376     /* Set output to render buffer */
377     (*buf_out) = buf_render;
378
379     if( buf_tmp_in == NULL )
380     {
381         /* Teardown and cleanup buffers if we are emitting NULL */
382         if( buf_in && *buf_in )
383         {
384             hb_buffer_close( buf_in );
385             *buf_in = NULL;
386         }
387         if( buf_out && *buf_out )
388         {
389             hb_buffer_close( buf_out );
390             *buf_out = NULL;
391         }
392     }
393     else if( buf_tmp_in != buf_render )
394     {
395         /* Copy temporary results and settings into render buffer */
396         memcpy( buf_render->data, buf_tmp_in->data, buf_render->size );
397         hb_buffer_copy_settings( buf_render, buf_tmp_in );
398     }
399
400     if (*buf_out && job->vfr)
401     {
402         hb_fifo_push( pv->delay_queue, *buf_out );
403         *buf_out = NULL;
404     }
405
406     /*
407      * Keep the last three frames in our queue, this ensures that we have the last
408      * two always in there should we need to rewrite the durations on them.
409      */
410
411     if( job->vfr )
412     {
413         if( hb_fifo_size( pv->delay_queue ) >= 4 )
414         {
415             *buf_out = hb_fifo_get( pv->delay_queue );
416         }
417     }
418
419     if( *buf_out && job->vfr)
420     {
421         /* The current frame exists. That means it hasn't been dropped by a filter.
422            Make it accessible as ivtc_buffer so we can edit its duration if needed. */
423         ivtc_buffer = *buf_out;
424
425         if( pv->lost_time[3] > 0 )
426         {
427             /*
428              * A frame's been dropped earlier by VFR detelecine.
429              * Gotta make up the lost time. This will also
430              * slow down the video.
431              * The dropped frame's has to be accounted for, so
432              * divvy it up amongst the 4 frames left behind.
433              * This is what the delay_queue is for;
434              * telecined sequences start 2 frames before
435              * the dropped frame, so to slow down the right
436              * ones you need a 2 frame delay between
437              * reading input and writing output.
438              */
439
440             /* We want to extend the outputted frame's duration by the value
441               stored in the 4th slot of the lost_time array. Because we need
442               to adjust all the values in the array so they're contiguous,
443               extend the duration inside the array first, before applying
444               it to the current frame buffer. */
445             pv->last_stop[3] += pv->lost_time[3];
446
447             /* Log how much time has been added back in to the video. */
448             pv->total_gained_time += pv->lost_time[3];
449
450             /* We've pulled the 4th value from the lost_time array
451                and added it to the last_stop array's 4th slot. Now, rotate the
452                 lost_time array so the 4th slot now holds the 3rd's value, and
453                so on down the line, and set the 0 index to a value of 0. */
454             int i;
455             for( i=2; i >=  0; i--)
456             {
457                 pv->lost_time[i+1] = pv->lost_time[i];
458             }
459             pv->lost_time[0] = 0;
460
461             /* Log how many frames have had their durations extended. */
462             pv->extended_frames++;
463         }
464
465         /* We can't use the given time stamps. Previous frames
466            might already have been extended, throwing off the
467            raw values fed to render.c. Instead, their
468            stop and start times are stored in arrays.
469            The 4th cached frame will be the to use.
470            If it needed its duration extended to make up
471            lost time, it will have happened above. */
472         ivtc_buffer->start = pv->last_start[3];
473         ivtc_buffer->stop = pv->last_stop[3];
474
475         /* Set the 3rd cached frame to start when this one stops,
476            and so on down the line. If any of them need to be
477            extended as well to make up lost time, it'll be handled
478            on the next loop through the renderer.  */
479         int i;
480         for (i = 2; i >= 0; i--)
481         {
482             int temp_duration = pv->last_stop[i] - pv->last_start[i];
483             pv->last_start[i] = pv->last_stop[i+1];
484             pv->last_stop[i] = pv->last_start[i] + temp_duration;
485         }
486
487         /* If we have a pending chapter mark and this frame is at
488            or after the time of the mark, mark this frame & clear
489            our pending mark. */
490         if( pv->chapter_time && pv->chapter_time <= ivtc_buffer->start )
491         {
492             ivtc_buffer->new_chap = pv->chapter_val;
493             pv->chapter_time = 0;
494         }
495
496     }
497
498     return HB_WORK_OK;
499 }
500
501 void renderClose( hb_work_object_t * w )
502 {
503     hb_work_private_t * pv = w->private_data;
504
505     hb_log("render: lost time: %lld (%i frames)", pv->total_lost_time, pv->dropped_frames);
506     hb_log("render: gained time: %lld (%i frames) (%lld not accounted for)", pv->total_gained_time, pv->extended_frames, pv->total_lost_time - pv->total_gained_time);
507     if (pv->dropped_frames)
508         hb_log("render: average dropped frame duration: %lld", (pv->total_lost_time / pv->dropped_frames) );
509
510     /* Cleanup subtitle queue */
511     if( pv->subtitle_queue )
512     {
513         hb_fifo_close( &pv->subtitle_queue );
514     }
515
516     if( pv->delay_queue )
517     {
518         hb_fifo_close( &pv->delay_queue );
519     }
520
521     /* Cleanup render work structure */
522     free( pv );
523     w->private_data = NULL;
524 }
525
526 int renderInit( hb_work_object_t * w, hb_job_t * job )
527 {
528     /* Allocate new private work object */
529     hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
530     pv->job = job;
531     w->private_data = pv;
532     uint32_t    swsflags;
533
534     swsflags = SWS_LANCZOS | SWS_ACCURATE_RND;
535
536     /* Get title and title size */
537     hb_title_t * title = job->title;
538
539     /* If crop or scale is specified, setup rescale context */
540     if( job->crop[0] || job->crop[1] || job->crop[2] || job->crop[3] ||
541         job->width != title->width || job->height != title->height )
542     {
543         pv->context = sws_getContext(title->width  - (job->crop[2] + job->crop[3]),
544                                      title->height - (job->crop[0] + job->crop[1]),
545                                      PIX_FMT_YUV420P,
546                                      job->width, job->height, PIX_FMT_YUV420P,
547                                      swsflags, NULL, NULL, NULL);
548     }
549
550     /* Setup FIFO queue for subtitle cache */
551     pv->subtitle_queue = hb_fifo_init( 8 );
552     pv->delay_queue = hb_fifo_init( 8 );
553
554     /* VFR IVTC needs a bunch of time-keeping variables to track
555       how many frames are dropped, how many are extended, what the
556       last 4 start and stop times were (so they can be modified),
557       how much time has been lost and gained overall, how much time
558       the latest 4 frames should be extended by, and where chapter
559       markers are (so they can be saved if their frames are dropped.) */
560     pv->dropped_frames = 0;
561     pv->extended_frames = 0;
562     pv->last_start[0] = 0;
563     pv->last_stop[0] = 0;
564     pv->total_lost_time = 0;
565     pv->total_gained_time = 0;
566     pv->lost_time[0] = 0; pv->lost_time[1] = 0; pv->lost_time[2] = 0; pv->lost_time[3] = 0;
567     pv->chapter_time = 0;
568     pv->chapter_val  = 0;
569
570     /* Setup filters */
571     /* TODO: Move to work.c? */
572     if( job->filters )
573     {
574         int filter_count = hb_list_count( job->filters );
575         int i;
576
577         for( i = 0; i < filter_count; i++ )
578         {
579             hb_filter_object_t * filter = hb_list_item( job->filters, i );
580
581             if( !filter ) continue;
582
583             filter->private_data = filter->init( PIX_FMT_YUV420P,
584                                                  title->width,
585                                                  title->height,
586                                                  filter->settings );
587         }
588     }
589
590     return 0;
591 }