1 /**
2 * @file vidloop.c Video loop
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6 #define _DEFAULT_SOURCE 1
7 #define _BSD_SOURCE 1
8 #include <string.h>
9 #include <time.h>
10 #include <re.h>
11 #include <rem.h>
12 #include <baresip.h>
13
14
15 /**
16 * @defgroup vidloop vidloop
17 *
18 * A video-loop module for testing
19 *
20 * Simple test module that loops back the video frames from a
21 * video-source to a video-display, optionally via a video codec.
22 *
23 * Example usage without codec:
24 \verbatim
25 baresip -e/vidloop
26 \endverbatim
27 *
28 * Example usage with codec:
29 \verbatim
30 baresip -e"/vidloop h264"
31 \endverbatim
32 */
33
34
35 /** Video Statistics */
36 struct vstat {
37 uint64_t tsamp;
38 uint32_t frames;
39 size_t bytes;
40 uint32_t bitrate;
41 double efps;
42 size_t n_intra;
43 };
44
45
46 /** Video loop */
47 struct video_loop {
48 const struct vidcodec *vc_enc;
49 const struct vidcodec *vc_dec;
50 struct config_video cfg;
51 struct videnc_state *enc;
52 struct viddec_state *dec;
53 struct vidisp_st *vidisp;
54 struct vidsrc_st *vsrc;
55 struct list filtencl;
56 struct list filtdecl;
57 struct vstat stat;
58 struct tmr tmr_bw;
59 uint16_t seq;
60 bool need_conv;
61 int err;
62 };
63
64
65 static struct video_loop *gvl;
66
67
display(struct video_loop * vl,struct vidframe * frame)68 static int display(struct video_loop *vl, struct vidframe *frame)
69 {
70 struct vidframe *frame_filt = NULL;
71 struct le *le;
72 int err = 0;
73
74 if (!vidframe_isvalid(frame))
75 return 0;
76
77 /* Process video frame through all Video Filters */
78 for (le = vl->filtdecl.head; le; le = le->next) {
79
80 struct vidfilt_dec_st *st = le->data;
81
82 /* Some video decoders keeps the displayed video frame
83 * in memory and we should not write to that frame.
84 */
85 if (!frame_filt) {
86
87 err = vidframe_alloc(&frame_filt, frame->fmt,
88 &frame->size);
89 if (err)
90 return err;
91
92 vidframe_copy(frame_filt, frame);
93
94 frame = frame_filt;
95 }
96
97 if (st->vf->dech)
98 err |= st->vf->dech(st, frame);
99 }
100
101 if (err) {
102 warning("vidloop: error in video-filters (%m)\n", err);
103 }
104
105 /* display frame */
106 err = vidisp_display(vl->vidisp, "Video Loop", frame);
107 if (err == ENODEV) {
108 info("vidloop: video-display was closed\n");
109 vl->vidisp = mem_deref(vl->vidisp);
110 vl->err = err;
111 }
112
113 mem_deref(frame_filt);
114
115 return err;
116 }
117
118
packet_handler(bool marker,uint32_t rtp_ts,const uint8_t * hdr,size_t hdr_len,const uint8_t * pld,size_t pld_len,void * arg)119 static int packet_handler(bool marker, uint32_t rtp_ts,
120 const uint8_t *hdr, size_t hdr_len,
121 const uint8_t *pld, size_t pld_len,
122 void *arg)
123 {
124 struct video_loop *vl = arg;
125 struct vidframe frame;
126 struct mbuf *mb;
127 bool intra;
128 int err = 0;
129 (void)rtp_ts;
130
131 mb = mbuf_alloc(hdr_len + pld_len);
132 if (!mb)
133 return ENOMEM;
134
135 if (hdr_len)
136 mbuf_write_mem(mb, hdr, hdr_len);
137 mbuf_write_mem(mb, pld, pld_len);
138
139 mb->pos = 0;
140
141 vl->stat.bytes += mbuf_get_left(mb);
142
143 /* decode */
144 frame.data[0] = NULL;
145 if (vl->vc_dec && vl->dec) {
146 err = vl->vc_dec->dech(vl->dec, &frame, &intra,
147 marker, vl->seq++, mb);
148 if (err) {
149 warning("vidloop: codec decode: %m\n", err);
150 goto out;
151 }
152
153 if (intra)
154 ++vl->stat.n_intra;
155 }
156
157 if (vidframe_isvalid(&frame)) {
158
159 display(vl, &frame);
160 }
161
162 out:
163 mem_deref(mb);
164
165 return 0;
166 }
167
168
vidsrc_frame_handler(struct vidframe * frame,void * arg)169 static void vidsrc_frame_handler(struct vidframe *frame, void *arg)
170 {
171 struct video_loop *vl = arg;
172 struct vidframe *f2 = NULL;
173 struct le *le;
174 int err = 0;
175
176 ++vl->stat.frames;
177
178 if (frame->fmt != (enum vidfmt)vl->cfg.enc_fmt) {
179
180 if (!vl->need_conv) {
181 info("vidloop: NOTE: pixel-format conversion"
182 " needed: %s --> %s\n",
183 vidfmt_name(frame->fmt),
184 vidfmt_name(vl->cfg.enc_fmt));
185 vl->need_conv = true;
186 }
187
188 if (vidframe_alloc(&f2, vl->cfg.enc_fmt, &frame->size))
189 return;
190
191 vidconv(f2, frame, 0);
192
193 frame = f2;
194 }
195
196 /* Process video frame through all Video Filters */
197 for (le = vl->filtencl.head; le; le = le->next) {
198
199 struct vidfilt_enc_st *st = le->data;
200
201 if (st->vf->ench)
202 err |= st->vf->ench(st, frame);
203 }
204
205 if (vl->vc_enc && vl->enc) {
206 err = vl->vc_enc->ench(vl->enc, false, frame);
207 if (err) {
208 warning("vidloop: encoder error (%m)\n", err);
209 }
210 }
211 else {
212 vl->stat.bytes += vidframe_size(frame->fmt, &frame->size);
213 (void)display(vl, frame);
214 }
215
216 mem_deref(f2);
217 }
218
219
vidloop_destructor(void * arg)220 static void vidloop_destructor(void *arg)
221 {
222 struct video_loop *vl = arg;
223
224 tmr_cancel(&vl->tmr_bw);
225 mem_deref(vl->vsrc);
226 mem_deref(vl->enc);
227 mem_deref(vl->dec);
228 mem_deref(vl->vidisp);
229 list_flush(&vl->filtencl);
230 list_flush(&vl->filtdecl);
231 }
232
233
enable_codec(struct video_loop * vl,const char * name)234 static int enable_codec(struct video_loop *vl, const char *name)
235 {
236 struct list *vidcodecl = baresip_vidcodecl();
237 struct videnc_param prm;
238 int err;
239
240 prm.fps = vl->cfg.fps;
241 prm.pktsize = 1480;
242 prm.bitrate = vl->cfg.bitrate;
243 prm.max_fs = -1;
244
245 /* Use the first video codec */
246
247 vl->vc_enc = vidcodec_find_encoder(vidcodecl, name);
248 if (!vl->vc_enc) {
249 warning("vidloop: could not find encoder (%s)\n", name);
250 return ENOENT;
251 }
252
253 info("vidloop: enabled encoder %s (%u fps, %u bit/s)\n",
254 vl->vc_enc->name, prm.fps, prm.bitrate);
255
256 vl->vc_dec = vidcodec_find_decoder(vidcodecl, name);
257 if (!vl->vc_dec) {
258 warning("vidloop: could not find decoder (%s)\n", name);
259 return ENOENT;
260 }
261
262 info("vidloop: enabled decoder %s\n", vl->vc_dec->name);
263
264 err = vl->vc_enc->encupdh(&vl->enc, vl->vc_enc, &prm, NULL,
265 packet_handler, vl);
266 if (err) {
267 warning("vidloop: update encoder failed: %m\n", err);
268 return err;
269 }
270
271 if (vl->vc_dec->decupdh) {
272 err = vl->vc_dec->decupdh(&vl->dec, vl->vc_dec, NULL);
273 if (err) {
274 warning("vidloop: update decoder failed: %m\n", err);
275 return err;
276 }
277 }
278
279 return 0;
280 }
281
282
print_status(struct video_loop * vl)283 static void print_status(struct video_loop *vl)
284 {
285 (void)re_fprintf(stdout,
286 "\rstatus:"
287 " [%s] [%s] fmt=%s intra=%zu "
288 " EFPS=%.1f %u kbit/s \r",
289 vl->vc_enc ? vl->vc_enc->name : "",
290 vl->vc_dec ? vl->vc_dec->name : "",
291 vidfmt_name(vl->cfg.enc_fmt),
292 vl->stat.n_intra,
293 vl->stat.efps, vl->stat.bitrate);
294 fflush(stdout);
295 }
296
297
calc_bitrate(struct video_loop * vl)298 static void calc_bitrate(struct video_loop *vl)
299 {
300 const uint64_t now = tmr_jiffies();
301
302 if (now > vl->stat.tsamp) {
303
304 const uint32_t dur = (uint32_t)(now - vl->stat.tsamp);
305
306 vl->stat.efps = 1000.0f * vl->stat.frames / dur;
307
308 vl->stat.bitrate = (uint32_t) (8 * vl->stat.bytes / dur);
309 }
310
311 vl->stat.frames = 0;
312 vl->stat.bytes = 0;
313 vl->stat.tsamp = now;
314 }
315
316
timeout_bw(void * arg)317 static void timeout_bw(void *arg)
318 {
319 struct video_loop *vl = arg;
320
321 if (vl->err) {
322 info("error in video-loop -- closing (%m)\n", vl->err);
323 gvl = mem_deref(gvl);
324 return;
325 }
326
327 tmr_start(&vl->tmr_bw, 2000, timeout_bw, vl);
328
329 calc_bitrate(vl);
330 print_status(vl);
331 }
332
333
vsrc_reopen(struct video_loop * vl,const struct vidsz * sz)334 static int vsrc_reopen(struct video_loop *vl, const struct vidsz *sz)
335 {
336 struct vidsrc_prm prm;
337 int err;
338
339 info("vidloop: %s,%s: open video source: %u x %u at %u fps\n",
340 vl->cfg.src_mod, vl->cfg.src_dev,
341 sz->w, sz->h, vl->cfg.fps);
342
343 prm.orient = VIDORIENT_PORTRAIT;
344 prm.fps = vl->cfg.fps;
345
346 vl->vsrc = mem_deref(vl->vsrc);
347 err = vidsrc_alloc(&vl->vsrc, baresip_vidsrcl(),
348 vl->cfg.src_mod, NULL, &prm, sz,
349 NULL, vl->cfg.src_dev, vidsrc_frame_handler,
350 NULL, vl);
351 if (err) {
352 warning("vidloop: vidsrc '%s' failed: %m\n",
353 vl->cfg.src_dev, err);
354 }
355
356 return err;
357 }
358
359
video_loop_alloc(struct video_loop ** vlp)360 static int video_loop_alloc(struct video_loop **vlp)
361 {
362 struct video_loop *vl;
363 struct config *cfg;
364 struct le *le;
365 int err = 0;
366
367 cfg = conf_config();
368 if (!cfg)
369 return EINVAL;
370
371 vl = mem_zalloc(sizeof(*vl), vidloop_destructor);
372 if (!vl)
373 return ENOMEM;
374
375 vl->cfg = cfg->video;
376 tmr_init(&vl->tmr_bw);
377
378 /* Video filters */
379 for (le = list_head(baresip_vidfiltl()); le; le = le->next) {
380 struct vidfilt *vf = le->data;
381 void *ctx = NULL;
382
383 info("vidloop: added video-filter `%s'\n", vf->name);
384
385 err |= vidfilt_enc_append(&vl->filtencl, &ctx, vf);
386 err |= vidfilt_dec_append(&vl->filtdecl, &ctx, vf);
387 if (err) {
388 warning("vidloop: vidfilt error: %m\n", err);
389 }
390 }
391
392 info("vidloop: open video display (%s.%s)\n",
393 vl->cfg.disp_mod, vl->cfg.disp_dev);
394
395 err = vidisp_alloc(&vl->vidisp, baresip_vidispl(),
396 vl->cfg.disp_mod, NULL,
397 vl->cfg.disp_dev, NULL, vl);
398 if (err) {
399 warning("vidloop: video display failed: %m\n", err);
400 goto out;
401 }
402
403 tmr_start(&vl->tmr_bw, 1000, timeout_bw, vl);
404
405 out:
406 if (err)
407 mem_deref(vl);
408 else
409 *vlp = vl;
410
411 return err;
412 }
413
414
415 /**
416 * Start the video loop (for testing)
417 */
vidloop_start(struct re_printf * pf,void * arg)418 static int vidloop_start(struct re_printf *pf, void *arg)
419 {
420 const struct cmd_arg *carg = arg;
421 struct vidsz size;
422 struct config *cfg = conf_config();
423 const char *codec_name = carg->prm;
424 int err = 0;
425
426 size.w = cfg->video.width;
427 size.h = cfg->video.height;
428
429 if (gvl) {
430 return re_hprintf(pf, "video-loop already running.\n");
431 }
432
433 (void)re_hprintf(pf, "Enable video-loop on %s,%s: %u x %u\n",
434 cfg->video.src_mod, cfg->video.src_dev,
435 size.w, size.h);
436
437 err = video_loop_alloc(&gvl);
438 if (err) {
439 warning("vidloop: alloc: %m\n", err);
440 return err;
441 }
442
443 if (str_isset(codec_name)) {
444
445 err = enable_codec(gvl, codec_name);
446 if (err) {
447 gvl = mem_deref(gvl);
448 return err;
449 }
450
451 (void)re_hprintf(pf, "%sabled codec: %s\n",
452 gvl->vc_enc ? "En" : "Dis",
453 gvl->vc_enc ? gvl->vc_enc->name : "");
454 }
455
456 /* Start video source, after codecs are created */
457 err = vsrc_reopen(gvl, &size);
458 if (err) {
459 gvl = mem_deref(gvl);
460 return err;
461 }
462
463 return err;
464 }
465
466
vidloop_stop(struct re_printf * pf,void * arg)467 static int vidloop_stop(struct re_printf *pf, void *arg)
468 {
469 (void)arg;
470
471 if (gvl)
472 (void)re_hprintf(pf, "Disable video-loop\n");
473 gvl = mem_deref(gvl);
474 return 0;
475 }
476
477
478 static const struct cmd cmdv[] = {
479 {"vidloop", 0, CMD_PRM, "Start video-loop <codec>", vidloop_start},
480 {"vidloop_stop",0, 0, "Stop video-loop", vidloop_stop },
481 };
482
483
module_init(void)484 static int module_init(void)
485 {
486 return cmd_register(baresip_commands(), cmdv, ARRAY_SIZE(cmdv));
487 }
488
489
module_close(void)490 static int module_close(void)
491 {
492 gvl = mem_deref(gvl);
493 cmd_unregister(baresip_commands(), cmdv);
494 return 0;
495 }
496
497
498 EXPORT_SYM const struct mod_export DECL_EXPORTS(vidloop) = {
499 "vidloop",
500 "application",
501 module_init,
502 module_close,
503 };
504