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 (filter_t
*);
69 static int OpenVideo(filter_t
*);
70 static void Close (filter_t
*);
74 set_category(CAT_VIDEO
)
75 set_subcategory(SUBCAT_VIDEO_SUBPIC
)
77 set_callback_sub_source(OpenSub
, 0)
78 set_description(N_("Audio Bar Graph Video sub source"))
79 set_shortname(N_("Audio Bar Graph Video"))
80 add_shortcut("audiobargraph_v")
82 add_obsolete_string(CFG_PREFIX
"i_values")
83 add_integer(CFG_PREFIX
"x", 0, POSX_TEXT
, POSX_LONGTEXT
, true)
84 add_integer(CFG_PREFIX
"y", 0, POSY_TEXT
, POSY_LONGTEXT
, true)
85 add_integer_with_range(CFG_PREFIX
"transparency", 255, 0, 255,
86 TRANS_TEXT
, TRANS_LONGTEXT
, false)
87 add_integer(CFG_PREFIX
"position", -1, POS_TEXT
, POS_LONGTEXT
, false)
88 change_integer_list(pi_pos_values
, ppsz_pos_descriptions
)
89 add_obsolete_integer(CFG_PREFIX
"alarm")
90 add_integer(CFG_PREFIX
"barWidth", 10, BARWIDTH_TEXT
, BARWIDTH_LONGTEXT
, true)
91 add_integer(CFG_PREFIX
"barHeight", 400, BARHEIGHT_TEXT
, BARHEIGHT_LONGTEXT
, true)
93 /* video output filter submodule */
95 set_callback_video_filter(OpenVideo
)
96 set_description(N_("Audio Bar Graph Video sub source"))
97 add_shortcut("audiobargraph_v")
101 /*****************************************************************************
103 *****************************************************************************/
105 /*****************************************************************************
106 * Structure to hold the Bar Graph properties
107 ****************************************************************************/
110 int i_alpha
; /* -1 means use default alpha */
122 * Private data holder
126 vlc_blender_t
*p_blend
;
130 BarGraph_t p_BarGraph
;
137 /* On the fly control variable */
141 static const char *const ppsz_filter_options
[] = {
142 "x", "y", "transparency", "position", "barWidth", "barHeight", NULL
145 static const char *const ppsz_filter_callbacks
[] = {
148 "audiobargraph_v-transparency",
149 "audiobargraph_v-position",
150 "audiobargraph_v-barWidth",
151 "audiobargraph_v-barHeight",
155 /*****************************************************************************
156 * IEC 268-18 Source: meterbridge
157 *****************************************************************************/
158 static float iec_scale(float dB
)
163 return (dB
+ 70.0f
) * 0.0025f
;
165 return (dB
+ 60.0f
) * 0.005f
+ 0.025f
;
167 return (dB
+ 50.0f
) * 0.0075f
+ 0.075f
;
169 return (dB
+ 40.0f
) * 0.015f
+ 0.15f
;
171 return (dB
+ 30.0f
) * 0.02f
+ 0.3f
;
172 if (dB
< -0.001f
|| dB
> 0.001f
) /* if (dB < 0.0f) */
173 return (dB
+ 20.0f
) * 0.025f
+ 0.5f
;
177 /*****************************************************************************
178 * parse_i_values : parse i_values parameter and store the corresponding values
179 *****************************************************************************/
180 static void parse_i_values(BarGraph_t
*p_BarGraph
, char *i_values
)
185 p_BarGraph
->nbChannels
= 0;
186 free(p_BarGraph
->i_values
);
187 p_BarGraph
->i_values
= NULL
;
188 char *res
= strtok_r(i_values
, delim
, &tok
);
189 while (res
!= NULL
) {
190 p_BarGraph
->nbChannels
++;
191 p_BarGraph
->i_values
= xrealloc(p_BarGraph
->i_values
,
192 p_BarGraph
->nbChannels
*sizeof(int));
193 float db
= log10(atof(res
)) * 20;
194 p_BarGraph
->i_values
[p_BarGraph
->nbChannels
-1] = VLC_CLIP(iec_scale(db
)*p_BarGraph
->scale
, 0, p_BarGraph
->scale
);
195 res
= strtok_r(NULL
, delim
, &tok
);
201 static const uint8_t bright_red
[4] = { 76, 85, 0xff, 0xff };
202 static const uint8_t black
[4] = { 0x00, 0x80, 0x80, 0xff };
203 static const uint8_t white
[4] = { 0xff, 0x80, 0x80, 0xff };
204 static const uint8_t bright_green
[4] = { 150, 44, 21, 0xff };
205 static const uint8_t bright_yellow
[4] = { 226, 1, 148, 0xff };
206 static const uint8_t green
[4] = { 74, 85, 74, 0xff };
207 static const uint8_t yellow
[4] = { 112, 64, 138, 0xff };
208 static const uint8_t red
[4] = { 37, 106, 191, 0xff };
210 static inline void DrawHLine(plane_t
*p
, int line
, int col
, const uint8_t color
[4], int w
)
212 for (int j
= 0; j
< 4; j
++)
213 memset(&p
[j
].p_pixels
[line
* p
[j
].i_pitch
+ col
], color
[j
], w
);
216 static void Draw2VLines(plane_t
*p
, int scale
, int col
, const uint8_t color
[4])
218 for (int i
= 10; i
< scale
+ 10; i
++)
219 DrawHLine(p
, i
, col
, color
, 2);
222 static void DrawHLines(plane_t
*p
, int line
, int col
, const uint8_t color
[4], int h
, int w
)
224 for (int i
= line
; i
< line
+ h
; i
++)
225 DrawHLine(p
, i
, col
, color
, w
);
228 static void DrawNumber(plane_t
*p
, int h
, const uint8_t data
[5], int l
)
230 for (int i
= 0; i
< 5; i
++) {
232 for (int j
= 0; j
< 7; j
++) {
235 DrawHLine(p
, h
- l
+ 2 - 1 - i
, 12 + j
, black
, 1);
239 /*****************************************************************************
240 * Draw: creates and returns the bar graph image
241 *****************************************************************************/
242 static void Draw(BarGraph_t
*b
)
244 int nbChannels
= b
->nbChannels
;
245 int scale
= b
->scale
;
246 int barWidth
= b
->barWidth
;
250 w
= 2 * nbChannels
* barWidth
+ 30;
254 for (int i
= 0; i
< 6; i
++)
255 level
[i
] = iec_scale(-(i
+1) * 10) * scale
+ 20;
258 picture_Release(b
->p_pic
);
259 b
->p_pic
= picture_New(VLC_FOURCC('Y','U','V','A'), w
, h
, 1, 1);
262 picture_t
*p_pic
= b
->p_pic
;
263 plane_t
*p
= p_pic
->p
;
265 for (int i
= 0 ; i
< p_pic
->i_planes
; i
++)
266 memset(p
[i
].p_pixels
, 0x00, p
[i
].i_visible_lines
* p
[i
].i_pitch
);
268 Draw2VLines(p
, scale
, 20, black
);
269 Draw2VLines(p
, scale
, 22, white
);
271 static const uint8_t pixmap
[6][5] = {
272 { 0x17, 0x15, 0x15, 0x15, 0x17 },
273 { 0x77, 0x45, 0x75, 0x15, 0x77 },
274 { 0x77, 0x15, 0x75, 0x15, 0x77 },
275 { 0x17, 0x15, 0x75, 0x55, 0x57 },
276 { 0x77, 0x15, 0x75, 0x45, 0x77 },
277 { 0x77, 0x55, 0x75, 0x45, 0x77 },
280 for (int i
= 0; i
< 6; i
++) {
281 DrawHLines(p
, h
- 1 - level
[i
] - 1, 24, white
, 1, 3);
282 DrawHLines(p
, h
- 1 - level
[i
], 24, black
, 2, 3);
283 DrawNumber(p
, h
, pixmap
[i
], level
[i
]);
286 int minus8
= iec_scale(- 8) * scale
+ 20;
287 int minus18
= iec_scale(-18) * scale
+ 20;
288 int *i_values
= b
->i_values
;
289 const uint8_t *indicator_color
= b
->alarm
? bright_red
: black
;
291 for (int i
= 0; i
< nbChannels
; i
++) {
292 int pi
= 30 + i
* (5 + barWidth
);
294 DrawHLines(p
, h
- 20 - 1, pi
, indicator_color
, 8, barWidth
);
296 for (int line
= 20; line
< i_values
[i
] + 20; line
++) {
298 DrawHLines(p
, h
- line
- 1, pi
, bright_green
, 1, barWidth
);
299 else if (line
< minus8
)
300 DrawHLines(p
, h
- line
- 1, pi
, bright_yellow
, 1, barWidth
);
302 DrawHLines(p
, h
- line
- 1, pi
, bright_red
, 1, barWidth
);
305 for (int line
= i_values
[i
] + 20; line
< scale
+ 20; line
++) {
307 DrawHLines(p
, h
- line
- 1, pi
, green
, 1, barWidth
);
308 else if (line
< minus8
)
309 DrawHLines(p
, h
- line
- 1, pi
, yellow
, 1, barWidth
);
311 DrawHLines(p
, h
- line
- 1, pi
, red
, 1, barWidth
);
316 /*****************************************************************************
317 * Callback to update params on the fly
318 *****************************************************************************/
319 static int BarGraphCallback(vlc_object_t
*p_this
, char const *psz_var
,
320 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
322 VLC_UNUSED(p_this
); VLC_UNUSED(oldval
);
323 filter_sys_t
*p_sys
= p_data
;
324 BarGraph_t
*p_BarGraph
= &p_sys
->p_BarGraph
;
326 vlc_mutex_lock(&p_sys
->lock
);
327 if (!strcmp(psz_var
, CFG_PREFIX
"x"))
328 p_sys
->i_pos_x
= newval
.i_int
;
329 else if (!strcmp(psz_var
, CFG_PREFIX
"y"))
330 p_sys
->i_pos_y
= newval
.i_int
;
331 else if (!strcmp(psz_var
, CFG_PREFIX
"position"))
332 p_sys
->i_pos
= newval
.i_int
;
333 else if (!strcmp(psz_var
, CFG_PREFIX
"transparency"))
334 p_BarGraph
->i_alpha
= VLC_CLIP(newval
.i_int
, 0, 255);
335 else if (!strcmp(psz_var
, CFG_PREFIX
"i_values")) {
336 if (newval
.psz_string
)
337 parse_i_values(p_BarGraph
, newval
.psz_string
);
339 } else if (!strcmp(psz_var
, CFG_PREFIX
"alarm")) {
340 p_BarGraph
->alarm
= newval
.b_bool
;
342 } else if (!strcmp(psz_var
, CFG_PREFIX
"barWidth")) {
343 p_BarGraph
->barWidth
= newval
.i_int
;
345 } else if (!strcmp(psz_var
, CFG_PREFIX
"barHeight")) {
346 p_BarGraph
->scale
= newval
.i_int
;
349 p_sys
->b_spu_update
= true;
350 vlc_mutex_unlock(&p_sys
->lock
);
358 static subpicture_t
*FilterSub(filter_t
*p_filter
, vlc_tick_t date
)
360 filter_sys_t
*p_sys
= p_filter
->p_sys
;
361 BarGraph_t
*p_BarGraph
= &(p_sys
->p_BarGraph
);
364 subpicture_region_t
*p_region
;
368 vlc_mutex_lock(&p_sys
->lock
);
369 /* Basic test: b_spu_update occurs on a dynamic change */
370 if (!p_sys
->b_spu_update
) {
371 vlc_mutex_unlock(&p_sys
->lock
);
375 p_pic
= p_BarGraph
->p_pic
;
377 /* Allocate the subpicture internal data. */
378 p_spu
= filter_NewSubpicture(p_filter
);
382 p_spu
->b_absolute
= p_sys
->b_absolute
;
383 p_spu
->i_start
= date
;
385 p_spu
->b_ephemer
= true;
387 /* Send an empty subpicture to clear the display when needed */
388 if (!p_pic
|| !p_BarGraph
->i_alpha
)
391 /* Create new SPU region */
392 video_format_Init(&fmt
, VLC_CODEC_YUVA
);
393 fmt
.i_sar_num
= fmt
.i_sar_den
= 1;
394 fmt
.i_width
= fmt
.i_visible_width
= p_pic
->p
[Y_PLANE
].i_visible_pitch
;
395 fmt
.i_height
= fmt
.i_visible_height
= p_pic
->p
[Y_PLANE
].i_visible_lines
;
396 fmt
.i_x_offset
= fmt
.i_y_offset
= 0;
397 p_region
= subpicture_region_New(&fmt
);
399 msg_Err(p_filter
, "cannot allocate SPU region");
400 subpicture_Delete(p_spu
);
406 picture_Copy(p_region
->p_picture
, p_pic
);
408 /* where to locate the bar graph: */
409 if (p_sys
->i_pos
< 0) { /* set to an absolute xy */
410 p_region
->i_align
= SUBPICTURE_ALIGN_RIGHT
| SUBPICTURE_ALIGN_TOP
;
411 p_spu
->b_absolute
= true;
412 } else { /* set to one of the 9 relative locations */
413 p_region
->i_align
= p_sys
->i_pos
;
414 p_spu
->b_absolute
= false;
417 p_region
->i_x
= p_sys
->i_pos_x
;
418 p_region
->i_y
= p_sys
->i_pos_y
;
420 p_spu
->p_region
= p_region
;
422 p_spu
->i_alpha
= p_BarGraph
->i_alpha
;
425 vlc_mutex_unlock(&p_sys
->lock
);
433 static picture_t
*FilterVideo(filter_t
*p_filter
, picture_t
*p_src
)
435 filter_sys_t
*p_sys
= p_filter
->p_sys
;
436 BarGraph_t
*p_BarGraph
= &(p_sys
->p_BarGraph
);
438 picture_t
*p_dst
= filter_NewPicture(p_filter
);
440 picture_Release(p_src
);
444 picture_Copy(p_dst
, p_src
);
447 vlc_mutex_lock(&p_sys
->lock
);
450 const picture_t
*p_pic
= p_BarGraph
->p_pic
;
454 const video_format_t
*p_fmt
= &p_pic
->format
;
455 const int i_dst_w
= p_filter
->fmt_out
.video
.i_visible_width
;
456 const int i_dst_h
= p_filter
->fmt_out
.video
.i_visible_height
;
459 if (p_sys
->i_pos
& SUBPICTURE_ALIGN_BOTTOM
)
460 p_sys
->i_pos_y
= i_dst_h
- p_fmt
->i_visible_height
;
461 else if (!(p_sys
->i_pos
& SUBPICTURE_ALIGN_TOP
))
462 p_sys
->i_pos_y
= (i_dst_h
- p_fmt
->i_visible_height
) / 2;
466 if (p_sys
->i_pos
& SUBPICTURE_ALIGN_RIGHT
)
467 p_sys
->i_pos_x
= i_dst_w
- p_fmt
->i_visible_width
;
468 else if (!(p_sys
->i_pos
& SUBPICTURE_ALIGN_LEFT
))
469 p_sys
->i_pos_x
= (i_dst_w
- p_fmt
->i_visible_width
) / 2;
475 const int i_alpha
= p_BarGraph
->i_alpha
;
476 if (filter_ConfigureBlend(p_sys
->p_blend
, i_dst_w
, i_dst_h
, p_fmt
) ||
477 filter_Blend(p_sys
->p_blend
, p_dst
, p_sys
->i_pos_x
, p_sys
->i_pos_y
,
479 msg_Err(p_filter
, "failed to blend a picture");
482 vlc_mutex_unlock(&p_sys
->lock
);
484 picture_Release(p_src
);
488 static const struct vlc_filter_operations filter_sub_ops
= {
489 .source_sub
= FilterSub
, .close
= Close
492 static const struct vlc_filter_operations filter_video_ops
= {
493 .filter_video
= FilterVideo
, .close
= Close
,
497 * Common open function
499 static int OpenCommon(filter_t
*p_filter
, bool b_sub
)
504 if (!b_sub
&& !es_format_IsSimilar(&p_filter
->fmt_in
, &p_filter
->fmt_out
)) {
505 msg_Err(p_filter
, "Input and output format does not match");
511 p_filter
->p_sys
= p_sys
= malloc(sizeof(*p_sys
));
516 p_sys
->p_blend
= NULL
;
518 p_sys
->p_blend
= filter_NewBlend(VLC_OBJECT(p_filter
),
519 &p_filter
->fmt_in
.video
);
520 if (!p_sys
->p_blend
) {
527 config_ChainParse(p_filter
, CFG_PREFIX
, ppsz_filter_options
,
530 /* create and initialize variables */
531 p_sys
->i_pos
= var_CreateGetInteger(p_filter
, CFG_PREFIX
"position");
532 p_sys
->i_pos_x
= var_CreateGetInteger(p_filter
, CFG_PREFIX
"x");
533 p_sys
->i_pos_y
= var_CreateGetInteger(p_filter
, CFG_PREFIX
"y");
534 BarGraph_t
*p_BarGraph
= &p_sys
->p_BarGraph
;
535 p_BarGraph
->p_pic
= NULL
;
536 p_BarGraph
->i_alpha
= var_CreateGetInteger(p_filter
, CFG_PREFIX
"transparency");
537 p_BarGraph
->i_alpha
= VLC_CLIP(p_BarGraph
->i_alpha
, 0, 255);
538 p_BarGraph
->i_values
= NULL
;
539 parse_i_values(p_BarGraph
, &(char){ 0 });
540 p_BarGraph
->alarm
= false;
542 p_BarGraph
->barWidth
= var_CreateGetInteger(p_filter
, CFG_PREFIX
"barWidth");
543 p_BarGraph
->scale
= var_CreateGetInteger( p_filter
, CFG_PREFIX
"barHeight");
545 /* Ignore aligment if a position is given for video filter */
546 if (!b_sub
&& p_sys
->i_pos_x
>= 0 && p_sys
->i_pos_y
>= 0)
549 vlc_object_t
*vlc
= VLC_OBJECT(vlc_object_instance(p_filter
));
551 vlc_mutex_init(&p_sys
->lock
);
552 var_Create(vlc
, CFG_PREFIX
"alarm", VLC_VAR_BOOL
);
553 var_Create(vlc
, CFG_PREFIX
"i_values", VLC_VAR_STRING
);
555 var_AddCallback(vlc
, CFG_PREFIX
"alarm", BarGraphCallback
, p_sys
);
556 var_AddCallback(vlc
, CFG_PREFIX
"i_values", BarGraphCallback
, p_sys
);
558 var_TriggerCallback(vlc
, CFG_PREFIX
"alarm");
559 var_TriggerCallback(vlc
, CFG_PREFIX
"i_values");
561 for (int i
= 0; ppsz_filter_callbacks
[i
]; i
++)
562 var_AddCallback(p_filter
, ppsz_filter_callbacks
[i
],
563 BarGraphCallback
, p_sys
);
566 p_filter
->ops
= &filter_sub_ops
;
568 p_filter
->ops
= &filter_video_ops
;
574 * Open the sub source
576 static int OpenSub(filter_t
*p_filter
)
578 return OpenCommon(p_filter
, true);
582 * Open the video filter
584 static int OpenVideo(filter_t
*p_filter
)
586 return OpenCommon(p_filter
, false);
590 * Common close function
592 static void Close(filter_t
*p_filter
)
594 filter_sys_t
*p_sys
= p_filter
->p_sys
;
595 vlc_object_t
*vlc
= VLC_OBJECT(vlc_object_instance(p_filter
));
597 for (int i
= 0; ppsz_filter_callbacks
[i
]; i
++)
598 var_DelCallback(p_filter
, ppsz_filter_callbacks
[i
],
599 BarGraphCallback
, p_sys
);
601 var_DelCallback(vlc
, CFG_PREFIX
"i_values", BarGraphCallback
, p_sys
);
602 var_DelCallback(vlc
, CFG_PREFIX
"alarm", BarGraphCallback
, p_sys
);
603 var_Destroy(vlc
, CFG_PREFIX
"i_values");
604 var_Destroy(vlc
, CFG_PREFIX
"alarm");
607 filter_DeleteBlend(p_sys
->p_blend
);
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
);