OSDN Git Service

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