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