OSDN Git Service

WinGui:
[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     int                  count_frames;      // frames output so far
31     double               frame_rate;        // 90KHz ticks per frame (for CFR/PFR)
32     double               out_last_stop;     // where last frame ended (for CFR/PFR)
33     int                  drops;             // frames dropped (for CFR/PFR)
34     int                  dups;              // frames duped (for CFR/PFR)
35 };
36
37 int  renderInit( hb_work_object_t *, hb_job_t * );
38 int  renderWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
39 void renderClose( hb_work_object_t * );
40
41 hb_work_object_t hb_render =
42 {
43     WORK_RENDER,
44     "Renderer",
45     renderInit,
46     renderWork,
47     renderClose
48 };
49
50 /*
51  * getU() & getV()
52  *
53  * Utility function that finds where the U is in the YUV sub-picture
54  *
55  * The Y data is at the top, followed by U and V, but the U and V
56  * are half the width of the Y, i.e. each chroma element covers 2x2
57  * of the Y's.
58  */
59 static uint8_t *getU(uint8_t *data, int width, int height, int x, int y)
60 {
61     return(&data[(y>>1) * ((width+1)>>1) + (x>>1) + width*height]);
62 }
63
64 static uint8_t *getV(uint8_t *data, int width, int height, int x, int y)
65 {
66     int w2 = (width+1) >> 1, h2 = (height+1) >> 1;
67     return(&data[(y>>1) * w2 + (x>>1) + width*height + w2*h2]);
68 }
69
70 // Draws the specified PICTURESUB subtitle on the specified video packet.
71 // Disposes the subtitle afterwards.
72 static void ApplySub( hb_job_t * job, hb_buffer_t * buf,
73                       hb_buffer_t ** _sub )
74 {
75     hb_buffer_t * sub = *_sub;
76     hb_title_t * title = job->title;
77     int i, j, offset_top, offset_left, margin_top, margin_percent;
78     uint8_t * lum, * alpha, * out, * sub_chromaU, * sub_chromaV;
79
80     /*
81      * Percent of height of picture that form a margin that subtitles
82      * should not be displayed within.
83      */
84     margin_percent = 2;
85
86     if( !sub )
87     {
88         return;
89     }
90
91     /*
92      * If necessary, move the subtitle so it is not in a cropped zone.
93      * When it won't fit, we center it so we lose as much on both ends.
94      * Otherwise we try to leave a 20px or 2% margin around it.
95      */
96     margin_top = ( ( title->height - job->crop[0] - job->crop[1] ) *
97                    margin_percent ) / 100;
98
99     if( margin_top > 20 )
100     {
101         /*
102          * A maximum margin of 20px regardless of height of the picture.
103          */
104         margin_top = 20;
105     }
106
107     if( sub->height > title->height - job->crop[0] - job->crop[1] -
108         ( margin_top * 2 ) )
109     {
110         /*
111          * The subtitle won't fit in the cropped zone, so center
112          * it vertically so we fit in as much as we can.
113          */
114         offset_top = job->crop[0] + ( title->height - job->crop[0] -
115                                       job->crop[1] - sub->height ) / 2;
116     }
117     else if( sub->y < job->crop[0] + margin_top )
118     {
119         /*
120          * The subtitle fits in the cropped zone, but is currently positioned
121          * within our top margin, so move it outside of our margin.
122          */
123         offset_top = job->crop[0] + margin_top;
124     }
125     else if( sub->y > title->height - job->crop[1] - margin_top - sub->height )
126     {
127         /*
128          * The subtitle fits in the cropped zone, and is not within the top
129          * margin but is within the bottom margin, so move it to be above
130          * the margin.
131          */
132         offset_top = title->height - job->crop[1] - margin_top - sub->height;
133     }
134     else
135     {
136         /*
137          * The subtitle is fine where it is.
138          */
139         offset_top = sub->y;
140     }
141
142     if( sub->width > title->width - job->crop[2] - job->crop[3] - 40 )
143         offset_left = job->crop[2] + ( title->width - job->crop[2] -
144                 job->crop[3] - sub->width ) / 2;
145     else if( sub->x < job->crop[2] + 20 )
146         offset_left = job->crop[2] + 20;
147     else if( sub->x > title->width - job->crop[3] - 20 - sub->width )
148         offset_left = title->width - job->crop[3] - 20 - sub->width;
149     else
150         offset_left = sub->x;
151
152     lum   = sub->data;
153     alpha = lum + sub->width * sub->height;
154     sub_chromaU = alpha + sub->width * sub->height;
155     sub_chromaV = sub_chromaU + sub->width * sub->height;
156
157     out   = buf->data + offset_top * title->width + offset_left;
158
159     for( i = 0; i < sub->height; i++ )
160     {
161         if( offset_top + i >= 0 && offset_top + i < title->height )
162         {
163             for( j = 0; j < sub->width; j++ )
164             {
165                 if( offset_left + j >= 0 && offset_left + j < title->width )
166                 {
167                     uint8_t *chromaU, *chromaV;
168
169                     /*
170                      * Merge the luminance and alpha with the picture
171                      */
172                     out[j] = ( (uint16_t) out[j] * ( 16 - (uint16_t) alpha[j] ) +
173                                (uint16_t) lum[j] * (uint16_t) alpha[j] ) >> 4;
174                     /*
175                      * Set the chroma (colour) based on whether there is
176                      * any alpha at all. Don't try to blend with the picture.
177                      */
178                     chromaU = getU(buf->data, title->width, title->height,
179                                    offset_left+j, offset_top+i);
180
181                     chromaV = getV(buf->data, title->width, title->height,
182                                    offset_left+j, offset_top+i);
183
184                     if( alpha[j] > 0 )
185                     {
186                         /*
187                          * Add the chroma from the sub-picture, as this is
188                          * not a transparent element.
189                          */
190                         *chromaU = sub_chromaU[j];
191                         *chromaV = sub_chromaV[j];
192                     }
193                 }
194             }
195         }
196
197         lum   += sub->width;
198         alpha += sub->width;
199         sub_chromaU += sub->width;
200         sub_chromaV += sub->width;
201         out   += title->width;
202     }
203
204     hb_buffer_close( _sub );
205 }
206
207 // delete the buffer 'out' from the chain of buffers whose head is 'buf_out'.
208 // out & buf_out must be non-null (checks prior to anywhere this routine
209 // can be called guarantee this) and out must be on buf_out's chain
210 // (all places that call this get 'out' while traversing the chain).
211 // 'out' is freed and its predecessor is returned.
212 static hb_buffer_t *delete_buffer_from_chain( hb_buffer_t **buf_out, hb_buffer_t *out)
213 {
214     hb_buffer_t *succ = out->next;
215     hb_buffer_t *pred = *buf_out;
216     if ( pred == out )
217     {
218         // we're deleting the first buffer
219         *buf_out = succ;
220     }
221     else
222     {
223         // target isn't the first buf so search for its predecessor.
224         while ( pred->next != out )
225         {
226             pred = pred->next;
227         }
228         // found 'out' - remove it from the chain
229         pred->next = succ;
230     }
231     out->next = 0;
232     hb_buffer_close( &out );
233     return succ;
234 }
235
236 // insert buffer 'succ' after buffer chain element 'pred'.
237 // caller must guarantee that 'pred' and 'succ' are non-null.
238 static hb_buffer_t *insert_buffer_in_chain( hb_buffer_t *pred, hb_buffer_t *succ )
239 {
240     succ->next = pred->next;
241     pred->next = succ;
242     return succ;
243 }
244
245 // This section of the code implements video frame rate control.
246 // Since filters are allowed to duplicate and drop frames (which
247 // changes the timing), this has to be the last thing done in render.
248 //
249 // There are three options, selected by the value of job->cfr:
250 //   0 - Variable Frame Rate (VFR) or 'same as source': frame times
251 //       are left alone
252 //   1 - Constant Frame Rate (CFR): Frame timings are adjusted so that all
253 //       frames are exactly job->vrate_base ticks apart. Frames are dropped
254 //       or duplicated if necessary to maintain this spacing.
255 //   2 - Peak Frame Rate (PFR): job->vrate_base is treated as the peak
256 //       average frame rate. I.e., the average frame rate (current frame
257 //       end time divided by number of frames so far) is never allowed to be
258 //       greater than job->vrate_base and frames are dropped if necessary
259 //       to keep the average under this value. Other than those drops, frame
260 //       times are left alone.
261 //
262
263 static void adjust_frame_rate( hb_work_private_t *pv, hb_buffer_t **buf_out )
264 {
265     hb_buffer_t *out = *buf_out;
266
267     while ( out && out->size > 0 )
268     {
269         // this frame has to start where the last one stopped.
270         out->start = pv->out_last_stop;
271
272         // compute where this frame would stop if the frame rate were constant
273         // (this is our target stopping time for CFR and earliest possible
274         // stopping time for PFR).
275         double cfr_stop = pv->frame_rate * ( pv->count_frames + 1 );
276
277         if ( cfr_stop - (double)out->stop >= pv->frame_rate )
278         {
279             // This frame stops a frame time or more in the past - drop it
280             // but don't lose its chapter mark.
281             if ( out->new_chap )
282             {
283                 pv->chapter_time = out->start;
284                 pv->chapter_val = out->new_chap;
285             }
286             ++pv->drops;
287             out = delete_buffer_from_chain( buf_out, out );
288             continue;
289         }
290
291         // at this point we know that this frame doesn't push the average
292         // rate over the limit so we just pass it on for PFR. For CFR we're
293         // going to return it (with its start & stop times modified) and
294         // we may have to dup it.
295         ++pv->count_frames;
296         if ( pv->job->cfr > 1 )
297         {
298             // PFR - we're going to keep the frame but may need to
299             // adjust it's stop time to meet the average rate constraint.
300             if ( out->stop <= cfr_stop )
301             {
302                 out->stop = cfr_stop;
303             }
304         }
305         else
306         {
307             // we're doing CFR so we have to either trim some time from a
308             // buffer that ends too far in the future or, if the buffer is
309             // two or more frame times long, split it into multiple pieces,
310             // each of which is a frame time long.
311             double excess_dur = (double)out->stop - cfr_stop;
312             out->stop = cfr_stop;
313             for ( ; excess_dur >= pv->frame_rate; excess_dur -= cfr_stop )
314             {
315                 /* next frame too far ahead - dup current frame */
316                 hb_buffer_t *dup = hb_buffer_init( out->size );
317                 memcpy( dup->data, out->data, out->size );
318                 hb_buffer_copy_settings( dup, out );
319                 dup->new_chap = 0;
320                 dup->start = cfr_stop;
321                 cfr_stop += pv->frame_rate;
322                 dup->stop = cfr_stop;
323                 out = insert_buffer_in_chain( out, dup );
324                 ++pv->dups;
325                 ++pv->count_frames;
326             }
327         }
328         pv->out_last_stop = out->stop;
329         out = out->next;
330     }
331 }
332
333 int renderWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
334                 hb_buffer_t ** buf_out )
335 {
336     hb_work_private_t * pv = w->private_data;
337     hb_job_t   * job   = pv->job;
338     hb_title_t * title = job->title;
339     hb_buffer_t * in = *buf_in, * buf_tmp_in = *buf_in;
340     hb_buffer_t * ivtc_buffer = NULL;
341
342     if( in->size <= 0 )
343     {
344         hb_buffer_t *head = NULL, *tail = NULL, *next;
345         int counter = 2;
346
347         /* If the input buffer is end of stream, send out an empty one
348          * to the next stage as well. To avoid losing the contents of
349          * the delay queue connect the buffers in the delay queue in 
350          * the correct order, and add the end of stream buffer to the
351          * end.
352          */     
353         while( ( next = hb_fifo_get( pv->delay_queue ) ) != NULL )
354         {
355             
356             /* We can't use the given time stamps. Previous frames
357                might already have been extended, throwing off the
358                raw values fed to render.c. Instead, their
359                stop and start times are stored in arrays.
360                The 4th cached frame will be the to use.
361                If it needed its duration extended to make up
362                lost time, it will have happened above. */
363             next->start = pv->last_start[counter];
364             next->stop = pv->last_stop[counter--];
365             
366             if( !head && !tail )
367             {
368                 head = tail = next;
369             } else {
370                 tail->next = next;
371                 tail = next;
372             }
373         }
374         if( tail )
375         {
376             tail->next = in;
377             *buf_out = head;
378             if ( job->cfr )
379             {
380                 adjust_frame_rate( pv, buf_out );
381             }
382         } else {
383             *buf_out = in;
384         }     
385         *buf_in = NULL;
386         return HB_WORK_DONE;
387     }
388
389     /*
390      * During the indepth_scan ditch the buffers here before applying filters or attempting to
391      * use the subtitles.
392      */
393     if( job->indepth_scan )
394     {
395         *buf_out = NULL;
396         return HB_WORK_OK;
397     }
398
399     /* Push subtitles onto queue just in case we need to delay a frame */
400     if( in->sub )
401     {
402         hb_fifo_push_list_element( pv->subtitle_queue, in->sub );
403     }
404     else
405     {
406         hb_fifo_push_list_element( pv->subtitle_queue, NULL );
407     }
408
409     /* If there's a chapter mark remember it in case we delay or drop its frame */
410     if( in->new_chap )
411     {
412         pv->chapter_time = in->start;
413         pv->chapter_val = in->new_chap;
414         in->new_chap = 0;
415     }
416
417     /* Setup render buffer */
418     hb_buffer_t * buf_render = hb_video_buffer_init( job->width, job->height );
419
420     /* Apply filters */
421     if( job->filters )
422     {
423         int filter_count = hb_list_count( job->filters );
424         int i;
425
426         for( i = 0; i < filter_count; i++ )
427         {
428             hb_filter_object_t * filter = hb_list_item( job->filters, i );
429
430             if( !filter )
431             {
432                 continue;
433             }
434
435             hb_buffer_t * buf_tmp_out = NULL;
436
437             int result = filter->work( buf_tmp_in,
438                                        &buf_tmp_out,
439                                        PIX_FMT_YUV420P,
440                                        title->width,
441                                        title->height,
442                                        filter->private_data );
443
444             /*
445              * FILTER_OK:      set temp buffer to filter buffer, continue
446              * FILTER_DELAY:   set temp buffer to NULL, abort
447              * FILTER_DROP:    set temp buffer to NULL, pop subtitle, abort
448              * FILTER_FAILED:  leave temp buffer alone, continue
449              */
450             if( result == FILTER_OK )
451             {
452                 buf_tmp_in = buf_tmp_out;
453             }
454             else if( result == FILTER_DELAY )
455             {
456                 // Process the current frame later
457                 
458                 buf_tmp_in = NULL;
459                 break;
460             }
461             else if( result == FILTER_DROP )
462             {
463                 // Drop the current frame
464                 
465                 /* We need to compensate for the time lost by dropping this frame.
466                    Spread its duration out in quarters, because usually dropped frames
467                    maintain a 1-out-of-5 pattern and this spreads it out amongst the remaining ones.
468                    Store these in the lost_time array, which has 4 slots in it.
469                    Because not every frame duration divides evenly by 4, and we can't lose the
470                    remainder, we have to go through an awkward process to preserve it in the 4th array index. */
471                 uint64_t temp_duration = buf_tmp_out->stop - buf_tmp_out->start;
472                 pv->lost_time[0] += (temp_duration / 4);
473                 pv->lost_time[1] += (temp_duration / 4);
474                 pv->lost_time[2] += (temp_duration / 4);
475                 pv->lost_time[3] += ( temp_duration - (temp_duration / 4) - (temp_duration / 4) - (temp_duration / 4) );
476
477                 pv->total_lost_time += temp_duration;
478                 pv->dropped_frames++;
479
480                 /* Pop the frame's subtitle list and dispose of it. */
481                 hb_buffer_t * sub_list = hb_fifo_get_list_element( pv->subtitle_queue );
482                 hb_buffer_t * sub;
483                 hb_buffer_t * sub_next;
484                 for ( sub = sub_list; sub; sub = sub_next )
485                 {
486                     sub_next = sub->next;
487                     // XXX: Prevent hb_buffer_close from killing the whole list
488                     //      before we finish iterating over it
489                     sub->next = NULL;
490                     
491                     hb_buffer_t * subpicture_list = sub;
492                     hb_buffer_t * subpicture;
493                     hb_buffer_t * subpicture_next;
494                     for ( subpicture = subpicture_list; subpicture; subpicture = subpicture_next )
495                     {
496                         subpicture_next = subpicture->next_subpicture;
497                         
498                         hb_buffer_close( &subpicture );
499                     }
500                 }
501                 
502                 buf_tmp_in = NULL;
503                 break;
504             }
505         }
506     }
507
508     if( buf_tmp_in )
509     {
510         /* Cache frame start and stop times, so we can renumber
511            time stamps if dropping frames for VFR.              */
512         int i;
513         for( i = 3; i >= 1; i-- )
514         {
515             pv->last_start[i] = pv->last_start[i-1];
516             pv->last_stop[i] = pv->last_stop[i-1];
517         }
518
519         /* In order to make sure we have continuous time stamps, store
520            the current frame's duration as starting when the last one stopped. */
521         pv->last_start[0] = pv->last_stop[1];
522         pv->last_stop[0] = pv->last_start[0] + (buf_tmp_in->stop - buf_tmp_in->start);
523     }
524
525     /* Apply subtitles and dispose them */
526     if( buf_tmp_in )
527     {
528         hb_buffer_t * sub_list = hb_fifo_get_list_element( pv->subtitle_queue );
529         hb_buffer_t * sub;
530         hb_buffer_t * sub_next;
531         for ( sub = sub_list; sub; sub = sub_next )
532         {
533             sub_next = sub->next;
534             // XXX: Prevent hb_buffer_close inside ApplySub from killing the whole list
535             //      before we finish iterating over it
536             sub->next = NULL;
537             
538             hb_buffer_t * subpicture_list = sub;
539             hb_buffer_t * subpicture;
540             hb_buffer_t * subpicture_next;
541             for ( subpicture = subpicture_list; subpicture; subpicture = subpicture_next )
542             {
543                 subpicture_next = subpicture->next_subpicture;
544                 
545                 ApplySub( job, buf_tmp_in, &subpicture );
546             }
547         }
548     }
549
550     /* Apply crop/scale if specified */
551     if( buf_tmp_in && pv->context )
552     {
553         avpicture_fill( &pv->pic_tmp_in, buf_tmp_in->data,
554                         PIX_FMT_YUV420P,
555                         title->width, title->height );
556
557         avpicture_fill( &pv->pic_tmp_out, buf_render->data,
558                         PIX_FMT_YUV420P,
559                         job->width, job->height );
560
561         // Crop; this alters the pointer to the data to point to the correct place for cropped frame
562         av_picture_crop( &pv->pic_tmp_crop, &pv->pic_tmp_in, PIX_FMT_YUV420P,
563                          job->crop[0], job->crop[2] );
564
565         // Scale pic_crop into pic_render according to the context set up in renderInit
566         sws_scale(pv->context,
567                   pv->pic_tmp_crop.data, pv->pic_tmp_crop.linesize,
568                   0, title->height - (job->crop[0] + job->crop[1]),
569                   pv->pic_tmp_out.data,  pv->pic_tmp_out.linesize);
570
571         hb_buffer_copy_settings( buf_render, buf_tmp_in );
572
573         buf_tmp_in = buf_render;
574     }
575
576     /* Set output to render buffer */
577     (*buf_out) = buf_render;
578
579     if( buf_tmp_in == NULL )
580     {
581         /* Teardown and cleanup buffers if we are emitting NULL */
582         if( buf_in && *buf_in )
583         {
584             hb_buffer_close( buf_in );
585             *buf_in = NULL;
586         }
587         if( buf_out && *buf_out )
588         {
589             hb_buffer_close( buf_out );
590             *buf_out = NULL;
591         }
592     }
593     else if( buf_tmp_in != buf_render )
594     {
595         /* Copy temporary results and settings into render buffer */
596         memcpy( buf_render->data, buf_tmp_in->data, buf_render->size );
597         hb_buffer_copy_settings( buf_render, buf_tmp_in );
598     }
599
600     if (*buf_out )
601     {
602         hb_fifo_push( pv->delay_queue, *buf_out );
603         *buf_out = NULL;
604     }
605
606     /*
607      * Keep the last three frames in our queue, this ensures that we have the last
608      * two always in there should we need to rewrite the durations on them.
609      */
610
611     if( hb_fifo_size( pv->delay_queue ) >= 4 )
612     {
613         *buf_out = hb_fifo_get( pv->delay_queue );
614     }
615
616     if( *buf_out )
617     {
618         /* The current frame exists. That means it hasn't been dropped by a filter.
619            Make it accessible as ivtc_buffer so we can edit its duration if needed. */
620         ivtc_buffer = *buf_out;
621
622         if( pv->lost_time[3] > 0 )
623         {
624             /*
625              * A frame's been dropped earlier by VFR detelecine.
626              * Gotta make up the lost time. This will also
627              * slow down the video.
628              * The dropped frame's has to be accounted for, so
629              * divvy it up amongst the 4 frames left behind.
630              * This is what the delay_queue is for;
631              * telecined sequences start 2 frames before
632              * the dropped frame, so to slow down the right
633              * ones you need a 2 frame delay between
634              * reading input and writing output.
635              */
636
637             /* We want to extend the outputted frame's duration by the value
638               stored in the 4th slot of the lost_time array. Because we need
639               to adjust all the values in the array so they're contiguous,
640               extend the duration inside the array first, before applying
641               it to the current frame buffer. */
642             pv->last_stop[3] += pv->lost_time[3];
643
644             /* Log how much time has been added back in to the video. */
645             pv->total_gained_time += pv->lost_time[3];
646
647             /* We've pulled the 4th value from the lost_time array
648                and added it to the last_stop array's 4th slot. Now, rotate the
649                 lost_time array so the 4th slot now holds the 3rd's value, and
650                so on down the line, and set the 0 index to a value of 0. */
651             int i;
652             for( i=2; i >=  0; i--)
653             {
654                 pv->lost_time[i+1] = pv->lost_time[i];
655             }
656             pv->lost_time[0] = 0;
657
658             /* Log how many frames have had their durations extended. */
659             pv->extended_frames++;
660         }
661
662         /* We can't use the given time stamps. Previous frames
663            might already have been extended, throwing off the
664            raw values fed to render.c. Instead, their
665            stop and start times are stored in arrays.
666            The 4th cached frame will be the to use.
667            If it needed its duration extended to make up
668            lost time, it will have happened above. */
669         ivtc_buffer->start = pv->last_start[3];
670         ivtc_buffer->stop = pv->last_stop[3];
671
672         /* Set the 3rd cached frame to start when this one stops,
673            and so on down the line. If any of them need to be
674            extended as well to make up lost time, it'll be handled
675            on the next loop through the renderer.  */
676         int i;
677         for (i = 2; i >= 0; i--)
678         {
679             int temp_duration = pv->last_stop[i] - pv->last_start[i];
680             pv->last_start[i] = pv->last_stop[i+1];
681             pv->last_stop[i] = pv->last_start[i] + temp_duration;
682         }
683
684         /* If we have a pending chapter mark and this frame is at
685            or after the time of the mark, mark this frame & clear
686            our pending mark. */
687         if( pv->chapter_time && pv->chapter_time <= ivtc_buffer->start )
688         {
689             ivtc_buffer->new_chap = pv->chapter_val;
690             pv->chapter_time = 0;
691         }
692
693     }
694
695     if ( buf_out && *buf_out && job->cfr )
696     {
697         adjust_frame_rate( pv, buf_out );
698     }
699     return HB_WORK_OK;
700 }
701
702 void renderClose( hb_work_object_t * w )
703 {
704     hb_work_private_t * pv = w->private_data;
705
706     if ( pv->job->cfr )
707     {
708         hb_log("render: %d frames output, %d dropped and %d duped for CFR/PFR",
709                pv->count_frames, pv->drops, pv->dups );
710     }
711
712     hb_interjob_t * interjob = hb_interjob_get( w->private_data->job->h );
713     
714     /* Preserve dropped frame count for more accurate framerates in 2nd passes. */
715     interjob->render_dropped = pv->dropped_frames;
716
717     hb_log("render: lost time: %"PRId64" (%i frames)", pv->total_lost_time, pv->dropped_frames);
718     hb_log("render: gained time: %"PRId64" (%i frames) (%"PRId64" not accounted for)", pv->total_gained_time, pv->extended_frames, pv->total_lost_time - pv->total_gained_time);
719     if (pv->dropped_frames)
720         hb_log("render: average dropped frame duration: %"PRId64, (pv->total_lost_time / pv->dropped_frames) );
721
722     /* Cleanup subtitle queue */
723     if( pv->subtitle_queue )
724     {
725         hb_fifo_close( &pv->subtitle_queue );
726     }
727
728     if( pv->delay_queue )
729     {
730         hb_fifo_close( &pv->delay_queue );
731     }
732
733     /* Cleanup render work structure */
734     free( pv );
735     w->private_data = NULL;
736 }
737
738 int renderInit( hb_work_object_t * w, hb_job_t * job )
739 {
740     /* Allocate new private work object */
741     hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
742     pv->job = job;
743     w->private_data = pv;
744     uint32_t    swsflags;
745
746     swsflags = SWS_LANCZOS | SWS_ACCURATE_RND;
747
748     /* Get title and title size */
749     hb_title_t * title = job->title;
750
751     /* If crop or scale is specified, setup rescale context */
752     if( job->crop[0] || job->crop[1] || job->crop[2] || job->crop[3] ||
753         job->width != title->width || job->height != title->height )
754     {
755         pv->context = hb_sws_get_context(title->width  - (job->crop[2] + job->crop[3]),
756                                      title->height - (job->crop[0] + job->crop[1]),
757                                      PIX_FMT_YUV420P,
758                                      job->width, job->height, PIX_FMT_YUV420P,
759                                      swsflags);
760     }
761
762     /* Setup FIFO queue for subtitle cache */
763     pv->subtitle_queue = hb_fifo_init( 8, 1 );
764     pv->delay_queue = hb_fifo_init( 8, 1 );
765
766     /* VFR IVTC needs a bunch of time-keeping variables to track
767       how many frames are dropped, how many are extended, what the
768       last 4 start and stop times were (so they can be modified),
769       how much time has been lost and gained overall, how much time
770       the latest 4 frames should be extended by, and where chapter
771       markers are (so they can be saved if their frames are dropped.) */
772     pv->dropped_frames = 0;
773     pv->extended_frames = 0;
774     pv->last_start[0] = 0;
775     pv->last_stop[0] = 0;
776     pv->total_lost_time = 0;
777     pv->total_gained_time = 0;
778     pv->lost_time[0] = 0; pv->lost_time[1] = 0; pv->lost_time[2] = 0; pv->lost_time[3] = 0;
779     pv->chapter_time = 0;
780     pv->chapter_val  = 0;
781
782     pv->frame_rate = (double)job->vrate_base * (1./300.);
783
784     /* Setup filters */
785     /* TODO: Move to work.c? */
786     if( job->filters )
787     {
788         int filter_count = hb_list_count( job->filters );
789         int i;
790
791         for( i = 0; i < filter_count; i++ )
792         {
793             hb_filter_object_t * filter = hb_list_item( job->filters, i );
794
795             if( !filter ) continue;
796
797             filter->private_data = filter->init( PIX_FMT_YUV420P,
798                                                  title->width,
799                                                  title->height,
800                                                  filter->settings );
801         }
802     }
803
804     return 0;
805 }