1 /*****************************************************************************
2  * mirror.c : Mirror video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2009 VLC authors and VideoLAN
5  * $Id: f6137c508a4e421fdfcc12eb4479b5d5a6c7dc9f $
6  *
7  * Authors: Branko Kokanovic <branko.kokanovic@gmail.com>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23 
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31 
32 #include <assert.h>
33 
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_atomic.h>
37 #include <vlc_filter.h>
38 #include <vlc_picture.h>
39 #include "filter_picture.h"
40 
41 /*****************************************************************************
42  * Local prototypes
43  *****************************************************************************/
44 static int  Create      ( vlc_object_t * );
45 static void Destroy     ( vlc_object_t * );
46 
47 static picture_t *Filter( filter_t *, picture_t * );
48 static void VerticalMirror( picture_t *, picture_t *, int plane, bool );
49 static void HorizontalMirror( picture_t *, picture_t *, int, bool );
50 static void PlanarVerticalMirror( picture_t *, picture_t *, int plane, bool );
51 static void YUV422VerticalMirror( picture_t *, picture_t *, int plane, bool, bool );
52 static void RV24VerticalMirror( picture_t *, picture_t *, int plane, bool );
53 static void RV32VerticalMirror( picture_t *, picture_t *, int plane, bool );
54 
55 static void YUV422Mirror2Pixels( uint8_t *, uint8_t *, bool );
56 
57 static const char *const ppsz_filter_options[] = {
58     "split", "direction", NULL
59 };
60 
61 /*****************************************************************************
62  * Module descriptor
63  *****************************************************************************/
64 #define ORIENTATION_TEXT N_("Mirror orientation")
65 #define ORIENTATION_LONGTEXT N_("Defines orientation of the mirror splitting. " \
66     "Can be vertical or horizontal." )
67 static const int pi_orientation_values[] = { 0, 1 };
68 static const char *const ppsz_orientation_descriptions[] = {
69   N_("Vertical"), N_("Horizontal") };
70 
71 #define DIRECTION_TEXT N_("Direction")
72 #define DIRECTION_LONGTEXT N_("Direction of the mirroring." )
73 static const int pi_direction_values[] = { 0, 1 };
74 static const char *const ppsz_direction_descriptions[] = {
75   N_("Left to right/Top to bottom"), N_("Right to left/Bottom to top") };
76 
77 #define CFG_PREFIX "mirror-"
78 
79 vlc_module_begin ()
80     set_description( N_("Mirror video filter") )
81     set_shortname( N_("Mirror video" ))
82     set_help( N_("Splits video in two same parts, like in a mirror") )
83     set_category( CAT_VIDEO )
84     set_subcategory( SUBCAT_VIDEO_VFILTER )
85     set_capability( "video filter", 0 )
86     add_integer( CFG_PREFIX "split", 0, ORIENTATION_TEXT,
87                 ORIENTATION_LONGTEXT, false )
88         change_integer_list( pi_orientation_values,
89                             ppsz_orientation_descriptions )
90     add_integer( CFG_PREFIX "direction", 0, DIRECTION_TEXT,
91                 DIRECTION_LONGTEXT, false )
92         change_integer_list( pi_direction_values, ppsz_direction_descriptions )
93     set_callbacks( Create, Destroy )
94 vlc_module_end ()
95 
96 /*****************************************************************************
97  * callback prototypes
98  *****************************************************************************/
99 static int FilterCallback( vlc_object_t *, char const *,
100                            vlc_value_t, vlc_value_t, void * );
101 
102 /*****************************************************************************
103  * filter_sys_t: adjust filter method descriptor
104  *****************************************************************************/
105 struct filter_sys_t
106 {
107     atomic_int i_split;
108     atomic_int i_direction;
109 };
110 
111 /*****************************************************************************
112  * Create: allocates Mirror video thread output method
113  *****************************************************************************
114  * This function allocates and initializes a Mirror vout method.
115  *****************************************************************************/
Create(vlc_object_t * p_this)116 static int Create( vlc_object_t *p_this )
117 {
118     filter_t *p_filter = (filter_t *)p_this;
119     filter_sys_t *p_sys;
120 
121     switch( p_filter->fmt_in.video.i_chroma )
122     {
123         CASE_PLANAR_YUV_SQUARE
124             break;
125         CASE_PACKED_YUV_422
126             break;
127         case VLC_CODEC_RGB24:
128         case VLC_CODEC_RGB32:
129             break;
130 
131         default:
132             msg_Err( p_filter, "Unsupported input chroma (%4.4s)",
133                      (char*)&(p_filter->fmt_in.video.i_chroma) );
134             return VLC_EGENERIC;
135     }
136 
137     if( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma )
138     {
139         msg_Err( p_filter, "Input and output chromas don't match" );
140         return VLC_EGENERIC;
141     }
142 
143     /* Allocate structure */
144     p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
145     if( p_filter->p_sys == NULL )
146         return VLC_ENOMEM;
147 
148     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
149                        p_filter->p_cfg );
150     atomic_init( &p_sys->i_split,
151                  var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "split" ) );
152     atomic_init( &p_sys->i_direction,
153                  var_CreateGetIntegerCommand( p_filter,
154                                                     CFG_PREFIX "direction" ) );
155 
156     var_AddCallback( p_filter, CFG_PREFIX "split", FilterCallback, p_sys );
157     var_AddCallback( p_filter, CFG_PREFIX "direction", FilterCallback, p_sys );
158 
159     p_filter->pf_video_filter = Filter;
160 
161     return VLC_SUCCESS;
162 }
163 
164 /*****************************************************************************
165  * Destroy: destroy Mirror video thread output method
166  *****************************************************************************
167  * Terminate an output method created by MirrorCreateOutputMethod
168  *****************************************************************************/
Destroy(vlc_object_t * p_this)169 static void Destroy( vlc_object_t *p_this )
170 {
171     filter_t *p_filter = (filter_t *)p_this;
172     filter_sys_t *p_sys = p_filter->p_sys;
173 
174     var_DelCallback( p_filter, CFG_PREFIX "split", FilterCallback, p_sys );
175     var_DelCallback( p_filter, CFG_PREFIX "direction", FilterCallback, p_sys );
176     free( p_sys );
177 }
178 
179 /*****************************************************************************
180  * Render: displays previously rendered output
181  *****************************************************************************
182  * This function send the currently rendered image to Mirror image, waits
183  * until it is displayed and switch the two rendering buffers, preparing next
184  * frame.
185  *****************************************************************************/
Filter(filter_t * p_filter,picture_t * p_pic)186 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
187 {
188     picture_t *p_outpic;
189     bool b_vertical_split, b_left_to_right;
190 
191     if( !p_pic ) return NULL;
192 
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 );
196 
197     p_outpic = filter_NewPicture( p_filter );
198     if( !p_outpic )
199     {
200         msg_Warn( p_filter, "can't get output picture" );
201         picture_Release( p_pic );
202         return NULL;
203     }
204 
205     for( int i_index = 0 ; i_index < p_pic->i_planes ; i_index++ )
206     {
207         if ( b_vertical_split )
208             VerticalMirror( p_pic, p_outpic, i_index, b_left_to_right );
209         else
210             HorizontalMirror( p_pic, p_outpic, i_index, b_left_to_right );
211     }
212 
213     return CopyInfoAndRelease( p_outpic, p_pic );
214 }
215 
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  *****************************************************************************/
VerticalMirror(picture_t * p_pic,picture_t * p_outpic,int i_plane,bool b_left_to_right)222 static void VerticalMirror( picture_t *p_pic, picture_t *p_outpic, int i_plane,
223                            bool b_left_to_right )
224 {
225     switch( p_pic->format.i_chroma )
226     {
227         CASE_PLANAR_YUV_SQUARE
228             PlanarVerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right );
229             break;
230         case VLC_CODEC_YUYV:
231         case VLC_CODEC_YVYU:
232             YUV422VerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right,
233                                  true );
234             break;
235         case VLC_CODEC_UYVY:
236             break;
237         case VLC_CODEC_RGB24:
238             RV24VerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right );
239             break;
240         case VLC_CODEC_RGB32:
241             RV32VerticalMirror( p_pic, p_outpic, i_plane, b_left_to_right );
242             break;
243         default:
244             vlc_assert_unreachable();
245     }
246 }
247 
248 /*****************************************************************************
249  * PlanarVerticalMirror: Mirrors vertically image byte by byte
250  *****************************************************************************
251  * This function mirrors image vertically. It iterates for all lines in
252  * image and for every line, it mirrors byte for byte if needed.
253  * This function works for planar formats only.
254  *****************************************************************************/
PlanarVerticalMirror(picture_t * p_pic,picture_t * p_outpic,int i_plane,bool b_left_to_right)255 static void PlanarVerticalMirror( picture_t *p_pic, picture_t *p_outpic,
256                                  int i_plane, bool b_left_to_right )
257 {
258     uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
259 
260     p_in = p_pic->p[i_plane].p_pixels;
261     p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
262         * p_pic->p[i_plane].i_pitch;
263     p_out = p_outpic->p[i_plane].p_pixels;
264 
265     while( p_in < p_in_end ) {
266         p_line_start = p_in;
267         p_line_end = p_in + p_pic->p[i_plane].i_visible_pitch;
268         while( p_in < p_line_end )
269         {
270             /* are we in the left part of the line */
271             if ( p_in < p_line_start + ( p_line_end - p_line_start ) / 2 )
272             {
273                 if ( b_left_to_right )
274                     *p_out = *p_in;
275                 else
276                     *p_out = *( p_line_end - ( p_in - p_line_start ) );
277             }
278             else
279             {
280                 if ( b_left_to_right )
281                     *p_out = *( p_line_start + ( p_line_end - p_in ) );
282                 else
283                     *p_out = *p_in;
284             }
285             p_in++;
286             p_out++;
287         }
288         p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
289         p_out += p_outpic->p[i_plane].i_pitch
290             - p_outpic->p[i_plane].i_visible_pitch;
291     }
292 }
293 
294 /*****************************************************************************
295  * YUV422VerticalMirror: Mirrors vertically image byte by byte for YUV422 format
296  *****************************************************************************
297  * This function mirrors image vertically. It iterates for all lines in
298  * image and for every line, iterates for 4-byte chucks, properly mirroring
299  * them vertically (swapping Y components and keeping Cb and Cr components).
300  * This function works only for YUV422 packed formats.
301  *****************************************************************************/
YUV422VerticalMirror(picture_t * p_pic,picture_t * p_outpic,int i_plane,bool b_left_to_right,bool b_y_is_odd)302 static void YUV422VerticalMirror( picture_t *p_pic, picture_t *p_outpic,
303                                  int i_plane, bool b_left_to_right,
304                                  bool b_y_is_odd )
305 {
306     uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
307 
308     p_in = p_pic->p[i_plane].p_pixels;
309     p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
310         * p_pic->p[i_plane].i_pitch;
311     p_out = p_outpic->p[i_plane].p_pixels;
312 
313     while( p_in < p_in_end )
314     {
315         p_line_start = p_in;
316         p_line_end = p_in + p_pic->p[i_plane].i_visible_pitch;
317         while( p_in < p_line_end )
318         {
319             /* are we in the left part of the line */
320             if ( p_in < p_line_start + ( p_line_end - p_line_start ) / 2 )
321             {
322                 if ( b_left_to_right )
323                 {
324                     *p_out++ = *p_in++;
325                     *p_out++ = *p_in++;
326                     *p_out++ = *p_in++;
327                     *p_out++ = *p_in++;
328                 }
329                 else
330                 {
331                     uint8_t *p_start = p_line_end - ( p_in - p_line_start );
332                     YUV422Mirror2Pixels( p_out, p_start, b_y_is_odd );
333                     p_in += 4;
334                     p_out += 4;
335                 }
336             }
337             else
338             {
339                 if ( b_left_to_right )
340                 {
341                     uint8_t *p_start = p_line_end - ( p_in - p_line_start );
342                     YUV422Mirror2Pixels( p_out, p_start, b_y_is_odd );
343                     p_in += 4;
344                     p_out += 4;
345                 }
346                 else
347                 {
348                     *p_out++ = *p_in++;
349                     *p_out++ = *p_in++;
350                     *p_out++ = *p_in++;
351                     *p_out++ = *p_in++;
352                 }
353             }
354         }
355         p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
356         p_out += p_outpic->p[i_plane].i_pitch
357             - p_outpic->p[i_plane].i_visible_pitch;
358     }
359 }
360 
361 /*****************************************************************************
362  * YUV422Mirror2Pixels: Mirrors 2 consecutive pixels
363  *****************************************************************************
364  * This function mirrors two consecutive pixels of 4 byte size. Depending of
365  * position of Y components (b_y_is_odd is true if Y components are first and
366  * third (like in YUYV or YVYU), they are properly swapped, and Cb and Cr
367  * component positions are preserved.
368  *****************************************************************************/
YUV422Mirror2Pixels(uint8_t * p_dst,uint8_t * p_src,bool b_y_is_odd)369 static void YUV422Mirror2Pixels( uint8_t* p_dst, uint8_t *p_src,
370                                 bool b_y_is_odd )
371 {
372     if ( b_y_is_odd )
373     {
374         /* swap Y components */
375         *p_dst = *( p_src + 2 );
376         *( p_dst + 2 ) = *p_src;
377         /* copy Cb and Cr components */
378         *( p_dst + 1 ) = *( p_src + 1 );
379         *( p_dst + 3 ) = *( p_src + 3 );
380     }
381     else{
382         /* swap Y components */
383         *( p_dst + 1 )= *( p_src + 3 );
384         *( p_dst + 3 ) = *( p_src + 1);
385         /* copy Cb and Cr components */
386         *p_dst = *( p_src + 2 );
387         *( p_dst + 2 ) = *p_src;
388     }
389 }
390 
391 /*****************************************************************************
392  * RV24VerticalMirror: Mirrors vertically image byte by byte for RV24 format
393  *****************************************************************************
394  * This function mirrors image vertically. It iterates for all lines in
395  * image and for every line, it iterates for 3-byte chunks.
396  * This function works only for RV24 formats.
397  *****************************************************************************/
RV24VerticalMirror(picture_t * p_pic,picture_t * p_outpic,int i_plane,bool b_left_to_right)398 static void RV24VerticalMirror( picture_t *p_pic, picture_t *p_outpic,
399                                  int i_plane, bool b_left_to_right )
400 {
401     uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
402 
403     p_in = p_pic->p[i_plane].p_pixels;
404     p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
405         * p_pic->p[i_plane].i_pitch;
406     p_out = p_outpic->p[i_plane].p_pixels;
407 
408     while( p_in < p_in_end )
409     {
410         p_line_start = p_in;
411         p_line_end = p_in + p_pic->p[i_plane].i_visible_pitch;
412         while( p_in < p_line_end )
413         {
414             /* are we in the left part of the line */
415             if ( p_in < p_line_start + ( p_line_end - p_line_start ) / 2 )
416             {
417                 if ( b_left_to_right )
418                 {
419                     *p_out++ = *p_in++;
420                     *p_out++ = *p_in++;
421                     *p_out++ = *p_in++;
422                 }
423                 else
424                 {
425                     uint8_t *p_pixel = p_line_end - ( p_in - p_line_start );
426                     p_in += 3;
427                     *p_out++ = *p_pixel++;
428                     *p_out++ = *p_pixel++;
429                     *p_out++ = *p_pixel++;
430                 }
431             }
432             else
433             {
434                 if ( b_left_to_right )
435                 {
436                     uint8_t *p_pixel = p_line_end - ( p_in - p_line_start );
437                     p_in += 3;
438                     *p_out++ = *p_pixel++;
439                     *p_out++ = *p_pixel++;
440                     *p_out++ = *p_pixel++;
441                 }
442                 else
443                 {
444                     *p_out++ = *p_in++;
445                     *p_out++ = *p_in++;
446                     *p_out++ = *p_in++;
447                 }
448             }
449         }
450         p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
451         p_out += p_outpic->p[i_plane].i_pitch
452             - p_outpic->p[i_plane].i_visible_pitch;
453     }
454 }
455 
456 /*****************************************************************************
457  * RV32VerticalMirror: Mirrors vertically image byte by byte for RV32 format
458  *****************************************************************************
459  * This function mirrors image vertically. It iterates for all lines in
460  * image and for every line, it iterates for 4-byte chunks as 32-bit pointers.
461  * This function works only for RV32 formats.
462  *****************************************************************************/
RV32VerticalMirror(picture_t * p_pic,picture_t * p_outpic,int i_plane,bool b_left_to_right)463 static void RV32VerticalMirror( picture_t *p_pic, picture_t *p_outpic,
464                                  int i_plane, bool b_left_to_right )
465 {
466     uint8_t *p_in, *p_in_end, *p_out;
467 
468     p_in = p_pic->p[i_plane].p_pixels;
469     p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
470         * p_pic->p[i_plane].i_pitch;
471     p_out = p_outpic->p[i_plane].p_pixels;
472 
473     while( p_in < p_in_end )
474     {
475         uint32_t *p_in32, *p_out32, *p_line_start32, *p_line_end32;
476         p_in32 = (uint32_t*) p_in;
477         p_out32 = (uint32_t*) p_out;
478         p_line_start32 = p_in32;
479         p_line_end32 = (uint32_t*) ( p_in + p_pic->p[i_plane].i_visible_pitch) ;
480 
481         while( p_in32 < p_line_end32 )
482         {
483             /* are we in the left part of the line */
484             if ( p_in32 < p_line_start32 + ( p_line_end32 - p_line_start32 ) / 2 )
485             {
486                 if ( b_left_to_right )
487                 {
488                     *p_out32++ = *p_in32++;
489                 }
490                 else
491                 {
492                     uint32_t *p_pixel32 = p_line_end32 - ( p_in32 - p_line_start32 );
493                     p_in32++;
494                     *p_out++ = *p_pixel32;
495                 }
496             }
497             else
498             {
499                 if ( b_left_to_right )
500                 {
501                     uint32_t *p_pixel32 = p_line_end32 - ( p_in32 - p_line_start32 );
502                     p_in32++;
503                     *p_out++ = *p_pixel32;
504                 }
505                 else
506                 {
507                     *p_out32++ = *p_in32++;
508                 }
509             }
510         }
511         p_in = (uint8_t*) p_in32;
512         p_out = (uint8_t*) p_out32;
513         p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
514         p_out += p_outpic->p[i_plane].i_pitch
515             - p_outpic->p[i_plane].i_visible_pitch;
516     }
517 }
518 
519 /*****************************************************************************
520  * HorizontalMirror: Mirrors horizontally image byte by byte
521  *****************************************************************************
522  * This function mirrors image horizontally. It iterates for all lines in
523  * image and for every line, determines if it should be copied, and if it does,
524  * finds opposite line in picture and copies current byte from opposite line.
525  * This function works both for planar, packed and RV24 formats.
526  *****************************************************************************/
HorizontalMirror(picture_t * p_pic,picture_t * p_outpic,int i_plane,bool b_top_to_bottom)527 static void HorizontalMirror( picture_t *p_pic, picture_t *p_outpic, int i_plane,
528                              bool b_top_to_bottom )
529 {
530     uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
531 
532     int i_curr_line = 0;
533     int i_max_lines = p_pic->p[i_plane].i_visible_lines;
534 
535     p_in = p_pic->p[i_plane].p_pixels;
536     p_in_end = p_in + p_pic->p[i_plane].i_visible_lines
537         * p_pic->p[i_plane].i_pitch;
538     p_out = p_outpic->p[i_plane].p_pixels;
539 
540     while( p_in < p_in_end )
541     {
542         p_line_start = p_in;
543         p_line_end = p_in + p_pic->p[i_plane].i_visible_pitch;
544         while( p_in < p_line_end )
545         {
546             uint8_t *p_inverse_line;
547             /* are we in the upper part of the picture */
548             if ( i_curr_line < i_max_lines/2 )
549             {
550                 if ( b_top_to_bottom )
551                 {
552                     *p_out = *p_in;
553                 }
554                 else
555                 {
556                     /* determines line inverse to current line */
557                     p_inverse_line = p_pic->p[i_plane].p_pixels +
558                         ( i_max_lines - i_curr_line - 1 ) * p_pic->p[i_plane].i_pitch;
559                     *p_out = *( p_inverse_line + ( p_in - p_line_start ) );
560                 }
561             }
562             else
563             {
564                 if ( b_top_to_bottom )
565                 {
566                     /* determines line inverse to current line */
567                     p_inverse_line = p_pic->p[i_plane].p_pixels +
568                         ( i_max_lines - i_curr_line - 1 ) * p_pic->p[i_plane].i_pitch;
569                     *p_out = *( p_inverse_line + ( p_in - p_line_start ) );
570                 }
571                 else
572                 {
573                     *p_out = *p_in;
574                 }
575             }
576             p_in++;
577             p_out++;
578         }
579         i_curr_line++;
580         p_in += p_pic->p[i_plane].i_pitch - p_pic->p[i_plane].i_visible_pitch;
581         p_out += p_outpic->p[i_plane].i_pitch
582             - p_outpic->p[i_plane].i_visible_pitch;
583     }
584 }
585 
FilterCallback(vlc_object_t * p_this,char const * psz_var,vlc_value_t oldval,vlc_value_t newval,void * p_data)586 static int FilterCallback ( vlc_object_t *p_this, char const *psz_var,
587                             vlc_value_t oldval, vlc_value_t newval, void *p_data )
588 {
589     (void) p_this; (void)oldval;
590     filter_sys_t *p_sys = p_data;
591 
592     if( !strcmp( psz_var, CFG_PREFIX "split" ) )
593         atomic_store( &p_sys->i_split, newval.i_int );
594     else /* CFG_PREFIX "direction" */
595         atomic_store( &p_sys->i_direction, newval.i_int );
596 
597     return VLC_SUCCESS;
598 }
599