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() */
38 #include "filter_common.h"
40 #define DEINTERLACE_DISCARD 1
41 #define DEINTERLACE_MEAN 2
42 #define DEINTERLACE_BLEND 3
43 #define DEINTERLACE_BOB 4
44 #define DEINTERLACE_LINEAR 5
46 /*****************************************************************************
48 *****************************************************************************/
49 static int Create ( vlc_object_t * );
50 static void Destroy ( vlc_object_t * );
52 static int Init ( vout_thread_t * );
53 static void End ( vout_thread_t * );
54 static void Render ( vout_thread_t *, picture_t * );
56 static void RenderDiscard( vout_thread_t *, picture_t *, picture_t *, int );
57 static void RenderBob ( vout_thread_t *, picture_t *, picture_t *, int );
58 static void RenderMean ( vout_thread_t *, picture_t *, picture_t * );
59 static void RenderBlend ( vout_thread_t *, picture_t *, picture_t * );
60 static void RenderLinear ( vout_thread_t *, picture_t *, picture_t *, int );
62 static void MergeGeneric ( void *, const void *, const void *, size_t );
63 #if defined(CAN_COMPILE_C_ALTIVEC)
64 static void MergeAltivec ( void *, const void *, const void *, size_t );
66 #if defined(CAN_COMPILE_MMXEXT)
67 static void MergeMMX ( void *, const void *, const void *, size_t );
69 #if defined(CAN_COMPILE_SSE)
70 static void MergeSSE2 ( void *, const void *, const void *, size_t );
72 #if defined(CAN_COMPILE_MMXEXT) || defined(CAN_COMPILE_SSE)
73 static void EndMMX ( void );
76 static int SendEvents ( vlc_object_t *, char const *,
77 vlc_value_t, vlc_value_t, void * );
79 static void SetFilterMethod( vout_thread_t *p_vout, char *psz_method );
80 static vout_thread_t *SpawnRealVout( vout_thread_t *p_vout );
82 /*****************************************************************************
84 *****************************************************************************/
85 static int FilterCallback ( vlc_object_t *, char const *,
86 vlc_value_t, vlc_value_t, void * );
88 /*****************************************************************************
90 *****************************************************************************/
91 #define MODE_TEXT N_("Deinterlace mode")
92 #define MODE_LONGTEXT N_("You can choose the default deinterlace mode")
94 static char *mode_list[] = { "discard", "blend", "mean", "bob", "linear" };
95 static char *mode_list_text[] = { N_("Discard"), N_("Blend"), N_("Mean"),
96 N_("Bob"), N_("Linear") };
99 set_description( _("Deinterlacing video filter") );
100 set_shortname( N_("Deinterlace" ));
101 set_capability( "video filter", 0 );
102 set_category( CAT_VIDEO );
103 set_subcategory( SUBCAT_VIDEO_VFILTER );
105 add_string( "deinterlace-mode", "discard", NULL, MODE_TEXT,
106 MODE_LONGTEXT, VLC_FALSE );
107 change_string_list( mode_list, mode_list_text, 0 );
109 add_shortcut( "deinterlace" );
110 set_callbacks( Create, Destroy );
113 /*****************************************************************************
114 * vout_sys_t: Deinterlace video output method descriptor
115 *****************************************************************************
116 * This structure is part of the video output thread descriptor.
117 * It describes the Deinterlace specific properties of an output thread.
118 *****************************************************************************/
121 int i_mode; /* Deinterlace mode */
122 vlc_bool_t b_double_rate; /* Shall we double the framerate? */
127 vout_thread_t *p_vout;
129 vlc_mutex_t filter_lock;
131 void (*pf_merge) ( void *, const void *, const void *, size_t );
132 void (*pf_end_merge) ( void );
135 /*****************************************************************************
136 * Control: control facility for the vout (forwards to child vout)
137 *****************************************************************************/
138 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
140 return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
143 /*****************************************************************************
144 * Create: allocates Deinterlace video thread output method
145 *****************************************************************************
146 * This function allocates and initializes a Deinterlace vout method.
147 *****************************************************************************/
148 static int Create( vlc_object_t *p_this )
150 vout_thread_t *p_vout = (vout_thread_t *)p_this;
153 /* Allocate structure */
154 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
155 if( p_vout->p_sys == NULL )
157 msg_Err( p_vout, "out of memory" );
161 p_vout->pf_init = Init;
162 p_vout->pf_end = End;
163 p_vout->pf_manage = NULL;
164 p_vout->pf_render = Render;
165 p_vout->pf_display = NULL;
166 p_vout->pf_control = Control;
168 p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
169 p_vout->p_sys->b_double_rate = 0;
170 p_vout->p_sys->last_date = 0;
171 p_vout->p_sys->p_vout = 0;
172 vlc_mutex_init( p_vout, &p_vout->p_sys->filter_lock );
174 #if defined(CAN_COMPILE_C_ALTIVEC)
175 if( p_vout->p_libvlc->i_cpu & CPU_CAPABILITY_ALTIVEC )
177 p_vout->p_sys->pf_merge = MergeAltivec;
178 p_vout->p_sys->pf_end_merge = NULL;
182 #if defined(CAN_COMPILE_SSE)
183 if( p_vout->p_libvlc->i_cpu & CPU_CAPABILITY_SSE2 )
185 p_vout->p_sys->pf_merge = MergeSSE2;
186 p_vout->p_sys->pf_end_merge = EndMMX;
190 #if defined(CAN_COMPILE_MMXEXT)
191 if( p_vout->p_libvlc->i_cpu & CPU_CAPABILITY_MMX )
193 p_vout->p_sys->pf_merge = MergeMMX;
194 p_vout->p_sys->pf_end_merge = EndMMX;
199 p_vout->p_sys->pf_merge = MergeGeneric;
200 p_vout->p_sys->pf_end_merge = NULL;
203 /* Look what method was requested */
204 var_Create( p_vout, "deinterlace-mode", VLC_VAR_STRING );
205 var_Change( p_vout, "deinterlace-mode", VLC_VAR_INHERITVALUE, &val, NULL );
207 if( val.psz_string == NULL )
209 msg_Err( p_vout, "configuration variable deinterlace-mode empty" );
210 msg_Err( p_vout, "no deinterlace mode provided, using \"discard\"" );
212 val.psz_string = strdup( "discard" );
215 msg_Dbg( p_vout, "using %s deinterlace mode", val.psz_string );
217 SetFilterMethod( p_vout, val.psz_string );
219 free( val.psz_string );
221 var_AddCallback( p_vout, "deinterlace-mode", FilterCallback, NULL );
226 /*****************************************************************************
227 * SetFilterMethod: setup the deinterlace method to use.
228 *****************************************************************************/
229 static void SetFilterMethod( vout_thread_t *p_vout, char *psz_method )
231 if( !strcmp( psz_method, "discard" ) )
233 p_vout->p_sys->i_mode = DEINTERLACE_DISCARD;
234 p_vout->p_sys->b_double_rate = 0;
236 else if( !strcmp( psz_method, "mean" ) )
238 p_vout->p_sys->i_mode = DEINTERLACE_MEAN;
239 p_vout->p_sys->b_double_rate = 0;
241 else if( !strcmp( psz_method, "blend" )
242 || !strcmp( psz_method, "average" )
243 || !strcmp( psz_method, "combine-fields" ) )
245 p_vout->p_sys->i_mode = DEINTERLACE_BLEND;
246 p_vout->p_sys->b_double_rate = 0;
248 else if( !strcmp( psz_method, "bob" )
249 || !strcmp( psz_method, "progressive-scan" ) )
251 p_vout->p_sys->i_mode = DEINTERLACE_BOB;
252 p_vout->p_sys->b_double_rate = 1;
254 else if( !strcmp( psz_method, "linear" ) )
256 p_vout->p_sys->i_mode = DEINTERLACE_LINEAR;
257 p_vout->p_sys->b_double_rate = 1;
261 msg_Err( p_vout, "no valid deinterlace mode provided, "
262 "using \"discard\"" );
265 msg_Dbg( p_vout, "using %s deinterlace method", psz_method );
268 /*****************************************************************************
269 * Init: initialize Deinterlace video thread output method
270 *****************************************************************************/
271 static int Init( vout_thread_t *p_vout )
276 I_OUTPUTPICTURES = 0;
278 /* Initialize the output structure, full of directbuffers since we want
279 * the decoder to output directly to our structures. */
280 switch( p_vout->render.i_chroma )
282 case VLC_FOURCC('I','4','2','0'):
283 case VLC_FOURCC('I','Y','U','V'):
284 case VLC_FOURCC('Y','V','1','2'):
285 case VLC_FOURCC('I','4','2','2'):
286 p_vout->output.i_chroma = p_vout->render.i_chroma;
287 p_vout->output.i_width = p_vout->render.i_width;
288 p_vout->output.i_height = p_vout->render.i_height;
289 p_vout->output.i_aspect = p_vout->render.i_aspect;
293 return VLC_EGENERIC; /* unknown chroma */
297 /* Try to open the real video output */
298 p_vout->p_sys->p_vout = SpawnRealVout( p_vout );
300 if( p_vout->p_sys->p_vout == NULL )
302 /* Everything failed */
303 msg_Err( p_vout, "cannot open vout, aborting" );
308 ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
310 ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
312 ADD_PARENT_CALLBACKS( SendEventsToChild );
317 /*****************************************************************************
318 * SpawnRealVout: spawn the real video output.
319 *****************************************************************************/
320 static vout_thread_t *SpawnRealVout( vout_thread_t *p_vout )
322 vout_thread_t *p_real_vout = NULL;
324 msg_Dbg( p_vout, "spawning the real video output" );
326 switch( p_vout->render.i_chroma )
328 case VLC_FOURCC('I','4','2','0'):
329 case VLC_FOURCC('I','Y','U','V'):
330 case VLC_FOURCC('Y','V','1','2'):
331 switch( p_vout->p_sys->i_mode )
333 case DEINTERLACE_MEAN:
334 case DEINTERLACE_DISCARD:
337 p_vout->output.i_width, p_vout->output.i_height / 2,
338 p_vout->output.i_chroma, p_vout->output.i_aspect );
341 case DEINTERLACE_BOB:
342 case DEINTERLACE_BLEND:
343 case DEINTERLACE_LINEAR:
346 p_vout->output.i_width, p_vout->output.i_height,
347 p_vout->output.i_chroma, p_vout->output.i_aspect );
352 case VLC_FOURCC('I','4','2','2'):
355 p_vout->output.i_width, p_vout->output.i_height,
356 VLC_FOURCC('I','4','2','0'), p_vout->output.i_aspect );
366 /*****************************************************************************
367 * End: terminate Deinterlace video thread output method
368 *****************************************************************************/
369 static void End( vout_thread_t *p_vout )
373 /* Free the fake output buffers we allocated */
374 for( i_index = I_OUTPUTPICTURES ; i_index ; )
377 free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
381 /*****************************************************************************
382 * Destroy: destroy Deinterlace video thread output method
383 *****************************************************************************
384 * Terminate an output method created by DeinterlaceCreateOutputMethod
385 *****************************************************************************/
386 static void Destroy( vlc_object_t *p_this )
388 vout_thread_t *p_vout = (vout_thread_t *)p_this;
390 if( p_vout->p_sys->p_vout )
392 DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
393 vlc_object_detach( p_vout->p_sys->p_vout );
394 vout_Destroy( p_vout->p_sys->p_vout );
397 DEL_PARENT_CALLBACKS( SendEventsToChild );
399 free( p_vout->p_sys );
402 /*****************************************************************************
403 * Render: displays previously rendered output
404 *****************************************************************************
405 * This function send the currently rendered image to Deinterlace image,
406 * waits until it is displayed and switch the two rendering buffers, preparing
408 *****************************************************************************/
409 static void Render ( vout_thread_t *p_vout, picture_t *p_pic )
411 picture_t *pp_outpic[2];
413 vlc_mutex_lock( &p_vout->p_sys->filter_lock );
415 /* Get a new picture */
416 while( ( pp_outpic[0] = vout_CreatePicture( p_vout->p_sys->p_vout,
420 if( p_vout->b_die || p_vout->b_error )
422 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
425 msleep( VOUT_OUTMEM_SLEEP );
428 vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[0], p_pic->date );
430 /* If we are using double rate, get an additional new picture */
431 if( p_vout->p_sys->b_double_rate )
433 while( ( pp_outpic[1] = vout_CreatePicture( p_vout->p_sys->p_vout,
437 if( p_vout->b_die || p_vout->b_error )
439 vout_DestroyPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
440 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
443 msleep( VOUT_OUTMEM_SLEEP );
446 /* 20ms is a bit arbitrary, but it's only for the first image we get */
447 if( !p_vout->p_sys->last_date )
449 vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
450 p_pic->date + 20000 );
454 vout_DatePicture( p_vout->p_sys->p_vout, pp_outpic[1],
455 (3 * p_pic->date - p_vout->p_sys->last_date) / 2 );
457 p_vout->p_sys->last_date = p_pic->date;
460 switch( p_vout->p_sys->i_mode )
462 case DEINTERLACE_DISCARD:
463 RenderDiscard( p_vout, pp_outpic[0], p_pic, 0 );
464 vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
467 case DEINTERLACE_BOB:
468 RenderBob( p_vout, pp_outpic[0], p_pic, 0 );
469 vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
470 RenderBob( p_vout, pp_outpic[1], p_pic, 1 );
471 vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
474 case DEINTERLACE_LINEAR:
475 RenderLinear( p_vout, pp_outpic[0], p_pic, 0 );
476 vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
477 RenderLinear( p_vout, pp_outpic[1], p_pic, 1 );
478 vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[1] );
481 case DEINTERLACE_MEAN:
482 RenderMean( p_vout, pp_outpic[0], p_pic );
483 vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
486 case DEINTERLACE_BLEND:
487 RenderBlend( p_vout, pp_outpic[0], p_pic );
488 vout_DisplayPicture( p_vout->p_sys->p_vout, pp_outpic[0] );
492 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
495 /*****************************************************************************
496 * RenderDiscard: only keep TOP or BOTTOM field, discard the other.
497 *****************************************************************************/
498 static void RenderDiscard( vout_thread_t *p_vout,
499 picture_t *p_outpic, picture_t *p_pic, int i_field )
503 /* Copy image and skip lines */
504 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
506 uint8_t *p_in, *p_out_end, *p_out;
509 p_in = p_pic->p[i_plane].p_pixels
510 + i_field * p_pic->p[i_plane].i_pitch;
512 p_out = p_outpic->p[i_plane].p_pixels;
513 p_out_end = p_out + p_outpic->p[i_plane].i_pitch
514 * p_outpic->p[i_plane].i_visible_lines;
516 switch( p_vout->render.i_chroma )
518 case VLC_FOURCC('I','4','2','0'):
519 case VLC_FOURCC('I','Y','U','V'):
520 case VLC_FOURCC('Y','V','1','2'):
522 for( ; p_out < p_out_end ; )
524 p_vout->p_vlc->pf_memcpy( p_out, p_in,
525 p_pic->p[i_plane].i_pitch );
527 p_out += p_pic->p[i_plane].i_pitch;
528 p_in += 2 * p_pic->p[i_plane].i_pitch;
532 case VLC_FOURCC('I','4','2','2'):
534 i_increment = 2 * p_pic->p[i_plane].i_pitch;
536 if( i_plane == Y_PLANE )
538 for( ; p_out < p_out_end ; )
540 p_vout->p_vlc->pf_memcpy( p_out, p_in,
541 p_pic->p[i_plane].i_pitch );
542 p_out += p_pic->p[i_plane].i_pitch;
543 p_vout->p_vlc->pf_memcpy( p_out, p_in,
544 p_pic->p[i_plane].i_pitch );
545 p_out += p_pic->p[i_plane].i_pitch;
551 for( ; p_out < p_out_end ; )
553 p_vout->p_vlc->pf_memcpy( p_out, p_in,
554 p_pic->p[i_plane].i_pitch );
555 p_out += p_pic->p[i_plane].i_pitch;
567 /*****************************************************************************
568 * RenderBob: renders a BOB picture - simple copy
569 *****************************************************************************/
570 static void RenderBob( vout_thread_t *p_vout,
571 picture_t *p_outpic, picture_t *p_pic, int i_field )
575 /* Copy image and skip lines */
576 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
578 uint8_t *p_in, *p_out_end, *p_out;
580 p_in = p_pic->p[i_plane].p_pixels;
581 p_out = p_outpic->p[i_plane].p_pixels;
582 p_out_end = p_out + p_outpic->p[i_plane].i_pitch
583 * p_outpic->p[i_plane].i_visible_lines;
585 switch( p_vout->render.i_chroma )
587 case VLC_FOURCC('I','4','2','0'):
588 case VLC_FOURCC('I','Y','U','V'):
589 case VLC_FOURCC('Y','V','1','2'):
590 /* For BOTTOM field we need to add the first line */
593 p_vout->p_vlc->pf_memcpy( p_out, p_in,
594 p_pic->p[i_plane].i_pitch );
595 p_in += p_pic->p[i_plane].i_pitch;
596 p_out += p_pic->p[i_plane].i_pitch;
599 p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
601 for( ; p_out < p_out_end ; )
603 p_vout->p_vlc->pf_memcpy( p_out, p_in,
604 p_pic->p[i_plane].i_pitch );
606 p_out += p_pic->p[i_plane].i_pitch;
608 p_vout->p_vlc->pf_memcpy( p_out, p_in,
609 p_pic->p[i_plane].i_pitch );
611 p_in += 2 * p_pic->p[i_plane].i_pitch;
612 p_out += p_pic->p[i_plane].i_pitch;
615 p_vout->p_vlc->pf_memcpy( p_out, p_in,
616 p_pic->p[i_plane].i_pitch );
618 /* For TOP field we need to add the last line */
621 p_in += p_pic->p[i_plane].i_pitch;
622 p_out += p_pic->p[i_plane].i_pitch;
623 p_vout->p_vlc->pf_memcpy( p_out, p_in,
624 p_pic->p[i_plane].i_pitch );
628 case VLC_FOURCC('I','4','2','2'):
629 /* For BOTTOM field we need to add the first line */
632 p_vout->p_vlc->pf_memcpy( p_out, p_in,
633 p_pic->p[i_plane].i_pitch );
634 p_in += p_pic->p[i_plane].i_pitch;
635 p_out += p_pic->p[i_plane].i_pitch;
638 p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
640 if( i_plane == Y_PLANE )
642 for( ; p_out < p_out_end ; )
644 p_vout->p_vlc->pf_memcpy( p_out, p_in,
645 p_pic->p[i_plane].i_pitch );
647 p_out += 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 p_in += 2 * p_pic->p[i_plane].i_pitch;
653 p_out += p_pic->p[i_plane].i_pitch;
658 for( ; p_out < p_out_end ; )
660 p_vout->p_vlc->pf_memcpy( p_out, p_in,
661 p_pic->p[i_plane].i_pitch );
663 p_out += p_pic->p[i_plane].i_pitch;
664 p_in += 2 * p_pic->p[i_plane].i_pitch;
668 p_vout->p_vlc->pf_memcpy( p_out, p_in,
669 p_pic->p[i_plane].i_pitch );
671 /* For TOP field we need to add the last line */
674 p_in += p_pic->p[i_plane].i_pitch;
675 p_out += p_pic->p[i_plane].i_pitch;
676 p_vout->p_vlc->pf_memcpy( p_out, p_in,
677 p_pic->p[i_plane].i_pitch );
684 #define Merge p_vout->p_sys->pf_merge
685 #define EndMerge if(p_vout->p_sys->pf_end_merge) p_vout->p_sys->pf_end_merge
687 /*****************************************************************************
688 * RenderLinear: BOB with linear interpolation
689 *****************************************************************************/
690 static void RenderLinear( vout_thread_t *p_vout,
691 picture_t *p_outpic, picture_t *p_pic, int i_field )
695 /* Copy image and skip lines */
696 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
698 uint8_t *p_in, *p_out_end, *p_out;
700 p_in = p_pic->p[i_plane].p_pixels;
701 p_out = p_outpic->p[i_plane].p_pixels;
702 p_out_end = p_out + p_outpic->p[i_plane].i_pitch
703 * p_outpic->p[i_plane].i_visible_lines;
705 /* For BOTTOM field we need to add the first line */
708 p_vout->p_vlc->pf_memcpy( p_out, p_in,
709 p_pic->p[i_plane].i_pitch );
710 p_in += p_pic->p[i_plane].i_pitch;
711 p_out += p_pic->p[i_plane].i_pitch;
714 p_out_end -= 2 * p_outpic->p[i_plane].i_pitch;
716 for( ; p_out < p_out_end ; )
718 p_vout->p_vlc->pf_memcpy( p_out, p_in,
719 p_pic->p[i_plane].i_pitch );
721 p_out += p_pic->p[i_plane].i_pitch;
723 Merge( p_out, p_in, p_in + 2 * p_pic->p[i_plane].i_pitch,
724 p_pic->p[i_plane].i_pitch );
726 p_in += 2 * p_pic->p[i_plane].i_pitch;
727 p_out += p_pic->p[i_plane].i_pitch;
730 p_vout->p_vlc->pf_memcpy( p_out, p_in,
731 p_pic->p[i_plane].i_pitch );
733 /* For TOP field we need to add the last line */
736 p_in += p_pic->p[i_plane].i_pitch;
737 p_out += p_pic->p[i_plane].i_pitch;
738 p_vout->p_vlc->pf_memcpy( p_out, p_in,
739 p_pic->p[i_plane].i_pitch );
745 static void RenderMean( vout_thread_t *p_vout,
746 picture_t *p_outpic, picture_t *p_pic )
750 /* Copy image and skip lines */
751 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
753 uint8_t *p_in, *p_out_end, *p_out;
755 p_in = p_pic->p[i_plane].p_pixels;
757 p_out = p_outpic->p[i_plane].p_pixels;
758 p_out_end = p_out + p_outpic->p[i_plane].i_pitch
759 * p_outpic->p[i_plane].i_visible_lines;
761 /* All lines: mean value */
762 for( ; p_out < p_out_end ; )
764 Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
765 p_pic->p[i_plane].i_pitch );
767 p_out += p_pic->p[i_plane].i_pitch;
768 p_in += 2 * p_pic->p[i_plane].i_pitch;
774 static void RenderBlend( vout_thread_t *p_vout,
775 picture_t *p_outpic, picture_t *p_pic )
779 /* Copy image and skip lines */
780 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
782 uint8_t *p_in, *p_out_end, *p_out;
784 p_in = p_pic->p[i_plane].p_pixels;
786 p_out = p_outpic->p[i_plane].p_pixels;
787 p_out_end = p_out + p_outpic->p[i_plane].i_pitch
788 * p_outpic->p[i_plane].i_visible_lines;
790 switch( p_vout->render.i_chroma )
792 case VLC_FOURCC('I','4','2','0'):
793 case VLC_FOURCC('I','Y','U','V'):
794 case VLC_FOURCC('Y','V','1','2'):
795 /* First line: simple copy */
796 p_vout->p_vlc->pf_memcpy( p_out, p_in,
797 p_pic->p[i_plane].i_pitch );
798 p_out += p_pic->p[i_plane].i_pitch;
800 /* Remaining lines: mean value */
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;
811 case VLC_FOURCC('I','4','2','2'):
812 /* First line: simple copy */
813 p_vout->p_vlc->pf_memcpy( p_out, p_in,
814 p_pic->p[i_plane].i_pitch );
815 p_out += p_pic->p[i_plane].i_pitch;
817 /* Remaining lines: mean value */
818 if( i_plane == Y_PLANE )
820 for( ; p_out < p_out_end ; )
822 Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
823 p_pic->p[i_plane].i_pitch );
825 p_out += p_pic->p[i_plane].i_pitch;
826 p_in += p_pic->p[i_plane].i_pitch;
832 for( ; p_out < p_out_end ; )
834 Merge( p_out, p_in, p_in + p_pic->p[i_plane].i_pitch,
835 p_pic->p[i_plane].i_pitch );
837 p_out += p_pic->p[i_plane].i_pitch;
838 p_in += 2*p_pic->p[i_plane].i_pitch;
849 static void MergeGeneric( void *_p_dest, const void *_p_s1,
850 const void *_p_s2, size_t i_bytes )
852 uint8_t* p_dest = (uint8_t*)_p_dest;
853 const uint8_t *p_s1 = (const uint8_t *)_p_s1;
854 const uint8_t *p_s2 = (const uint8_t *)_p_s2;
855 uint8_t* p_end = p_dest + i_bytes - 8;
857 while( p_dest < p_end )
859 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
860 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
861 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
862 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
863 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
864 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
865 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
866 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
871 while( p_dest < p_end )
873 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
877 #if defined(CAN_COMPILE_MMXEXT)
878 static void MergeMMX( void *_p_dest, const void *_p_s1, const void *_p_s2,
881 uint8_t* p_dest = (uint8_t*)_p_dest;
882 const uint8_t *p_s1 = (const uint8_t *)_p_s1;
883 const uint8_t *p_s2 = (const uint8_t *)_p_s2;
884 uint8_t* p_end = p_dest + i_bytes - 8;
885 while( p_dest < p_end )
887 __asm__ __volatile__( "movq %2,%%mm1;"
889 "movq %%mm1, %0" :"=m" (*p_dest):
899 while( p_dest < p_end )
901 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
906 #if defined(CAN_COMPILE_SSE)
907 static void MergeSSE2( void *_p_dest, const void *_p_s1, const void *_p_s2,
910 uint8_t* p_dest = (uint8_t*)_p_dest;
911 const uint8_t *p_s1 = (const uint8_t *)_p_s1;
912 const uint8_t *p_s2 = (const uint8_t *)_p_s2;
914 while( (int)p_s1 % 16 )
916 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
918 p_end = p_dest + i_bytes - 16;
919 while( p_dest < p_end )
921 __asm__ __volatile__( "movdqu %2,%%xmm1;"
923 "movdqu %%xmm1, %0" :"=m" (*p_dest):
933 while( p_dest < p_end )
935 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
940 #if defined(CAN_COMPILE_MMXEXT) || defined(CAN_COMPILE_SSE)
941 static void EndMMX( void )
943 __asm__ __volatile__( "emms" :: );
947 #ifdef CAN_COMPILE_C_ALTIVEC
948 static void MergeAltivec( void *_p_dest, const void *_p_s1,
949 const void *_p_s2, size_t i_bytes )
951 uint8_t *p_dest = (uint8_t *)_p_dest;
952 uint8_t *p_s1 = (uint8_t *)_p_s1;
953 uint8_t *p_s2 = (uint8_t *)_p_s2;
954 uint8_t *p_end = p_dest + i_bytes - 15;
956 /* Use C until the first 16-bytes aligned destination pixel */
957 while( (int)p_dest & 0xF )
959 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
962 if( ( (int)p_s1 & 0xF ) | ( (int)p_s2 & 0xF ) )
964 /* Unaligned source */
965 vector unsigned char s1v, s2v, destv;
966 vector unsigned char s1oldv, s2oldv, s1newv, s2newv;
967 vector unsigned char perm1v, perm2v;
969 perm1v = vec_lvsl( 0, p_s1 );
970 perm2v = vec_lvsl( 0, p_s2 );
971 s1oldv = vec_ld( 0, p_s1 );
972 s2oldv = vec_ld( 0, p_s2 );
974 while( p_dest < p_end )
976 s1newv = vec_ld( 16, p_s1 );
977 s2newv = vec_ld( 16, p_s2 );
978 s1v = vec_perm( s1oldv, s1newv, perm1v );
979 s2v = vec_perm( s2oldv, s2newv, perm2v );
982 destv = vec_avg( s1v, s2v );
983 vec_st( destv, 0, p_dest );
993 vector unsigned char s1v, s2v, destv;
995 while( p_dest < p_end )
997 s1v = vec_ld( 0, p_s1 );
998 s2v = vec_ld( 0, p_s2 );
999 destv = vec_avg( s1v, s2v );
1000 vec_st( destv, 0, p_dest );
1010 while( p_dest < p_end )
1012 *p_dest++ = ( (uint16_t)(*p_s1++) + (uint16_t)(*p_s2++) ) >> 1;
1017 /*****************************************************************************
1018 * SendEvents: forward mouse and keyboard events to the parent p_vout
1019 *****************************************************************************/
1020 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
1021 vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
1023 vout_thread_t *p_vout = (vout_thread_t *)_p_vout;
1024 vlc_value_t sentval = newval;
1026 if( !strcmp( psz_var, "mouse-y" ) )
1028 switch( p_vout->p_sys->i_mode )
1030 case DEINTERLACE_MEAN:
1031 case DEINTERLACE_DISCARD:
1037 var_Set( p_vout, psz_var, sentval );
1042 /*****************************************************************************
1043 * FilterCallback: called when changing the deinterlace method on the fly.
1044 *****************************************************************************/
1045 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
1046 vlc_value_t oldval, vlc_value_t newval,
1049 vout_thread_t * p_vout = (vout_thread_t *)p_this;
1050 int i_old_mode = p_vout->p_sys->i_mode;
1052 msg_Dbg( p_vout, "using %s deinterlace mode", newval.psz_string );
1054 vlc_mutex_lock( &p_vout->p_sys->filter_lock );
1056 SetFilterMethod( p_vout, newval.psz_string );
1058 switch( p_vout->render.i_chroma )
1060 case VLC_FOURCC('I','4','2','2'):
1061 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1065 case VLC_FOURCC('I','4','2','0'):
1066 case VLC_FOURCC('I','Y','U','V'):
1067 case VLC_FOURCC('Y','V','1','2'):
1068 switch( p_vout->p_sys->i_mode )
1070 case DEINTERLACE_MEAN:
1071 case DEINTERLACE_DISCARD:
1072 if( ( i_old_mode == DEINTERLACE_MEAN )
1073 || ( i_old_mode == DEINTERLACE_DISCARD ) )
1075 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1080 case DEINTERLACE_BOB:
1081 case DEINTERLACE_BLEND:
1082 case DEINTERLACE_LINEAR:
1083 if( ( i_old_mode == DEINTERLACE_BOB )
1084 || ( i_old_mode == DEINTERLACE_BLEND )
1085 || ( i_old_mode == DEINTERLACE_LINEAR ) )
1087 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1098 /* We need to kill the old vout */
1100 DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
1102 vlc_object_detach( p_vout->p_sys->p_vout );
1103 vout_Destroy( p_vout->p_sys->p_vout );
1105 /* Try to open a new video output */
1106 p_vout->p_sys->p_vout = SpawnRealVout( p_vout );
1108 if( p_vout->p_sys->p_vout == NULL )
1110 /* Everything failed */
1111 msg_Err( p_vout, "cannot open vout, aborting" );
1113 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1114 return VLC_EGENERIC;
1117 ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
1119 vlc_mutex_unlock( &p_vout->p_sys->filter_lock );
1123 /*****************************************************************************
1124 * SendEventsToChild: forward events to the child/children vout
1125 *****************************************************************************/
1126 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
1127 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1129 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1130 var_Set( p_vout->p_sys->p_vout, psz_var, newval );