]> git.sesse.net Git - vlc/blob - src/video_output/video_epg.c
Reduced EPG OSD down to 3s and enable fade out.
[vlc] / src / video_output / video_epg.c
1 /*****************************************************************************
2  * video_epg.c : EPG manipulation functions
3  *****************************************************************************
4  * Copyright (C) 2010 Adrien Maglo
5  *
6  * Author: Adrien Maglo <magsoft@videolan.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include <vlc_common.h>
28 #include <vlc_vout.h>
29 #include <vlc_block.h>
30 #include <vlc_filter.h>
31 #include <vlc_osd.h>
32 #include <vlc_events.h>
33 #include <vlc_input_item.h>
34 #include <vlc_epg.h>
35
36
37 /* Layout percentage defines */
38 #define EPG_TOP 0.7
39 #define EPG_LEFT 0.1
40 #define EPG_NAME_SIZE 0.05
41 #define EPG_PROGRAM_SIZE 0.03
42
43 #define NO_EPG N_( "No EPG found for this program." )
44
45
46 static subpicture_region_t * vout_OSDEpgSlider( vout_thread_t *p_vout,
47                                                 int i_x, int i_y,
48                                                 int i_width, int i_height,
49                                                 float f_ratio )
50 {
51     video_format_t fmt;
52     subpicture_region_t *p_region;
53
54     /* Create a new subpicture region */
55     video_format_Init( &fmt, VLC_CODEC_YUVA );
56     fmt.i_width = fmt.i_visible_width = i_width;
57     fmt.i_height = fmt.i_visible_height = i_height;
58
59     p_region = subpicture_region_New( &fmt );
60     if( !p_region )
61     {
62         msg_Err( p_vout, "Cannot allocate SPU region." );
63         return NULL;
64     }
65
66     p_region->i_x = i_x;
67     p_region->i_y = i_y;
68
69     picture_t *p_picture = p_region->p_picture;
70
71     f_ratio = __MIN( __MAX( f_ratio, 0 ), 1 );
72     int i_filled_part_width = f_ratio * i_width;
73
74     for( int j = 0; j < i_height; j++ )
75     {
76         for( int i = 0; i < i_width; i++ )
77         {
78             #define WRITE_COMP( plane, value ) \
79                 p_picture->p[plane].p_pixels[p_picture->p[plane].i_pitch * j + i] = value
80
81             /* Draw the slider. */
82             bool is_outline = j == 0 || j == i_height - 1
83                               || i == 0 || i == i_width - 1;
84             WRITE_COMP( 0, is_outline ? 0x00 : 0xff );
85             WRITE_COMP( 1, 0x80 );
86             WRITE_COMP( 2, 0x80 );
87
88             /* We can see the video through the part of the slider
89                which corresponds to the leaving time. */
90             bool is_border = j < 3 || j > i_height - 4
91                              || i < 3 || i > i_width - 4
92                              || i < i_filled_part_width;
93             WRITE_COMP( 3, is_border ? 0xff : 0x00 );
94
95             #undef WRITE_COMP
96         }
97     }
98
99     return p_region;
100 }
101
102
103 static subpicture_region_t * vout_OSDEpgText( vout_thread_t *p_vout,
104                                               const char *psz_string,
105                                               int i_x, int i_y,
106                                               int i_size, uint32_t i_color )
107 {
108     video_format_t fmt;
109     subpicture_region_t *p_region;
110
111     if( !psz_string )
112         return NULL;
113
114     /* Create a new subpicture region */
115     video_format_Init( &fmt, VLC_CODEC_TEXT );
116
117     p_region = subpicture_region_New( &fmt );
118     if( !p_region )
119     {
120         msg_Err( p_vout, "Cannot allocate SPU region." );
121         return NULL;
122     }
123
124     /* Set subpicture parameters */
125     p_region->psz_text = strdup( psz_string );
126     p_region->i_align = 0;
127     p_region->i_x = i_x;
128     p_region->i_y = i_y;
129
130     /* Set text style */
131     p_region->p_style = text_style_New();
132     if( p_region->p_style )
133     {
134         p_region->p_style->i_font_size = i_size;
135         p_region->p_style->i_font_color = i_color;
136         p_region->p_style->i_font_alpha = 0;
137     }
138
139     return p_region;
140 }
141
142
143 static subpicture_region_t * vout_BuildOSDEpg( vout_thread_t *p_vout,
144                                                vlc_epg_t *p_epg,
145                                                int i_visible_width,
146                                                int i_visible_height )
147 {
148     subpicture_region_t *p_region_ret;
149     subpicture_region_t **pp_region = &p_region_ret;
150
151     time_t i_test = time( NULL );
152
153     /* Display the name of the channel. */
154     *pp_region = vout_OSDEpgText( p_vout,
155                                   p_epg->psz_name,
156                                   i_visible_width * EPG_LEFT,
157                                   i_visible_height * EPG_TOP,
158                                   i_visible_height * EPG_NAME_SIZE,
159                                   0x00ffffff );
160
161     if( !*pp_region )
162         return p_region_ret;
163
164     /* Display the name of the current program. */
165     pp_region = &(* pp_region)->p_next;
166     *pp_region = vout_OSDEpgText( p_vout, p_epg->p_current->psz_name,
167                                   i_visible_width * ( EPG_LEFT + 0.025 ),
168                                   i_visible_height * ( EPG_TOP + 0.05 ),
169                                   i_visible_height * EPG_PROGRAM_SIZE,
170                                   0x00ffffff );
171
172     if( !*pp_region )
173         return p_region_ret;
174
175     /* Display the current program time slider. */
176     pp_region = &(* pp_region)->p_next;
177     *pp_region = vout_OSDEpgSlider( p_vout,
178                                     i_visible_width * EPG_LEFT,
179                                     i_visible_height * ( EPG_TOP + 0.1 ),
180                                     i_visible_width * ( 1 - 2 * EPG_LEFT ),
181                                     i_visible_height * 0.05,
182                                     ( i_test - p_epg->p_current->i_start )
183                                     / (float)p_epg->p_current->i_duration );
184
185     if( !*pp_region )
186         return p_region_ret;
187
188     /* Format the hours of the beginning and the end of the current program. */
189     struct tm tm_start, tm_end;
190     time_t t_start = p_epg->p_current->i_start;
191     time_t t_end = p_epg->p_current->i_start + p_epg->p_current->i_duration;
192     localtime_r( &t_start, &tm_start );
193     localtime_r( &t_end, &tm_end );
194     char psz_start[128];
195     char psz_end[128];
196     snprintf( psz_start, sizeof(psz_start), "%2.2d:%2.2d",
197               tm_start.tm_hour, tm_start.tm_min );
198     snprintf( psz_end, sizeof(psz_end), "%2.2d:%2.2d",
199               tm_end.tm_hour, tm_end.tm_min );
200
201     /* Display those hours. */
202     pp_region = &(* pp_region)->p_next;
203     *pp_region = vout_OSDEpgText( p_vout, psz_start,
204                                   i_visible_width * ( EPG_LEFT + 0.02 ),
205                                   i_visible_height * ( EPG_TOP + 0.15 ),
206                                   i_visible_height * EPG_PROGRAM_SIZE,
207                                   0x00ffffff );
208
209     if( !*pp_region )
210         return p_region_ret;
211
212     pp_region = &(* pp_region)->p_next;
213     *pp_region = vout_OSDEpgText( p_vout, psz_end,
214                                   i_visible_width * ( 1 - EPG_LEFT - 0.085 ),
215                                   i_visible_height * ( EPG_TOP + 0.15 ),
216                                   i_visible_height * EPG_PROGRAM_SIZE,
217                                   0x00ffffff );
218
219     return p_region_ret;
220 }
221
222
223 /**
224  * \brief Show EPG information about the current program of an input item
225  * \param p_vout pointer to the vout the information is to be showed on
226  * \param p_input pointer to the input item the information is to be showed
227  */
228 int vout_OSDEpg( vout_thread_t *p_vout, input_item_t *p_input )
229 {
230     subpicture_t *p_spu;
231     mtime_t i_now = mdate();
232
233     int i_visible_width = p_vout->fmt_in.i_width;
234     int i_visible_height = p_vout->fmt_in.i_height;
235
236     if( !p_input )
237         return VLC_EGENERIC;
238
239     p_spu = subpicture_New();
240     if( !p_spu )
241         return VLC_EGENERIC;
242
243     p_spu->i_channel = DEFAULT_CHAN;
244     p_spu->i_start = i_now;
245     p_spu->i_stop = i_now + 3000 * INT64_C(1000);
246     p_spu->b_ephemer = true;
247     p_spu->b_absolute = true;
248     p_spu->b_fade = true;
249
250     char *psz_now_playing = input_item_GetNowPlaying( p_input );
251     vlc_epg_t *p_epg = NULL;
252
253     vlc_mutex_lock( &p_input->lock );
254
255     /* Look for the current program EPG event */
256     for( int i = 0; i < p_input->i_epg; i++ )
257     {
258         vlc_epg_t *p_tmp = p_input->pp_epg[i];
259
260         if( p_tmp->p_current && p_tmp->p_current->psz_name
261             && psz_now_playing != NULL
262             && !strcmp( p_tmp->p_current->psz_name, psz_now_playing ) )
263         {
264             p_epg = vlc_epg_New( p_tmp->psz_name );
265             vlc_epg_Merge( p_epg, p_tmp );
266             break;
267         }
268     }
269
270     vlc_mutex_unlock( &p_input->lock );
271
272     if( p_epg != NULL )
273     {
274         /* Build the EPG event subpictures. */
275         p_spu->p_region = vout_BuildOSDEpg( p_vout, p_epg,
276                                             i_visible_width,
277                                             i_visible_height );
278         vlc_epg_Delete( p_epg );
279     }
280     else
281     {
282         /* If no EPG event has been found, then display a warning message. */
283         p_spu->p_region = vout_OSDEpgText( p_vout, NO_EPG,
284                                            i_visible_width * EPG_LEFT,
285                                            i_visible_height * EPG_TOP,
286                                            i_visible_height * EPG_NAME_SIZE,
287                                            0x00ffffff );
288     }
289
290     spu_DisplaySubpicture( p_vout->p_spu, p_spu );
291
292     return VLC_SUCCESS;
293 }