1 /*****************************************************************************
2 * audiobargraph_v.c : audiobargraph video plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2003-2006 VLC authors and VideoLAN
6 * Authors: Clement CHESNIN <clement.chesnin@gmail.com>
7 * Philippe COENT <philippe.coent@tdf.fr>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_filter.h>
37 #include <vlc_subpicture.h>
38 #include <vlc_image.h>
40 /*****************************************************************************
42 *****************************************************************************/
44 #define POSX_TEXT N_("X coordinate")
45 #define POSX_LONGTEXT N_("X coordinate of the bargraph.")
46 #define POSY_TEXT N_("Y coordinate")
47 #define POSY_LONGTEXT N_("Y coordinate of the bargraph.")
48 #define TRANS_TEXT N_("Transparency of the bargraph")
49 #define TRANS_LONGTEXT N_("Bargraph transparency value " \
50 "(from 0 for full transparency to 255 for full opacity).")
51 #define POS_TEXT N_("Bargraph position")
52 #define POS_LONGTEXT N_(\
53 "Enforce the bargraph position on the video " \
54 "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
55 "also use combinations of these values, eg 6 = top-right).")
56 #define BARWIDTH_TEXT N_("Bar width in pixel")
57 #define BARWIDTH_LONGTEXT N_("Width in pixel of each bar in the BarGraph to be displayed." )
58 #define BARHEIGHT_TEXT N_("Bar Height in pixel")
59 #define BARHEIGHT_LONGTEXT N_("Height in pixel of BarGraph to be displayed." )
61 #define CFG_PREFIX "audiobargraph_v-"
63 static const int pi_pos_values
[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
64 static const char *const ppsz_pos_descriptions
[] =
65 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
66 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
68 static int OpenSub (vlc_object_t
*);
69 static int OpenVideo(vlc_object_t
*);
70 static void Close (vlc_object_t
*);
74 set_category(CAT_VIDEO
)
75 set_subcategory(SUBCAT_VIDEO_SUBPIC
)
77 set_capability("sub source", 0)
78 set_callbacks(OpenSub
, Close
)
79 set_description(N_("Audio Bar Graph Video sub source"))
80 set_shortname(N_("Audio Bar Graph Video"))
81 add_shortcut("audiobargraph_v")
83 add_obsolete_string(CFG_PREFIX
"i_values")
84 add_integer(CFG_PREFIX
"x", 0, POSX_TEXT
, POSX_LONGTEXT
, true)
85 add_integer(CFG_PREFIX
"y", 0, POSY_TEXT
, POSY_LONGTEXT
, true)
86 add_integer_with_range(CFG_PREFIX
"transparency", 255, 0, 255,
87 TRANS_TEXT
, TRANS_LONGTEXT
, false)
88 add_integer(CFG_PREFIX
"position", -1, POS_TEXT
, POS_LONGTEXT
, false)
89 change_integer_list(pi_pos_values
, ppsz_pos_descriptions
)
90 add_obsolete_integer(CFG_PREFIX
"alarm")
91 add_integer(CFG_PREFIX
"barWidth", 10, BARWIDTH_TEXT
, BARWIDTH_LONGTEXT
, true)
92 add_integer(CFG_PREFIX
"barHeight", 400, BARHEIGHT_TEXT
, BARHEIGHT_LONGTEXT
, true)
94 /* video output filter submodule */
96 set_capability("video filter", 0)
97 set_callbacks(OpenVideo
, Close
)
98 set_description(N_("Audio Bar Graph Video sub source"))
99 add_shortcut("audiobargraph_v")
103 /*****************************************************************************
105 *****************************************************************************/
107 /*****************************************************************************
108 * Structure to hold the Bar Graph properties
109 ****************************************************************************/
112 int i_alpha
; /* -1 means use default alpha */
124 * Private data holder
132 BarGraph_t p_BarGraph
;
139 /* On the fly control variable */
143 static const char *const ppsz_filter_options
[] = {
144 "x", "y", "transparency", "position", "barWidth", "barHeight", NULL
147 static const char *const ppsz_filter_callbacks
[] = {
150 "audiobargraph_v-transparency",
151 "audiobargraph_v-position",
152 "audiobargraph_v-barWidth",
153 "audiobargraph_v-barHeight",
157 /*****************************************************************************
158 * IEC 268-18 Source: meterbridge
159 *****************************************************************************/
160 static float iec_scale(float dB
)
165 return (dB
+ 70.0f
) * 0.0025f
;
167 return (dB
+ 60.0f
) * 0.005f
+ 0.025f
;
169 return (dB
+ 50.0f
) * 0.0075f
+ 0.075f
;
171 return (dB
+ 40.0f
) * 0.015f
+ 0.15f
;
173 return (dB
+ 30.0f
) * 0.02f
+ 0.3f
;
174 if (dB
< -0.001f
|| dB
> 0.001f
) /* if (dB < 0.0f) */
175 return (dB
+ 20.0f
) * 0.025f
+ 0.5f
;
179 /*****************************************************************************
180 * parse_i_values : parse i_values parameter and store the corresponding values
181 *****************************************************************************/
182 static void parse_i_values(BarGraph_t
*p_BarGraph
, char *i_values
)
187 p_BarGraph
->nbChannels
= 0;
188 free(p_BarGraph
->i_values
);
189 p_BarGraph
->i_values
= NULL
;
190 char *res
= strtok_r(i_values
, delim
, &tok
);
191 while (res
!= NULL
) {
192 p_BarGraph
->nbChannels
++;
193 p_BarGraph
->i_values
= xrealloc(p_BarGraph
->i_values
,
194 p_BarGraph
->nbChannels
*sizeof(int));
195 float db
= log10(atof(res
)) * 20;
196 p_BarGraph
->i_values
[p_BarGraph
->nbChannels
-1] = VLC_CLIP(iec_scale(db
)*p_BarGraph
->scale
, 0, p_BarGraph
->scale
);
197 res
= strtok_r(NULL
, delim
, &tok
);
203 static const uint8_t bright_red
[4] = { 76, 85, 0xff, 0xff };
204 static const uint8_t black
[4] = { 0x00, 0x80, 0x80, 0xff };
205 static const uint8_t white
[4] = { 0xff, 0x80, 0x80, 0xff };
206 static const uint8_t bright_green
[4] = { 150, 44, 21, 0xff };
207 static const uint8_t bright_yellow
[4] = { 226, 1, 148, 0xff };
208 static const uint8_t green
[4] = { 74, 85, 74, 0xff };
209 static const uint8_t yellow
[4] = { 112, 64, 138, 0xff };
210 static const uint8_t red
[4] = { 37, 106, 191, 0xff };
212 static inline void DrawHLine(plane_t
*p
, int line
, int col
, const uint8_t color
[4], int w
)
214 for (int j
= 0; j
< 4; j
++)
215 memset(&p
[j
].p_pixels
[line
* p
[j
].i_pitch
+ col
], color
[j
], w
);
218 static void Draw2VLines(plane_t
*p
, int scale
, int col
, const uint8_t color
[4])
220 for (int i
= 10; i
< scale
+ 10; i
++)
221 DrawHLine(p
, i
, col
, color
, 2);
224 static void DrawHLines(plane_t
*p
, int line
, int col
, const uint8_t color
[4], int h
, int w
)
226 for (int i
= line
; i
< line
+ h
; i
++)
227 DrawHLine(p
, i
, col
, color
, w
);
230 static void DrawNumber(plane_t
*p
, int h
, const uint8_t data
[5], int l
)
232 for (int i
= 0; i
< 5; i
++) {
234 for (int j
= 0; j
< 7; j
++) {
237 DrawHLine(p
, h
- l
+ 2 - 1 - i
, 12 + j
, black
, 1);
241 /*****************************************************************************
242 * Draw: creates and returns the bar graph image
243 *****************************************************************************/
244 static void Draw(BarGraph_t
*b
)
246 int nbChannels
= b
->nbChannels
;
247 int scale
= b
->scale
;
248 int barWidth
= b
->barWidth
;
252 w
= 2 * nbChannels
* barWidth
+ 30;
256 for (int i
= 0; i
< 6; i
++)
257 level
[i
] = iec_scale(-(i
+1) * 10) * scale
+ 20;
260 picture_Release(b
->p_pic
);
261 b
->p_pic
= picture_New(VLC_FOURCC('Y','U','V','A'), w
, h
, 1, 1);
264 picture_t
*p_pic
= b
->p_pic
;
265 plane_t
*p
= p_pic
->p
;
267 for (int i
= 0 ; i
< p_pic
->i_planes
; i
++)
268 memset(p
[i
].p_pixels
, 0x00, p
[i
].i_visible_lines
* p
[i
].i_pitch
);
270 Draw2VLines(p
, scale
, 20, black
);
271 Draw2VLines(p
, scale
, 22, white
);
273 static const uint8_t pixmap
[6][5] = {
274 { 0x17, 0x15, 0x15, 0x15, 0x17 },
275 { 0x77, 0x45, 0x75, 0x15, 0x77 },
276 { 0x77, 0x15, 0x75, 0x15, 0x77 },
277 { 0x17, 0x15, 0x75, 0x55, 0x57 },
278 { 0x77, 0x15, 0x75, 0x45, 0x77 },
279 { 0x77, 0x55, 0x75, 0x45, 0x77 },
282 for (int i
= 0; i
< 6; i
++) {
283 DrawHLines(p
, h
- 1 - level
[i
] - 1, 24, white
, 1, 3);
284 DrawHLines(p
, h
- 1 - level
[i
], 24, black
, 2, 3);
285 DrawNumber(p
, h
, pixmap
[i
], level
[i
]);
288 int minus8
= iec_scale(- 8) * scale
+ 20;
289 int minus18
= iec_scale(-18) * scale
+ 20;
290 int *i_values
= b
->i_values
;
291 const uint8_t *indicator_color
= b
->alarm
? bright_red
: black
;
293 for (int i
= 0; i
< nbChannels
; i
++) {
294 int pi
= 30 + i
* (5 + barWidth
);
296 DrawHLines(p
, h
- 20 - 1, pi
, indicator_color
, 8, barWidth
);
298 for (int line
= 20; line
< i_values
[i
] + 20; line
++) {
300 DrawHLines(p
, h
- line
- 1, pi
, bright_green
, 1, barWidth
);
301 else if (line
< minus8
)
302 DrawHLines(p
, h
- line
- 1, pi
, bright_yellow
, 1, barWidth
);
304 DrawHLines(p
, h
- line
- 1, pi
, bright_red
, 1, barWidth
);
307 for (int line
= i_values
[i
] + 20; line
< scale
+ 20; line
++) {
309 DrawHLines(p
, h
- line
- 1, pi
, green
, 1, barWidth
);
310 else if (line
< minus8
)
311 DrawHLines(p
, h
- line
- 1, pi
, yellow
, 1, barWidth
);
313 DrawHLines(p
, h
- line
- 1, pi
, red
, 1, barWidth
);
318 /*****************************************************************************
319 * Callback to update params on the fly
320 *****************************************************************************/
321 static int BarGraphCallback(vlc_object_t
*p_this
, char const *psz_var
,
322 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
324 VLC_UNUSED(p_this
); VLC_UNUSED(oldval
);
325 filter_sys_t
*p_sys
= p_data
;
326 BarGraph_t
*p_BarGraph
= &p_sys
->p_BarGraph
;
328 vlc_mutex_lock(&p_sys
->lock
);
329 if (!strcmp(psz_var
, CFG_PREFIX
"x"))
330 p_sys
->i_pos_x
= newval
.i_int
;
331 else if (!strcmp(psz_var
, CFG_PREFIX
"y"))
332 p_sys
->i_pos_y
= newval
.i_int
;
333 else if (!strcmp(psz_var
, CFG_PREFIX
"position"))
334 p_sys
->i_pos
= newval
.i_int
;
335 else if (!strcmp(psz_var
, CFG_PREFIX
"transparency"))
336 p_BarGraph
->i_alpha
= VLC_CLIP(newval
.i_int
, 0, 255);
337 else if (!strcmp(psz_var
, CFG_PREFIX
"i_values")) {
338 if (newval
.psz_string
)
339 parse_i_values(p_BarGraph
, newval
.psz_string
);
341 } else if (!strcmp(psz_var
, CFG_PREFIX
"alarm")) {
342 p_BarGraph
->alarm
= newval
.b_bool
;
344 } else if (!strcmp(psz_var
, CFG_PREFIX
"barWidth")) {
345 p_BarGraph
->barWidth
= newval
.i_int
;
347 } else if (!strcmp(psz_var
, CFG_PREFIX
"barHeight")) {
348 p_BarGraph
->scale
= newval
.i_int
;
351 p_sys
->b_spu_update
= true;
352 vlc_mutex_unlock(&p_sys
->lock
);
360 static subpicture_t
*FilterSub(filter_t
*p_filter
, mtime_t date
)
362 filter_sys_t
*p_sys
= p_filter
->p_sys
;
363 BarGraph_t
*p_BarGraph
= &(p_sys
->p_BarGraph
);
366 subpicture_region_t
*p_region
;
370 vlc_mutex_lock(&p_sys
->lock
);
371 /* Basic test: b_spu_update occurs on a dynamic change */
372 if (!p_sys
->b_spu_update
) {
373 vlc_mutex_unlock(&p_sys
->lock
);
377 p_pic
= p_BarGraph
->p_pic
;
379 /* Allocate the subpicture internal data. */
380 p_spu
= filter_NewSubpicture(p_filter
);
384 p_spu
->b_absolute
= p_sys
->b_absolute
;
385 p_spu
->i_start
= date
;
387 p_spu
->b_ephemer
= true;
389 /* Send an empty subpicture to clear the display when needed */
390 if (!p_pic
|| !p_BarGraph
->i_alpha
)
393 /* Create new SPU region */
394 memset(&fmt
, 0, sizeof(video_format_t
));
395 fmt
.i_chroma
= VLC_CODEC_YUVA
;
396 fmt
.i_sar_num
= fmt
.i_sar_den
= 1;
397 fmt
.i_width
= fmt
.i_visible_width
= p_pic
->p
[Y_PLANE
].i_visible_pitch
;
398 fmt
.i_height
= fmt
.i_visible_height
= p_pic
->p
[Y_PLANE
].i_visible_lines
;
399 fmt
.i_x_offset
= fmt
.i_y_offset
= 0;
400 p_region
= subpicture_region_New(&fmt
);
402 msg_Err(p_filter
, "cannot allocate SPU region");
403 subpicture_Delete(p_spu
);
409 picture_Copy(p_region
->p_picture
, p_pic
);
411 /* where to locate the bar graph: */
412 if (p_sys
->i_pos
< 0) { /* set to an absolute xy */
413 p_region
->i_align
= SUBPICTURE_ALIGN_RIGHT
| SUBPICTURE_ALIGN_TOP
;
414 p_spu
->b_absolute
= true;
415 } else { /* set to one of the 9 relative locations */
416 p_region
->i_align
= p_sys
->i_pos
;
417 p_spu
->b_absolute
= false;
420 p_region
->i_x
= p_sys
->i_pos_x
;
421 p_region
->i_y
= p_sys
->i_pos_y
;
423 p_spu
->p_region
= p_region
;
425 p_spu
->i_alpha
= p_BarGraph
->i_alpha
;
428 vlc_mutex_unlock(&p_sys
->lock
);
436 static picture_t
*FilterVideo(filter_t
*p_filter
, picture_t
*p_src
)
438 filter_sys_t
*p_sys
= p_filter
->p_sys
;
439 BarGraph_t
*p_BarGraph
= &(p_sys
->p_BarGraph
);
441 picture_t
*p_dst
= filter_NewPicture(p_filter
);
443 picture_Release(p_src
);
447 picture_Copy(p_dst
, p_src
);
450 vlc_mutex_lock(&p_sys
->lock
);
453 const picture_t
*p_pic
= p_BarGraph
->p_pic
;
457 const video_format_t
*p_fmt
= &p_pic
->format
;
458 const int i_dst_w
= p_filter
->fmt_out
.video
.i_visible_width
;
459 const int i_dst_h
= p_filter
->fmt_out
.video
.i_visible_height
;
462 if (p_sys
->i_pos
& SUBPICTURE_ALIGN_BOTTOM
)
463 p_sys
->i_pos_y
= i_dst_h
- p_fmt
->i_visible_height
;
464 else if (!(p_sys
->i_pos
& SUBPICTURE_ALIGN_TOP
))
465 p_sys
->i_pos_y
= (i_dst_h
- p_fmt
->i_visible_height
) / 2;
469 if (p_sys
->i_pos
& SUBPICTURE_ALIGN_RIGHT
)
470 p_sys
->i_pos_x
= i_dst_w
- p_fmt
->i_visible_width
;
471 else if (!(p_sys
->i_pos
& SUBPICTURE_ALIGN_LEFT
))
472 p_sys
->i_pos_x
= (i_dst_w
- p_fmt
->i_visible_width
) / 2;
478 const int i_alpha
= p_BarGraph
->i_alpha
;
479 if (filter_ConfigureBlend(p_sys
->p_blend
, i_dst_w
, i_dst_h
, p_fmt
) ||
480 filter_Blend(p_sys
->p_blend
, p_dst
, p_sys
->i_pos_x
, p_sys
->i_pos_y
,
482 msg_Err(p_filter
, "failed to blend a picture");
485 vlc_mutex_unlock(&p_sys
->lock
);
487 picture_Release(p_src
);
492 * Common open function
494 static int OpenCommon(vlc_object_t
*p_this
, bool b_sub
)
496 filter_t
*p_filter
= (filter_t
*)p_this
;
500 if (!b_sub
&& !es_format_IsSimilar(&p_filter
->fmt_in
, &p_filter
->fmt_out
)) {
501 msg_Err(p_filter
, "Input and output format does not match");
507 p_filter
->p_sys
= p_sys
= malloc(sizeof(*p_sys
));
512 p_sys
->p_blend
= NULL
;
514 p_sys
->p_blend
= filter_NewBlend(VLC_OBJECT(p_filter
),
515 &p_filter
->fmt_in
.video
);
516 if (!p_sys
->p_blend
) {
523 config_ChainParse(p_filter
, CFG_PREFIX
, ppsz_filter_options
,
526 /* create and initialize variables */
527 p_sys
->i_pos
= var_CreateGetInteger(p_filter
, CFG_PREFIX
"position");
528 p_sys
->i_pos_x
= var_CreateGetInteger(p_filter
, CFG_PREFIX
"x");
529 p_sys
->i_pos_y
= var_CreateGetInteger(p_filter
, CFG_PREFIX
"y");
530 BarGraph_t
*p_BarGraph
= &p_sys
->p_BarGraph
;
531 p_BarGraph
->p_pic
= NULL
;
532 p_BarGraph
->i_alpha
= var_CreateGetInteger(p_filter
, CFG_PREFIX
"transparency");
533 p_BarGraph
->i_alpha
= VLC_CLIP(p_BarGraph
->i_alpha
, 0, 255);
534 p_BarGraph
->i_values
= NULL
;
535 parse_i_values(p_BarGraph
, &(char){ 0 });
536 p_BarGraph
->alarm
= false;
538 p_BarGraph
->barWidth
= var_CreateGetInteger(p_filter
, CFG_PREFIX
"barWidth");
539 p_BarGraph
->scale
= var_CreateGetInteger( p_filter
, CFG_PREFIX
"barHeight");
541 /* Ignore aligment if a position is given for video filter */
542 if (!b_sub
&& p_sys
->i_pos_x
>= 0 && p_sys
->i_pos_y
>= 0)
545 vlc_mutex_init(&p_sys
->lock
);
546 var_Create(p_filter
->obj
.libvlc
, CFG_PREFIX
"alarm", VLC_VAR_BOOL
);
547 var_Create(p_filter
->obj
.libvlc
, CFG_PREFIX
"i_values", VLC_VAR_STRING
);
549 var_AddCallback(p_filter
->obj
.libvlc
, CFG_PREFIX
"alarm",
550 BarGraphCallback
, p_sys
);
551 var_AddCallback(p_filter
->obj
.libvlc
, CFG_PREFIX
"i_values",
552 BarGraphCallback
, p_sys
);
554 var_TriggerCallback(p_filter
->obj
.libvlc
, CFG_PREFIX
"alarm");
555 var_TriggerCallback(p_filter
->obj
.libvlc
, CFG_PREFIX
"i_values");
557 for (int i
= 0; ppsz_filter_callbacks
[i
]; i
++)
558 var_AddCallback(p_filter
, ppsz_filter_callbacks
[i
],
559 BarGraphCallback
, p_sys
);
562 p_filter
->pf_sub_source
= FilterSub
;
564 p_filter
->pf_video_filter
= FilterVideo
;
570 * Open the sub source
572 static int OpenSub(vlc_object_t
*p_this
)
574 return OpenCommon(p_this
, true);
578 * Open the video filter
580 static int OpenVideo(vlc_object_t
*p_this
)
582 return OpenCommon(p_this
, false);
586 * Common close function
588 static void Close(vlc_object_t
*p_this
)
590 filter_t
*p_filter
= (filter_t
*)p_this
;
591 filter_sys_t
*p_sys
= p_filter
->p_sys
;
593 for (int i
= 0; ppsz_filter_callbacks
[i
]; i
++)
594 var_DelCallback(p_filter
, ppsz_filter_callbacks
[i
],
595 BarGraphCallback
, p_sys
);
597 var_DelCallback(p_filter
->obj
.libvlc
, CFG_PREFIX
"i_values",
598 BarGraphCallback
, p_sys
);
599 var_DelCallback(p_filter
->obj
.libvlc
, CFG_PREFIX
"alarm",
600 BarGraphCallback
, p_sys
);
601 var_Destroy(p_filter
->obj
.libvlc
, CFG_PREFIX
"i_values");
602 var_Destroy(p_filter
->obj
.libvlc
, CFG_PREFIX
"alarm");
605 filter_DeleteBlend(p_sys
->p_blend
);
607 vlc_mutex_destroy(&p_sys
->lock
);
609 if (p_sys
->p_BarGraph
.p_pic
)
610 picture_Release(p_sys
->p_BarGraph
.p_pic
);
612 free(p_sys
->p_BarGraph
.i_values
);