1 /*****************************************************************************
2 * mirror.c : Mirror video plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2009 VLC authors and VideoLAN
7 * Authors: Branko Kokanovic <branko.kokanovic@gmail.com>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_atomic.h>
37 #include <vlc_filter.h>
38 #include "filter_picture.h"
40 /*****************************************************************************
42 *****************************************************************************/
43 static int Create ( vlc_object_t * );
44 static void Destroy ( vlc_object_t * );
46 static picture_t *Filter( filter_t *, picture_t * );
47 static void VerticalMirror( picture_t *, picture_t *, int plane, bool );
48 static void HorizontalMirror( picture_t *, picture_t *, int, bool );
49 static void PlanarVerticalMirror( picture_t *, picture_t *, int plane, bool );
50 static void YUV422VerticalMirror( picture_t *, picture_t *, int plane, bool, bool );
51 static void RV24VerticalMirror( picture_t *, picture_t *, int plane, bool );
52 static void RV32VerticalMirror( picture_t *, picture_t *, int plane, bool );
54 static void YUV422Mirror2Pixels( uint8_t *, uint8_t *, bool );
56 static const char *const ppsz_filter_options[] = {
57 "split", "direction", NULL
60 /*****************************************************************************
62 *****************************************************************************/
63 #define ORIENTATION_TEXT N_("Mirror orientation")
64 #define ORIENTATION_LONGTEXT N_("Defines orientation of the mirror splitting. \
65 Can be vertical or horizontal" )
66 static const int pi_orientation_values[] = { 0, 1 };
67 static const char *const ppsz_orientation_descriptions[] = {
68 N_("Vertical"), N_("Horizontal") };
70 #define DIRECTION_TEXT N_("Direction")
71 #define DIRECTION_LONGTEXT N_("Direction of the mirroring" )
72 static const int pi_direction_values[] = { 0, 1 };
73 static const char *const ppsz_direction_descriptions[] = {
74 N_("Left to right/Top to bottom"), N_("Right to left/Bottom to top") };
76 #define CFG_PREFIX "mirror-"
79 set_description( N_("Mirror video filter") )
80 set_shortname( N_("Mirror video" ))
81 set_help( N_("Splits video in two same parts, like in a mirror") )
82 set_category( CAT_VIDEO )
83 set_subcategory( SUBCAT_VIDEO_VFILTER )
84 set_capability( "video filter2", 0 )
85 add_integer( CFG_PREFIX "split", 0, ORIENTATION_TEXT,
86 ORIENTATION_LONGTEXT, false )
87 change_integer_list( pi_orientation_values,
88 ppsz_orientation_descriptions )
89 add_integer( CFG_PREFIX "direction", 0, DIRECTION_TEXT,
90 DIRECTION_LONGTEXT, false )
91 change_integer_list( pi_direction_values, ppsz_direction_descriptions )
92 set_callbacks( Create, Destroy )
95 /*****************************************************************************
97 *****************************************************************************/
98 static int FilterCallback( vlc_object_t *, char const *,
99 vlc_value_t, vlc_value_t, void * );
101 /*****************************************************************************
102 * filter_sys_t: adjust filter method descriptor
103 *****************************************************************************/
107 atomic_int i_direction;
110 /*****************************************************************************
111 * Create: allocates Mirror video thread output method
112 *****************************************************************************
113 * This function allocates and initializes a Mirror vout method.
114 *****************************************************************************/
115 static int Create( vlc_object_t *p_this )
117 filter_t *p_filter = (filter_t *)p_this;
120 switch( p_filter->fmt_in.video.i_chroma )
122 CASE_PLANAR_YUV_SQUARE
126 case VLC_CODEC_RGB24:
127 case VLC_CODEC_RGB32:
131 msg_Err( p_filter, "Unsupported input chroma (%4.4s)",
132 (char*)&(p_filter->fmt_in.video.i_chroma) );
136 if( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma )
138 msg_Err( p_filter, "Input and output chromas don't match" );
142 /* Allocate structure */
143 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
144 if( p_filter->p_sys == NULL )
147 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
149 atomic_init( &p_sys->i_split,
150 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "split" ) );
151 atomic_init( &p_sys->i_direction,
152 var_CreateGetIntegerCommand( p_filter,
153 CFG_PREFIX "direction" ) );
155 var_AddCallback( p_filter, CFG_PREFIX "split", FilterCallback, p_sys );
156 var_AddCallback( p_filter, CFG_PREFIX "direction", FilterCallback, p_sys );
158 p_filter->pf_video_filter = Filter;
163 /*****************************************************************************
164 * Destroy: destroy Mirror video thread output method
165 *****************************************************************************
166 * Terminate an output method created by MirrorCreateOutputMethod
167 *****************************************************************************/
168 static void Destroy( vlc_object_t *p_this )
170 filter_t *p_filter = (filter_t *)p_this;
171 filter_sys_t *p_sys = p_filter->p_sys;
173 var_DelCallback( p_filter, CFG_PREFIX "split", FilterCallback, p_sys );
174 var_DelCallback( p_filter, CFG_PREFIX "direction", FilterCallback, p_sys );
178 /*****************************************************************************
179 * Render: displays previously rendered output
180 *****************************************************************************
181 * This function send the currently rendered image to Mirror image, waits
182 * until it is displayed and switch the two rendering buffers, preparing next
184 *****************************************************************************/
185 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
188 bool b_vertical_split, b_left_to_right;
191 if( !p_pic ) return NULL;
193 filter_sys_t *p_sys = p_filter->p_sys;
194 b_vertical_split = !atomic_load( &p_sys->i_split );
195 b_left_to_right = !atomic_load( &p_sys->i_direction );
197 p_outpic = filter_NewPicture( p_filter );
200 msg_Warn( p_filter, "can't get output picture" );
201 picture_Release( p_pic );
205 for( i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
207 if ( b_vertical_split )
208 VerticalMirror( p_pic, p_outpic, i_index, b_left_to_right );
210 HorizontalMirror( p_pic, p_outpic, i_index, b_left_to_right );
213 return CopyInfoAndRelease( p_outpic, p_pic );
216 /*****************************************************************************
217 * VerticalMirror: Mirrors vertically image
218 *****************************************************************************
219 * This function is a simple delegate to concrete function for vertical
220 * mirroring depending on the input format.
221 *****************************************************************************/
222 static void VerticalMirror( picture_t *p_pic, picture_t *p_outpic, int i_plane,
223 bool b_left_to_right )
225 switch( p_pic->format.i_chroma )
227 CASE_PLANAR_YUV_SQUARE
228 PlanarVerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right );
232 YUV422VerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right,
237 YUV422VerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right,
240 case VLC_CODEC_RGB24:
241 RV24VerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right );
243 case VLC_CODEC_RGB32:
244 RV32VerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right );
251 /*****************************************************************************
252 * PlanarVerticalMirror: Mirrors vertically image byte by byte
253 *****************************************************************************
254 * This function mirrors image vertically. It iterates for all lines in
255 * image and for every line, it mirrors byte for byte if needed.
256 * This function works for planar formats only.
257 *****************************************************************************/
258 static void PlanarVerticalMirror( picture_t *p_pic, picture_t *p_outpic,
259 int i_plane, bool b_left_to_right )
261 uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
263 p_in = p_pic->p[i_plane].p_pixels;
264 p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
265 * p_pic->p[i_plane].i_pitch;
266 p_out = p_outpic->p[i_plane].p_pixels;
268 while( p_in < p_in_end ) {
270 p_line_end = p_in + p_pic->p[i_plane].i_visible_pitch;
271 while( p_in < p_line_end )
273 /* are we in the left part of the line */
274 if ( p_in < p_line_start + ( p_line_end - p_line_start ) / 2 )
276 if ( b_left_to_right )
279 *p_out = *( p_line_end - ( p_in - p_line_start ) );
283 if ( b_left_to_right )
284 *p_out = *( p_line_start + ( p_line_end - p_in ) );
291 p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
292 p_out += p_outpic->p[i_plane].i_pitch
293 - p_outpic->p[i_plane].i_visible_pitch;
297 /*****************************************************************************
298 * YUV422VerticalMirror: Mirrors vertically image byte by byte for YUV422 format
299 *****************************************************************************
300 * This function mirrors image vertically. It iterates for all lines in
301 * image and for every line, iterates for 4-byte chucks, properly mirroring
302 * them vertically (swapping Y components and keeping Cb and Cr components).
303 * This function works only for YUV422 packed formats.
304 *****************************************************************************/
305 static void YUV422VerticalMirror( picture_t *p_pic, picture_t *p_outpic,
306 int i_plane, bool b_left_to_right,
309 uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
311 p_in = p_pic->p[i_plane].p_pixels;
312 p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
313 * p_pic->p[i_plane].i_pitch;
314 p_out = p_outpic->p[i_plane].p_pixels;
316 while( p_in < p_in_end )
319 p_line_end = p_in + p_pic->p[i_plane].i_visible_pitch;
320 while( p_in < p_line_end )
322 /* are we in the left part of the line */
323 if ( p_in < p_line_start + ( p_line_end - p_line_start ) / 2 )
325 if ( b_left_to_right )
334 uint8_t *p_start = p_line_end - ( p_in - p_line_start );
335 YUV422Mirror2Pixels( p_out, p_start, b_y_is_odd );
342 if ( b_left_to_right )
344 uint8_t *p_start = p_line_end - ( p_in - p_line_start );
345 YUV422Mirror2Pixels( p_out, p_start, b_y_is_odd );
358 p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
359 p_out += p_outpic->p[i_plane].i_pitch
360 - p_outpic->p[i_plane].i_visible_pitch;
364 /*****************************************************************************
365 * YUV422Mirror2Pixels: Mirrors 2 consecutive pixels
366 *****************************************************************************
367 * This function mirrors two consecutive pixels of 4 byte size. Depending of
368 * position of Y components (b_y_is_odd is true if Y components are first and
369 * third (like in YUYV or YVYU), they are properly swapped, and Cb and Cr
370 * component positions are preserved.
371 *****************************************************************************/
372 static void YUV422Mirror2Pixels( uint8_t* p_dst, uint8_t *p_src,
377 /* swap Y components */
378 *p_dst = *( p_src + 2 );
379 *( p_dst + 2 ) = *p_src;
380 /* copy Cb and Cr components */
381 *( p_dst + 1 ) = *( p_src + 1 );
382 *( p_dst + 3 ) = *( p_src + 3 );
385 /* swap Y components */
386 *( p_dst + 1 )= *( p_src + 3 );
387 *( p_dst + 3 ) = *( p_src + 1);
388 /* copy Cb and Cr components */
389 *p_dst = *( p_src + 2 );
390 *( p_dst + 2 ) = *p_src;
394 /*****************************************************************************
395 * RV24VerticalMirror: Mirrors vertically image byte by byte for RV24 format
396 *****************************************************************************
397 * This function mirrors image vertically. It iterates for all lines in
398 * image and for every line, it iterates for 3-byte chunks.
399 * This function works only for RV24 formats.
400 *****************************************************************************/
401 static void RV24VerticalMirror( picture_t *p_pic, picture_t *p_outpic,
402 int i_plane, bool b_left_to_right )
404 uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
406 p_in = p_pic->p[i_plane].p_pixels;
407 p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
408 * p_pic->p[i_plane].i_pitch;
409 p_out = p_outpic->p[i_plane].p_pixels;
411 while( p_in < p_in_end )
414 p_line_end = p_in + p_pic->p[i_plane].i_visible_pitch;
415 while( p_in < p_line_end )
417 /* are we in the left part of the line */
418 if ( p_in < p_line_start + ( p_line_end - p_line_start ) / 2 )
420 if ( b_left_to_right )
428 uint8_t *p_pixel = p_line_end - ( p_in - p_line_start );
430 *p_out++ = *p_pixel++;
431 *p_out++ = *p_pixel++;
432 *p_out++ = *p_pixel++;
437 if ( b_left_to_right )
439 uint8_t *p_pixel = p_line_end - ( p_in - p_line_start );
441 *p_out++ = *p_pixel++;
442 *p_out++ = *p_pixel++;
443 *p_out++ = *p_pixel++;
453 p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
454 p_out += p_outpic->p[i_plane].i_pitch
455 - p_outpic->p[i_plane].i_visible_pitch;
459 /*****************************************************************************
460 * RV32VerticalMirror: Mirrors vertically image byte by byte for RV32 format
461 *****************************************************************************
462 * This function mirrors image vertically. It iterates for all lines in
463 * image and for every line, it iterates for 4-byte chunks as 32-bit pointers.
464 * This function works only for RV32 formats.
465 *****************************************************************************/
466 static void RV32VerticalMirror( picture_t *p_pic, picture_t *p_outpic,
467 int i_plane, bool b_left_to_right )
469 uint8_t *p_in, *p_in_end, *p_out;
471 p_in = p_pic->p[i_plane].p_pixels;
472 p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
473 * p_pic->p[i_plane].i_pitch;
474 p_out = p_outpic->p[i_plane].p_pixels;
476 while( p_in < p_in_end )
478 uint32_t *p_in32, *p_out32, *p_line_start32, *p_line_end32;
479 p_in32 = (uint32_t*) p_in;
480 p_out32 = (uint32_t*) p_out;
481 p_line_start32 = p_in32;
482 p_line_end32 = (uint32_t*) ( p_in + p_pic->p[i_plane].i_visible_pitch) ;
484 while( p_in32 < p_line_end32 )
486 /* are we in the left part of the line */
487 if ( p_in32 < p_line_start32 + ( p_line_end32 - p_line_start32 ) / 2 )
489 if ( b_left_to_right )
491 *p_out32++ = *p_in32++;
495 uint32_t *p_pixel32 = p_line_end32 - ( p_in32 - p_line_start32 );
497 *p_out++ = *p_pixel32;
502 if ( b_left_to_right )
504 uint32_t *p_pixel32 = p_line_end32 - ( p_in32 - p_line_start32 );
506 *p_out++ = *p_pixel32;
510 *p_out32++ = *p_in32++;
514 p_in = (uint8_t*) p_in32;
515 p_out = (uint8_t*) p_out32;
516 p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
517 p_out += p_outpic->p[i_plane].i_pitch
518 - p_outpic->p[i_plane].i_visible_pitch;
522 /*****************************************************************************
523 * HorizontalMirror: Mirrors horizontally image byte by byte
524 *****************************************************************************
525 * This function mirrors image horizontally. It iterates for all lines in
526 * image and for every line, determines if it should be copied, and if it does,
527 * finds opposite line in picture and copies current byte from opposite line.
528 * This function works both for planar, packed and RV24 formats.
529 *****************************************************************************/
530 static void HorizontalMirror( picture_t *p_pic, picture_t *p_outpic, int i_plane,
531 bool b_top_to_bottom )
533 uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
536 int i_max_lines = p_pic->p[i_plane].i_visible_lines;
538 p_in = p_pic->p[i_plane].p_pixels;
539 p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
540 * p_pic->p[i_plane].i_pitch;
541 p_out = p_outpic->p[i_plane].p_pixels;
543 while( p_in < p_in_end )
546 p_line_end = p_in + p_pic->p[i_plane].i_visible_pitch;
547 while( p_in < p_line_end )
549 uint8_t *p_inverse_line;
550 /* are we in the upper part of the picture */
551 if ( i_curr_line < i_max_lines/2 )
553 if ( b_top_to_bottom )
559 /* determines line inverse to current line */
560 p_inverse_line = p_pic->p[i_plane].p_pixels +
561 ( i_max_lines - i_curr_line - 1 ) * p_pic->p[i_plane].i_pitch;
562 *p_out = *( p_inverse_line + ( p_in - p_line_start ) );
567 if ( b_top_to_bottom )
569 /* determines line inverse to current line */
570 p_inverse_line = p_pic->p[i_plane].p_pixels +
571 ( i_max_lines - i_curr_line - 1 ) * p_pic->p[i_plane].i_pitch;
572 *p_out = *( p_inverse_line + ( p_in - p_line_start ) );
583 p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
584 p_out += p_outpic->p[i_plane].i_pitch
585 - p_outpic->p[i_plane].i_visible_pitch;
589 static int FilterCallback ( vlc_object_t *p_this, char const *psz_var,
590 vlc_value_t oldval, vlc_value_t newval, void *p_data )
592 (void) p_this; (void)oldval;
593 filter_sys_t *p_sys = p_data;
595 if( !strcmp( psz_var, CFG_PREFIX "split" ) )
596 atomic_store( &p_sys->i_split, newval.i_int );
597 else /* CFG_PREFIX "direction" */
598 atomic_store( &p_sys->i_direction, newval.i_int );