1 /**
2  * @file panel.c  Video-info filter -- panel
3  *
4  * Copyright (C) 2010 - 2015 Creytiv.com
5  */
6 #include <re.h>
7 #include <rem.h>
8 #include <baresip.h>
9 #include "vidinfo.h"
10 
11 
rrd_append(struct panel * panel,uint64_t val)12 static void rrd_append(struct panel *panel, uint64_t val)
13 {
14 	if (!panel)
15 		return;
16 
17 	panel->rrdv[panel->rrdc++] = val;
18 	panel->rrd_sum += val;
19 
20 	if (panel->rrdc >= panel->rrdsz) {
21 		panel->rrdc = 0;
22 		panel->rrd_sum = 0;
23 	}
24 }
25 
26 
rrd_get_average(struct panel * panel,uint64_t * average)27 static int rrd_get_average(struct panel *panel, uint64_t *average)
28 {
29 	if (!panel->rrdc)
30 		return ENOENT;
31 
32 	*average = panel->rrd_sum / panel->rrdc;
33 
34 	return 0;
35 }
36 
37 
tmr_handler(void * arg)38 static void tmr_handler(void *arg)
39 {
40 	struct panel *panel = arg;
41 	uint64_t now = tmr_jiffies();
42 
43 	tmr_start(&panel->tmr, 2000, tmr_handler, panel);
44 
45 	if (panel->ts) {
46 		panel->fps = 1000.0 * panel->nframes / (now - panel->ts);
47 	}
48 	panel->nframes = 0;
49 
50 	panel->ts = now;
51 }
52 
53 
destructor(void * arg)54 static void destructor(void *arg)
55 {
56 	struct panel *panel = arg;
57 
58 	tmr_cancel(&panel->tmr);
59 	mem_deref(panel->label);
60 	mem_deref(panel->rrdv);
61 
62 	if (panel->cr)
63 		cairo_destroy(panel->cr);
64 	if (panel->surface)
65 		cairo_surface_destroy(panel->surface);
66 }
67 
68 
panel_alloc(struct panel ** panelp,const char * label,unsigned yoffs,int width,int height)69 int panel_alloc(struct panel **panelp, const char *label,
70 		unsigned yoffs, int width, int height)
71 {
72 	struct panel *panel;
73 	int err;
74 
75 	if (!panelp || !width || !height)
76 		return EINVAL;
77 
78 	panel = mem_zalloc(sizeof(*panel), destructor);
79 	if (!panel)
80 		return ENOMEM;
81 
82 	err = str_dup(&panel->label, label);
83 	if (err)
84 		goto out;
85 
86 	panel->size.w = width;
87 	panel->size.h = height;
88 	panel->yoffs = yoffs;
89 	panel->xoffs = TEXT_WIDTH;
90 
91 	panel->size_text.w = TEXT_WIDTH;
92 	panel->size_text.h = height;
93 
94 	panel->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
95 						    panel->size_text.w,
96 						    panel->size_text.h);
97 	panel->cr = cairo_create(panel->surface);
98 	if (!panel->surface || !panel->cr) {
99 		warning("vidinfo: cairo error\n");
100 		return ENOMEM;
101 	}
102 
103 	cairo_select_font_face (panel->cr, "Hyperfont",
104 				CAIRO_FONT_SLANT_NORMAL,
105 				CAIRO_FONT_WEIGHT_NORMAL);
106 	cairo_set_font_size (panel->cr, height-2);
107 
108 	panel->rrdc  = 0;
109 	panel->rrdsz = (width - TEXT_WIDTH) / 2;
110 	panel->rrdv  = mem_reallocarray(NULL, panel->rrdsz,
111 					sizeof(*panel->rrdv), NULL);
112 	if (!panel->rrdv) {
113 		err = ENOMEM;
114 		goto out;
115 	}
116 
117 	tmr_start(&panel->tmr, 0, tmr_handler, panel);
118 
119 	info("new panel '%s' (%u x %u) with RRD size %u\n",
120 	     label, width, height, panel->rrdsz);
121 
122  out:
123 	if (err)
124 		mem_deref(panel);
125 	else
126 		*panelp = panel;
127 
128 	return err;
129 }
130 
131 
overlay(struct vidframe * dst,unsigned yoffs,struct vidframe * src)132 static void overlay(struct vidframe *dst, unsigned yoffs, struct vidframe *src)
133 {
134 	uint8_t *pdst, *psrc;
135 	unsigned x, y;
136 
137 	pdst = dst->data[0] + yoffs * dst->linesize[0];
138 	psrc = src->data[0];
139 
140 	for (y=0; y<src->size.h; y++) {
141 
142 		for (x=0; x<src->size.w; x++) {
143 
144 			/* copy the luma component if visible */
145 			if (psrc[x] > 16)
146 				pdst[x] = psrc[x];
147 		}
148 
149 		pdst += dst->linesize[0];
150 		psrc += src->linesize[0];
151 	}
152 }
153 
154 
draw_text(struct panel * panel,struct vidframe * frame)155 static int draw_text(struct panel *panel, struct vidframe *frame)
156 {
157 	char buf[256];
158 	int width = panel->size_text.w;
159 	int height = panel->size_text.h;
160 	struct vidframe f;
161 	struct vidframe *f2 = NULL;
162 	cairo_t *cr = panel->cr;
163 	double tx, ty;
164 	int err;
165 
166 	tx = 1;
167 	ty = height - 3;
168 
169 	/* draw background */
170 	cairo_rectangle (cr, 0, 0, width, height);
171 	cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
172 	cairo_fill (cr);
173 
174 	/* Draw text */
175 	if (re_snprintf(buf, sizeof(buf), "%s %2.2f fps",
176 			panel->label, panel->fps) < 0)
177 		return ENOMEM;
178 
179 	cairo_move_to (cr, tx, ty);
180 	cairo_text_path (cr, buf);
181 	cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
182 	cairo_fill_preserve (cr);
183 	cairo_set_line_width (cr, 0.6);
184 	cairo_stroke (cr);
185 
186 	vidframe_init_buf(&f, VID_FMT_RGB32, &panel->size_text,
187 			  cairo_image_surface_get_data(panel->surface));
188 
189 	err = vidframe_alloc(&f2, frame->fmt, &panel->size_text);
190 	if (err)
191 		goto out;
192 
193 	vidconv(f2, &f, 0);
194 
195 	overlay(frame, panel->yoffs, f2);
196 
197  out:
198 	mem_deref(f2);
199 	return err;
200 }
201 
202 
dim_frame(struct vidframe * frame,unsigned yoffs,unsigned height)203 static void dim_frame(struct vidframe *frame, unsigned yoffs, unsigned height)
204 {
205 	unsigned x, y;
206 	uint8_t *p;
207 	bool lower = (yoffs > 0);
208 	double grade = lower ? 1.00 : (1.00 - PANEL_HEIGHT/100.0);
209 
210 	p = frame->data[0] + yoffs * frame->linesize[0];
211 
212 	/* first dim the background */
213 	for (y = 0; y < height; y++) {
214 
215 		for (x = 0; x < frame->size.w; x++) {
216 			p[x] = p[x] * grade;
217 		}
218 
219 		p += frame->linesize[0];
220 
221 		if (lower)
222 			grade -= 0.01;
223 		else
224 			grade += 0.01;
225 	}
226 }
227 
228 
draw_graph(struct panel * panel,struct vidframe * frame)229 static void draw_graph(struct panel *panel, struct vidframe *frame)
230 {
231 	uint64_t avg;
232 	unsigned y0 = panel->yoffs;
233 	size_t i;
234 
235 	if (rrd_get_average(panel, &avg))
236 		return;
237 
238 	for (i=0; i<panel->rrdc; i++) {
239 
240 		uint64_t value;
241 		double ratio;
242 		unsigned pixels;
243 		unsigned x = panel->xoffs + (unsigned)i * 2;
244 		unsigned y;
245 		value = panel->rrdv[i];
246 
247 		ratio = (double)value / (double)avg;
248 
249 		pixels = (unsigned)((double)panel->size.h * ratio * 0.5f);
250 
251 		pixels = min(pixels, panel->size.h);
252 
253 		y = y0 + panel->size.h - pixels;
254 
255 		vidframe_draw_vline(frame, x, y, pixels, 220, 220, 220);
256 	}
257 }
258 
259 
panel_draw(struct panel * panel,struct vidframe * frame)260 int panel_draw(struct panel *panel, struct vidframe *frame)
261 {
262 	int err;
263 
264 	if (!panel || !frame)
265 		return EINVAL;
266 
267 	dim_frame(frame, panel->yoffs, panel->size.h);
268 
269 	err = draw_text(panel, frame);
270 	if (err)
271 		return err;
272 	draw_graph(panel, frame);
273 
274 	return 0;
275 }
276 
277 
panel_add_frame(struct panel * panel,uint64_t pts)278 void panel_add_frame(struct panel *panel, uint64_t pts)
279 {
280 	if (!panel)
281 		return;
282 
283 	if (panel->pts_prev) {
284 		rrd_append(panel, pts - panel->pts_prev);
285 	}
286 
287 	panel->nframes++;
288 	panel->pts_prev = pts;
289 }
290