OSDN Git Service

Push an EOF onto the subtitle fifos from the reader for DVD VOBSUBs and also from...
[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     hb_buffer_close( &out );
230     return succ;
231 }
232
233 // insert buffer 'succ' after buffer chain element 'pred'.
234 // caller must guarantee that 'pred' and 'succ' are non-null.
235 static hb_buffer_t *insert_buffer_in_chain( hb_buffer_t *pred, hb_buffer_t *succ )
236 {
237     succ->next = pred->next;
238     pred->next = succ;
239     return succ;
240 }
241
242 // This section of the code implements video frame rate control.
243 // Since filters are allowed to duplicate and drop frames (which
244 // changes the timing), this has to be the last thing done in render.
245 //
246 // There are three options, selected by the value of job->cfr:
247 //   0 - Variable Frame Rate (VFR) or 'same as source': frame times
248 //       are left alone
249 //   1 - Constant Frame Rate (CFR): Frame timings are adjusted so that all
250 //       frames are exactly job->vrate_base ticks apart. Frames are dropped
251 //       or duplicated if necessary to maintain this spacing.
252 //   2 - Peak Frame Rate (PFR): job->vrate_base is treated as the peak
253 //       average frame rate. I.e., the average frame rate (current frame
254 //       end time divided by number of frames so far) is never allowed to be
255 //       greater than job->vrate_base and frames are dropped if necessary
256 //       to keep the average under this value. Other than those drops, frame
257 //       times are left alone.
258 //
259
260 static void adjust_frame_rate( hb_work_private_t *pv, hb_buffer_t **buf_out )
261 {
262     hb_buffer_t *out = *buf_out;
263
264     while ( out && out->size > 0 )
265     {
266         // this frame has to start where the last one stopped.
267         out->start = pv->out_last_stop;
268
269         // compute where this frame would stop if the frame rate were constant
270         // (this is our target stopping time for CFR and earliest possible
271         // stopping time for PFR).
272         double cfr_stop = pv->frame_rate * ( pv->count_frames + 1 );
273
274         if ( cfr_stop - (double)out->stop >= pv->frame_rate )
275         {
276             // This frame stops a frame time or more in the past - drop it
277             // but don't lose its chapter mark.
278             if ( out->new_chap )
279             {
280                 pv->chapter_time = out->start;
281                 pv->chapter_val = out->new_chap;
282             }
283             ++pv->drops;
284             out = delete_buffer_from_chain( buf_out, out );
285             continue;
286         }
287
288         // at this point we know that this frame doesn't push the average
289         // rate over the limit so we just pass it on for PFR. For CFR we're
290         // going to return it (with its start & stop times modified) and
291         // we may have to dup it.
292         ++pv->count_frames;
293         if ( pv->job->cfr > 1 )
294         {
295             // PFR - we're going to keep the frame but may need to
296             // adjust it's stop time to meet the average rate constraint.
297             if ( out->stop <= cfr_stop )
298             {
299                 out->stop = cfr_stop;
300             }
301         }
302         else
303         {
304             // we're doing CFR so we have to either trim some time from a
305             // buffer that ends too far in the future or, if the buffer is
306             // two or more frame times long, split it into multiple pieces,
307             // each of which is a frame time long.
308             double excess_dur = (double)out->stop - cfr_stop;
309             out->stop = cfr_stop;
310             for ( ; excess_dur >= pv->frame_rate; excess_dur -= cfr_stop )
311             {
312                 /* next frame too far ahead - dup current frame */
313                 hb_buffer_t *dup = hb_buffer_init( out->size );
314                 memcpy( dup->data, out->data, out->size );
315                 hb_buffer_copy_settings( dup, out );
316                 dup->new_chap = 0;
317                 dup->start = cfr_stop;
318                 cfr_stop += pv->frame_rate;
319                 dup->stop = cfr_stop;
320                 out = insert_buffer_in_chain( out, dup );
321                 ++pv->dups;
322                 ++pv->count_frames;
323             }
324         }
325         pv->out_last_stop = out->stop;
326         out = out->next;
327     }
328 }
329
330 int renderWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
331                 hb_buffer_t ** buf_out )
332 {
333     hb_work_private_t * pv = w->private_data;
334     hb_job_t   * job   = pv->job;
335     hb_title_t * title = job->title;
336     hb_buffer_t * in = *buf_in, * buf_tmp_in = *buf_in;
337     hb_buffer_t * ivtc_buffer = NULL;
338
339     if( in->size <= 0 )
340     {
341         hb_buffer_t *head = NULL, *tail = NULL, *next;
342         int counter = 2;
343
344         /* If the input buffer is end of stream, send out an empty one
345          * to the next stage as well. To avoid losing the contents of
346          * the delay queue connect the buffers in the delay queue in 
347          * the correct order, and add the end of stream buffer to the
348          * end.
349          */     
350         while( ( next = hb_fifo_get( pv->delay_queue ) ) != NULL )
351         {
352             
353             /* We can't use the given time stamps. Previous frames
354                might already have been extended, throwing off the
355                raw values fed to render.c. Instead, their
356                stop and start times are stored in arrays.
357                The 4th cached frame will be the to use.
358                If it needed its duration extended to make up
359                lost time, it will have happened above. */
360             next->start = pv->last_start[counter];
361             next->stop = pv->last_stop[counter--];
362             
363             if( !head && !tail )
364             {
365                 head = tail = next;
366             } else {
367                 tail->next = next;
368                 tail = next;
369             }
370         }
371         if( tail )
372         {
373             tail->next = in;
374             *buf_out = head;
375             if ( job->cfr )
376             {
377                 adjust_frame_rate( pv, buf_out );
378             }
379         } else {
380             *buf_out = in;
381         }     
382         *buf_in = NULL;
383         return HB_WORK_DONE;
384     }
385
386     /*
387      * During the indepth_scan ditch the buffers here before applying filters or attempting to
388      * use the subtitles.
389      */
390     if( job->indepth_scan )
391     {
392         *buf_out = NULL;
393         return HB_WORK_OK;
394     }
395
396     /* Push subtitles onto queue just in case we need to delay a frame */
397     if( in->sub )
398     {
399         hb_fifo_push( pv->subtitle_queue, in->sub );
400     }
401     else
402     {
403         hb_fifo_push( pv->subtitle_queue,  hb_buffer_init(0) );
404     }
405
406     /* If there's a chapter mark remember it in case we delay or drop its frame */
407     if( in->new_chap )
408     {
409         pv->chapter_time = in->start;
410         pv->chapter_val = in->new_chap;
411         in->new_chap = 0;
412     }
413
414     /* Setup render buffer */
415     hb_buffer_t * buf_render = hb_video_buffer_init( job->width, job->height );
416
417     /* Apply filters */
418     if( job->filters )
419     {
420         int filter_count = hb_list_count( job->filters );
421         int i;
422
423         for( i = 0; i < filter_count; i++ )
424         {
425             hb_filter_object_t * filter = hb_list_item( job->filters, i );
426
427             if( !filter )
428             {
429                 continue;
430             }
431
432             hb_buffer_t * buf_tmp_out = NULL;
433
434             int result = filter->work( buf_tmp_in,
435                                        &buf_tmp_out,
436                                        PIX_FMT_YUV420P,
437                                        title->width,
438                                        title->height,
439                                        filter->private_data );
440
441             /*
442              * FILTER_OK:      set temp buffer to filter buffer, continue
443              * FILTER_DELAY:   set temp buffer to NULL, abort
444              * FILTER_DROP:    set temp buffer to NULL, pop subtitle, abort
445              * FILTER_FAILED:  leave temp buffer alone, continue
446              */
447             if( result == FILTER_OK )
448             {
449                 buf_tmp_in = buf_tmp_out;
450             }
451             else if( result == FILTER_DELAY )
452             {
453                 buf_tmp_in = NULL;
454                 break;
455             }
456             else if( result == FILTER_DROP )
457             {
458                 /* We need to compensate for the time lost by dropping this frame.
459                    Spread its duration out in quarters, because usually dropped frames
460                    maintain a 1-out-of-5 pattern and this spreads it out amongst the remaining ones.
461                    Store these in the lost_time array, which has 4 slots in it.
462                    Because not every frame duration divides evenly by 4, and we can't lose the
463                    remainder, we have to go through an awkward process to preserve it in the 4th array index. */
464                 uint64_t temp_duration = buf_tmp_out->stop - buf_tmp_out->start;
465                 pv->lost_time[0] += (temp_duration / 4);
466                 pv->lost_time[1] += (temp_duration / 4);
467                 pv->lost_time[2] += (temp_duration / 4);
468                 pv->lost_time[3] += ( temp_duration - (temp_duration / 4) - (temp_duration / 4) - (temp_duration / 4) );
469
470                 pv->total_lost_time += temp_duration;
471                 pv->dropped_frames++;
472
473                 /* Pop the frame's subtitle and dispose of it. */
474                 hb_buffer_t * subtitles = hb_fifo_get( pv->subtitle_queue );
475                 hb_buffer_close( &subtitles );
476                 buf_tmp_in = NULL;
477                 break;
478             }
479         }
480     }
481
482     if( buf_tmp_in )
483     {
484         /* Cache frame start and stop times, so we can renumber
485            time stamps if dropping frames for VFR.              */
486         int i;
487         for( i = 3; i >= 1; i-- )
488         {
489             pv->last_start[i] = pv->last_start[i-1];
490             pv->last_stop[i] = pv->last_stop[i-1];
491         }
492
493         /* In order to make sure we have continuous time stamps, store
494            the current frame's duration as starting when the last one stopped. */
495         pv->last_start[0] = pv->last_stop[1];
496         pv->last_stop[0] = pv->last_start[0] + (in->stop - in->start);
497     }
498
499     /* Apply subtitles */
500     if( buf_tmp_in )
501     {
502         hb_buffer_t * subtitles = hb_fifo_get( pv->subtitle_queue );
503         if( subtitles )
504         {
505             ApplySub( job, buf_tmp_in, &subtitles );
506         }
507     }
508
509     /* Apply crop/scale if specified */
510     if( buf_tmp_in && pv->context )
511     {
512         avpicture_fill( &pv->pic_tmp_in, buf_tmp_in->data,
513                         PIX_FMT_YUV420P,
514                         title->width, title->height );
515
516         avpicture_fill( &pv->pic_tmp_out, buf_render->data,
517                         PIX_FMT_YUV420P,
518                         job->width, job->height );
519
520         // Crop; this alters the pointer to the data to point to the correct place for cropped frame
521         av_picture_crop( &pv->pic_tmp_crop, &pv->pic_tmp_in, PIX_FMT_YUV420P,
522                          job->crop[0], job->crop[2] );
523
524         // Scale pic_crop into pic_render according to the context set up in renderInit
525         sws_scale(pv->context,
526                   pv->pic_tmp_crop.data, pv->pic_tmp_crop.linesize,
527                   0, title->height - (job->crop[0] + job->crop[1]),
528                   pv->pic_tmp_out.data,  pv->pic_tmp_out.linesize);
529
530         hb_buffer_copy_settings( buf_render, buf_tmp_in );
531
532         buf_tmp_in = buf_render;
533     }
534
535     /* Set output to render buffer */
536     (*buf_out) = buf_render;
537
538     if( buf_tmp_in == NULL )
539     {
540         /* Teardown and cleanup buffers if we are emitting NULL */
541         if( buf_in && *buf_in )
542         {
543             hb_buffer_close( buf_in );
544             *buf_in = NULL;
545         }
546         if( buf_out && *buf_out )
547         {
548             hb_buffer_close( buf_out );
549             *buf_out = NULL;
550         }
551     }
552     else if( buf_tmp_in != buf_render )
553     {
554         /* Copy temporary results and settings into render buffer */
555         memcpy( buf_render->data, buf_tmp_in->data, buf_render->size );
556         hb_buffer_copy_settings( buf_render, buf_tmp_in );
557     }
558
559     if (*buf_out )
560     {
561         hb_fifo_push( pv->delay_queue, *buf_out );
562         *buf_out = NULL;
563     }
564
565     /*
566      * Keep the last three frames in our queue, this ensures that we have the last
567      * two always in there should we need to rewrite the durations on them.
568      */
569
570     if( hb_fifo_size( pv->delay_queue ) >= 4 )
571     {
572         *buf_out = hb_fifo_get( pv->delay_queue );
573     }
574
575     if( *buf_out )
576     {
577         /* The current frame exists. That means it hasn't been dropped by a filter.
578            Make it accessible as ivtc_buffer so we can edit its duration if needed. */
579         ivtc_buffer = *buf_out;
580
581         if( pv->lost_time[3] > 0 )
582         {
583             /*
584              * A frame's been dropped earlier by VFR detelecine.
585              * Gotta make up the lost time. This will also
586              * slow down the video.
587              * The dropped frame's has to be accounted for, so
588              * divvy it up amongst the 4 frames left behind.
589              * This is what the delay_queue is for;
590              * telecined sequences start 2 frames before
591              * the dropped frame, so to slow down the right
592              * ones you need a 2 frame delay between
593              * reading input and writing output.
594              */
595
596             /* We want to extend the outputted frame's duration by the value
597               stored in the 4th slot of the lost_time array. Because we need
598               to adjust all the values in the array so they're contiguous,
599               extend the duration inside the array first, before applying
600               it to the current frame buffer. */
601             pv->last_stop[3] += pv->lost_time[3];
602
603             /* Log how much time has been added back in to the video. */
604             pv->total_gained_time += pv->lost_time[3];
605
606             /* We've pulled the 4th value from the lost_time array
607                and added it to the last_stop array's 4th slot. Now, rotate the
608                 lost_time array so the 4th slot now holds the 3rd's value, and
609                so on down the line, and set the 0 index to a value of 0. */
610             int i;
611             for( i=2; i >=  0; i--)
612             {
613                 pv->lost_time[i+1] = pv->lost_time[i];
614             }
615             pv->lost_time[0] = 0;
616
617             /* Log how many frames have had their durations extended. */
618             pv->extended_frames++;
619         }
620
621         /* We can't use the given time stamps. Previous frames
622            might already have been extended, throwing off the
623            raw values fed to render.c. Instead, their
624            stop and start times are stored in arrays.
625            The 4th cached frame will be the to use.
626            If it needed its duration extended to make up
627            lost time, it will have happened above. */
628         ivtc_buffer->start = pv->last_start[3];
629         ivtc_buffer->stop = pv->last_stop[3];
630
631         /* Set the 3rd cached frame to start when this one stops,
632            and so on down the line. If any of them need to be
633            extended as well to make up lost time, it'll be handled
634            on the next loop through the renderer.  */
635         int i;
636         for (i = 2; i >= 0; i--)
637         {
638             int temp_duration = pv->last_stop[i] - pv->last_start[i];
639             pv->last_start[i] = pv->last_stop[i+1];
640             pv->last_stop[i] = pv->last_start[i] + temp_duration;
641         }
642
643         /* If we have a pending chapter mark and this frame is at
644            or after the time of the mark, mark this frame & clear
645            our pending mark. */
646         if( pv->chapter_time && pv->chapter_time <= ivtc_buffer->start )
647         {
648             ivtc_buffer->new_chap = pv->chapter_val;
649             pv->chapter_time = 0;
650         }
651
652     }
653
654     if ( buf_out && *buf_out && job->cfr )
655     {
656         adjust_frame_rate( pv, buf_out );
657     }
658     return HB_WORK_OK;
659 }
660
661 void renderClose( hb_work_object_t * w )
662 {
663     hb_work_private_t * pv = w->private_data;
664
665     if ( pv->job->cfr )
666     {
667         hb_log("render: %d frames output, %d dropped and %d duped for CFR/PFR",
668                pv->count_frames, pv->drops, pv->dups );
669     }
670
671     hb_log("render: lost time: %lld (%i frames)", pv->total_lost_time, pv->dropped_frames);
672     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);
673     if (pv->dropped_frames)
674         hb_log("render: average dropped frame duration: %lld", (pv->total_lost_time / pv->dropped_frames) );
675
676     /* Cleanup subtitle queue */
677     if( pv->subtitle_queue )
678     {
679         hb_fifo_close( &pv->subtitle_queue );
680     }
681
682     if( pv->delay_queue )
683     {
684         hb_fifo_close( &pv->delay_queue );
685     }
686
687     /* Cleanup render work structure */
688     free( pv );
689     w->private_data = NULL;
690 }
691
692 int renderInit( hb_work_object_t * w, hb_job_t * job )
693 {
694     /* Allocate new private work object */
695     hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
696     pv->job = job;
697     w->private_data = pv;
698     uint32_t    swsflags;
699
700     swsflags = SWS_LANCZOS | SWS_ACCURATE_RND;
701
702     /* Get title and title size */
703     hb_title_t * title = job->title;
704
705     /* If crop or scale is specified, setup rescale context */
706     if( job->crop[0] || job->crop[1] || job->crop[2] || job->crop[3] ||
707         job->width != title->width || job->height != title->height )
708     {
709         pv->context = sws_getContext(title->width  - (job->crop[2] + job->crop[3]),
710                                      title->height - (job->crop[0] + job->crop[1]),
711                                      PIX_FMT_YUV420P,
712                                      job->width, job->height, PIX_FMT_YUV420P,
713                                      swsflags, NULL, NULL, NULL);
714     }
715
716     /* Setup FIFO queue for subtitle cache */
717     pv->subtitle_queue = hb_fifo_init( 8 );
718     pv->delay_queue = hb_fifo_init( 8 );
719
720     /* VFR IVTC needs a bunch of time-keeping variables to track
721       how many frames are dropped, how many are extended, what the
722       last 4 start and stop times were (so they can be modified),
723       how much time has been lost and gained overall, how much time
724       the latest 4 frames should be extended by, and where chapter
725       markers are (so they can be saved if their frames are dropped.) */
726     pv->dropped_frames = 0;
727     pv->extended_frames = 0;
728     pv->last_start[0] = 0;
729     pv->last_stop[0] = 0;
730     pv->total_lost_time = 0;
731     pv->total_gained_time = 0;
732     pv->lost_time[0] = 0; pv->lost_time[1] = 0; pv->lost_time[2] = 0; pv->lost_time[3] = 0;
733     pv->chapter_time = 0;
734     pv->chapter_val  = 0;
735
736     pv->frame_rate = (double)job->vrate_base * (1./300.);
737
738     /* Setup filters */
739     /* TODO: Move to work.c? */
740     if( job->filters )
741     {
742         int filter_count = hb_list_count( job->filters );
743         int i;
744
745         for( i = 0; i < filter_count; i++ )
746         {
747             hb_filter_object_t * filter = hb_list_item( job->filters, i );
748
749             if( !filter ) continue;
750
751             filter->private_data = filter->init( PIX_FMT_YUV420P,
752                                                  title->width,
753                                                  title->height,
754                                                  filter->settings );
755         }
756     }
757
758     return 0;
759 }