1 /*****************************************************************************
2 * video_epg.c : EPG manipulation functions
3 *****************************************************************************
4 * Copyright (C) 2010 Adrien Maglo
5 * 2017 VLC authors, VideoLAN and VideoLabs
7 * Author: Adrien Maglo <magsoft@videolan.org>
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 *****************************************************************************/
30 #include <vlc_common.h>
32 #include <vlc_vout_osd.h>
33 #include <vlc_events.h>
34 #include <vlc_input_item.h>
37 #include "vout_spuregion_helper.h"
39 /* Layout percentage defines */
40 #define OSDEPG_MARGIN 0.025
41 #define OSDEPG_MARGINS (OSDEPG_MARGIN * 2)
42 #define OSDEPG_PADDING 0.05 /* inner margins */
43 #define OSDEPG_WIDTH (1.0 - OSDEPG_MARGINS)
44 #define OSDEPG_HEIGHT 0.25
45 #define OSDEPG_LEFT OSDEPG_MARGIN
46 #define OSDEPG_TOP (1.0 - OSDEPG_MARGINS - OSDEPG_HEIGHT + OSDEPG_MARGIN)
49 #define OSDEPG_ROWS_COUNT 10
50 #define OSDEPG_ROW_HEIGHT (1.0 / OSDEPG_ROWS_COUNT)
51 #define OSDEPG_LOGO_SIZE (OSDEPG_HEIGHT)
54 #define OSDEPG_RIGHT (1.0 - OSDEPG_MARGIN)
56 #define OSDEPG_ROWS(x) (OSDEPG_ROW_HEIGHT * x)
57 #define OSDEPG_ROW(x) (OSDEPG_ROWS(x))
59 #define EPGOSD_TEXTSIZE_NAME (OSDEPG_ROWS(2))
60 #define EPGOSD_TEXTSIZE_PROG (OSDEPG_ROWS(2))
61 #define EPGOSD_TEXTSIZE_NTWK (OSDEPG_ROWS(2))
63 //#define RGB_COLOR1 0xf48b00
64 //#define ARGB_BGCOLOR 0xC0333333
66 #define RGB_COLOR1 0x2badde
67 #define ARGB_BGCOLOR 0xc003182d
69 struct subpicture_updater_sys_t
77 static char * GetDefaultArtUri( void )
80 char *psz_path
= config_GetSysPath(VLC_SYSDATA_DIR
, "icons/hicolor/"
81 "128x128/"PACKAGE_NAME
".png");
82 if( psz_path
!= NULL
)
84 psz_uri
= vlc_path2uri( psz_path
, NULL
);
90 #define GRADIENT_COLORS 40
92 static subpicture_region_t
* vout_OSDBackground(int x
, int y
,
93 int width
, int height
,
96 /* Create a new subpicture region */
97 video_palette_t palette
;
98 spuregion_CreateVGradientPalette( &palette
, GRADIENT_COLORS
, i_argb
, 0xFF000000 );
101 video_format_Init(&fmt
, VLC_CODEC_YUVP
);
102 fmt
.i_width
= fmt
.i_visible_width
= width
;
103 fmt
.i_height
= fmt
.i_visible_height
= height
;
106 fmt
.p_palette
= &palette
;
108 subpicture_region_t
*region
= subpicture_region_New(&fmt
);
112 region
->i_align
= SUBPICTURE_ALIGN_LEFT
| SUBPICTURE_ALIGN_TOP
;
116 spuregion_CreateVGradientFill( region
->p_picture
->p
, palette
.i_entries
);
121 static subpicture_region_t
* vout_OSDEpgSlider(int x
, int y
,
122 int width
, int height
,
125 /* Create a new subpicture region */
126 video_palette_t palette
= {
129 [0] = { HEX2YUV(RGB_COLOR1
), 0x20 }, /* Bar fill remain/background */
130 [1] = { HEX2YUV(0x00ff00), 0xff },
131 [2] = { HEX2YUV(RGB_COLOR1
), 0xC0 }, /* Bar fill */
132 [3] = { HEX2YUV(0xffffff), 0xff }, /* Bar outline */
137 video_format_Init(&fmt
, VLC_CODEC_YUVP
);
138 fmt
.i_width
= fmt
.i_visible_width
= width
;
139 fmt
.i_height
= fmt
.i_visible_height
= height
;
142 fmt
.p_palette
= &palette
;
144 subpicture_region_t
*region
= subpicture_region_New(&fmt
);
148 region
->i_align
= SUBPICTURE_ALIGN_LEFT
| SUBPICTURE_ALIGN_TOP
;
152 picture_t
*picture
= region
->p_picture
;
154 ratio
= VLC_CLIP(ratio
, 0, 1);
155 int filled_part_width
= ratio
* width
;
157 for (int j
= 0; j
< height
; j
++) {
158 for (int i
= 0; i
< width
; ) {
160 bool is_outline
= j
== 0 || j
== height
- 1 ||
161 i
== 0 || i
== width
- 1;
162 /* We can see the video through the part of the slider
163 which corresponds to the leaving time. */
164 bool is_border
= j
< 3 || j
> height
- 4 ||
165 i
< 3 || i
> width
- 4 ||
166 i
< filled_part_width
;
168 uint8_t color
= 2 * is_border
+ is_outline
;
169 if(i
>= 3 && i
< width
- 4)
171 if(filled_part_width
> 4)
172 memset(&picture
->p
->p_pixels
[picture
->p
->i_pitch
* j
+ i
],
173 color
, filled_part_width
- 4);
174 if(width
> filled_part_width
+ 4)
175 memset(&picture
->p
->p_pixels
[picture
->p
->i_pitch
* j
+ filled_part_width
],
176 color
, width
- filled_part_width
- 4);
177 i
= __MAX(i
+1, filled_part_width
- 1);
181 picture
->p
->p_pixels
[picture
->p
->i_pitch
* j
+ i
] = color
;
190 static void vout_OSDSegmentSetNoWrap(text_segment_t
*p_segment
)
192 for( ; p_segment
; p_segment
= p_segment
->p_next
)
194 p_segment
->style
->e_wrapinfo
= STYLE_WRAP_NONE
;
195 p_segment
->style
->i_features
|= STYLE_HAS_WRAP_INFO
;
199 static text_segment_t
* vout_OSDSegment(const char *psz_text
, int size
, uint32_t color
)
201 text_segment_t
*p_segment
= text_segment_New(psz_text
);
202 if(unlikely(!p_segment
))
206 p_segment
->style
= text_style_Create(STYLE_NO_DEFAULTS
);
207 if (unlikely(!p_segment
->style
))
209 text_segment_Delete(p_segment
);
213 p_segment
->style
->i_font_size
= __MAX(size
,1 );
214 p_segment
->style
->i_font_color
= color
;
215 p_segment
->style
->i_font_alpha
= STYLE_ALPHA_OPAQUE
;
216 p_segment
->style
->i_outline_alpha
= STYLE_ALPHA_TRANSPARENT
;
217 p_segment
->style
->i_shadow_alpha
= STYLE_ALPHA_TRANSPARENT
;
218 p_segment
->style
->i_features
|= STYLE_HAS_FONT_ALPHA
| STYLE_HAS_FONT_COLOR
|
219 STYLE_HAS_OUTLINE_ALPHA
| STYLE_HAS_SHADOW_ALPHA
;
224 static subpicture_region_t
* vout_OSDImage( vlc_object_t
*p_obj
,
225 int x
, int y
, int w
, int h
,
226 const char *psz_uri
)
228 video_format_t fmt_out
;
229 video_format_Init( &fmt_out
, VLC_CODEC_YUVA
);
230 fmt_out
.i_width
= fmt_out
.i_visible_width
= w
;
231 fmt_out
.i_height
= fmt_out
.i_visible_height
= h
;
233 subpicture_region_t
*image
=
234 spuregion_CreateFromPicture( p_obj
, &fmt_out
, psz_uri
);
239 image
->i_align
= SUBPICTURE_ALIGN_LEFT
|SUBPICTURE_ALIGN_TOP
;
244 static void vout_OSDRegionConstrain(subpicture_region_t
*p_region
, int w
, int h
)
248 p_region
->i_max_width
= w
;
249 p_region
->i_max_height
= h
;
253 static subpicture_region_t
* vout_OSDTextRegion(text_segment_t
*p_segment
,
257 subpicture_region_t
*region
;
262 /* Create a new subpicture region */
263 video_format_Init(&fmt
, VLC_CODEC_TEXT
);
267 region
= subpicture_region_New(&fmt
);
271 region
->p_text
= p_segment
;
272 region
->i_align
= SUBPICTURE_ALIGN_LEFT
| SUBPICTURE_ALIGN_TOP
;
273 region
->i_text_align
= SUBPICTURE_ALIGN_LEFT
| SUBPICTURE_ALIGN_TOP
;
276 region
->b_balanced_text
= false;
281 static subpicture_region_t
* vout_OSDEpgText(const char *text
,
283 int size
, uint32_t color
)
285 return vout_OSDTextRegion(vout_OSDSegment(text
, size
, color
), x
, y
);
288 static char * vout_OSDPrintTime(time_t t
)
292 localtime_r(&t
, &tms
);
293 if(asprintf(&psz
, "%2.2d:%2.2d", tms
.tm_hour
, tms
.tm_min
) < 0)
298 static subpicture_region_t
* vout_OSDEpgEvent(const vlc_epg_event_t
*p_evt
,
299 int x
, int y
, int size
)
301 text_segment_t
*p_segment
= NULL
;
302 char *psz_start
= vout_OSDPrintTime(p_evt
->i_start
);
303 char *psz_end
= vout_OSDPrintTime(p_evt
->i_start
+ p_evt
->i_duration
);
305 if( -1 < asprintf(&psz_text
, "%s-%s ", psz_start
, psz_end
))
307 p_segment
= vout_OSDSegment(psz_text
, size
, RGB_COLOR1
);
309 p_segment
->p_next
= vout_OSDSegment(p_evt
->psz_name
, size
, 0xffffff);
310 vout_OSDSegmentSetNoWrap( p_segment
);
316 return vout_OSDTextRegion(p_segment
, x
, y
);
319 static void vout_FillRightPanel(subpicture_updater_sys_t
*p_sys
,
321 int width
, int height
,
323 subpicture_region_t
**last_ptr
)
325 float f_progress
= 0;
328 /* Display the name of the channel. */
329 *last_ptr
= vout_OSDEpgText(p_sys
->epg
->psz_name
,
332 height
* EPGOSD_TEXTSIZE_NAME
,
335 last_ptr
= &(*last_ptr
)->p_next
;
337 const vlc_epg_event_t
*p_current
= p_sys
->epg
->p_current
;
338 vlc_epg_event_t
*p_next
= NULL
;
339 if(!p_sys
->epg
->p_current
&& p_sys
->epg
->i_event
)
340 p_current
= p_sys
->epg
->pp_event
[0];
342 for(size_t i
=0; i
<p_sys
->epg
->i_event
; i
++)
344 if( p_sys
->epg
->pp_event
[i
]->i_id
!= p_current
->i_id
)
346 p_next
= p_sys
->epg
->pp_event
[i
];
351 /* Display the name of the current program. */
354 *last_ptr
= vout_OSDEpgEvent(p_current
,
356 y
+ height
* OSDEPG_ROW(2),
357 height
* EPGOSD_TEXTSIZE_PROG
);
358 /* region rendering limits */
359 vout_OSDRegionConstrain(*last_ptr
, width
, 0);
361 last_ptr
= &(*last_ptr
)->p_next
;
367 *last_ptr
= vout_OSDEpgEvent(p_next
,
369 y
+ height
* OSDEPG_ROW(5),
370 height
* EPGOSD_TEXTSIZE_PROG
);
371 /* region rendering limits */
372 vout_OSDRegionConstrain(*last_ptr
, width
, 0);
374 last_ptr
= &(*last_ptr
)->p_next
;
379 f_progress
= (p_sys
->time
- p_sys
->epg
->p_current
->i_start
) /
380 (float)p_sys
->epg
->p_current
->i_duration
;
383 /* Display the current program time slider. */
384 *last_ptr
= vout_OSDEpgSlider(x
+ width
* 0.05,
385 y
+ height
* OSDEPG_ROW(9),
387 height
* OSDEPG_ROWS(1),
390 last_ptr
= &(*last_ptr
)->p_next
;
392 /* Format the hours */
395 char *psz_network
= vout_OSDPrintTime(p_sys
->time
);
398 *last_ptr
= vout_OSDEpgText(psz_network
,
400 y
+ height
* OSDEPG_ROW(0),
401 height
* EPGOSD_TEXTSIZE_NTWK
,
406 (*last_ptr
)->i_align
= SUBPICTURE_ALIGN_TOP
|SUBPICTURE_ALIGN_RIGHT
;
407 last_ptr
= &(*last_ptr
)->p_next
;
413 static subpicture_region_t
* vout_BuildOSDEpg(subpicture_updater_sys_t
*p_sys
,
418 subpicture_region_t
*head
;
419 subpicture_region_t
**last_ptr
= &head
;
421 const int i_padding
= visible_height
* (OSDEPG_HEIGHT
* OSDEPG_PADDING
);
423 *last_ptr
= vout_OSDBackground(x
+ visible_width
* OSDEPG_LEFT
,
424 y
+ visible_height
* OSDEPG_TOP
,
425 visible_width
* OSDEPG_WIDTH
,
426 visible_height
* OSDEPG_HEIGHT
,
429 last_ptr
= &(*last_ptr
)->p_next
;
440 x
+ visible_width
* OSDEPG_LEFT
+ i_padding
,
441 y
+ visible_height
* OSDEPG_TOP
+ i_padding
,
442 visible_width
* OSDEPG_WIDTH
- 2 * i_padding
,
443 visible_height
* OSDEPG_HEIGHT
- 2 * i_padding
,
444 visible_width
* OSDEPG_LEFT
+ i_padding
,
445 visible_height
* (1.0 - OSDEPG_TOP
- OSDEPG_HEIGHT
) + i_padding
,
464 *last_ptr
= vout_OSDBackground(logo
.x
,
468 0xFF000000 | RGB_COLOR1
);
470 last_ptr
= &(*last_ptr
)->p_next
;
472 int logo_padding
= visible_height
* (OSDEPG_LOGO_SIZE
* OSDEPG_PADDING
);
473 *last_ptr
= vout_OSDImage( p_sys
->obj
,
474 logo
.x
+ logo_padding
,
475 logo
.y
+ logo_padding
,
476 logo
.w
- 2 * logo_padding
,
477 logo
.h
- 2 * logo_padding
,
480 last_ptr
= &(*last_ptr
)->p_next
;
483 panel
.x
+= logo
.w
+ i_padding
;
484 panel
.w
-= logo
.w
+ i_padding
;
487 vout_FillRightPanel( p_sys
,
499 static int OSDEpgValidate(subpicture_t
*subpic
,
500 bool has_src_changed
, const video_format_t
*fmt_src
,
501 bool has_dst_changed
, const video_format_t
*fmt_dst
,
504 VLC_UNUSED(subpic
); VLC_UNUSED(ts
);
505 VLC_UNUSED(fmt_src
); VLC_UNUSED(has_src_changed
);
508 if (!has_dst_changed
)
513 static void OSDEpgUpdate(subpicture_t
*subpic
,
514 const video_format_t
*fmt_src
,
515 const video_format_t
*fmt_dst
,
518 subpicture_updater_sys_t
*sys
= subpic
->updater
.p_sys
;
519 VLC_UNUSED(fmt_src
); VLC_UNUSED(ts
);
521 video_format_t fmt
= *fmt_dst
;
522 fmt
.i_width
= fmt
.i_width
* fmt
.i_sar_num
/ fmt
.i_sar_den
;
523 fmt
.i_visible_width
= fmt
.i_visible_width
* fmt
.i_sar_num
/ fmt
.i_sar_den
;
524 fmt
.i_x_offset
= fmt
.i_x_offset
* fmt
.i_sar_num
/ fmt
.i_sar_den
;
526 subpic
->i_original_picture_width
= fmt
.i_visible_width
;
527 subpic
->i_original_picture_height
= fmt
.i_visible_height
;
529 subpic
->p_region
= vout_BuildOSDEpg(sys
,
533 fmt
.i_visible_height
);
536 static void OSDEpgDestroy(subpicture_t
*subpic
)
538 subpicture_updater_sys_t
*sys
= subpic
->updater
.p_sys
;
540 vlc_epg_Delete(sys
->epg
);
546 * \brief Show EPG information about the current program of an input item
547 * \param vout pointer to the vout the information is to be showed on
548 * \param p_input pointer to the input item the information is to be showed
549 * \param i_action osd_epg_action_e action
551 int vout_OSDEpg(vout_thread_t
*vout
, input_item_t
*input
)
553 vlc_epg_t
*epg
= NULL
;
556 /* Look for the current program EPG event */
557 vlc_mutex_lock(&input
->lock
);
559 const vlc_epg_t
*tmp
= input
->p_epg_table
;
562 /* Pick table designated event, or first/next one */
563 const vlc_epg_event_t
*p_current_event
= tmp
->p_current
;
564 epg
= vlc_epg_New(tmp
->i_id
, tmp
->i_source_id
);
567 if( p_current_event
)
569 vlc_epg_event_t
*p_event
= vlc_epg_event_Duplicate(p_current_event
);
572 if(!vlc_epg_AddEvent(epg
, p_event
))
573 vlc_epg_event_Delete(p_event
);
575 vlc_epg_SetCurrent(epg
, p_event
->i_start
);
579 /* Add next event if any */
580 vlc_epg_event_t
*p_next
= NULL
;
581 for(size_t i
=0; i
<tmp
->i_event
; i
++)
583 vlc_epg_event_t
*p_evt
= tmp
->pp_event
[i
];
584 if((!p_next
|| p_next
->i_start
> p_evt
->i_start
) &&
585 (!p_current_event
|| (p_evt
->i_id
!= p_current_event
->i_id
&&
586 p_evt
->i_start
>= p_current_event
->i_start
+
587 p_current_event
->i_duration
)))
589 p_next
= tmp
->pp_event
[i
];
594 vlc_epg_event_t
*p_event
= vlc_epg_event_Duplicate(p_next
);
595 if(!vlc_epg_AddEvent(epg
, p_event
))
596 vlc_epg_event_Delete(p_event
);
601 epg
->psz_name
= strdup(tmp
->psz_name
);
610 epg_time
= input
->i_epg_time
;
611 vlc_mutex_unlock(&input
->lock
);
613 /* If no EPG event has been found. */
617 if(epg
->psz_name
== NULL
) /* Fallback (title == channel name) */
618 epg
->psz_name
= input_item_GetMeta( input
, vlc_meta_Title
);
620 subpicture_updater_sys_t
*sys
= malloc(sizeof(*sys
));
626 sys
->obj
= VLC_OBJECT(vout
);
627 sys
->time
= epg_time
;
628 sys
->art
= input_item_GetMeta( input
, vlc_meta_ArtworkURL
);
630 sys
->art
= GetDefaultArtUri();
632 subpicture_updater_t updater
= {
633 .pf_validate
= OSDEpgValidate
,
634 .pf_update
= OSDEpgUpdate
,
635 .pf_destroy
= OSDEpgDestroy
,
639 const mtime_t now
= mdate();
640 subpicture_t
*subpic
= subpicture_New(&updater
);
642 vlc_epg_Delete(sys
->epg
);
647 subpic
->i_channel
= VOUT_SPU_CHANNEL_OSD
;
648 subpic
->i_start
= now
;
649 subpic
->i_stop
= now
+ 3000 * INT64_C(1000);
650 subpic
->b_ephemer
= true;
651 subpic
->b_absolute
= false;
652 subpic
->b_fade
= true;
653 subpic
->b_subtitle
= false;
655 vout_PutSubpicture(vout
, subpic
);