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