]> git.sesse.net Git - vlc/blob - modules/video_filter/mirror.c
decoder: fix data race in input_DecoderChangePause()
[vlc] / modules / video_filter / mirror.c
1 /*****************************************************************************
2  * mirror.c : Mirror video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2009 VLC authors and VideoLAN
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 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 "filter_picture.h"
39
40 /*****************************************************************************
41  * Local prototypes
42  *****************************************************************************/
43 static int  Create      ( vlc_object_t * );
44 static void Destroy     ( vlc_object_t * );
45
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 );
53
54 static void YUV422Mirror2Pixels( uint8_t *, uint8_t *, bool );
55
56 static const char *const ppsz_filter_options[] = {
57     "split", "direction", NULL
58 };
59
60 /*****************************************************************************
61  * Module descriptor
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") };
69
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") };
75
76 #define CFG_PREFIX "mirror-"
77
78 vlc_module_begin ()
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 )
93 vlc_module_end ()
94
95 /*****************************************************************************
96  * callback prototypes
97  *****************************************************************************/
98 static int FilterCallback( vlc_object_t *, char const *,
99                            vlc_value_t, vlc_value_t, void * );
100
101 /*****************************************************************************
102  * filter_sys_t: adjust filter method descriptor
103  *****************************************************************************/
104 struct filter_sys_t
105 {
106     atomic_int i_split;
107     atomic_int i_direction;
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     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" ) );
154
155     var_AddCallback( p_filter, CFG_PREFIX "split", FilterCallback, p_sys );
156     var_AddCallback( p_filter, CFG_PREFIX "direction", FilterCallback, p_sys );
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     filter_sys_t *p_sys = p_filter->p_sys;
172
173     var_DelCallback( p_filter, CFG_PREFIX "split", FilterCallback, p_sys );
174     var_DelCallback( p_filter, CFG_PREFIX "direction", FilterCallback, p_sys );
175     free( p_sys );
176 }
177
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
183  * frame.
184  *****************************************************************************/
185 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
186 {
187     picture_t *p_outpic;
188     bool b_vertical_split, b_left_to_right;
189     int i_index;
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( 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  *****************************************************************************/
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  *****************************************************************************/
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  *****************************************************************************/
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  *****************************************************************************/
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  *****************************************************************************/
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  *****************************************************************************/
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  *****************************************************************************/
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
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 }