]> git.sesse.net Git - vlc/blob - src/video_output/video_epg.c
Fixed EPG OSD aspect ratio.
[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     fmt.i_sar_num = 0;
59     fmt.i_sar_den = 1;
60
61     p_region = subpicture_region_New( &fmt );
62     if( !p_region )
63     {
64         msg_Err( p_vout, "Cannot allocate SPU region." );
65         return NULL;
66     }
67
68     p_region->i_x = i_x;
69     p_region->i_y = i_y;
70
71     picture_t *p_picture = p_region->p_picture;
72
73     f_ratio = __MIN( __MAX( f_ratio, 0 ), 1 );
74     int i_filled_part_width = f_ratio * i_width;
75
76     for( int j = 0; j < i_height; j++ )
77     {
78         for( int i = 0; i < i_width; i++ )
79         {
80             #define WRITE_COMP( plane, value ) \
81                 p_picture->p[plane].p_pixels[p_picture->p[plane].i_pitch * j + i] = value
82
83             /* Draw the slider. */
84             bool is_outline = j == 0 || j == i_height - 1
85                               || i == 0 || i == i_width - 1;
86             WRITE_COMP( 0, is_outline ? 0x00 : 0xff );
87             WRITE_COMP( 1, 0x80 );
88             WRITE_COMP( 2, 0x80 );
89
90             /* We can see the video through the part of the slider
91                which corresponds to the leaving time. */
92             bool is_border = j < 3 || j > i_height - 4
93                              || i < 3 || i > i_width - 4
94                              || i < i_filled_part_width;
95             WRITE_COMP( 3, is_border ? 0xff : 0x00 );
96
97             #undef WRITE_COMP
98         }
99     }
100
101     return p_region;
102 }
103
104
105 static subpicture_region_t * vout_OSDEpgText( vout_thread_t *p_vout,
106                                               const char *psz_string,
107                                               int i_x, int i_y,
108                                               int i_size, uint32_t i_color )
109 {
110     video_format_t fmt;
111     subpicture_region_t *p_region;
112
113     if( !psz_string )
114         return NULL;
115
116     /* Create a new subpicture region */
117     video_format_Init( &fmt, VLC_CODEC_TEXT );
118     fmt.i_sar_num = 0;
119     fmt.i_sar_den = 1;
120
121     p_region = subpicture_region_New( &fmt );
122     if( !p_region )
123     {
124         msg_Err( p_vout, "Cannot allocate SPU region." );
125         return NULL;
126     }
127
128     /* Set subpicture parameters */
129     p_region->psz_text = strdup( psz_string );
130     p_region->i_align = 0;
131     p_region->i_x = i_x;
132     p_region->i_y = i_y;
133
134     /* Set text style */
135     p_region->p_style = text_style_New();
136     if( p_region->p_style )
137     {
138         p_region->p_style->i_font_size = i_size;
139         p_region->p_style->i_font_color = i_color;
140         p_region->p_style->i_font_alpha = 0;
141     }
142
143     return p_region;
144 }
145
146
147 static subpicture_region_t * vout_BuildOSDEpg( vout_thread_t *p_vout,
148                                                vlc_epg_t *p_epg,
149                                                int i_visible_width,
150                                                int i_visible_height )
151 {
152     subpicture_region_t *p_region_ret;
153     subpicture_region_t **pp_region = &p_region_ret;
154
155     time_t i_test = time( NULL );
156
157     /* Display the name of the channel. */
158     *pp_region = vout_OSDEpgText( p_vout,
159                                   p_epg->psz_name,
160                                   i_visible_width * EPG_LEFT,
161                                   i_visible_height * EPG_TOP,
162                                   i_visible_height * EPG_NAME_SIZE,
163                                   0x00ffffff );
164
165     if( !*pp_region )
166         return p_region_ret;
167
168     /* Display the name of the current program. */
169     pp_region = &(* pp_region)->p_next;
170     *pp_region = vout_OSDEpgText( p_vout, p_epg->p_current->psz_name,
171                                   i_visible_width * ( EPG_LEFT + 0.025 ),
172                                   i_visible_height * ( EPG_TOP + 0.05 ),
173                                   i_visible_height * EPG_PROGRAM_SIZE,
174                                   0x00ffffff );
175
176     if( !*pp_region )
177         return p_region_ret;
178
179     /* Display the current program time slider. */
180     pp_region = &(* pp_region)->p_next;
181     *pp_region = vout_OSDEpgSlider( p_vout,
182                                     i_visible_width * EPG_LEFT,
183                                     i_visible_height * ( EPG_TOP + 0.1 ),
184                                     i_visible_width * ( 1 - 2 * EPG_LEFT ),
185                                     i_visible_height * 0.05,
186                                     ( i_test - p_epg->p_current->i_start )
187                                     / (float)p_epg->p_current->i_duration );
188
189     if( !*pp_region )
190         return p_region_ret;
191
192     /* Format the hours of the beginning and the end of the current program. */
193     struct tm tm_start, tm_end;
194     time_t t_start = p_epg->p_current->i_start;
195     time_t t_end = p_epg->p_current->i_start + p_epg->p_current->i_duration;
196     localtime_r( &t_start, &tm_start );
197     localtime_r( &t_end, &tm_end );
198     char psz_start[128];
199     char psz_end[128];
200     snprintf( psz_start, sizeof(psz_start), "%2.2d:%2.2d",
201               tm_start.tm_hour, tm_start.tm_min );
202     snprintf( psz_end, sizeof(psz_end), "%2.2d:%2.2d",
203               tm_end.tm_hour, tm_end.tm_min );
204
205     /* Display those hours. */
206     pp_region = &(* pp_region)->p_next;
207     *pp_region = vout_OSDEpgText( p_vout, psz_start,
208                                   i_visible_width * ( EPG_LEFT + 0.02 ),
209                                   i_visible_height * ( EPG_TOP + 0.15 ),
210                                   i_visible_height * EPG_PROGRAM_SIZE,
211                                   0x00ffffff );
212
213     if( !*pp_region )
214         return p_region_ret;
215
216     pp_region = &(* pp_region)->p_next;
217     *pp_region = vout_OSDEpgText( p_vout, psz_end,
218                                   i_visible_width * ( 1 - EPG_LEFT - 0.085 ),
219                                   i_visible_height * ( EPG_TOP + 0.15 ),
220                                   i_visible_height * EPG_PROGRAM_SIZE,
221                                   0x00ffffff );
222
223     return p_region_ret;
224 }
225
226
227 /**
228  * \brief Show EPG information about the current program of an input item
229  * \param p_vout pointer to the vout the information is to be showed on
230  * \param p_input pointer to the input item the information is to be showed
231  */
232 int vout_OSDEpg( vout_thread_t *p_vout, input_item_t *p_input )
233 {
234     subpicture_t *p_spu;
235     mtime_t i_now = mdate();
236
237     int i_visible_width = p_vout->fmt_in.i_width;
238     int i_visible_height = p_vout->fmt_in.i_height;
239
240     if( !p_input )
241         return VLC_EGENERIC;
242
243     p_spu = subpicture_New();
244     if( !p_spu )
245         return VLC_EGENERIC;
246
247     p_spu->i_channel = DEFAULT_CHAN;
248     p_spu->i_start = i_now;
249     p_spu->i_stop = i_now + 3000 * INT64_C(1000);
250     p_spu->b_ephemer = true;
251     p_spu->b_absolute = true;
252     p_spu->b_fade = true;
253
254     char *psz_now_playing = input_item_GetNowPlaying( p_input );
255     vlc_epg_t *p_epg = NULL;
256
257     vlc_mutex_lock( &p_input->lock );
258
259     /* Look for the current program EPG event */
260     for( int i = 0; i < p_input->i_epg; i++ )
261     {
262         vlc_epg_t *p_tmp = p_input->pp_epg[i];
263
264         if( p_tmp->p_current && p_tmp->p_current->psz_name
265             && psz_now_playing != NULL
266             && !strcmp( p_tmp->p_current->psz_name, psz_now_playing ) )
267         {
268             p_epg = vlc_epg_New( p_tmp->psz_name );
269             vlc_epg_Merge( p_epg, p_tmp );
270             break;
271         }
272     }
273
274     vlc_mutex_unlock( &p_input->lock );
275
276     if( p_epg != NULL )
277     {
278         /* Build the EPG event subpictures. */
279         p_spu->p_region = vout_BuildOSDEpg( p_vout, p_epg,
280                                             i_visible_width,
281                                             i_visible_height );
282         vlc_epg_Delete( p_epg );
283     }
284     else
285     {
286         /* If no EPG event has been found, then display a warning message. */
287         p_spu->p_region = vout_OSDEpgText( p_vout, NO_EPG,
288                                            i_visible_width * EPG_LEFT,
289                                            i_visible_height * EPG_TOP,
290                                            i_visible_height * EPG_NAME_SIZE,
291                                            0x00ffffff );
292     }
293
294     spu_DisplaySubpicture( p_vout->p_spu, p_spu );
295
296     return VLC_SUCCESS;
297 }