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 ¶m_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 *)¶ms_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