1 /*
2  * Copyright (C) 2000-2018 the xine project
3  *
4  * This file is part of xine, a free video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  * advanced video deinterlacer plugin
21  * Jun/2003 by Miguel Freitas
22  *
23  * heavily based on tvtime.sf.net by Billy Biggs
24  */
25 
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 
30 /*
31 #define LOG
32 */
33 
34 #include <xine/xine_internal.h>
35 #include <xine/post.h>
36 #include <xine/xineutils.h>
37 #include <xine/xine_buffer.h>
38 #include <pthread.h>
39 
40 #include "tvtime.h"
41 #include "speedy.h"
42 #include "deinterlace.h"
43 #include "plugins/plugins.h"
44 
45 
46 typedef struct post_plugin_deinterlace_s post_plugin_deinterlace_t;
47 
48 #define MAX_NUM_METHODS 30
49 static const char *enum_methods[MAX_NUM_METHODS];
50 static const char *const enum_pulldown[] = { "none", "vektor", NULL };
51 static const char *const enum_framerate[] = { "full", "half_top", "half_bottom", NULL };
52 
53 static void *help_string;
54 
55 /*
56  * this is the struct used by "parameters api"
57  */
58 typedef struct deinterlace_parameters_s {
59 
60   int method;
61   int enabled;
62   int pulldown;
63   int pulldown_error_wait;
64   int framerate_mode;
65   int judder_correction;
66   int use_progressive_frame_flag;
67   int chroma_filter;
68   int cheap_mode;
69 
70 } deinterlace_parameters_t;
71 
72 /*
73  * description of params struct
74  */
75 START_PARAM_DESCR( deinterlace_parameters_t )
76 PARAM_ITEM( POST_PARAM_TYPE_INT, method, (char **)enum_methods, 0, 0, 0,
77             "deinterlace method" )
78 PARAM_ITEM( POST_PARAM_TYPE_BOOL, enabled, NULL, 0, 1, 0,
79             "enable/disable" )
80 PARAM_ITEM( POST_PARAM_TYPE_INT, pulldown, (char **)enum_pulldown, 0, 0, 0,
81             "pulldown algorithm" )
82 PARAM_ITEM( POST_PARAM_TYPE_INT, pulldown_error_wait, NULL, 0, 0, 0,
83             "number of frames of telecine pattern sync required before mode change" )
84 PARAM_ITEM( POST_PARAM_TYPE_INT, framerate_mode, (char **)enum_framerate, 0, 0, 0,
85             "framerate output mode" )
86 PARAM_ITEM( POST_PARAM_TYPE_BOOL, judder_correction, NULL, 0, 1, 0,
87             "make frames evenly spaced for film mode (24 fps)" )
88 PARAM_ITEM( POST_PARAM_TYPE_BOOL, use_progressive_frame_flag, NULL, 0, 1, 0,
89             "disable deinterlacing when progressive_frame flag is set" )
90 PARAM_ITEM( POST_PARAM_TYPE_BOOL, chroma_filter, NULL, 0, 1, 0,
91             "apply chroma filter after deinterlacing" )
92 PARAM_ITEM( POST_PARAM_TYPE_BOOL, cheap_mode, NULL, 0, 1, 0,
93             "skip image format conversion - cheaper but not 100% correct" )
94 END_PARAM_DESCR( param_descr )
95 
96 
97 #define NUM_RECENT_FRAMES  2
98 #define FPS_24_DURATION    3754
99 #define FRAMES_TO_SYNC     20
100 
101 typedef struct post_class_deinterlace_s {
102   post_class_t class;
103 
104   deinterlace_methods_t    methods;
105 } post_class_deinterlace_t;
106 
107 /* plugin structure */
108 struct post_plugin_deinterlace_s {
109   post_plugin_t      post;
110 
111   /* private data */
112   int                cur_method;
113   int                enabled;
114   int                pulldown;
115   int                framerate_mode;
116   int                judder_correction;
117   int                use_progressive_frame_flag;
118   int                chroma_filter;
119   int                cheap_mode;
120   tvtime_t          *tvtime;
121   int                tvtime_changed;
122   int                tvtime_last_filmmode;
123   int                vo_deinterlace_enabled;
124 
125   int                framecounter;
126   uint8_t            rff_pattern;
127 
128   vo_frame_t        *recent_frame[NUM_RECENT_FRAMES];
129 
130   pthread_mutex_t    lock;
131 
132   post_class_deinterlace_t *class;
133 };
134 
135 
_flush_frames(post_plugin_deinterlace_t * this)136 static void _flush_frames(post_plugin_deinterlace_t *this)
137 {
138   int i;
139 
140   for( i = 0; i < NUM_RECENT_FRAMES; i++ ) {
141     if( this->recent_frame[i] ) {
142       this->recent_frame[i]->free(this->recent_frame[i]);
143       this->recent_frame[i] = NULL;
144     }
145   }
146   this->tvtime_changed++;
147 }
148 
set_parameters(xine_post_t * this_gen,const void * param_gen)149 static int set_parameters (xine_post_t *this_gen, const void *param_gen) {
150   post_plugin_deinterlace_t *this = (post_plugin_deinterlace_t *)this_gen;
151   const deinterlace_parameters_t *param = (const deinterlace_parameters_t *)param_gen;
152 
153   pthread_mutex_lock (&this->lock);
154 
155   if( this->enabled != param->enabled ||
156       this->cheap_mode != param->cheap_mode )
157     _flush_frames(this);
158 
159   this->cur_method = param->method;
160 
161   this->enabled = param->enabled;
162 
163   this->pulldown = param->pulldown;
164   this->tvtime->pulldown_error_wait = param->pulldown_error_wait;
165   this->framerate_mode = param->framerate_mode;
166   this->judder_correction = param->judder_correction;
167   this->use_progressive_frame_flag = param->use_progressive_frame_flag;
168   this->chroma_filter = param->chroma_filter;
169   this->cheap_mode = param->cheap_mode;
170 
171   this->tvtime_changed++;
172 
173   pthread_mutex_unlock (&this->lock);
174 
175   return 1;
176 }
177 
get_parameters(xine_post_t * this_gen,void * param_gen)178 static int get_parameters (xine_post_t *this_gen, void *param_gen) {
179   post_plugin_deinterlace_t *this = (post_plugin_deinterlace_t *)this_gen;
180   deinterlace_parameters_t *param = (deinterlace_parameters_t *)param_gen;
181 
182   param->method = this->cur_method;
183   param->enabled = this->enabled;
184   param->pulldown = this->pulldown;
185   param->pulldown_error_wait = this->tvtime->pulldown_error_wait;
186   param->framerate_mode = this->framerate_mode;
187   param->judder_correction = this->judder_correction;
188   param->use_progressive_frame_flag = this->use_progressive_frame_flag;
189   param->chroma_filter = this->chroma_filter;
190   param->cheap_mode = this->cheap_mode;
191 
192   return 1;
193 }
194 
get_param_descr(void)195 static xine_post_api_descr_t * get_param_descr (void) {
196   return &param_descr;
197 }
198 
get_static_help(void)199 static char * get_static_help (void) {
200   return _("Advanced tvtime/deinterlacer plugin with pulldown detection\n"
201            "This plugin aims to provide deinterlacing mechanisms comparable "
202            "to high quality progressive DVD players and so called "
203            "line-doublers, for use with computer monitors, projectors and "
204            "other progressive display devices.\n"
205            "\n"
206            "Parameters\n"
207            "\n"
208            "  Method: Select deinterlacing method/algorithm to use, see below for "
209            "explanation of each method.\n"
210            "\n"
211            "  Enabled: Enable/disable the plugin.\n"
212            "\n"
213            "  Pulldown_error_wait: Ensures that the telecine pattern has been "
214            "locked for this many frames before changing to filmmode.\n"
215            "\n"
216            "  Pulldown: Choose the 2-3 pulldown detection algorithm. 24 FPS films "
217            "that have being converted to NTSC can be detected and intelligently "
218            "reconstructed to their original (non-interlaced) frames.\n"
219            "\n"
220            "  Framerate_mode: Selecting 'full' will deinterlace every field "
221            "to an unique frame for television quality and beyond. This feature will "
222            "effetively double the frame rate, improving smoothness. Note, however, "
223            "that full 59.94 FPS is not possible with plain 2.4 Linux kernel (that "
224            "use a timer interrupt frequency of 100Hz). Newer RedHat and 2.6 kernels "
225            "use higher HZ settings (512 and 1000, respectively) and should work fine.\n"
226            "\n"
227            "  Judder_correction: Once 2-3 pulldown is enabled and a film material "
228            "is detected, it is possible to reduce the frame rate to original rate "
229            "used (24 FPS). This will make the frames evenly spaced in time, "
230            "matching the speed they were shot and eliminating the judder effect.\n"
231            "\n"
232            "  Use_progressive_frame_flag: Well mastered MPEG2 streams uses a flag "
233            "to indicate progressive material. This setting control whether we trust "
234            "this flag or not (some rare and buggy mpeg2 streams set it wrong).\n"
235            "\n"
236            "  Chroma_filter: DVD/MPEG2 use an interlaced image format that has "
237            "a very poor vertical chroma resolution. Upsampling the chroma for purposes "
238            "of deinterlacing may cause some artifacts to occur (eg. colour stripes). Use "
239            "this option to blur the chroma vertically after deinterlacing to remove "
240            "the artifacts. Warning: cpu intensive.\n"
241            "\n"
242            "  Cheap_mode: This will skip the expensive YV12->YUY2 image conversion, "
243            "tricking tvtime/dscaler routines like if they were still handling YUY2 "
244            "images. Of course, this is not correct, not all pixels will be evaluated "
245            "by the algorithms to decide the regions to deinterlace and chroma will be "
246            "processed separately. Nevertheless, it allows people with not so fast "
247            "systems to try deinterlace algorithms, in a tradeoff between quality "
248            "and cpu usage.\n"
249            "\n"
250            "* Uses several algorithms from tvtime and dscaler projects.\n"
251            "Deinterlacing methods: (Not all methods are available for all platforms)\n"
252            "\n"
253            );
254 }
255 
get_help(void)256 static char * get_help (void) {
257   return (char *)help_string;
258 }
259 
260 
261 /* plugin class functions */
262 static post_plugin_t *deinterlace_open_plugin(post_class_t *class_gen, int inputs,
263 					 xine_audio_port_t **audio_target,
264 					 xine_video_port_t **video_target);
265 static void           deinterlace_class_dispose(post_class_t *class_gen);
266 
267 /* plugin instance functions */
268 static void           deinterlace_dispose(post_plugin_t *this_gen);
269 
270 /* replaced video_port functions */
271 static int            deinterlace_get_property(xine_video_port_t *port_gen, int property);
272 static int            deinterlace_set_property(xine_video_port_t *port_gen, int property, int value);
273 static void           deinterlace_flush(xine_video_port_t *port_gen);
274 static void           deinterlace_open(xine_video_port_t *port_gen, xine_stream_t *stream);
275 static void           deinterlace_close(xine_video_port_t *port_gen, xine_stream_t *stream);
276 
277 /* frame intercept check */
278 static int            deinterlace_intercept_frame(post_video_port_t *port, vo_frame_t *frame);
279 
280 /* replaced vo_frame functions */
281 static int            deinterlace_draw(vo_frame_t *frame, xine_stream_t *stream);
282 
283 
deinterlace_init_plugin(xine_t * xine,const void * data)284 static void *deinterlace_init_plugin(xine_t *xine, const void *data)
285 {
286   post_class_deinterlace_t *class = calloc(1, sizeof(post_class_deinterlace_t));
287   uint32_t config_flags = xine_mm_accel();
288   int i;
289 
290   if (!class)
291     return NULL;
292 
293   (void)data;
294 
295   class->class.open_plugin     = deinterlace_open_plugin;
296   class->class.identifier      = "tvtime";
297   class->class.description     = N_("advanced deinterlacer plugin with pulldown detection");
298   class->class.dispose         = deinterlace_class_dispose;
299 
300 
301   setup_speedy_calls(xine_mm_accel(),0);
302 
303 
304   register_deinterlace_method( &class->methods, linear_get_method() );
305   register_deinterlace_method( &class->methods, linearblend_get_method() );
306   register_deinterlace_method( &class->methods, greedy_get_method() );
307   register_deinterlace_method( &class->methods, greedy2frame_get_method() );
308   register_deinterlace_method( &class->methods, weave_get_method() );
309   register_deinterlace_method( &class->methods, double_get_method() );
310   register_deinterlace_method( &class->methods, vfir_get_method() );
311   register_deinterlace_method( &class->methods, scalerbob_get_method() );
312   register_deinterlace_method( &class->methods, dscaler_greedyh_get_method() );
313   register_deinterlace_method( &class->methods, dscaler_tomsmocomp_get_method() );
314 
315   filter_deinterlace_methods( &class->methods, config_flags, 5 /*fieldsavailable*/ );
316   if( !get_num_deinterlace_methods( class->methods ) ) {
317       xprintf(xine, XINE_VERBOSITY_LOG,
318 	      _("tvtime: No deinterlacing methods available, exiting.\n"));
319       free(class);
320       return NULL;
321   }
322 
323   help_string = xine_buffer_init(1024);
324   xine_buffer_strcat( help_string, get_static_help() );
325 
326   enum_methods[0] = "use_vo_driver";
327   for(i = 0; i < get_num_deinterlace_methods( class->methods ); i++ ) {
328     const deinterlace_method_t *method;
329 
330     method = get_deinterlace_method( class->methods, i );
331 
332     enum_methods[i+1] = method->short_name;
333     xine_buffer_strcat( help_string, "[" );
334     xine_buffer_strcat( help_string, method->short_name );
335     xine_buffer_strcat( help_string, "] " );
336     xine_buffer_strcat( help_string, method->name );
337     xine_buffer_strcat( help_string, ":\n" );
338     if (method->description)
339       xine_buffer_strcat( help_string, method->description );
340     xine_buffer_strcat( help_string, "\n---\n" );
341   }
342   enum_methods[i+1] = NULL;
343 
344   return &class->class;
345 }
346 
347 
deinterlace_open_plugin(post_class_t * class_gen,int inputs,xine_audio_port_t ** audio_target,xine_video_port_t ** video_target)348 static post_plugin_t *deinterlace_open_plugin(post_class_t *class_gen, int inputs,
349 					 xine_audio_port_t **audio_target,
350 					 xine_video_port_t **video_target)
351 {
352   post_plugin_deinterlace_t *this = calloc(1, sizeof(post_plugin_deinterlace_t));
353   post_in_t                 *input;
354   post_out_t                *output;
355   post_video_port_t *port;
356 
357   static const xine_post_api_t post_api = {
358     .set_parameters  = set_parameters,
359     .get_parameters  = get_parameters,
360     .get_param_descr = get_param_descr,
361     .get_help        = get_help,
362   };
363   static const xine_post_in_t params_input = {
364     .name = "parameters",
365     .type = XINE_POST_DATA_PARAMETERS,
366     .data = (void *)&post_api,
367   };
368 
369   /* Some default values */
370   static const deinterlace_parameters_t init_param = {
371     .method                     = 1, /* First (plugin) method available */
372     .enabled                    = 1,
373     .pulldown                   = 1, /* vektor */
374     .pulldown_error_wait        = 60, /* about one second */
375     .framerate_mode             = 0, /* full */
376     .judder_correction          = 1,
377     .use_progressive_frame_flag = 1,
378     .chroma_filter              = 0,
379     .cheap_mode                 = 0,
380   };
381 
382   if (!this || !video_target || !video_target[0]) {
383     free(this);
384     return NULL;
385   }
386 
387   (void)class_gen;
388   (void)inputs;
389   (void)audio_target;
390 
391   _x_post_init(&this->post, 0, 1);
392 
393   this->tvtime = tvtime_new_context();
394   this->tvtime_changed++;
395   this->tvtime_last_filmmode = 0;
396   this->class = (post_class_deinterlace_t *)class_gen;
397 
398   pthread_mutex_init (&this->lock, NULL);
399 
400   set_parameters (&this->post.xine_post, &init_param);
401 
402   port = _x_post_intercept_video_port(&this->post, video_target[0], &input, &output);
403   /* replace with our own get_frame function */
404   port->new_port.open         = deinterlace_open;
405   port->new_port.close        = deinterlace_close;
406   port->new_port.get_property = deinterlace_get_property;
407   port->new_port.set_property = deinterlace_set_property;
408   port->new_port.flush        = deinterlace_flush;
409   port->intercept_frame       = deinterlace_intercept_frame;
410   port->new_frame->draw       = deinterlace_draw;
411 
412   xine_list_push_back(this->post.input, (void *)&params_input);
413 
414   input->xine_in.name     = "video";
415   output->xine_out.name   = "deinterlaced video";
416 
417   this->post.xine_post.video_input[0] = &port->new_port;
418 
419   this->post.dispose = deinterlace_dispose;
420 
421   return &this->post;
422 }
423 
deinterlace_class_dispose(post_class_t * class_gen)424 static void deinterlace_class_dispose(post_class_t *class_gen)
425 {
426   post_class_deinterlace_t  *class = (post_class_deinterlace_t *)class_gen;
427 
428   xine_buffer_free(help_string);
429   help_string = NULL;
430 
431   free_deinterlace_methods( &class->methods );
432 
433   free(class_gen);
434 }
435 
436 
deinterlace_dispose(post_plugin_t * this_gen)437 static void deinterlace_dispose(post_plugin_t *this_gen)
438 {
439   post_plugin_deinterlace_t *this = (post_plugin_deinterlace_t *)this_gen;
440 
441   if (_x_post_dispose(this_gen)) {
442     _flush_frames(this);
443     pthread_mutex_destroy(&this->lock);
444     free(this->tvtime);
445     free(this);
446   }
447 }
448 
449 
deinterlace_get_property(xine_video_port_t * port_gen,int property)450 static int deinterlace_get_property(xine_video_port_t *port_gen, int property) {
451   post_video_port_t *port = (post_video_port_t *)port_gen;
452   post_plugin_deinterlace_t *this = (post_plugin_deinterlace_t *)port->post;
453   if( property == XINE_PARAM_VO_DEINTERLACE && this->cur_method )
454     return this->enabled;
455   else
456     return port->original_port->get_property(port->original_port, property);
457 }
458 
deinterlace_set_property(xine_video_port_t * port_gen,int property,int value)459 static int deinterlace_set_property(xine_video_port_t *port_gen, int property, int value) {
460   post_video_port_t *port = (post_video_port_t *)port_gen;
461   post_plugin_deinterlace_t *this = (post_plugin_deinterlace_t *)port->post;
462   if( property == XINE_PARAM_VO_DEINTERLACE ) {
463     pthread_mutex_lock (&this->lock);
464 
465     if( this->enabled != value )
466       _flush_frames(this);
467 
468     this->enabled = value;
469 
470     pthread_mutex_unlock (&this->lock);
471 
472     this->vo_deinterlace_enabled = this->enabled && (!this->cur_method);
473 
474     port->original_port->set_property(port->original_port,
475                                       XINE_PARAM_VO_DEINTERLACE,
476                                       this->vo_deinterlace_enabled);
477 
478     return this->enabled;
479   } else
480     return port->original_port->set_property(port->original_port, property, value);
481 }
482 
deinterlace_flush(xine_video_port_t * port_gen)483 static void deinterlace_flush(xine_video_port_t *port_gen) {
484   post_video_port_t *port = (post_video_port_t *)port_gen;
485   post_plugin_deinterlace_t *this = (post_plugin_deinterlace_t *)port->post;
486 
487   _flush_frames(this);
488 
489   port->original_port->flush(port->original_port);
490 }
491 
deinterlace_open(xine_video_port_t * port_gen,xine_stream_t * stream)492 static void deinterlace_open(xine_video_port_t *port_gen, xine_stream_t *stream)
493 {
494   post_video_port_t *port = (post_video_port_t *)port_gen;
495   post_plugin_deinterlace_t *this = (post_plugin_deinterlace_t *)port->post;
496 
497   _x_post_rewire(&this->post);
498   _x_post_inc_usage(port);
499   port->stream = stream;
500   (port->original_port->open) (port->original_port, stream);
501   this->vo_deinterlace_enabled = !this->cur_method;
502   port->original_port->set_property(port->original_port,
503                                     XINE_PARAM_VO_DEINTERLACE,
504                                     this->vo_deinterlace_enabled);
505 }
506 
deinterlace_close(xine_video_port_t * port_gen,xine_stream_t * stream)507 static void deinterlace_close(xine_video_port_t *port_gen, xine_stream_t *stream)
508 {
509   post_video_port_t *port = (post_video_port_t *)port_gen;
510   post_plugin_deinterlace_t *this = (post_plugin_deinterlace_t *)port->post;
511 
512   port->stream = NULL;
513   _flush_frames(this);
514   port->original_port->set_property(port->original_port,
515                                     XINE_PARAM_VO_DEINTERLACE,
516                                     0);
517   port->original_port->close(port->original_port, stream);
518   _x_post_dec_usage(port);
519 }
520 
521 
deinterlace_intercept_frame(post_video_port_t * port,vo_frame_t * frame)522 static int deinterlace_intercept_frame(post_video_port_t *port, vo_frame_t *frame)
523 {
524   post_plugin_deinterlace_t *this = (post_plugin_deinterlace_t *)port->post;
525   int vo_deinterlace_enabled = 0;
526 
527   vo_deinterlace_enabled = ( frame->format != XINE_IMGFMT_YV12 &&
528                              frame->format != XINE_IMGFMT_YUY2 &&
529                              this->enabled );
530 
531   if( this->cur_method &&
532       this->vo_deinterlace_enabled != vo_deinterlace_enabled ) {
533     this->vo_deinterlace_enabled = vo_deinterlace_enabled;
534     port->original_port->set_property(port->original_port,
535                                       XINE_PARAM_VO_DEINTERLACE,
536                                       this->vo_deinterlace_enabled);
537   }
538 
539   return (this->enabled && this->cur_method &&
540       (frame->flags & VO_INTERLACED_FLAG) &&
541       (frame->format == XINE_IMGFMT_YV12 || frame->format == XINE_IMGFMT_YUY2) );
542 }
543 
544 
apply_chroma_filter(uint8_t * data,int stride,int width,int height)545 static void apply_chroma_filter( uint8_t *data, int stride, int width, int height )
546 {
547   int i;
548 
549   /* ok, using linearblend inplace is a bit weird: the result of a scanline
550    * interpolation will affect the next scanline. this might not be a problem
551    * at all, we just want a kind of filter here.
552    */
553   for( i = 0; i < height; i++, data += stride ) {
554     vfilter_chroma_332_packed422_scanline( data, width,
555                                            data,
556                                            (i) ? (data - stride) : data,
557                                            (i < height-1) ? (data + stride) : data );
558   }
559 }
560 
561 /* Build the output frame from the specified field. */
deinterlace_build_output_field(post_plugin_deinterlace_t * this,post_video_port_t * port,xine_stream_t * stream,vo_frame_t * frame,vo_frame_t * yuy2_frame,int bottom_field,int second_field,int64_t pts,int64_t duration,int skip)562 static int deinterlace_build_output_field(
563              post_plugin_deinterlace_t *this, post_video_port_t *port,
564              xine_stream_t *stream,
565              vo_frame_t *frame, vo_frame_t *yuy2_frame,
566              int bottom_field, int second_field,
567              int64_t pts, int64_t duration, int skip)
568 {
569   vo_frame_t *deinterlaced_frame;
570   int scaler = 1;
571   int force24fps;
572 
573   force24fps = this->judder_correction && !this->cheap_mode &&
574                ( this->pulldown == PULLDOWN_VEKTOR && this->tvtime->filmmode );
575 
576   if( this->tvtime->curmethod->doscalerbob ) {
577     scaler = 2;
578   }
579 
580   pthread_mutex_unlock (&this->lock);
581   deinterlaced_frame = port->original_port->get_frame(port->original_port,
582     frame->width, frame->height / scaler, frame->ratio, yuy2_frame->format,
583     frame->flags | VO_BOTH_FIELDS);
584   pthread_mutex_lock (&this->lock);
585 
586   deinterlaced_frame->crop_left   = frame->crop_left;
587   deinterlaced_frame->crop_right  = frame->crop_right;
588   deinterlaced_frame->crop_top    = frame->crop_top;
589   deinterlaced_frame->crop_bottom = frame->crop_bottom;
590 
591   _x_extra_info_merge(deinterlaced_frame->extra_info, frame->extra_info);
592 
593   if( skip > 0 && !this->pulldown ) {
594     deinterlaced_frame->bad_frame = 1;
595   } else {
596     if( this->tvtime->curmethod->doscalerbob ) {
597       if( yuy2_frame->format == XINE_IMGFMT_YUY2 ) {
598         deinterlaced_frame->bad_frame = !tvtime_build_copied_field(this->tvtime,
599                            deinterlaced_frame->base[0],
600                            yuy2_frame->base[0], bottom_field,
601                            frame->width, frame->height,
602                            yuy2_frame->pitches[0], deinterlaced_frame->pitches[0] );
603       } else {
604         deinterlaced_frame->bad_frame = !tvtime_build_copied_field(this->tvtime,
605                            deinterlaced_frame->base[0],
606                            yuy2_frame->base[0], bottom_field,
607                            frame->width/2, frame->height,
608                            yuy2_frame->pitches[0], deinterlaced_frame->pitches[0] );
609         deinterlaced_frame->bad_frame += !tvtime_build_copied_field(this->tvtime,
610                            deinterlaced_frame->base[1],
611                            yuy2_frame->base[1], bottom_field,
612                            frame->width/4, frame->height/2,
613                            yuy2_frame->pitches[1], deinterlaced_frame->pitches[1] );
614         deinterlaced_frame->bad_frame += !tvtime_build_copied_field(this->tvtime,
615                            deinterlaced_frame->base[2],
616                            yuy2_frame->base[2], bottom_field,
617                            frame->width/4, frame->height/2,
618                            yuy2_frame->pitches[2], deinterlaced_frame->pitches[2] );
619       }
620     } else {
621       if( yuy2_frame->format == XINE_IMGFMT_YUY2 ) {
622         deinterlaced_frame->bad_frame = !tvtime_build_deinterlaced_frame(this->tvtime,
623                            deinterlaced_frame->base[0],
624                            yuy2_frame->base[0],
625                            (this->recent_frame[0])?this->recent_frame[0]->base[0]:yuy2_frame->base[0],
626                            (this->recent_frame[1])?this->recent_frame[1]->base[0]:yuy2_frame->base[0],
627                            bottom_field, second_field, frame->width, frame->height,
628                            yuy2_frame->pitches[0], deinterlaced_frame->pitches[0]);
629       } else {
630         deinterlaced_frame->bad_frame = !tvtime_build_deinterlaced_frame(this->tvtime,
631                            deinterlaced_frame->base[0],
632                            yuy2_frame->base[0],
633                            (this->recent_frame[0])?this->recent_frame[0]->base[0]:yuy2_frame->base[0],
634                            (this->recent_frame[1])?this->recent_frame[1]->base[0]:yuy2_frame->base[0],
635                            bottom_field, second_field, frame->width/2, frame->height,
636                            yuy2_frame->pitches[0], deinterlaced_frame->pitches[0]);
637         deinterlaced_frame->bad_frame += !tvtime_build_deinterlaced_frame(this->tvtime,
638                            deinterlaced_frame->base[1],
639                            yuy2_frame->base[1],
640                            (this->recent_frame[0])?this->recent_frame[0]->base[1]:yuy2_frame->base[1],
641                            (this->recent_frame[1])?this->recent_frame[1]->base[1]:yuy2_frame->base[1],
642                            bottom_field, second_field, frame->width/4, frame->height/2,
643                            yuy2_frame->pitches[1], deinterlaced_frame->pitches[1]);
644         deinterlaced_frame->bad_frame += !tvtime_build_deinterlaced_frame(this->tvtime,
645                            deinterlaced_frame->base[2],
646                            yuy2_frame->base[2],
647                            (this->recent_frame[0])?this->recent_frame[0]->base[2]:yuy2_frame->base[2],
648                            (this->recent_frame[1])?this->recent_frame[1]->base[2]:yuy2_frame->base[2],
649                            bottom_field, second_field, frame->width/4, frame->height/2,
650                            yuy2_frame->pitches[2], deinterlaced_frame->pitches[2]);
651       }
652     }
653   }
654 
655   pthread_mutex_unlock (&this->lock);
656   if( force24fps ) {
657     if( !deinterlaced_frame->bad_frame ) {
658       this->framecounter++;
659       if( pts && this->framecounter > FRAMES_TO_SYNC ) {
660         deinterlaced_frame->pts = pts;
661         this->framecounter = 0;
662       } else
663         deinterlaced_frame->pts = 0;
664       deinterlaced_frame->duration = FPS_24_DURATION;
665       if( this->chroma_filter && !this->cheap_mode )
666         apply_chroma_filter( deinterlaced_frame->base[0], deinterlaced_frame->pitches[0],
667                              frame->width, frame->height / scaler );
668       skip = deinterlaced_frame->draw(deinterlaced_frame, stream);
669     } else {
670       skip = 0;
671     }
672   } else {
673     deinterlaced_frame->pts = pts;
674     deinterlaced_frame->duration = duration;
675     if( this->chroma_filter && !this->cheap_mode && !deinterlaced_frame->bad_frame )
676       apply_chroma_filter( deinterlaced_frame->base[0], deinterlaced_frame->pitches[0],
677                            frame->width, frame->height / scaler );
678     skip = deinterlaced_frame->draw(deinterlaced_frame, stream);
679   }
680 
681   /* _x_post_frame_copy_up(frame, deinterlaced_frame); */
682   deinterlaced_frame->free(deinterlaced_frame);
683   pthread_mutex_lock (&this->lock);
684 
685   return skip;
686 }
687 
deinterlace_draw(vo_frame_t * frame,xine_stream_t * stream)688 static int deinterlace_draw(vo_frame_t *frame, xine_stream_t *stream)
689 {
690   post_video_port_t *port = (post_video_port_t *)frame->port;
691   post_plugin_deinterlace_t *this = (post_plugin_deinterlace_t *)port->post;
692   vo_frame_t *orig_frame;
693   vo_frame_t *yuy2_frame;
694   int i, skip = 0, progressive = 0;
695   int fields[2] = {0, 0};
696   int framerate_mode;
697 
698   orig_frame = frame;
699   _x_post_frame_copy_down(frame, frame->next);
700   frame = frame->next;
701 
702   /* update tvtime context and method */
703   pthread_mutex_lock (&this->lock);
704   if( this->tvtime_changed ) {
705     tvtime_reset_context(this->tvtime);
706 
707     if( this->cur_method )
708       this->tvtime->curmethod = get_deinterlace_method( this->class->methods, this->cur_method-1 );
709     else
710       this->tvtime->curmethod = NULL;
711 
712     port->original_port->set_property(port->original_port,
713                                 XINE_PARAM_VO_DEINTERLACE,
714                                 !this->cur_method);
715 
716     this->tvtime_changed = 0;
717   }
718   if( this->tvtime_last_filmmode != this->tvtime->filmmode ) {
719     xine_event_t event;
720     event.type = XINE_EVENT_POST_TVTIME_FILMMODE_CHANGE;
721     event.stream = stream;
722     event.data = (void *)&this->tvtime->filmmode;
723     event.data_length = sizeof(this->tvtime->filmmode);
724     xine_event_send(stream, &event);
725     this->tvtime_last_filmmode = this->tvtime->filmmode;
726   }
727   pthread_mutex_unlock (&this->lock);
728 
729   lprintf("frame flags pf: %d rff: %d tff: %d duration: %d\n",
730            frame->progressive_frame, frame->repeat_first_field,
731            frame->top_field_first, frame->duration);
732 
733   /* detect special rff patterns */
734   this->rff_pattern = this->rff_pattern << 1;
735   this->rff_pattern |= !!frame->repeat_first_field;
736 
737   if( ((this->rff_pattern & 0xff) == 0xaa ||
738       (this->rff_pattern & 0xff) == 0x55) ) {
739     /*
740      * special case for ntsc 3:2 pulldown (called flags or soft pulldown).
741      * we know all frames are indeed progressive.
742      */
743     progressive = 1;
744   }
745 
746   /* using frame->progressive_frame may help displaying still menus.
747    * however, it is known that some rare material set it wrong.
748    *
749    * we also assume that repeat_first_field is progressive (it doesn't
750    * make much sense to display interlaced fields out of order)
751    */
752   if( this->use_progressive_frame_flag &&
753       (frame->repeat_first_field || frame->progressive_frame) ) {
754     progressive = 1;
755   }
756 
757   if( !frame->bad_frame &&
758       (frame->flags & VO_INTERLACED_FLAG) &&
759       this->tvtime->curmethod ) {
760 
761     frame->flags &= ~VO_INTERLACED_FLAG;
762 
763     /* convert to YUY2 if needed */
764     if( frame->format == XINE_IMGFMT_YV12 && !this->cheap_mode ) {
765 
766       yuy2_frame = port->original_port->get_frame(port->original_port,
767         frame->width, frame->height, frame->ratio, XINE_IMGFMT_YUY2, frame->flags | VO_BOTH_FIELDS);
768       _x_post_frame_copy_down(frame, yuy2_frame);
769 
770       /* the logic for deciding upsampling to use comes from:
771        * http://www.hometheaterhifi.com/volume_8_2/dvd-benchmark-special-report-chroma-bug-4-2001.html
772        */
773       yv12_to_yuy2(frame->base[0], frame->pitches[0],
774                    frame->base[1], frame->pitches[1],
775                    frame->base[2], frame->pitches[2],
776                    yuy2_frame->base[0], yuy2_frame->pitches[0],
777                    frame->width, frame->height,
778                    frame->progressive_frame || progressive );
779 
780     } else {
781       yuy2_frame = frame;
782       yuy2_frame->lock(yuy2_frame);
783     }
784 
785 
786     pthread_mutex_lock (&this->lock);
787     /* check if frame format changed */
788     for(i = 0; i < NUM_RECENT_FRAMES; i++ ) {
789       if( this->recent_frame[i] &&
790           (this->recent_frame[i]->width != frame->width ||
791            this->recent_frame[i]->height != frame->height ||
792            this->recent_frame[i]->format != yuy2_frame->format ) ) {
793         this->recent_frame[i]->free(this->recent_frame[i]);
794         this->recent_frame[i] = NULL;
795       }
796     }
797 
798     if( !this->cheap_mode ) {
799       framerate_mode = this->framerate_mode;
800       this->tvtime->pulldown_alg = this->pulldown;
801     } else {
802       framerate_mode = FRAMERATE_HALF_TFF;
803       this->tvtime->pulldown_alg = PULLDOWN_NONE;
804     }
805 
806     if( framerate_mode == FRAMERATE_FULL ) {
807       int top_field_first = frame->top_field_first;
808 
809       /* if i understood mpeg2 specs correctly, top_field_first
810        * shall be zero for field pictures and the output order
811        * is the same that the fields are decoded.
812        * frame->flags allow us to find the first decoded field.
813        *
814        * note: frame->field() is called later to switch decoded
815        *       field but frame->flags do not change.
816        */
817       if ( (frame->flags & VO_BOTH_FIELDS) != VO_BOTH_FIELDS ) {
818         top_field_first = (frame->flags & VO_TOP_FIELD) ? 1 : 0;
819       }
820 
821       if ( top_field_first ) {
822         fields[0] = 0;
823         fields[1] = 1;
824       } else {
825         fields[0] = 1;
826         fields[1] = 0;
827       }
828     } else if ( framerate_mode == FRAMERATE_HALF_TFF ) {
829       fields[0] = 0;
830     } else if ( framerate_mode == FRAMERATE_HALF_BFF ) {
831       fields[0] = 1;
832     }
833 
834 
835     if( progressive ) {
836 
837       /* If the previous field was interlaced and this one is progressive
838        * we need to run a deinterlace on the first field of this frame
839        * in order to let output for the previous frames last field be
840        * generated. This is only necessary for the deinterlacers that
841        * delay output by one field.  This is signaled by the delaysfield
842        * flag in the deinterlace method structure. The previous frames
843        * duration is used in the calculation because the generated frame
844        * represents the second half of the previous frame.
845        */
846       if (this->recent_frame[0] && !this->recent_frame[0]->progressive_frame &&
847           this->tvtime->curmethod->delaysfield)
848       {
849         deinterlace_build_output_field(
850           this, port, stream,
851           frame, yuy2_frame,
852           fields[0], 0,
853 	  0,
854 	  (framerate_mode == FRAMERATE_FULL) ? this->recent_frame[0]->duration/2 : this->recent_frame[0]->duration,
855 	  0);
856       }
857       pthread_mutex_unlock (&this->lock);
858       skip = yuy2_frame->draw(yuy2_frame, stream);
859       pthread_mutex_lock (&this->lock);
860       _x_post_frame_copy_up(frame, yuy2_frame);
861 
862     } else {
863 
864 
865       /* If the previous field was progressive and we are using a
866        * filter that delays it's output by one field then we need
867        * to skip the first field's output. Otherwise the effective
868        * display duration of the previous frame will be extended
869        * by 1/2 of this frames duration when output is generated
870        * using the last field of the progressive frame. */
871 
872       /* Build the output from the first field. */
873       if ( !(this->recent_frame[0] && this->recent_frame[0]->progressive_frame &&
874              this->tvtime->curmethod->delaysfield) ) {
875         skip = deinterlace_build_output_field(
876           this, port, stream,
877           frame, yuy2_frame,
878           fields[0], 0,
879           frame->pts,
880           (framerate_mode == FRAMERATE_FULL) ? frame->duration/2 : frame->duration,
881           0);
882       }
883 
884       if( framerate_mode == FRAMERATE_FULL ) {
885 
886         /* Build the output from the second field. */
887         skip = deinterlace_build_output_field(
888           this, port, stream,
889           frame, yuy2_frame,
890           fields[1], 1,
891           0,
892           frame->duration/2,
893           skip);
894       }
895     }
896 
897     /* don't drop frames when pulldown mode is enabled. otherwise
898      * pulldown detection fails (yo-yo effect has also been seen)
899      */
900     if( this->pulldown )
901       skip = 0;
902 
903     /* store back progressive flag for frame history */
904     yuy2_frame->progressive_frame = progressive;
905 
906     /* keep track of recent frames */
907     i = NUM_RECENT_FRAMES-1;
908     if( this->recent_frame[i] )
909       this->recent_frame[i]->free(this->recent_frame[i]);
910     for( ; i ; i-- )
911       this->recent_frame[i] = this->recent_frame[i-1];
912     if (port->stream)
913       this->recent_frame[0] = yuy2_frame;
914     else {
915       /* do not keep this frame when no stream is connected to us,
916        * otherwise, this frame might never get freed */
917       yuy2_frame->free(yuy2_frame);
918       this->recent_frame[0] = NULL;
919     }
920 
921     pthread_mutex_unlock (&this->lock);
922 
923   } else {
924     skip = frame->draw(frame, stream);
925   }
926 
927   _x_post_frame_copy_up(orig_frame, frame);
928 
929   return skip;
930 }
931 
932 /* plugin catalog information */
933 static const post_info_t deinterlace_special_info = {
934   .type = XINE_POST_TYPE_VIDEO_FILTER,
935 };
936 
937 const plugin_info_t xine_plugin_info[] EXPORTED = {
938   /* type, API, "name", version, special_info, init_function */
939   { PLUGIN_POST | PLUGIN_MUST_PRELOAD, 10, "tvtime", XINE_VERSION_CODE, &deinterlace_special_info, &deinterlace_init_plugin },
940   { PLUGIN_NONE, 0, NULL, 0, NULL, NULL }
941 };
942