]> git.sesse.net Git - vlc/blob - modules/video_filter/audiobargraph_v.c
audiobargraph_v: simplify iec_scale
[vlc] / modules / video_filter / audiobargraph_v.c
1 /*****************************************************************************
2  * audiobargraph_v.c : audiobargraph video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2006 VLC authors and VideoLAN
5  *
6  * Authors: Clement CHESNIN <clement.chesnin@gmail.com>
7  *          Philippe COENT <philippe.coent@tdf.fr>
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 #include <string.h>
32 #include <math.h>
33
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_filter.h>
37
38 #include <vlc_image.h>
39
40 #ifdef LoadImage
41 #   undef LoadImage
42 #endif
43
44 /*****************************************************************************
45  * Module descriptor
46  *****************************************************************************/
47
48 #define POSX_TEXT N_("X coordinate")
49 #define POSX_LONGTEXT N_("X coordinate of the bargraph." )
50 #define POSY_TEXT N_("Y coordinate")
51 #define POSY_LONGTEXT N_("Y coordinate of the bargraph." )
52 #define TRANS_TEXT N_("Transparency of the bargraph")
53 #define TRANS_LONGTEXT N_("Bargraph transparency value " \
54   "(from 0 for full transparency to 255 for full opacity)." )
55 #define POS_TEXT N_("Bargraph position")
56 #define POS_LONGTEXT N_( \
57   "Enforce the bargraph position on the video " \
58   "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
59   "also use combinations of these values, eg 6 = top-right).")
60 #define BARWIDTH_TEXT N_("Bar width in pixel (default : 10)")
61 #define BARWIDTH_LONGTEXT N_("Width in pixel of each bar in the BarGraph to be displayed " \
62                 "(default : 10).")
63
64 #define CFG_PREFIX "audiobargraph_v-"
65
66 static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
67 static const char *const ppsz_pos_descriptions[] =
68 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
69   N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
70
71 static int  OpenSub  ( vlc_object_t * );
72 static int  OpenVideo( vlc_object_t * );
73 static void Close    ( vlc_object_t * );
74
75 vlc_module_begin ()
76
77     set_category( CAT_VIDEO )
78     set_subcategory( SUBCAT_VIDEO_SUBPIC )
79
80     set_capability( "sub source", 0 )
81     set_callbacks( OpenSub, Close )
82     set_description( N_("Audio Bar Graph Video sub source") )
83     set_shortname( N_("Audio Bar Graph Video") )
84     add_shortcut( "audiobargraph_v" )
85
86     add_obsolete_string( CFG_PREFIX "i_values" )
87     add_integer( CFG_PREFIX "x", 0, POSX_TEXT, POSX_LONGTEXT, true )
88     add_integer( CFG_PREFIX "y", 0, POSY_TEXT, POSY_LONGTEXT, true )
89     add_integer_with_range( CFG_PREFIX "transparency", 255, 0, 255,
90         TRANS_TEXT, TRANS_LONGTEXT, false )
91     add_integer( CFG_PREFIX "position", -1, POS_TEXT, POS_LONGTEXT, false )
92         change_integer_list( pi_pos_values, ppsz_pos_descriptions )
93     add_obsolete_integer( CFG_PREFIX "alarm" )
94     add_integer( CFG_PREFIX "barWidth", 10, BARWIDTH_TEXT, BARWIDTH_LONGTEXT, true )
95
96     /* video output filter submodule */
97     add_submodule ()
98     set_capability( "video filter2", 0 )
99     set_callbacks( OpenVideo, Close )
100     set_description( N_("Audio Bar Graph Video sub source") )
101     add_shortcut( "audiobargraph_v" )
102 vlc_module_end ()
103
104
105 /*****************************************************************************
106  * Local prototypes
107  *****************************************************************************/
108
109 /*****************************************************************************
110  * Structure to hold the Bar Graph properties
111  ****************************************************************************/
112 typedef struct
113 {
114     int i_alpha;       /* -1 means use default alpha */
115     int nbChannels;
116     int *i_values;
117     picture_t *p_pic;
118     mtime_t date;
119     int scale;
120     bool alarm;
121     int barWidth;
122
123 } BarGraph_t;
124
125 /**
126  * Private data holder
127  */
128 struct filter_sys_t
129 {
130     filter_t *p_blend;
131
132     vlc_mutex_t lock;
133
134     BarGraph_t p_BarGraph;
135
136     int i_pos;
137     int i_pos_x;
138     int i_pos_y;
139     bool b_absolute;
140
141     /* On the fly control variable */
142     bool b_spu_update;
143 };
144
145 static const char *const ppsz_filter_options[] = {
146     "x", "y", "transparency", "position", "barWidth", NULL
147 };
148
149 static const char *const ppsz_filter_callbacks[] = {
150     "audiobargraph_v-x",
151     "audiobargraph_v-y",
152     "audiobargraph_v-transparency",
153     "audiobargraph_v-position",
154     "audiobargraph_v-barWidth",
155     NULL
156 };
157
158 /*****************************************************************************
159  * IEC 268-18  Source: meterbridge
160  *****************************************************************************/
161 static float iec_scale(float dB)
162 {
163     if (dB < -70.0f)
164         return 0.0f;
165     if (dB < -60.0f)
166         return (dB + 70.0f) * 0.0025f;
167     if (dB < -50.0f)
168         return (dB + 60.0f) * 0.005f + 0.025f;
169     if (dB < -40.0)
170         return (dB + 50.0f) * 0.0075f + 0.075f;
171     if (dB < -30.0f)
172         return (dB + 40.0f) * 0.015f + 0.15f;
173     if (dB < -20.0f)
174         return (dB + 30.0f) * 0.02f + 0.3f;
175     if (dB < -0.001f || dB > 0.001f)  /* if (dB < 0.0f) */
176         return (dB + 20.0f) * 0.025f + 0.5f;
177     return 1.0f;
178 }
179
180 /*****************************************************************************
181  * parse_i_values : parse i_values parameter and store the corresponding values
182  *****************************************************************************/
183 static void parse_i_values( BarGraph_t *p_BarGraph, char *i_values)
184 {
185     char* res = NULL;
186     char delim[] = ":";
187     char* tok;
188     float db;
189
190     p_BarGraph->nbChannels = 0;
191     p_BarGraph->i_values = NULL;
192     res = strtok_r(i_values, delim, &tok);
193     while (res != NULL) {
194         p_BarGraph->nbChannels++;
195         p_BarGraph->i_values = xrealloc(p_BarGraph->i_values,
196                                           p_BarGraph->nbChannels*sizeof(int));
197         db = log10(atof(res)) * 20;
198         p_BarGraph->i_values[p_BarGraph->nbChannels-1] = VLC_CLIP( iec_scale(db)*p_BarGraph->scale, 0, p_BarGraph->scale );
199         res = strtok_r(NULL, delim, &tok);
200     }
201
202 }
203
204 /*****************************************************************************
205  * LoadImage: creates and returns the bar graph image
206  *****************************************************************************/
207 static picture_t *LoadImage( vlc_object_t *p_this, int nbChannels, int* i_values, int scale, int alarm, int barWidth)
208 {
209     VLC_UNUSED(p_this);
210     picture_t *p_pic;
211     int i, j, pi;
212     int i_width = 0;
213     int i_line;
214     int minus8, minus10, minus18, minus20, minus30, minus40, minus50, minus60;
215
216     if (nbChannels == 0) {
217         i_width = 20;
218     } else {
219         i_width = 2 * nbChannels * barWidth + 10;
220     }
221     minus8  = iec_scale(-8)*scale + 20;
222     minus10 = iec_scale(-10)*scale + 20;
223     minus18 = iec_scale(-18)*scale + 20;
224     minus20 = iec_scale(-20)*scale + 20;
225     minus30 = iec_scale(-30)*scale + 20;
226     minus40 = iec_scale(-40)*scale + 20;
227     minus50 = iec_scale(-50)*scale + 20;
228     minus60 = iec_scale(-60)*scale + 20;
229
230     p_pic = picture_New(VLC_FOURCC('Y','U','V','A'), i_width+20, scale+30, 1, 1);
231
232
233 #define DrawLine(a,b,Y,U,V,A) \
234         for (i=a; i<b; i++) {\
235             *(p_pic->p[0].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[0].i_pitch + i ) = Y;\
236             *(p_pic->p[1].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[1].i_pitch + i ) = U;\
237             *(p_pic->p[2].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[2].i_pitch + i ) = V;\
238             *(p_pic->p[3].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[3].i_pitch + i ) = A; \
239         }
240 #define DrawLineBlack(a,b) DrawLine(a,b,0,128,128,0xFF)
241 #define DrawLineWhite(a,b) DrawLine(a,b,255,128,128,0xFF)
242
243     // blacken the whole picture
244     for( i = 0 ; i < p_pic->i_planes ; i++ )
245     {
246         memset( p_pic->p[i].p_pixels, 0x00,
247                 p_pic->p[i].i_visible_lines * p_pic->p[i].i_pitch );
248     }
249
250
251     // side bar
252     for ( i_line = 20; i_line < scale+20; i_line++ ) {
253         // vertical line
254         DrawLineBlack(20,22);
255         DrawLineWhite(22,24);
256
257         // -10dB
258         if (i_line == minus10 - 2) {
259             // 1
260             DrawLineBlack(14,15);
261             // 0
262             DrawLineBlack(16,19);
263         }
264         if (i_line == minus10 - 1) {
265             // 1
266             DrawLineBlack(14,15);
267             // 0
268             DrawLineBlack(16,17);
269             DrawLineBlack(18,19);
270             // limit
271             DrawLineWhite(24,27); //White
272         }
273         if (i_line == minus10) {
274             // 1
275             DrawLineBlack(14,15);
276             // 0
277             DrawLineBlack(16,17);
278             DrawLineBlack(18,19);
279             // limit
280             DrawLineBlack(24,27);
281         }
282         if (i_line == minus10 + 1) {
283             // 1
284             DrawLineBlack(14,15);
285             // 0
286             DrawLineBlack(16,17);
287             DrawLineBlack(18,19);
288             // limit
289             DrawLineBlack(24,27);
290         }
291         if (i_line == minus10 + 2) {
292             // 1
293             DrawLineBlack(14,15);
294             // 0
295             DrawLineBlack(16,19);
296         }
297
298         // -20dB
299         if (i_line == minus20 - 2) {
300             // 2
301             DrawLineBlack(12,15);
302             // 0
303             DrawLineBlack(16,19);
304         }
305         if (i_line == minus20 - 1) {
306             // 2
307             DrawLineBlack(12,13);
308             // 0
309             DrawLineBlack(16,17);
310             DrawLineBlack(18,19);
311             // limit
312             DrawLineWhite(24,27); //White
313         }
314         if (i_line == minus20) {
315             // 2
316             DrawLineBlack(12,15);
317             // 0
318             DrawLineBlack(16,17);
319             DrawLineBlack(18,19);
320             // limit
321             DrawLineBlack(24,27);
322         }
323         if (i_line == minus20 + 1) {
324             // 2
325             DrawLineBlack(14,15);
326             // 0
327             DrawLineBlack(16,17);
328             DrawLineBlack(18,19);
329             // limit
330             DrawLineBlack(24,27);
331         }
332         if (i_line == minus20 + 2) {
333             // 2
334             DrawLineBlack(12,15);
335             // 0
336             DrawLineBlack(16,19);
337         }
338
339         // -30dB
340         if (i_line == minus30 - 2) {
341             // 3
342             DrawLineBlack(12,15);
343             // 0
344             DrawLineBlack(16,19);
345         }
346         if (i_line == minus30 - 1) {
347             // 3
348             DrawLineBlack(14,15);
349             // 0
350             DrawLineBlack(16,17);
351             DrawLineBlack(18,19);
352             // limit
353             DrawLineWhite(24,27); //White
354         }
355         if (i_line == minus30) {
356             // 3
357             DrawLineBlack(12,15);
358             // 0
359             DrawLineBlack(16,17);
360             DrawLineBlack(18,19);
361             // limit
362             DrawLineBlack(24,27);
363         }
364         if (i_line == minus30 + 1) {
365             // 3
366             DrawLineBlack(14,15);
367             // 0
368             DrawLineBlack(16,17);
369             DrawLineBlack(18,19);
370             // limit
371             DrawLineBlack(24,27);
372         }
373         if (i_line == minus30 + 2) {
374             // 3
375             DrawLineBlack(12,15);
376             // 0
377             DrawLineBlack(16,19);
378         }
379
380         // -40dB
381         if (i_line == minus40 - 2) {
382             // 4
383             DrawLineBlack(14,15);
384             // 0
385             DrawLineBlack(16,19);
386         }
387         if (i_line == minus40 - 1) {
388             // 4
389             DrawLineBlack(14,15);
390             // 0
391             DrawLineBlack(16,17);
392             DrawLineBlack(18,19);
393             // limit
394             DrawLineWhite(24,27); // white
395         }
396         if (i_line == minus40) {
397             // 4
398             DrawLineBlack(12,15);
399             // 0
400             DrawLineBlack(16,17);
401             DrawLineBlack(18,19);
402             // limit
403             DrawLineBlack(24,27);
404         }
405         if (i_line == minus40 + 1) {
406             // 4
407             DrawLineBlack(12,13);
408             DrawLineBlack(14,15);
409             // 0
410             DrawLineBlack(16,17);
411             DrawLineBlack(18,19);
412             // limit
413             DrawLineBlack(24,27);
414         }
415         if (i_line == minus40 + 2) {
416             // 4
417             DrawLineBlack(12,13);
418             DrawLineBlack(14,15);
419             // 0
420             DrawLineBlack(16,19);
421         }
422
423         // -50dB
424         if (i_line == minus50 - 2) {
425             // 5
426             DrawLineBlack(12,15);
427             // 0
428             DrawLineBlack(16,19);
429         }
430         if (i_line == minus50 - 1) {
431             // 5
432             DrawLineBlack(14,15);
433             // 0
434             DrawLineBlack(16,17);
435             DrawLineBlack(18,19);
436             // limit
437             DrawLineWhite(24,27); //White
438         }
439         if (i_line == minus50) {
440             // 5
441             DrawLineBlack(12,15);
442             // 0
443             DrawLineBlack(16,17);
444             DrawLineBlack(18,19);
445             // limit
446             DrawLineBlack(24,27);
447         }
448         if (i_line == minus50 + 1) {
449             // 5
450             DrawLineBlack(12,13);
451             // 0
452             DrawLineBlack(16,17);
453             DrawLineBlack(18,19);
454             // limit
455             DrawLineBlack(24,27);
456         }
457         if (i_line == minus50 + 2) {
458             // 2
459             DrawLineBlack(12,15);
460             // 0
461             DrawLineBlack(16,19);
462         }
463         // -60dB
464         if (i_line == minus60 - 2) {
465             // 6
466             DrawLineBlack(12,15);
467             // 0
468             DrawLineBlack(16,19);
469         }
470         if (i_line == minus60 - 1) {
471             // 6
472             DrawLineBlack(12,13);
473             DrawLineBlack(14,15);
474             // 0
475             DrawLineBlack(16,17);
476             DrawLineBlack(18,19);
477             // limit
478             DrawLineWhite(24,27); //White
479         }
480         if (i_line == minus60) {
481             // 6
482             DrawLineBlack(12,15);
483             // 0
484             DrawLineBlack(16,17);
485             DrawLineBlack(18,19);
486             // limit
487             DrawLineBlack(24,27);
488         }
489         if (i_line == minus60 + 1) {
490             // 6
491             DrawLineBlack(12,13);
492             // 0
493             DrawLineBlack(16,17);
494             DrawLineBlack(18,19);
495             // limit
496             DrawLineBlack(24,27);
497         }
498         if (i_line == minus60 + 2) {
499             // 6
500             DrawLineBlack(12,15);
501             // 0
502             DrawLineBlack(16,19);
503         }
504     }
505
506 #define drawPoint(offset,y,u,v,a)  \
507                 *(p_pic->p[0].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[0].i_pitch + offset ) = y; \
508                 *(p_pic->p[1].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[1].i_pitch + offset ) = u; \
509                 *(p_pic->p[2].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[2].i_pitch + offset ) = v; \
510                 *(p_pic->p[3].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[3].i_pitch + offset ) = a;
511
512
513     // draw the bars and channel indicators
514     for (i=0; i<nbChannels; i++) {
515         pi =  25 + ((i+1)*5) + (i*barWidth) ;  // 25 separació amb indicador, 5 separació entre barres
516
517         for( j = pi; j < pi + barWidth; j++)
518         {
519             // channel indicators
520             for ( i_line = 12; i_line < 20; i_line++ ) {
521                 if( alarm ) {
522                     drawPoint(j,76,85,0xFF,0xFF);  // red
523                 }
524                 else {
525                     drawPoint(j,0,128,128,0xFF); // black             DrawLine(pi,pf,0xFF,128,128,0xFF);
526                 }
527             }
528
529             // bars
530             for( i_line = 20; i_line < i_values[i]+20; i_line++ )
531             {
532                 if (i_line < minus18) { // green if < -18 dB
533                     drawPoint(j,150,44,21,0xFF);
534                     //DrawLine(pi,pf,150,44,21,0xFF);
535                 } else if (i_line < minus8) { // yellow if > -18dB and < -8dB
536                     drawPoint(j,226,1,148,0xFF);
537                     //DrawLine(pi,pf,226,1,148,0xFF);
538                 } else { // red if > -8 dB
539                     drawPoint(j,76,85,0xFF,0xFF);
540                     //DrawLine(pi,pf,76,85,255,0xFF);
541                 }
542             }
543             // bars no signal
544             for( ; i_line < scale+20; i_line++ )
545             {
546                 if (i_line < minus18) { // green if < -18 dB
547                     drawPoint(j,74,85,74,0xFF);
548                     //DrawLine(pi,pf,74,85,74,0xFF);
549                 } else if (i_line < minus8) { // yellow if > -18dB and < -8dB
550                     drawPoint(j,112,64,138,0xFF);
551                     //DrawLine(pi,pf,112,64,138,0xFF);
552                 } else { // red if > -8 dB
553                     drawPoint(j,37,106,191,0xFF);
554                     //DrawLine(pi,pf,37,106,191,0xFF);
555                 }
556             }
557         }
558     }
559
560     return p_pic;
561 }
562 /*****************************************************************************
563  * LoadBarGraph: loads the BarGraph images into memory
564  *****************************************************************************/
565 static void LoadBarGraph( vlc_object_t *p_this, BarGraph_t *p_BarGraph )
566 {
567
568     p_BarGraph->p_pic = LoadImage( p_this, p_BarGraph->nbChannels, p_BarGraph->i_values, p_BarGraph->scale, p_BarGraph->alarm, p_BarGraph->barWidth);
569     if( !p_BarGraph->p_pic )
570     {
571         msg_Warn( p_this, "error while creating picture" );
572     }
573
574 }
575
576 /*****************************************************************************
577  * Callback to update params on the fly
578  *****************************************************************************/
579 static int BarGraphCallback( vlc_object_t *p_this, char const *psz_var,
580                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
581 {
582     VLC_UNUSED(oldval);
583     filter_sys_t *p_sys = (filter_sys_t *)p_data;
584     BarGraph_t *p_BarGraph = &(p_sys->p_BarGraph);
585     char* res = NULL;
586
587     vlc_mutex_lock( &p_sys->lock );
588     if ( !strcmp( psz_var, "audiobargraph_v-x" ) )
589     {
590         p_sys->i_pos_x = newval.i_int;
591     }
592     else if ( !strcmp( psz_var, "audiobargraph_v-y" ) )
593     {
594         p_sys->i_pos_y = newval.i_int;
595     }
596     else if ( !strcmp( psz_var, "audiobargraph_v-position" ) )
597     {
598         p_sys->i_pos = newval.i_int;
599     }
600     else if ( !strcmp( psz_var, "audiobargraph_v-transparency" ) )
601     {
602         p_BarGraph->i_alpha = VLC_CLIP( newval.i_int, 0, 255 );
603     }
604     else if ( !strcmp( psz_var, "audiobargraph_v-i_values" ) )
605     {
606         if( p_BarGraph->p_pic )
607         {
608             picture_Release( p_BarGraph->p_pic );
609             p_BarGraph->p_pic = NULL;
610         }
611
612         char *psz = xstrdup( newval.psz_string ? newval.psz_string : "" );
613         free(p_BarGraph->i_values);
614         //p_BarGraph->i_values = NULL;
615         //p_BarGraph->nbChannels = 0;
616         // in case many answer are received at the same time, only keep one
617         res = strchr(psz, '@');
618         if (res)
619             *res = 0;
620         parse_i_values( p_BarGraph, psz);
621         free( psz );
622         LoadBarGraph(p_this,p_BarGraph);
623     }
624     else if ( !strcmp( psz_var, "audiobargraph_v-alarm" ) )
625     {
626         if( p_BarGraph->p_pic )
627         {
628             picture_Release( p_BarGraph->p_pic );
629             p_BarGraph->p_pic = NULL;
630         }
631         p_BarGraph->alarm = newval.b_bool;
632         LoadBarGraph(p_this,p_BarGraph);
633     }
634     else if ( !strcmp( psz_var, "audiobargraph_v-barWidth" ) )
635     {
636         if( p_BarGraph->p_pic )
637         {
638             picture_Release( p_BarGraph->p_pic );
639             p_BarGraph->p_pic = NULL;
640         }
641         p_BarGraph->barWidth = newval.i_int;
642         LoadBarGraph(p_this,p_BarGraph);
643     }
644     p_sys->b_spu_update = true;
645     vlc_mutex_unlock( &p_sys->lock );
646
647     return VLC_SUCCESS;
648 }
649
650 /**
651  * Sub source
652  */
653 static subpicture_t *FilterSub( filter_t *p_filter, mtime_t date )
654 {
655     filter_sys_t *p_sys = p_filter->p_sys;
656     BarGraph_t *p_BarGraph = &(p_sys->p_BarGraph);
657
658     subpicture_t *p_spu;
659     subpicture_region_t *p_region;
660     video_format_t fmt;
661     picture_t *p_pic;
662
663     vlc_mutex_lock( &p_sys->lock );
664     /* Basic test:  b_spu_update occurs on a dynamic change */
665     if( !p_sys->b_spu_update )
666     {
667         vlc_mutex_unlock( &p_sys->lock );
668         return 0;
669     }
670
671     p_pic = p_BarGraph->p_pic;
672
673     /* Allocate the subpicture internal data. */
674     p_spu = filter_NewSubpicture( p_filter );
675     if( !p_spu )
676         goto exit;
677
678     p_spu->b_absolute = p_sys->b_absolute;
679     p_spu->i_start = date;
680     p_spu->i_stop = 0;
681     p_spu->b_ephemer = true;
682
683     /* Send an empty subpicture to clear the display when needed */
684     if( !p_pic || !p_BarGraph->i_alpha )
685         goto exit;
686
687     /* Create new SPU region */
688     memset( &fmt, 0, sizeof(video_format_t) );
689     fmt.i_chroma = VLC_CODEC_YUVA;
690     fmt.i_sar_num = fmt.i_sar_den = 1;
691     fmt.i_width = fmt.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
692     fmt.i_height = fmt.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
693     fmt.i_x_offset = fmt.i_y_offset = 0;
694     p_region = subpicture_region_New( &fmt );
695     if( !p_region )
696     {
697         msg_Err( p_filter, "cannot allocate SPU region" );
698         p_filter->pf_sub_buffer_del( p_filter, p_spu );
699         p_spu = NULL;
700         goto exit;
701     }
702
703     /* */
704     picture_Copy( p_region->p_picture, p_pic );
705
706     /*  where to locate the bar graph: */
707     if( p_sys->i_pos < 0 )
708     {   /*  set to an absolute xy */
709         p_region->i_align = SUBPICTURE_ALIGN_RIGHT | SUBPICTURE_ALIGN_TOP;
710         p_spu->b_absolute = true;
711     }
712     else
713     {   /* set to one of the 9 relative locations */
714         p_region->i_align = p_sys->i_pos;
715         p_spu->b_absolute = false;
716     }
717
718     p_region->i_x = p_sys->i_pos_x;
719     p_region->i_y = p_sys->i_pos_y;
720
721     p_spu->p_region = p_region;
722
723     p_spu->i_alpha = p_BarGraph->i_alpha ;
724
725 exit:
726     vlc_mutex_unlock( &p_sys->lock );
727
728     return p_spu;
729 }
730
731 /**
732  * Video filter
733  */
734 static picture_t *FilterVideo( filter_t *p_filter, picture_t *p_src )
735 {
736     filter_sys_t *p_sys = p_filter->p_sys;
737     BarGraph_t *p_BarGraph = &(p_sys->p_BarGraph);
738
739     picture_t *p_dst = filter_NewPicture( p_filter );
740     if( !p_dst )
741         goto exit;
742
743     picture_Copy( p_dst, p_src );
744
745     /* */
746     vlc_mutex_lock( &p_sys->lock );
747
748     /* */
749     const picture_t *p_pic = p_BarGraph->p_pic;
750     if( p_pic )
751     {
752         const video_format_t *p_fmt = &p_pic->format;
753         const int i_dst_w = p_filter->fmt_out.video.i_visible_width;
754         const int i_dst_h = p_filter->fmt_out.video.i_visible_height;
755
756         if( p_sys->i_pos )
757         {
758             if( p_sys->i_pos & SUBPICTURE_ALIGN_BOTTOM )
759             {
760                 p_sys->i_pos_y = i_dst_h - p_fmt->i_visible_height;
761             }
762             else if ( !(p_sys->i_pos & SUBPICTURE_ALIGN_TOP) )
763             {
764                 p_sys->i_pos_y = ( i_dst_h - p_fmt->i_visible_height ) / 2;
765             }
766             else
767             {
768                 p_sys->i_pos_y = 0;
769             }
770
771             if( p_sys->i_pos & SUBPICTURE_ALIGN_RIGHT )
772             {
773                 p_sys->i_pos_x = i_dst_w - p_fmt->i_visible_width;
774             }
775             else if ( !(p_sys->i_pos & SUBPICTURE_ALIGN_LEFT) )
776             {
777                 p_sys->i_pos_x = ( i_dst_w - p_fmt->i_visible_width ) / 2;
778             }
779             else
780             {
781                 p_sys->i_pos_x = 0;
782             }
783         }
784
785         /* */
786         const int i_alpha = p_BarGraph->i_alpha;
787         if( filter_ConfigureBlend( p_sys->p_blend, i_dst_w, i_dst_h, p_fmt ) ||
788             filter_Blend( p_sys->p_blend, p_dst, p_sys->i_pos_x, p_sys->i_pos_y,
789                           p_pic, i_alpha ) )
790         {
791             msg_Err( p_filter, "failed to blend a picture" );
792         }
793     }
794     vlc_mutex_unlock( &p_sys->lock );
795
796 exit:
797     picture_Release( p_src );
798     return p_dst;
799 }
800
801 /**
802  * Common open function
803  */
804 static int OpenCommon( vlc_object_t *p_this, bool b_sub )
805 {
806     filter_t *p_filter = (filter_t *)p_this;
807     filter_sys_t *p_sys;
808     BarGraph_t *p_BarGraph;
809     char* i_values = NULL;
810
811     /* */
812     if( !b_sub && !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) )
813     {
814         msg_Err( p_filter, "Input and output format does not match" );
815         return VLC_EGENERIC;
816     }
817
818
819     /* */
820     p_filter->p_sys = p_sys = malloc( sizeof( *p_sys ) );
821     if( !p_sys )
822         return VLC_ENOMEM;
823     p_BarGraph = &(p_sys->p_BarGraph);
824     p_BarGraph->p_pic = NULL;
825
826     /* */
827     p_sys->p_blend = NULL;
828     if( !b_sub )
829     {
830
831         p_sys->p_blend = filter_NewBlend( VLC_OBJECT(p_filter),
832                                           &p_filter->fmt_in.video );
833         if( !p_sys->p_blend )
834         {
835             //free( p_BarGraph );
836             free( p_sys );
837             return VLC_EGENERIC;
838         }
839     }
840
841     /* */
842     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
843                        p_filter->p_cfg );
844
845     /* create and initialize variables */
846     p_sys->i_pos = var_CreateGetIntegerCommand( p_filter, "audiobargraph_v-position" );
847     p_sys->i_pos_x = var_CreateGetIntegerCommand( p_filter, "audiobargraph_v-x" );
848     p_sys->i_pos_y = var_CreateGetIntegerCommand( p_filter, "audiobargraph_v-y" );
849     p_BarGraph->i_alpha = var_CreateGetIntegerCommand( p_filter,
850                                                         "audiobargraph_v-transparency" );
851     p_BarGraph->i_alpha = VLC_CLIP( p_BarGraph->i_alpha, 0, 255 );
852     //p_BarGraph->nbChannels = 0;
853     //p_BarGraph->i_values = NULL;
854     parse_i_values(p_BarGraph, &(char){ 0 });
855     p_BarGraph->alarm = false;
856
857     p_BarGraph->barWidth = var_CreateGetIntegerCommand( p_filter, "audiobargraph_v-barWidth" );
858     p_BarGraph->scale = 400;
859
860     /* Ignore aligment if a position is given for video filter */
861     if( !b_sub && p_sys->i_pos_x >= 0 && p_sys->i_pos_y >= 0 )
862         p_sys->i_pos = 0;
863
864     vlc_mutex_init( &p_sys->lock );
865
866     var_Create(p_filter->p_libvlc, "audiobargraph_v-alarm", VLC_VAR_BOOL);
867     var_Create(p_filter->p_libvlc, "audiobargraph_v-i_values", VLC_VAR_STRING);
868
869     var_AddCallback(p_filter->p_libvlc, "audiobargraph_v-alarm",
870                     BarGraphCallback, p_sys);
871     var_AddCallback(p_filter->p_libvlc, "audiobargraph_v-i_values",
872                     BarGraphCallback, p_sys);
873
874     var_TriggerCallback(p_filter->p_libvlc, "audiobargraph_v-alarm");
875     var_TriggerCallback(p_filter->p_libvlc, "audiobargraph_v-i_values");
876
877     for( int i = 0; ppsz_filter_callbacks[i]; i++ )
878         var_AddCallback( p_filter, ppsz_filter_callbacks[i],
879                          BarGraphCallback, p_sys );
880
881     /* Misc init */
882     if( b_sub )
883     {
884         p_filter->pf_sub_source = FilterSub;
885     }
886     else
887     {
888         p_filter->pf_video_filter = FilterVideo;
889     }
890
891     free( i_values );
892     return VLC_SUCCESS;
893 }
894
895 /**
896  * Open the sub source
897  */
898 static int OpenSub( vlc_object_t *p_this )
899 {
900     return OpenCommon( p_this, true );
901 }
902
903 /**
904  * Open the video filter
905  */
906 static int OpenVideo( vlc_object_t *p_this )
907 {
908     return OpenCommon( p_this, false );
909 }
910
911 /**
912  * Common close function
913  */
914 static void Close( vlc_object_t *p_this )
915 {
916     filter_t *p_filter = (filter_t *)p_this;
917     filter_sys_t *p_sys = p_filter->p_sys;
918     BarGraph_t *p_BarGraph = &(p_sys->p_BarGraph);
919
920     for( int i = 0; ppsz_filter_callbacks[i]; i++ )
921         var_DelCallback( p_filter, ppsz_filter_callbacks[i],
922                          BarGraphCallback, p_sys );
923
924     var_DelCallback(p_filter->p_libvlc, "audiobargraph_v-i_values",
925                     BarGraphCallback, p_sys);
926     var_DelCallback(p_filter->p_libvlc, "audiobargraph_v-alarm",
927                     BarGraphCallback, p_sys);
928     var_Destroy(p_filter->p_libvlc, "audiobargraph_v-i_values");
929     var_Destroy(p_filter->p_libvlc, "audiobargraph_v-alarm");
930
931     if( p_sys->p_blend )
932         filter_DeleteBlend( p_sys->p_blend );
933
934     vlc_mutex_destroy( &p_sys->lock );
935
936     if( p_BarGraph->p_pic )
937     {
938         picture_Release( p_BarGraph->p_pic );
939         p_BarGraph->p_pic = NULL;
940     }
941     free( p_BarGraph->i_values );
942
943     free( p_sys );
944 }