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