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