From f25cc7fb4c732184db80543d045f29bdeec70de3 Mon Sep 17 00:00:00 2001 From: Francois Cartegnie Date: Tue, 30 May 2017 17:42:19 +0200 Subject: [PATCH] vout: refresh EPG OSD --- src/Makefile.am | 1 + src/video_output/video_epg.c | 392 +++++++++++++++++++++++++------ src/video_output/vout_spuregion_helper.h | 94 ++++++++ 3 files changed, 411 insertions(+), 76 deletions(-) create mode 100644 src/video_output/vout_spuregion_helper.h diff --git a/src/Makefile.am b/src/Makefile.am index 9712483bb8..87ea540f69 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -287,6 +287,7 @@ libvlccore_la_SOURCES = \ video_output/video_epg.c \ video_output/video_widgets.c \ video_output/vout_subpictures.c \ + video_output/vout_spuregion_helper.h \ video_output/window.c \ video_output/window.h \ video_output/opengl.c \ diff --git a/src/video_output/video_epg.c b/src/video_output/video_epg.c index 7eb1cc8b9b..9576eb0682 100644 --- a/src/video_output/video_epg.c +++ b/src/video_output/video_epg.c @@ -2,6 +2,7 @@ * video_epg.c : EPG manipulation functions ***************************************************************************** * Copyright (C) 2010 Adrien Maglo + * 2017 VLC authors, VideoLAN and VideoLabs * * Author: Adrien Maglo * @@ -32,13 +33,32 @@ #include #include #include +#include +#include "vout_spuregion_helper.h" /* Layout percentage defines */ -#define EPG_TOP 0.7 -#define EPG_LEFT 0.1 -#define EPG_NAME_SIZE 0.05 -#define EPG_PROGRAM_SIZE 0.03 -#define EPG_TIME_SIZE 0.03 +#define OSDEPG_MARGIN 0.025 +#define OSDEPG_MARGINS (OSDEPG_MARGIN * 2) +#define OSDEPG_PADDING 0.05 /* inner margins */ +#define OSDEPG_WIDTH (1.0 - OSDEPG_MARGINS) +#define OSDEPG_HEIGHT 0.25 +#define OSDEPG_LEFT OSDEPG_MARGIN +#define OSDEPG_TOP (1.0 - OSDEPG_MARGINS - OSDEPG_HEIGHT + OSDEPG_MARGIN) + +/* layout */ +#define OSDEPG_ROWS_COUNT 10 +#define OSDEPG_ROW_HEIGHT (1.0 / OSDEPG_ROWS_COUNT) +#define OSDEPG_LOGO_SIZE (OSDEPG_HEIGHT) + +/* shortcuts */ +#define OSDEPG_RIGHT (1.0 - OSDEPG_MARGIN) + +#define OSDEPG_ROWS(x) (OSDEPG_ROW_HEIGHT * x) +#define OSDEPG_ROW(x) (OSDEPG_ROWS(x)) + +#define EPGOSD_TEXTSIZE_NAME (OSDEPG_ROWS(2)) +#define EPGOSD_TEXTSIZE_PROG (OSDEPG_ROWS(2)) +#define EPGOSD_TEXTSIZE_NTWK (OSDEPG_ROWS(2)) #define RGB2YUV( R, G, B ) \ ((0.257 * R) + (0.504 * G) + (0.098 * B) + 16), \ @@ -48,6 +68,65 @@ #define HEX2YUV( rgb ) \ RGB2YUV( (rgb >> 16), ((rgb & 0xFF00) >> 8), (rgb & 0xFF) ) +//#define RGB_COLOR1 0xf48b00 +//#define ARGB_BGCOLOR 0xC0333333 + +#define RGB_COLOR1 0x2badde +#define ARGB_BGCOLOR 0xc003182d + +struct subpicture_updater_sys_t +{ + vlc_epg_t *epg; + int64_t time; + char *art; + vlc_object_t *obj; +}; + +static char * GetDefaultArtUri( void ) +{ + char *psz_uri = NULL; + char *psz_path; + char *psz_datadir = config_GetDataDir(); + if( asprintf( &psz_path, "%s/icons/128x128/vlc.png", psz_datadir ) >= 0 ) + { + psz_uri = vlc_path2uri( psz_path, NULL ); + free( psz_path ); + } + free( psz_datadir ); + return psz_uri; +} + +#define GRADIENT_COLORS 40 + +static subpicture_region_t * vout_OSDBackground(int x, int y, + int width, int height, + uint32_t i_argb) +{ + /* Create a new subpicture region */ + video_palette_t palette; + spuregion_CreateVGradientPalette( &palette, GRADIENT_COLORS, i_argb, 0xFF000000 ); + + video_format_t fmt; + video_format_Init(&fmt, VLC_CODEC_YUVP); + fmt.i_width = fmt.i_visible_width = width; + fmt.i_height = fmt.i_visible_height = height; + fmt.i_sar_num = 1; + fmt.i_sar_den = 1; + fmt.p_palette = &palette; + + subpicture_region_t *region = subpicture_region_New(&fmt); + if (!region) + return NULL; + + region->i_align = SUBPICTURE_ALIGN_LEFT | SUBPICTURE_ALIGN_TOP; + region->i_x = x; + region->i_y = y; + + spuregion_CreateVGradientFill( region->p_picture->p, palette.i_entries ); + + return region; +} + static subpicture_region_t * vout_OSDEpgSlider(int x, int y, int width, int height, float ratio) @@ -56,10 +135,10 @@ static subpicture_region_t * vout_OSDEpgSlider(int x, int y, video_palette_t palette = { .i_entries = 4, .palette = { - [0] = { HEX2YUV(0xffffff), 0x00 }, /* Bar fill remain/background */ - [1] = { HEX2YUV(0x000000), 0x00 }, - [2] = { HEX2YUV(0xffffff), 0xff }, /* Bar fill */ - [3] = { HEX2YUV(0x000000), 0xff }, /* Bar outline */ + [0] = { HEX2YUV(RGB_COLOR1), 0x20 }, /* Bar fill remain/background */ + [1] = { HEX2YUV(0x00ff00), 0xff }, + [2] = { HEX2YUV(RGB_COLOR1), 0xC0 }, /* Bar fill */ + [3] = { HEX2YUV(0xffffff), 0xff }, /* Bar outline */ }, }; @@ -75,6 +154,7 @@ static subpicture_region_t * vout_OSDEpgSlider(int x, int y, if (!region) return NULL; + region->i_align = SUBPICTURE_ALIGN_LEFT | SUBPICTURE_ALIGN_TOP; region->i_x = x; region->i_y = y; @@ -101,6 +181,15 @@ static subpicture_region_t * vout_OSDEpgSlider(int x, int y, return region; } +static void vout_OSDSegmentSetNoWrap(text_segment_t *p_segment) +{ + for( ; p_segment; p_segment = p_segment->p_next ) + { + p_segment->style->e_wrapinfo = STYLE_WRAP_NONE; + p_segment->style->i_features |= STYLE_HAS_WRAP_INFO; + } +} + static text_segment_t * vout_OSDSegment(const char *psz_text, int size, uint32_t color) { text_segment_t *p_segment = text_segment_New(psz_text); @@ -118,11 +207,43 @@ static text_segment_t * vout_OSDSegment(const char *psz_text, int size, uint32_t p_segment->style->i_font_size = __MAX(size ,1 ); p_segment->style->i_font_color = color; p_segment->style->i_font_alpha = STYLE_ALPHA_OPAQUE; - p_segment->style->i_features |= STYLE_HAS_FONT_ALPHA | STYLE_HAS_FONT_COLOR; + p_segment->style->i_outline_alpha = STYLE_ALPHA_TRANSPARENT; + p_segment->style->i_shadow_alpha = STYLE_ALPHA_TRANSPARENT; + p_segment->style->i_features |= STYLE_HAS_FONT_ALPHA | STYLE_HAS_FONT_COLOR | + STYLE_HAS_OUTLINE_ALPHA | STYLE_HAS_SHADOW_ALPHA; return p_segment; } +static subpicture_region_t * vout_OSDImage( vlc_object_t *p_obj, + int x, int y, int w, int h, + const char *psz_uri ) +{ + video_format_t fmt_out; + video_format_Init( &fmt_out, VLC_CODEC_YUVA ); + fmt_out.i_width = fmt_out.i_visible_width = w; + fmt_out.i_height = fmt_out.i_visible_height = h; + + subpicture_region_t *image = + spuregion_CreateFromPicture( p_obj, &fmt_out, psz_uri ); + if( image ) + { + image->i_x = x; + image->i_y = y; + image->i_align = SUBPICTURE_ALIGN_LEFT|SUBPICTURE_ALIGN_TOP; + } + return image; +} + +static void vout_OSDRegionConstrain(subpicture_region_t *p_region, int w, int h) +{ + if( p_region ) + { + p_region->i_max_width = w; + p_region->i_max_height = h; + } +} + static subpicture_region_t * vout_OSDTextRegion(text_segment_t *p_segment, int x, int y ) { @@ -145,6 +266,7 @@ static subpicture_region_t * vout_OSDTextRegion(text_segment_t *p_segment, region->i_align = SUBPICTURE_ALIGN_LEFT | SUBPICTURE_ALIGN_TOP; region->i_x = x; region->i_y = y; + region->b_balanced_text = false; return region; } @@ -166,91 +288,203 @@ static char * vout_OSDPrintTime(time_t t) return psz; } -static subpicture_region_t * vout_BuildOSDEpg(vlc_epg_t *epg, - int64_t epgtime, - int x, int y, - int visible_width, - int visible_height) +static subpicture_region_t * vout_OSDEpgEvent(const vlc_epg_event_t *p_evt, + int x, int y, int size) { - subpicture_region_t *head; - subpicture_region_t **last_ptr = &head; + text_segment_t *p_segment = NULL; + char *psz_start = vout_OSDPrintTime(p_evt->i_start); + char *psz_end = vout_OSDPrintTime(p_evt->i_start + p_evt->i_duration); + char *psz_text; + if( -1 < asprintf(&psz_text, "%s-%s ", psz_start, psz_end)) + { + p_segment = vout_OSDSegment(psz_text, size, RGB_COLOR1); + if( p_segment ) + p_segment->p_next = vout_OSDSegment(p_evt->psz_name, size, 0xffffff); + vout_OSDSegmentSetNoWrap( p_segment ); + } + free( psz_start ); + free( psz_end ); + if(!p_segment) + return NULL; + return vout_OSDTextRegion(p_segment, x, y); +} +static void vout_FillRightPanel(subpicture_updater_sys_t *p_sys, + int x, int y, + int width, int height, + int rx, int ry, + subpicture_region_t **last_ptr) +{ float f_progress = 0; + VLC_UNUSED(ry); + + /* Format the hours */ + char *psz_network = vout_OSDPrintTime(p_sys->time); /* Display the name of the channel. */ - *last_ptr = vout_OSDEpgText(epg->psz_name, - x + visible_width * EPG_LEFT, - y + visible_height * EPG_TOP, - visible_height * EPG_NAME_SIZE, + *last_ptr = vout_OSDEpgText(p_sys->epg->psz_name, + x, + y, + height * EPGOSD_TEXTSIZE_NAME, 0x00ffffff); + if(*last_ptr) + last_ptr = &(*last_ptr)->p_next; - if (!*last_ptr) - return head; + const vlc_epg_event_t *p_current = p_sys->epg->p_current; + vlc_epg_event_t *p_next = NULL; + if(!p_sys->epg->p_current && p_sys->epg->i_event) + p_current = p_sys->epg->pp_event[0]; + + for(size_t i=0; iepg->i_event; i++) + { + if( p_sys->epg->pp_event[i]->i_id != p_current->i_id ) + { + p_next = p_sys->epg->pp_event[i]; + break; + } + } /* Display the name of the current program. */ - last_ptr = &(*last_ptr)->p_next; - *last_ptr = vout_OSDEpgText(epg->p_current ? epg->p_current->psz_name : NULL, - x + visible_width * (EPG_LEFT + 0.025), - y + visible_height * (EPG_TOP + 0.05), - visible_height * EPG_PROGRAM_SIZE, - 0x00ffffff); + if(p_current) + { + *last_ptr = vout_OSDEpgEvent(p_current, + x, + y + height * OSDEPG_ROW(2), + height * EPGOSD_TEXTSIZE_PROG); + /* region rendering limits */ + vout_OSDRegionConstrain(*last_ptr, width, 0); + if(*last_ptr) + last_ptr = &(*last_ptr)->p_next; + } - if (!*last_ptr || !epg->p_current) - return head; + /* NEXT EVENT */ + if(p_next) + { + *last_ptr = vout_OSDEpgEvent(p_next, + x, + y + height * OSDEPG_ROW(5), + height * EPGOSD_TEXTSIZE_PROG); + /* region rendering limits */ + vout_OSDRegionConstrain(*last_ptr, width, 0); + if(*last_ptr) + last_ptr = &(*last_ptr)->p_next; + } - if(epgtime) + if(p_sys->time) { - f_progress = (epgtime - epg->p_current->i_start) / - (float)epg->p_current->i_duration; + f_progress = (p_sys->time - p_sys->epg->p_current->i_start) / + (float)p_sys->epg->p_current->i_duration; } /* Display the current program time slider. */ - last_ptr = &(*last_ptr)->p_next; - *last_ptr = vout_OSDEpgSlider(x + visible_width * EPG_LEFT, - y + visible_height * (EPG_TOP + 0.1), - visible_width * (1 - 2 * EPG_LEFT), - visible_height * 0.05, + *last_ptr = vout_OSDEpgSlider(x + width * 0.05, + y + height * OSDEPG_ROW(9), + width * 0.90, + height * OSDEPG_ROWS(1), f_progress); + if (*last_ptr) + last_ptr = &(*last_ptr)->p_next; + + *last_ptr = vout_OSDEpgText(psz_network, + rx, + y + height * OSDEPG_ROW(0), + height * EPGOSD_TEXTSIZE_NTWK, + RGB_COLOR1); + if(*last_ptr) + { + (*last_ptr)->i_align = SUBPICTURE_ALIGN_TOP|SUBPICTURE_ALIGN_RIGHT; + last_ptr = &(*last_ptr)->p_next; + } - if (!*last_ptr) - return head; + free(psz_network); +} - /* Format the hours of the beginning and the end of the current program. */ - char *psz_start = vout_OSDPrintTime(epg->p_current->i_start); - char *psz_end = vout_OSDPrintTime(epg->p_current->i_start + - epg->p_current->i_duration); +static subpicture_region_t * vout_BuildOSDEpg(subpicture_updater_sys_t *p_sys, + int x, int y, + int visible_width, + int visible_height) +{ + subpicture_region_t *head; + subpicture_region_t **last_ptr = &head; - /* Display those hours. */ - last_ptr = &(*last_ptr)->p_next; - *last_ptr = vout_OSDEpgText(psz_start, - x + visible_width * (EPG_LEFT + 0.02), - y + visible_height * (EPG_TOP + 0.15), - visible_height * EPG_TIME_SIZE, - 0x00ffffff); + const int i_padding = visible_height * (OSDEPG_HEIGHT * OSDEPG_PADDING); - if (!*last_ptr) - goto end; + *last_ptr = vout_OSDBackground(x + visible_width * OSDEPG_LEFT, + y + visible_height * OSDEPG_TOP, + visible_width * OSDEPG_WIDTH, + visible_height * OSDEPG_HEIGHT, + ARGB_BGCOLOR); + if(*last_ptr) + last_ptr = &(*last_ptr)->p_next; - last_ptr = &(*last_ptr)->p_next; - *last_ptr = vout_OSDEpgText(psz_end, - x + visible_width * (1 - EPG_LEFT - 0.085), - y + visible_height * (EPG_TOP + 0.15), - visible_height * EPG_TIME_SIZE, - 0x00ffffff); + struct + { + int x; + int y; + int w; + int h; + int rx; + int ry; + } panel = { + x + visible_width * OSDEPG_LEFT + i_padding, + y + visible_height * OSDEPG_TOP + i_padding, + visible_width * OSDEPG_WIDTH - 2 * i_padding, + visible_height * OSDEPG_HEIGHT - 2 * i_padding, + visible_width * OSDEPG_LEFT + i_padding, + visible_height * (1.0 - OSDEPG_TOP - OSDEPG_HEIGHT) + i_padding, + }; + + + if( p_sys->art ) + { + struct + { + int x; + int y; + int w; + int h; + } logo = { + panel.x, + panel.y, + panel.h, + panel.h, + }; + + *last_ptr = vout_OSDBackground(logo.x, + logo.y, + logo.w, + logo.h, + 0xFF000000 | RGB_COLOR1); + if(*last_ptr) + last_ptr = &(*last_ptr)->p_next; + + int logo_padding = visible_height * (OSDEPG_LOGO_SIZE * OSDEPG_PADDING); + *last_ptr = vout_OSDImage( p_sys->obj, + logo.x + logo_padding, + logo.y + logo_padding, + logo.w - 2 * logo_padding, + logo.h - 2 * logo_padding, + p_sys->art ); + if(*last_ptr) + last_ptr = &(*last_ptr)->p_next; + + /* shrink */ + panel.x += logo.w + i_padding; + panel.w -= logo.w + i_padding; + } -end: - free(psz_start); - free(psz_end); + vout_FillRightPanel( p_sys, + panel.x, + panel.y, + panel.w, + panel.h, + panel.rx, + panel.ry, + last_ptr ); return head; } -struct subpicture_updater_sys_t -{ - vlc_epg_t *epg; - int64_t time; -}; - static int OSDEpgValidate(subpicture_t *subpic, bool has_src_changed, const video_format_t *fmt_src, bool has_dst_changed, const video_format_t *fmt_dst, @@ -281,8 +515,7 @@ static void OSDEpgUpdate(subpicture_t *subpic, subpic->i_original_picture_width = fmt.i_visible_width; subpic->i_original_picture_height = fmt.i_visible_height; - subpic->p_region = vout_BuildOSDEpg(sys->epg, - sys->time, + subpic->p_region = vout_BuildOSDEpg(sys, fmt.i_x_offset, fmt.i_y_offset, fmt.i_visible_width, @@ -292,8 +525,9 @@ static void OSDEpgUpdate(subpicture_t *subpic, static void OSDEpgDestroy(subpicture_t *subpic) { subpicture_updater_sys_t *sys = subpic->updater.p_sys; - - vlc_epg_Delete(sys->epg); + if( sys->epg ) + vlc_epg_Delete(sys->epg); + free( sys->art ); free(sys); } @@ -301,14 +535,14 @@ static void OSDEpgDestroy(subpicture_t *subpic) * \brief Show EPG information about the current program of an input item * \param vout pointer to the vout the information is to be showed on * \param p_input pointer to the input item the information is to be showed + * \param i_action osd_epg_action_e action */ -int vout_OSDEpg(vout_thread_t *vout, input_item_t *input) +int vout_OSDEpg(vout_thread_t *vout, input_item_t *input ) { vlc_epg_t *epg = NULL; int64_t epg_time; /* Look for the current program EPG event */ - vlc_mutex_lock(&input->lock); const vlc_epg_t *tmp = input->p_epg_table; @@ -378,7 +612,12 @@ int vout_OSDEpg(vout_thread_t *vout, input_item_t *input) return VLC_EGENERIC; } sys->epg = epg; + sys->obj = VLC_OBJECT(vout); sys->time = epg_time; + sys->art = input_item_GetMeta( input, vlc_meta_ArtworkURL ); + if( !sys->art ) + sys->art = GetDefaultArtUri(); + subpicture_updater_t updater = { .pf_validate = OSDEpgValidate, .pf_update = OSDEpgUpdate, @@ -398,8 +637,9 @@ int vout_OSDEpg(vout_thread_t *vout, input_item_t *input) subpic->i_start = now; subpic->i_stop = now + 3000 * INT64_C(1000); subpic->b_ephemer = true; - subpic->b_absolute = true; + subpic->b_absolute = false; subpic->b_fade = true; + subpic->b_subtitle = false; vout_PutSubpicture(vout, subpic); diff --git a/src/video_output/vout_spuregion_helper.h b/src/video_output/vout_spuregion_helper.h new file mode 100644 index 0000000000..9738e35ecd --- /dev/null +++ b/src/video_output/vout_spuregion_helper.h @@ -0,0 +1,94 @@ +/***************************************************************************** + * vout_spuregion_helper.h : vout subpicture region helpers + ***************************************************************************** + * Copyright (C) 2017 VLC authors, VideoLAN and VideoLabs + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ +#include + +#define RGB2YUV( R, G, B ) \ + ((0.257 * R) + (0.504 * G) + (0.098 * B) + 16), \ + (-(0.148 * R) - (0.291 * G) + (0.439 * B) + 128),\ + ((0.439 * R) - (0.368 * G) - (0.071 * B) + 128) + +#define HEX2YUV( rgb ) \ + RGB2YUV( (rgb >> 16), ((rgb & 0xFF00) >> 8), (rgb & 0xFF) ) + +static inline void +spuregion_CreateVGradientPalette( video_palette_t *p_palette, uint8_t i_splits, + uint32_t argb1, uint32_t argb2 ) +{ + for( uint8_t i = 0; i> 16) * (i_splits - i)) + (rgb2 >> 16) * i)) / i_splits; + uint32_t g = (((((rgb1 >> 8) & 0xFF) * (i_splits - i)) + ((rgb2 >> 8) & 0xFF) * i)) / i_splits; + uint32_t b = ((((rgb1 & 0xFF) * (i_splits - i)) + (rgb2 & 0xFF) * i)) / i_splits; + uint8_t entry[4] = { RGB2YUV( r,g,b ), argb1 >> 24 }; + memcpy( p_palette->palette[i], entry, 4 ); + } + p_palette->i_entries = i_splits; +} + +static inline void +spuregion_CreateVGradientFill( plane_t *p, uint8_t i_splits ) +{ + const int i_split = p->i_visible_lines / i_splits; + const int i_left = p->i_visible_lines % i_splits + p->i_lines - p->i_visible_lines; + for( int i = 0; ip_pixels[p->i_pitch * (i * i_split)], + i, + p->i_pitch * i_split ); + } + memset( &p->p_pixels[p->i_pitch * (i_splits - 1) * i_split], + i_splits - 1, + p->i_pitch * i_left ); +} + + +static inline subpicture_region_t * +spuregion_CreateFromPicture( vlc_object_t *p_this, video_format_t *p_fmt, + const char *psz_uri ) +{ + video_format_t fmt_in; + video_format_Init( &fmt_in, 0 ); + + picture_t *p_pic = NULL; + int i_flags = p_this->obj.flags; + p_this->obj.flags |= OBJECT_FLAGS_NOINTERACT|OBJECT_FLAGS_QUIET; + image_handler_t *p_image = image_HandlerCreate( p_this ); + if( p_image ) + { + p_pic = image_ReadUrl( p_image, psz_uri, &fmt_in, p_fmt ); + image_HandlerDelete( p_image ); + } + p_this->obj.flags = i_flags; + + if(!p_pic) + return NULL; + + subpicture_region_t *region = subpicture_region_New(p_fmt); + if (!region) + return NULL; + + picture_Release( region->p_picture ); + region->p_picture = p_pic; + + return region; +} -- 2.11.4.GIT