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