1 /*****************************************************************************
2 * deinterlace.c : deinterlacer plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2000, 2001, 2002, 2003 VideoLAN
7 * Author: Sam Hocevar <sam@zoy.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
28 #include <stdlib.h> /* malloc(), free() */
34 #include "../filter_common.h"
36 #define DEINTERLACE_DISCARD 1
37 #define DEINTERLACE_MEAN 2
38 #define DEINTERLACE_BLEND 3
39 #define DEINTERLACE_BOB 4
40 #define DEINTERLACE_LINEAR 5
42 /*****************************************************************************
44 *****************************************************************************/
45 static int Create ( vlc_object_t * );
46 static void Destroy ( vlc_object_t * );
48 static int Init ( vout_thread_t * );
49 static void End ( vout_thread_t * );
50 static void Render ( vout_thread_t *, picture_t * );
52 static void RenderDiscard( vout_thread_t *, picture_t *, picture_t *, int );
53 static void RenderBob ( vout_thread_t *, picture_t *, picture_t *, int );
54 static void RenderMean ( vout_thread_t *, picture_t *, picture_t * );
55 static void RenderBlend ( vout_thread_t *, picture_t *, picture_t * );
56 static void RenderLinear ( vout_thread_t *, picture_t *, picture_t *, int );
58 static void MergeGeneric ( void *, const void *, const void *, size_t );
59 #if defined(CAN_COMPILE_C_ALTIVEC)
60 static void MergeAltivec ( void *, const void *, const void *, size_t );
62 #if defined(CAN_COMPILE_MMX)
63 static void MergeMMX ( void *, const void *, const void *, size_t );
65 #if defined(CAN_COMPILE_SSE)
66 static void MergeSSE2 ( void *, const void *, const void *, size_t );
68 #if defined(CAN_COMPILE_MMX) || defined(CAN_COMPILE_SSE)
69 static void EndMMX ( void );
72 static int SendEvents ( vlc_object_t *, char const *,
73 vlc_value_t, vlc_value_t, void * );
75 static void SetFilterMethod( vout_thread_t *p_vout, char *psz_method );
76 static vout_thread_t *SpawnRealVout( vout_thread_t *p_vout );
78 /*****************************************************************************
80 *****************************************************************************/
81 static int FilterCallback ( vlc_object_t *, char const *,
82 vlc_value_t, vlc_value_t, void * );
84 /*****************************************************************************
86 *****************************************************************************/
87 #define MODE_TEXT N_("Deinterlace mode")
88 #define MODE_LONGTEXT N_("You can choose the default deinterlace mode")
90 static char *mode_list[] = { "discard", "blend", "mean", "bob", "linear" };
91 static char *mode_list_text[] = { N_("Discard"), N_("Blend"), N_("Mean"),
92 N_("Bob"), N_("Linear") };
95 set_description( _("Deinterlacing video filter") );
96 set_capability( "video filter", 0 );
98 add_string( "deinterlace-mode", "discard", NULL, MODE_TEXT,
99 MODE_LONGTEXT, VLC_FALSE );
100 change_string_list( mode_list, mode_list_text, 0 );
102 add_shortcut( "deinterlace" );
103 set_callbacks( Create, Destroy );
106 /*****************************************************************************
107 * vout_sys_t: Deinterlace video output method descriptor
108 *****************************************************************************
109 * This structure is part of the video output thread descriptor.
110 * It describes the Deinterlace specific properties of an output thread.
111 *****************************************************************************/
114 int i_mode; /* Deinterlace mode */
115 vlc_bool_t b_double_rate; /* Shall we double the framerate? */
120 vout_thread_t *p_vout;
122 vlc_mutex_t filter_lock;
124 void (*pf_merge) ( void *, const void *, const void *, size_t );
125 void (*pf_end_merge) ( void );
128 /*****************************************************************************
129 * Create: allocates Deinterlace video thread output method
130 *****************************************************************************
131 * This function allocates and initializes a Deinterlace vout method.
132 *****************************************************************************/
133 static int Create( vlc_object_t *p_this )
135 vout_thread_t *p_vout = (vout_thread_t *)p_this;
138 /* Allocate structure */
139 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
140 if( p_vout->p_sys == NULL )
142 msg_Err( p_vout, "out of memory" );
146 p_vout->pf_init = Init;
147 p_vout->pf_end = End;
148 p_vout->pf_manage = NULL;
149 p_vout->pf_render = Render;
150 p_vout->pf_display = NULL;
152 p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
153 p_vout->p_sys->b_double_rate = 0;
154 p_vout->p_sys->last_date = 0;
155 vlc_mutex_init( p_vout, &p_vout->p_sys->filter_lock );
157 #if defined(CAN_COMPILE_C_ALTIVEC)
158 if( p_vout->p_libvlc->i_cpu & CPU_CAPABILITY_ALTIVEC )
160 p_vout->p_sys->pf_merge = MergeAltivec;
161 p_vout->p_sys->pf_end_merge = NULL;
165 #if defined(CAN_COMPILE_SSE)
166 if( p_vout->p_libvlc->i_cpu & CPU_CAPABILITY_SSE2 )
168 p_vout->p_sys->pf_merge = MergeSSE2;
169 p_vout->p_sys->pf_end_merge = EndMMX;
173 #if defined(CAN_COMPILE_MMX)
174 if( p_vout->p_libvlc->i_cpu & CPU_CAPABILITY_MMX )
176 p_vout->p_sys->pf_merge = MergeMMX;
177 p_vout->p_sys->pf_end_merge = EndMMX;
182 p_vout->p_sys->pf_merge = MergeGeneric;
183 p_vout->p_sys->pf_end_merge = NULL;
186 /* Look what method was requested */
187 var_Create( p_vout, "deinterlace-mode", VLC_VAR_STRING );
188 var_Change( p_vout, "deinterlace-mode", VLC_VAR_INHERITVALUE, &val, NULL );
190 if( val.psz_string == NULL )
192 msg_Err( p_vout, "configuration variable deinterlace-mode empty" );
193 msg_Err( p_vout, "no deinterlace mode provided, using \"discard\"" );
195 val.psz_string = strdup( "discard" );
198 msg_Dbg( p_vout, "using %s deinterlace mode", val.psz_string );
200 SetFilterMethod( p_vout, val.psz_string );
202 free( val.psz_string );
204 var_AddCallback( p_vout, "deinterlace-mode", FilterCallback, NULL );
209 /*****************************************************************************
210 * SetFilterMethod: setup the deinterlace method to use.
211 *****************************************************************************/
212 static void SetFilterMethod( vout_thread_t *p_vout, char *psz_method )
214 if( !strcmp( psz_method, "discard" ) )
216 p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
217 p_vout->p_sys->b_double_rate = 0;
219 else if( !strcmp( psz_method, "mean" ) )
221 p_vout->p_sys->i_mode = DEINTERLACE_MEAN;
222 p_vout->p_sys->b_double_rate = 0;
224 else if( !strcmp( psz_method, "blend" )
225 || !strcmp( psz_method, "average" )
226 || !strcmp( psz_method, "combine-fields" ) )
228 p_vout->p_sys->i_mode = DEINTERLACE_BLEND;
229 p_vout->p_sys->b_double_rate = 0;
231 else if( !strcmp( psz_method, "bob" )
232 || !strcmp( psz_method, "progressive-scan" ) )
234 p_vout->p_sys->i_mode = DEINTERLACE_BOB;
235 p_vout->p_sys->b_double_rate = 1;
237 else if( !strcmp( psz_method, "linear" ) )
239 p_vout->p_sys->i_mode = DEINTERLACE_LINEAR;
240 p_vout->p_sys->b_double_rate = 1;
244 msg_Err( p_vout, "no valid deinterlace mode provided, "
245 "using \"discard\"" );
248 msg_Dbg( p_vout, "using %s deinterlace method", psz_method );
251 /*****************************************************************************
252 * Init: initialize Deinterlace video thread output method
253 *****************************************************************************/
254 static int Init( vout_thread_t *p_vout )
259 I_OUTPUTPICTURES = 0;
261 /* Initialize the output structure, full of directbuffers since we want
262 * the decoder to output directly to our structures. */
263 switch( p_vout->render.i_chroma )
265 case VLC_FOURCC('I','4','2','0'):
266 case VLC_FOURCC('I','Y','U','V'):
267 case VLC_FOURCC('Y','V','1','2'):
268 case VLC_FOURCC('I','4','2','2'):
269 p_vout->output.i_chroma = p_vout->render.i_chroma;
270 p_vout->output.i_width = p_vout->render.i_width;
271 p_vout->output.i_height = p_vout->render.i_height;
272 p_vout->output.i_aspect = p_vout->render.i_aspect;
276 return VLC_EGENERIC; /* unknown chroma */
280 /* Try to open the real video output */
281 p_vout->p_sys->p_vout = SpawnRealVout( p_vout );
283 if( p_vout->p_sys->p_vout == NULL )
285 /* Everything failed */
286 msg_Err( p_vout, "cannot open vout, aborting" );
291 ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
293 ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
295 ADD_PARENT_CALLBACKS( SendEventsToChild );
300 /*****************************************************************************
301 * SpawnRealVout: spawn the real video output.
302 *****************************************************************************/
303 static vout_thread_t *SpawnRealVout( vout_thread_t *p_vout )
305 vout_thread_t *p_real_vout = NULL;
307 msg_Dbg( p_vout, "spawning the real video output" );
309 switch( p_vout->render.i_chroma )
311 case VLC_FOURCC('I','4','2','0'):
312 case VLC_FOURCC('I','Y','U','V'):
313 case VLC_FOURCC('Y','V','1','2'):
314 switch( p_vout->p_sys->i_mode )
316 case DEINTERLACE_MEAN:
317 case DEINTERLACE_DISCARD:
320 p_vout->output.i_width, p_vout->output.i_height / 2,
321 p_vout->output.i_chroma, p_vout->output.i_aspect );
324 case DEINTERLACE_BOB:
325 case DEINTERLACE_BLEND:
326 case DEINTERLACE_LINEAR:
329 p_vout->output.i_width, p_vout->output.i_height,
330 p_vout->output.i_chroma, p_vout->output.i_aspect );
335 case VLC_FOURCC('I','4','2','2'):
338 p_vout->output.i_width, p_vout->output.i_height,
339 VLC_FOURCC('I','4','2','0'), p_vout->output.i_aspect );
349 /*****************************************************************************
350 * End: terminate Deinterlace video thread output method
351 *****************************************************************************/
352 static void End( vout_thread_t *p_vout )
356 /* Free the fake output buffers we allocated */
357 for( i_index = I_OUTPUTPICTURES ; i_index ; )
360 free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
364 /*****************************************************************************
365 * Destroy: destroy Deinterlace video thread output method
366 *****************************************************************************
367 * Terminate an output method created by DeinterlaceCreateOutputMethod
368 *****************************************************************************/
369 static void Destroy( vlc_object_t *p_this )
371 vout_thread_t *p_vout = (vout_thread_t *)p_this;
373 DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
375 vlc_object_detach( p_vout->p_sys->p_vout );
376 vout_Destroy( p_vout->p_sys->p_vout );
378 DEL_PARENT_CALLBACKS( SendEventsToChild );
380 free( p_vout->p_sys );
383 /*****************************************************************************
384 * Render: displays previously rendered output
385 *****************************************************************************
386 * This function send the currently rendered image to Deinterlace image,
387 * waits until it is displayed and switch the two rendering buffers, preparing
389 *****************************************************************************/
390 static void Render ( vout_thread_t *p_vout, picture_t *p_pic )
392 picture_t *pp_outpic[2];
394 vlc_mutex_lock( &p_vout->p_sys->filter_lock );
396 /* Get a new picture */
397 while( ( pp_outpic[0] = vout_CreatePicture( p_vout->p_sys->p_vout,
401 if( p_vout->b_die || p_vout->b_error )
403 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
406 msleep( VOUT_OUTMEM_SLEEP );
409 vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[0], p_pic->date );
411 /* If we are using double rate, get an additional new picture */
412 if( p_vout->p_sys->b_double_rate )
414 while( ( pp_outpic[1] = vout_CreatePicture( p_vout->p_sys->p_vout,
418 if( p_vout->b_die || p_vout->b_error )
420 vout_DestroyPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
421 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
424 msleep( VOUT_OUTMEM_SLEEP );
427 /* 20ms is a bit arbitrary, but it's only for the first image we get */
428 if( !p_vout->p_sys->last_date )
430 vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
431 p_pic->date + 20000 );
435 vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
436 (3 * p_pic->date - p_vout->p_sys->last_date) / 2 );
438 p_vout->p_sys->last_date = p_pic->date;
441 switch( p_vout->p_sys->i_mode )
443 case DEINTERLACE_DISCARD:
444 RenderDiscard( p_vout, pp_outpic[0], p_pic, 0 );
445 vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
448 case DEINTERLACE_BOB:
449 RenderBob( p_vout, pp_outpic[0], p_pic, 0 );
450 vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
451 RenderBob( p_vout, pp_outpic[1], p_pic, 1 );
452 vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
455 case DEINTERLACE_LINEAR:
456 RenderLinear( p_vout, pp_outpic[0], p_pic, 0 );
457 vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
458 RenderLinear( p_vout, pp_outpic[1], p_pic, 1 );
459 vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
462 case DEINTERLACE_MEAN:
463 RenderMean( p_vout, pp_outpic[0], p_pic );
464 vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
467 case DEINTERLACE_BLEND:
468 RenderBlend( p_vout, pp_outpic[0], p_pic );
469 vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
473 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
476 /*****************************************************************************
477 * RenderDiscard: only keep TOP or BOTTOM field, discard the other.
478 *****************************************************************************/
479 static void RenderDiscard( vout_thread_t *p_vout,
480 picture_t *p_outpic, picture_t *p_pic, int i_field )
484 /* Copy image and skip lines */
485 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
487 uint8_t *p_in, *p_out_end, *p_out;
490 p_in = p_pic->p[i_plane].p_pixels
491 + i_field * p_pic->p[i_plane].i_pitch;
493 p_out = p_outpic->p[i_plane].p_pixels;
494 p_out_end = p_out + p_outpic->p[i_plane].i_pitch
495 * p_outpic->p[i_plane].i_lines;
497 switch( p_vout->render.i_chroma )
499 case VLC_FOURCC('I','4','2','0'):
500 case VLC_FOURCC('I','Y','U','V'):
501 case VLC_FOURCC('Y','V','1','2'):
503 for( ; p_out < p_out_end ; )
505 p_vout->p_vlc->pf_memcpy( p_out, p_in,
506 p_pic->p[i_plane].i_pitch );
508 p_out += p_pic->p[i_plane].i_pitch;
509 p_in += 2 * p_pic->p[i_plane].i_pitch;
513 case VLC_FOURCC('I','4','2','2'):
515 i_increment = 2 * p_pic->p[i_plane].i_pitch;
517 if( i_plane == Y_PLANE )
519 for( ; p_out < p_out_end ; )
521 p_vout->p_vlc->pf_memcpy( p_out, p_in,
522 p_pic->p[i_plane].i_pitch );
523 p_out += p_pic->p[i_plane].i_pitch;
524 p_vout->p_vlc->pf_memcpy( p_out, p_in,
525 p_pic->p[i_plane].i_pitch );
526 p_out += p_pic->p[i_plane].i_pitch;
532 for( ; p_out < p_out_end ; )
534 p_vout->p_vlc->pf_memcpy( p_out, p_in,
535 p_pic->p[i_plane].i_pitch );
536 p_out += p_pic->p[i_plane].i_pitch;
548 /*****************************************************************************
549 * RenderBob: renders a BOB picture - simple copy
550 *****************************************************************************/
551 static void RenderBob( vout_thread_t *p_vout,
552 picture_t *p_outpic, picture_t *p_pic, int i_field )
556 /* Copy image and skip lines */
557 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
559 uint8_t *p_in, *p_out_end, *p_out;
561 p_in = p_pic->p[i_plane].p_pixels;
562 p_out = p_outpic->p[i_plane].p_pixels;
563 p_out_end = p_out + p_outpic->p[i_plane].i_pitch
564 * p_outpic->p[i_plane].i_lines;
566 switch( p_vout->render.i_chroma )
568 case VLC_FOURCC('I','4','2','0'):
569 case VLC_FOURCC('I','Y','U','V'):
570 case VLC_FOURCC('Y','V','1','2'):
571 /* For BOTTOM field we need to add the first line */
574 p_vout->p_vlc->pf_memcpy( p_out, p_in,
575 p_pic->p[i_plane].i_pitch );
576 p_in += p_pic->p[i_plane].i_pitch;
577 p_out += p_pic->p[i_plane].i_pitch;
580 p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
582 for( ; p_out < p_out_end ; )
584 p_vout->p_vlc->pf_memcpy( p_out, p_in,
585 p_pic->p[i_plane].i_pitch );
587 p_out += p_pic->p[i_plane].i_pitch;
589 p_vout->p_vlc->pf_memcpy( p_out, p_in,
590 p_pic->p[i_plane].i_pitch );
592 p_in += 2 * p_pic->p[i_plane].i_pitch;
593 p_out += p_pic->p[i_plane].i_pitch;
596 p_vout->p_vlc->pf_memcpy( p_out, p_in,
597 p_pic->p[i_plane].i_pitch );
599 /* For TOP field we need to add the last line */
602 p_in += p_pic->p[i_plane].i_pitch;
603 p_out += p_pic->p[i_plane].i_pitch;
604 p_vout->p_vlc->pf_memcpy( p_out, p_in,
605 p_pic->p[i_plane].i_pitch );
609 case VLC_FOURCC('I','4','2','2'):
610 /* For BOTTOM field we need to add the first line */
613 p_vout->p_vlc->pf_memcpy( p_out, p_in,
614 p_pic->p[i_plane].i_pitch );
615 p_in += p_pic->p[i_plane].i_pitch;
616 p_out += p_pic->p[i_plane].i_pitch;
619 p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
621 if( i_plane == Y_PLANE )
623 for( ; p_out < p_out_end ; )
625 p_vout->p_vlc->pf_memcpy( p_out, p_in,
626 p_pic->p[i_plane].i_pitch );
628 p_out += p_pic->p[i_plane].i_pitch;
630 p_vout->p_vlc->pf_memcpy( p_out, p_in,
631 p_pic->p[i_plane].i_pitch );
633 p_in += 2 * p_pic->p[i_plane].i_pitch;
634 p_out += p_pic->p[i_plane].i_pitch;
639 for( ; p_out < p_out_end ; )
641 p_vout->p_vlc->pf_memcpy( p_out, p_in,
642 p_pic->p[i_plane].i_pitch );
644 p_out += p_pic->p[i_plane].i_pitch;
645 p_in += 2 * p_pic->p[i_plane].i_pitch;
649 p_vout->p_vlc->pf_memcpy( p_out, p_in,
650 p_pic->p[i_plane].i_pitch );
652 /* For TOP field we need to add the last line */
655 p_in += p_pic->p[i_plane].i_pitch;
656 p_out += p_pic->p[i_plane].i_pitch;
657 p_vout->p_vlc->pf_memcpy( p_out, p_in,
658 p_pic->p[i_plane].i_pitch );
665 #define Merge p_vout->p_sys->pf_merge
666 #define EndMerge if(p_vout->p_sys->pf_end_merge) p_vout->p_sys->pf_end_merge
668 /*****************************************************************************
669 * RenderLinear: BOB with linear interpolation
670 *****************************************************************************/
671 static void RenderLinear( vout_thread_t *p_vout,
672 picture_t *p_outpic, picture_t *p_pic, int i_field )
676 /* Copy image and skip lines */
677 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
679 uint8_t *p_in, *p_out_end, *p_out;
681 p_in = p_pic->p[i_plane].p_pixels;
682 p_out = p_outpic->p[i_plane].p_pixels;
683 p_out_end = p_out + p_outpic->p[i_plane].i_pitch
684 * p_outpic->p[i_plane].i_lines;
686 /* For BOTTOM field we need to add the first line */
689 p_vout->p_vlc->pf_memcpy( p_out, p_in,
690 p_pic->p[i_plane].i_pitch );
691 p_in += p_pic->p[i_plane].i_pitch;
692 p_out += p_pic->p[i_plane].i_pitch;
695 p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
697 for( ; p_out < p_out_end ; )
699 p_vout->p_vlc->pf_memcpy( p_out, p_in,
700 p_pic->p[i_plane].i_pitch );
702 p_out += p_pic->p[i_plane].i_pitch;
704 Merge( p_out, p_in, p_in + 2 * p_pic->p[i_plane].i_pitch,
705 p_pic->p[i_plane].i_pitch );
707 p_in += 2 * p_pic->p[i_plane].i_pitch;
708 p_out += p_pic->p[i_plane].i_pitch;
711 p_vout->p_vlc->pf_memcpy( p_out, p_in,
712 p_pic->p[i_plane].i_pitch );
714 /* For TOP field we need to add the last line */
717 p_in += p_pic->p[i_plane].i_pitch;
718 p_out += p_pic->p[i_plane].i_pitch;
719 p_vout->p_vlc->pf_memcpy( p_out, p_in,
720 p_pic->p[i_plane].i_pitch );
726 static void RenderMean( vout_thread_t *p_vout,
727 picture_t *p_outpic, picture_t *p_pic )
731 /* Copy image and skip lines */
732 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
734 uint8_t *p_in, *p_out_end, *p_out;
736 p_in = p_pic->p[i_plane].p_pixels;
738 p_out = p_outpic->p[i_plane].p_pixels;
739 p_out_end = p_out + p_outpic->p[i_plane].i_pitch
740 * p_outpic->p[i_plane].i_lines;
742 /* All lines: mean value */
743 for( ; p_out < p_out_end ; )
745 Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
746 p_pic->p[i_plane].i_pitch );
748 p_out += p_pic->p[i_plane].i_pitch;
749 p_in += 2 * p_pic->p[i_plane].i_pitch;
755 static void RenderBlend( vout_thread_t *p_vout,
756 picture_t *p_outpic, picture_t *p_pic )
760 /* Copy image and skip lines */
761 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
763 uint8_t *p_in, *p_out_end, *p_out;
765 p_in = p_pic->p[i_plane].p_pixels;
767 p_out = p_outpic->p[i_plane].p_pixels;
768 p_out_end = p_out + p_outpic->p[i_plane].i_pitch
769 * p_outpic->p[i_plane].i_lines;
771 switch( p_vout->render.i_chroma )
773 case VLC_FOURCC('I','4','2','0'):
774 case VLC_FOURCC('I','Y','U','V'):
775 case VLC_FOURCC('Y','V','1','2'):
776 /* First line: simple copy */
777 p_vout->p_vlc->pf_memcpy( p_out, p_in,
778 p_pic->p[i_plane].i_pitch );
779 p_out += p_pic->p[i_plane].i_pitch;
781 /* Remaining lines: mean value */
782 for( ; p_out < p_out_end ; )
784 Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
785 p_pic->p[i_plane].i_pitch );
787 p_out += p_pic->p[i_plane].i_pitch;
788 p_in += p_pic->p[i_plane].i_pitch;
792 case VLC_FOURCC('I','4','2','2'):
793 /* First line: simple copy */
794 p_vout->p_vlc->pf_memcpy( p_out, p_in,
795 p_pic->p[i_plane].i_pitch );
796 p_out += p_pic->p[i_plane].i_pitch;
798 /* Remaining lines: mean value */
799 if( i_plane == Y_PLANE )
801 for( ; p_out < p_out_end ; )
803 Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
804 p_pic->p[i_plane].i_pitch );
806 p_out += p_pic->p[i_plane].i_pitch;
807 p_in += p_pic->p[i_plane].i_pitch;
813 for( ; p_out < p_out_end ; )
815 Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
816 p_pic->p[i_plane].i_pitch );
818 p_out += p_pic->p[i_plane].i_pitch;
819 p_in += 2*p_pic->p[i_plane].i_pitch;
830 static void MergeGeneric( void *_p_dest, const void *_p_s1,
831 const void *_p_s2, size_t i_bytes )
833 uint8_t* p_dest = (uint8_t*)_p_dest;
834 const uint8_t *p_s1 = (const uint8_t *)_p_s1;
835 const uint8_t *p_s2 = (const uint8_t *)_p_s2;
836 uint8_t* p_end = p_dest + i_bytes - 8;
838 while( p_dest < p_end )
840 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
841 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
842 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
843 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
844 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
845 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
846 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
847 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
852 while( p_dest < p_end )
854 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
858 #if defined(CAN_COMPILE_MMX)
859 static void MergeMMX( void *_p_dest, const void *_p_s1, const void *_p_s2,
862 uint8_t* p_dest = (uint8_t*)_p_dest;
863 const uint8_t *p_s1 = (const uint8_t *)_p_s1;
864 const uint8_t *p_s2 = (const uint8_t *)_p_s2;
865 uint8_t* p_end = p_dest + i_bytes - 8;
866 while( p_dest < p_end )
868 __asm__ __volatile__( "movq %2,%%mm1;"
870 "movq %%mm1, %0" :"=m" (*p_dest):
880 while( p_dest < p_end )
882 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
887 #if defined(CAN_COMPILE_SSE)
888 static void MergeSSE2( void *_p_dest, const void *_p_s1, const void *_p_s2,
891 uint8_t* p_dest = (uint8_t*)_p_dest;
892 const uint8_t *p_s1 = (const uint8_t *)_p_s1;
893 const uint8_t *p_s2 = (const uint8_t *)_p_s2;
894 while( (int)p_s1 % 16 )
896 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
898 uint8_t* p_end = p_dest + i_bytes - 16;
899 while( p_dest < p_end )
901 __asm__ __volatile__( "movdqu %2,%%xmm1;"
903 "movdqu %%xmm1, %0" :"=m" (*p_dest):
913 while( p_dest < p_end )
915 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
920 #if defined(CAN_COMPILE_MMX) || defined(CAN_COMPILE_SSE)
921 static void EndMMX( void )
923 __asm__ __volatile__( "emms" :: );
927 #ifdef CAN_COMPILE_C_ALTIVEC
928 static void MergeAltivec( void *_p_dest, const void *_p_s1,
929 const void *_p_s2, size_t i_bytes )
931 uint8_t *p_dest = (uint8_t*)_p_dest;
932 const uint8_t *p_s1 = (const uint8_t *)_p_s1;
933 const uint8_t *p_s2 = (const uint8_t *)_p_s2;
934 uint8_t *p_end = p_dest + i_bytes - 16;
936 if( ( (int)p_s1 & 0xF ) | ( (int)p_s2 & 0xF ) |
937 ( (int)p_dest & 0xF ) )
939 /* TODO Handle non 16-bytes aligned planes */
940 MergeGeneric( _p_dest, _p_s1, _p_s2, i_bytes );
944 while( p_dest < p_end )
946 vec_st( vec_avg( vec_ld( 0, p_s1 ), vec_ld( 0, p_s2 ) ),
955 while( p_dest < p_end )
957 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
962 /*****************************************************************************
963 * SendEvents: forward mouse and keyboard events to the parent p_vout
964 *****************************************************************************/
965 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
966 vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
968 vout_thread_t *p_vout = (vout_thread_t *)_p_vout;
969 vlc_value_t sentval = newval;
971 if( !strcmp( psz_var, "mouse-y" ) )
973 switch( p_vout->p_sys->i_mode )
975 case DEINTERLACE_MEAN:
976 case DEINTERLACE_DISCARD:
982 var_Set( p_vout, psz_var, sentval );
987 /*****************************************************************************
988 * FilterCallback: called when changing the deinterlace method on the fly.
989 *****************************************************************************/
990 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
991 vlc_value_t oldval, vlc_value_t newval,
994 vout_thread_t * p_vout = (vout_thread_t *)p_this;
995 int i_old_mode = p_vout->p_sys->i_mode;
997 msg_Dbg( p_vout, "using %s deinterlace mode", newval.psz_string );
999 vlc_mutex_lock( &p_vout->p_sys->filter_lock );
1001 SetFilterMethod( p_vout, newval.psz_string );
1003 switch( p_vout->render.i_chroma )
1005 case VLC_FOURCC('I','4','2','2'):
1006 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1010 case VLC_FOURCC('I','4','2','0'):
1011 case VLC_FOURCC('I','Y','U','V'):
1012 case VLC_FOURCC('Y','V','1','2'):
1013 switch( p_vout->p_sys->i_mode )
1015 case DEINTERLACE_MEAN:
1016 case DEINTERLACE_DISCARD:
1017 if( ( i_old_mode == DEINTERLACE_MEAN )
1018 || ( i_old_mode == DEINTERLACE_DISCARD ) )
1020 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1025 case DEINTERLACE_BOB:
1026 case DEINTERLACE_BLEND:
1027 case DEINTERLACE_LINEAR:
1028 if( ( i_old_mode == DEINTERLACE_BOB )
1029 || ( i_old_mode == DEINTERLACE_BLEND )
1030 || ( i_old_mode == DEINTERLACE_LINEAR ) )
1032 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1043 /* We need to kill the old vout */
1045 DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
1047 vlc_object_detach( p_vout->p_sys->p_vout );
1048 vout_Destroy( p_vout->p_sys->p_vout );
1050 /* Try to open a new video output */
1051 p_vout->p_sys->p_vout = SpawnRealVout( p_vout );
1053 if( p_vout->p_sys->p_vout == NULL )
1055 /* Everything failed */
1056 msg_Err( p_vout, "cannot open vout, aborting" );
1058 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1059 return VLC_EGENERIC;
1062 ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
1064 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1068 /*****************************************************************************
1069 * SendEventsToChild: forward events to the child/children vout
1070 *****************************************************************************/
1071 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
1072 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1074 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1075 var_Set( p_vout->p_sys->p_vout, psz_var, newval );