1 /**
2 * @file rst/video.c MP3/ICY HTTP Video Source
3 *
4 * Copyright (C) 2011 Creytiv.com
5 */
6 #define _DEFAULT_SOURCE 1
7 #define _BSD_SOURCE 1
8 #include <pthread.h>
9 #include <string.h>
10 #include <re.h>
11 #include <rem.h>
12 #include <baresip.h>
13 #include <cairo/cairo.h>
14 #include "rst.h"
15
16
17 struct vidsrc_st {
18 const struct vidsrc *vs; /* pointer to base-class (inheritance) */
19 pthread_mutex_t mutex;
20 pthread_t thread;
21 struct vidsrc_prm prm;
22 struct vidsz size;
23 struct rst *rst;
24 cairo_surface_t *surface;
25 cairo_t *cairo;
26 struct vidframe *frame;
27 vidsrc_frame_h *frameh;
28 void *arg;
29 bool run;
30 };
31
32
33 static struct vidsrc *vidsrc;
34
35
destructor(void * arg)36 static void destructor(void *arg)
37 {
38 struct vidsrc_st *st = arg;
39
40 rst_set_video(st->rst, NULL);
41 mem_deref(st->rst);
42
43 if (st->run) {
44 st->run = false;
45 pthread_join(st->thread, NULL);
46 }
47
48 if (st->cairo)
49 cairo_destroy(st->cairo);
50
51 if (st->surface)
52 cairo_surface_destroy(st->surface);
53
54 mem_deref(st->frame);
55 }
56
57
video_thread(void * arg)58 static void *video_thread(void *arg)
59 {
60 uint64_t now, ts = tmr_jiffies();
61 struct vidsrc_st *st = arg;
62
63 while (st->run) {
64
65 sys_msleep(4);
66
67 now = tmr_jiffies();
68
69 if (ts > now)
70 continue;
71
72 pthread_mutex_lock(&st->mutex);
73 st->frameh(st->frame, st->arg);
74 pthread_mutex_unlock(&st->mutex);
75
76 ts += 1000/st->prm.fps;
77 }
78
79 return NULL;
80 }
81
82
background(cairo_t * cr,unsigned width,unsigned height)83 static void background(cairo_t *cr, unsigned width, unsigned height)
84 {
85 cairo_pattern_t *pat;
86 double r, g, b;
87
88 pat = cairo_pattern_create_linear(0.0, 0.0, 0.0, height);
89 if (!pat)
90 return;
91
92 r = 0.0;
93 g = 0.0;
94 b = 0.8;
95
96 cairo_pattern_add_color_stop_rgba(pat, 1, r, g, b, 1);
97 cairo_pattern_add_color_stop_rgba(pat, 0, 0, 0, 0.2, 1);
98 cairo_rectangle(cr, 0, 0, width, height);
99 cairo_set_source(cr, pat);
100 cairo_fill(cr);
101
102 cairo_pattern_destroy(pat);
103 }
104
105
icy_printf(cairo_t * cr,int x,int y,double size,const char * fmt,...)106 static void icy_printf(cairo_t *cr, int x, int y, double size,
107 const char *fmt, ...)
108 {
109 char buf[4096] = "";
110 va_list ap;
111
112 va_start(ap, fmt);
113 (void)re_vsnprintf(buf, sizeof(buf), fmt, ap);
114 va_end(ap);
115
116 /* Draw text */
117 cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
118 CAIRO_FONT_WEIGHT_NORMAL);
119 cairo_set_font_size(cr, size);
120 cairo_move_to(cr, x, y);
121 cairo_text_path(cr, buf);
122 cairo_set_source_rgb(cr, 1, 1, 1);
123 cairo_fill(cr);
124 }
125
126
linelen(const struct pl * pl)127 static size_t linelen(const struct pl *pl)
128 {
129 size_t len = 72, i;
130
131 if (pl->l <= len)
132 return pl->l;
133
134 for (i=len; i>1; i--) {
135
136 if (pl->p[i-1] == ' ') {
137 len = i;
138 break;
139 }
140 }
141
142 return len;
143 }
144
145
rst_video_update(struct vidsrc_st * st,const char * name,const char * meta)146 void rst_video_update(struct vidsrc_st *st, const char *name, const char *meta)
147 {
148 struct vidframe frame;
149
150 if (!st)
151 return;
152
153 background(st->cairo, st->size.w, st->size.h);
154
155 icy_printf(st->cairo, 50, 100, 40.0, "%s", name);
156
157 if (meta) {
158
159 struct pl title;
160
161 if (!re_regex(meta, strlen(meta),
162 "StreamTitle='[ \t]*[^;]+;", NULL, &title)) {
163
164 unsigned i;
165
166 title.l--;
167
168 for (i=0; title.l; i++) {
169
170 const size_t len = linelen(&title);
171
172 icy_printf(st->cairo, 50, 150 + 25*i, 18.0,
173 "%b", title.p, len);
174
175 title.p += len;
176 title.l -= len;
177 }
178 }
179 }
180
181 vidframe_init_buf(&frame, VID_FMT_RGB32, &st->size,
182 cairo_image_surface_get_data(st->surface));
183
184 pthread_mutex_lock(&st->mutex);
185 vidconv(st->frame, &frame, NULL);
186 pthread_mutex_unlock(&st->mutex);
187 }
188
189
alloc_handler(struct vidsrc_st ** stp,const struct vidsrc * vs,struct media_ctx ** ctx,struct vidsrc_prm * prm,const struct vidsz * size,const char * fmt,const char * dev,vidsrc_frame_h * frameh,vidsrc_error_h * errorh,void * arg)190 static int alloc_handler(struct vidsrc_st **stp, const struct vidsrc *vs,
191 struct media_ctx **ctx, struct vidsrc_prm *prm,
192 const struct vidsz *size, const char *fmt,
193 const char *dev, vidsrc_frame_h *frameh,
194 vidsrc_error_h *errorh, void *arg)
195 {
196 struct vidsrc_st *st;
197 int err;
198
199 (void)fmt;
200 (void)errorh;
201
202 if (!stp || !vs || !prm || !size || !frameh)
203 return EINVAL;
204
205 st = mem_zalloc(sizeof(*st), destructor);
206 if (!st)
207 return ENOMEM;
208
209 err = pthread_mutex_init(&st->mutex, NULL);
210 if (err)
211 goto out;
212
213 st->vs = vs;
214 st->prm = *prm;
215 st->size = *size;
216 st->frameh = frameh;
217 st->arg = arg;
218
219 st->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
220 size->w, size->h);
221 if (!st->surface) {
222 err = ENOMEM;
223 goto out;
224 }
225
226 st->cairo = cairo_create(st->surface);
227 if (!st->cairo) {
228 err = ENOMEM;
229 goto out;
230 }
231
232 err = vidframe_alloc(&st->frame, VID_FMT_YUV420P, size);
233 if (err)
234 goto out;
235
236 vidframe_fill(st->frame, 0, 0, 0);
237
238 if (ctx && *ctx && (*ctx)->id && !strcmp((*ctx)->id, "rst")) {
239 st->rst = mem_ref(*ctx);
240 }
241 else {
242 err = rst_alloc(&st->rst, dev);
243 if (err)
244 goto out;
245
246 if (ctx)
247 *ctx = (struct media_ctx *)st->rst;
248 }
249
250 rst_set_video(st->rst, st);
251
252 st->run = true;
253
254 err = pthread_create(&st->thread, NULL, video_thread, st);
255 if (err) {
256 st->run = false;
257 goto out;
258 }
259
260 out:
261 if (err)
262 mem_deref(st);
263 else
264 *stp = st;
265
266 return err;
267 }
268
269
rst_video_init(void)270 int rst_video_init(void)
271 {
272 return vidsrc_register(&vidsrc, baresip_vidsrcl(),
273 "rst", alloc_handler, NULL);
274 }
275
276
rst_video_close(void)277 void rst_video_close(void)
278 {
279 vidsrc = mem_deref(vidsrc);
280 }
281