1 /**
2  * @file vidmix.c Video Mixer
3  *
4  * Copyright (C) 2010 Creytiv.com
5  */
6 
7 #define _BSD_SOURCE 1
8 #define _DEFAULT_SOURCE 1
9 #include <unistd.h>
10 #define __USE_UNIX98 1
11 #include <pthread.h>
12 #include <string.h>
13 #include <re.h>
14 #include <rem_vid.h>
15 #include <rem_vidconv.h>
16 #include <rem_vidmix.h>
17 
18 
19 struct vidmix {
20 	pthread_rwlock_t rwlock;
21 	struct list srcl;
22 	bool initialized;
23 };
24 
25 struct vidmix_source {
26 	struct le le;
27 	pthread_t thread;
28 	pthread_mutex_t mutex;
29 	struct vidframe *frame_tx;
30 	struct vidframe *frame_rx;
31 	struct vidmix *mix;
32 	vidmix_frame_h *fh;
33 	void *arg;
34 	void *focus;
35 	bool content_hide;
36 	bool focus_full;
37 	unsigned fint;
38 	bool selfview;
39 	bool content;
40 	bool clear;
41 	bool run;
42 };
43 
44 
45 static inline void source_mix_full(struct vidframe *mframe,
46 				   const struct vidframe *frame_src);
47 
48 
49 static inline void clear_frame(struct vidframe *vf)
50 {
51 	vidframe_fill(vf, 0, 0, 0);
52 }
53 
54 
55 static void clear_all(struct vidmix *mix)
56 {
57 	struct le *le;
58 
59 	for (le=mix->srcl.head; le; le=le->next) {
60 
61 		struct vidmix_source *src = le->data;
62 
63 		src->clear = true;
64 	}
65 }
66 
67 
68 static void destructor(void *arg)
69 {
70 	struct vidmix *mix = arg;
71 
72 	if (mix->initialized)
73 		(void)pthread_rwlock_destroy(&mix->rwlock);
74 }
75 
76 
77 static void source_destructor(void *arg)
78 {
79 	struct vidmix_source *src = arg;
80 
81 	if (src->run) {
82 		src->run = false;
83 		pthread_join(src->thread, NULL);
84 	}
85 
86 	if (src->le.list) {
87 		pthread_rwlock_wrlock(&src->mix->rwlock);
88 		list_unlink(&src->le);
89 		clear_all(src->mix);
90 		pthread_rwlock_unlock(&src->mix->rwlock);
91 	}
92 
93 	mem_deref(src->frame_tx);
94 	mem_deref(src->frame_rx);
95 	mem_deref(src->mix);
96 }
97 
98 
99 static inline void source_mix(struct vidframe *mframe,
100 			      const struct vidframe *frame_src,
101 			      unsigned n, unsigned rows, unsigned idx,
102 			      bool focus, bool focus_this, bool focus_full)
103 {
104 	struct vidrect rect;
105 
106 	if (!frame_src)
107 		return;
108 
109 	if (focus) {
110 
111 		const unsigned nmin = focus_full ? 12 : 6;
112 
113 		n = max((n+1), nmin)/2;
114 
115 		if (focus_this) {
116 			rect.w = mframe->size.w * (n-1) / n;
117 			rect.h = mframe->size.h * (n-1) / n;
118 			rect.x = 0;
119 			rect.y = 0;
120 		}
121 		else {
122 			rect.w = mframe->size.w / n;
123 			rect.h = mframe->size.h / n;
124 
125 			if (idx < n) {
126 				rect.x = mframe->size.w - rect.w;
127 				rect.y = rect.h * idx;
128 			}
129 			else if (idx < (n*2 - 1)) {
130 				rect.x = rect.w * (n*2 - 2 - idx);
131 				rect.y = mframe->size.h - rect.h;
132 			}
133 			else {
134 				return;
135 			}
136 		}
137 	}
138 	else if (rows == 1) {
139 
140 		source_mix_full(mframe, frame_src);
141 		return;
142 	}
143 	else {
144 		rect.w = mframe->size.w / rows;
145 		rect.h = mframe->size.h / rows;
146 		rect.x = rect.w * (idx % rows);
147 		rect.y = rect.h * (idx / rows);
148 	}
149 
150 	vidconv_aspect(mframe, frame_src, &rect);
151 }
152 
153 
154 static inline void source_mix_full(struct vidframe *mframe,
155 				   const struct vidframe *frame_src)
156 {
157 	if (!frame_src)
158 		return;
159 
160 	if (vidsz_cmp(&mframe->size, &frame_src->size)) {
161 
162 		vidframe_copy(mframe, frame_src);
163 	}
164 	else {
165 		struct vidrect rect;
166 
167 		rect.w = mframe->size.w;
168 		rect.h = mframe->size.h;
169 		rect.x = 0;
170 		rect.y = 0;
171 
172 		vidconv_aspect(mframe, frame_src, &rect);
173 	}
174 }
175 
176 
177 static inline unsigned calc_rows(unsigned n)
178 {
179 	unsigned rows;
180 
181 	for (rows=1;; rows++)
182 		if (n <= (rows * rows))
183 			return rows;
184 }
185 
186 
187 static void *vidmix_thread(void *arg)
188 {
189 	struct vidmix_source *src = arg;
190 	struct vidmix *mix = src->mix;
191 	uint64_t ts = tmr_jiffies();
192 
193 	pthread_mutex_lock(&src->mutex);
194 
195 	while (src->run) {
196 
197 		unsigned n, rows, idx;
198 		struct le *le;
199 		uint64_t now;
200 
201 		pthread_mutex_unlock(&src->mutex);
202 		(void)usleep(4000);
203 		pthread_mutex_lock(&src->mutex);
204 
205 		now = tmr_jiffies();
206 
207 		if (ts > now)
208 			continue;
209 
210 		if (!src->frame_tx) {
211 			ts += src->fint;
212 			continue;
213 		}
214 
215 		pthread_rwlock_rdlock(&mix->rwlock);
216 
217 		if (src->clear) {
218 			clear_frame(src->frame_tx);
219 			src->clear = false;
220 		}
221 
222 		for (le=mix->srcl.head, n=0; le; le=le->next) {
223 
224 			const struct vidmix_source *lsrc = le->data;
225 
226 			if (lsrc == src && !src->selfview)
227 				continue;
228 
229 			if (lsrc->content && src->content_hide)
230 				continue;
231 
232 			if (lsrc == src->focus && src->focus_full)
233 				source_mix_full(src->frame_tx, lsrc->frame_rx);
234 
235 			++n;
236 		}
237 
238 		rows = calc_rows(n);
239 
240 		for (le=mix->srcl.head, idx=0; le; le=le->next) {
241 
242 			const struct vidmix_source *lsrc = le->data;
243 
244 			if (lsrc == src && !src->selfview)
245 				continue;
246 
247 			if (lsrc->content && src->content_hide)
248 				continue;
249 
250 			if (lsrc == src->focus && src->focus_full)
251 				continue;
252 
253 			source_mix(src->frame_tx, lsrc->frame_rx, n, rows, idx,
254 				   src->focus != NULL, src->focus == lsrc,
255 				   src->focus_full);
256 
257 			if (src->focus != lsrc)
258 				++idx;
259 		}
260 
261 		pthread_rwlock_unlock(&mix->rwlock);
262 
263 		src->fh((uint32_t)ts * 90, src->frame_tx, src->arg);
264 
265 		ts += src->fint;
266 	}
267 
268 	pthread_mutex_unlock(&src->mutex);
269 
270 	return NULL;
271 }
272 
273 
274 static void *content_thread(void *arg)
275 {
276 	struct vidmix_source *src = arg;
277 	struct vidmix *mix = src->mix;
278 	uint64_t ts = tmr_jiffies();
279 
280 	pthread_mutex_lock(&src->mutex);
281 
282 	while (src->run) {
283 
284 		struct le *le;
285 		uint64_t now;
286 
287 		pthread_mutex_unlock(&src->mutex);
288 		(void)usleep(4000);
289 		pthread_mutex_lock(&src->mutex);
290 
291 		now = tmr_jiffies();
292 
293 		if (ts > now)
294 			continue;
295 
296 		pthread_rwlock_rdlock(&mix->rwlock);
297 
298 		for (le=mix->srcl.head; le; le=le->next) {
299 
300 			const struct vidmix_source *lsrc = le->data;
301 
302 			if (!lsrc->content || !lsrc->frame_rx || lsrc == src)
303 				continue;
304 
305 			src->fh((uint32_t)ts * 90, lsrc->frame_rx, src->arg);
306 			break;
307 		}
308 
309 		pthread_rwlock_unlock(&mix->rwlock);
310 
311 		ts += src->fint;
312 	}
313 
314 	pthread_mutex_unlock(&src->mutex);
315 
316 	return NULL;
317 }
318 
319 
320 /**
321  * Allocate a new Video mixer
322  *
323  * @param mixp Pointer to allocated video mixer
324  *
325  * @return 0 for success, otherwise error code
326  */
327 int vidmix_alloc(struct vidmix **mixp)
328 {
329 	pthread_rwlockattr_t attr;
330 	struct vidmix *mix;
331 	int err;
332 
333 	if (!mixp)
334 		return EINVAL;
335 
336 	mix = mem_zalloc(sizeof(*mix), destructor);
337 	if (!mix)
338 		return ENOMEM;
339 
340 	err = pthread_rwlockattr_init(&attr);
341 	if (err) {
342 		mem_deref(mix);
343 		return err;
344 	}
345 
346 #if defined(LINUX) && defined(__GLIBC__)
347 	err = pthread_rwlockattr_setkind_np(&attr,
348 				 PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
349 	if (err)
350 		goto out;
351 #endif
352 
353 	err = pthread_rwlock_init(&mix->rwlock, &attr);
354 	if (err)
355 		goto out;
356 
357 	mix->initialized = true;
358 
359  out:
360 	(void)pthread_rwlockattr_destroy(&attr);
361 
362 	if (err)
363 		mem_deref(mix);
364 	else
365 		*mixp = mix;
366 
367 	return err;
368 }
369 
370 
371 /**
372  * Allocate a video mixer source
373  *
374  * @param srcp    Pointer to allocated video source
375  * @param mix     Video mixer
376  * @param sz      Size of output video frame (optional)
377  * @param fps     Output frame rate (frames per second)
378  * @param content True if source is of type content
379  * @param fh      Mixer frame handler
380  * @param arg     Handler argument
381  *
382  * @return 0 for success, otherwise error code
383  */
384 int vidmix_source_alloc(struct vidmix_source **srcp, struct vidmix *mix,
385 			const struct vidsz *sz, unsigned fps, bool content,
386 			vidmix_frame_h *fh, void *arg)
387 {
388 	struct vidmix_source *src;
389 	int err;
390 
391 	if (!srcp || !mix || !fps || !fh)
392 		return EINVAL;
393 
394 	src = mem_zalloc(sizeof(*src), source_destructor);
395 	if (!src)
396 		return ENOMEM;
397 
398 	src->mix     = mem_ref(mix);
399 	src->fint    = 1000/fps;
400 	src->content = content;
401 	src->fh      = fh;
402 	src->arg     = arg;
403 
404 	err = pthread_mutex_init(&src->mutex, NULL);
405 	if (err)
406 		goto out;
407 
408 	if (sz) {
409 		err = vidframe_alloc(&src->frame_tx, VID_FMT_YUV420P, sz);
410 		if (err)
411 			goto out;
412 
413 		clear_frame(src->frame_tx);
414 	}
415 
416  out:
417 	if (err)
418 		mem_deref(src);
419 	else
420 		*srcp = src;
421 
422 	return err;
423 }
424 
425 
426 /**
427  * Check if vidmix source is enabled
428  *
429  * @param src Video mixer source
430  *
431  * @return true if enabled, otherwise false
432  */
433 bool vidmix_source_isenabled(const struct vidmix_source *src)
434 {
435 	return src ? (src->le.list != NULL) : false;
436 }
437 
438 
439 /**
440  * Check if vidmix source is running
441  *
442  * @param src Video mixer source
443  *
444  * @return true if running, otherwise false
445  */
446 bool vidmix_source_isrunning(const struct vidmix_source *src)
447 {
448 	return src ? src->run : false;
449 }
450 
451 
452 /**
453  * Get focus source
454  *
455  * @param src Video mixer source
456  *
457  * @return pointer of focused source or NULL if focus is not set
458  */
459 void *vidmix_source_get_focus(const struct vidmix_source *src)
460 {
461 	return src ? src->focus : NULL;
462 }
463 
464 
465 /**
466  * Enable/disable vidmix source
467  *
468  * @param src    Video mixer source
469  * @param enable True to enable, false to disable
470  */
471 void vidmix_source_enable(struct vidmix_source *src, bool enable)
472 {
473 	if (!src)
474 		return;
475 
476 	if (src->le.list && enable)
477 		return;
478 
479 	if (!src->le.list && !enable)
480 		return;
481 
482 	pthread_rwlock_wrlock(&src->mix->rwlock);
483 
484 	if (enable) {
485 		if (src->frame_rx)
486 			clear_frame(src->frame_rx);
487 
488 		list_append(&src->mix->srcl, &src->le, src);
489 	}
490 	else {
491 		list_unlink(&src->le);
492 	}
493 
494 	clear_all(src->mix);
495 
496 	pthread_rwlock_unlock(&src->mix->rwlock);
497 }
498 
499 
500 /**
501  * Start vidmix source thread
502  *
503  * @param src    Video mixer source
504  *
505  * @return 0 for success, otherwise error code
506  */
507 int vidmix_source_start(struct vidmix_source *src)
508 {
509 	int err;
510 
511 	if (!src)
512 		return EINVAL;
513 
514 	if (src->run)
515 		return EALREADY;
516 
517 	src->run = true;
518 
519 	err = pthread_create(&src->thread, NULL,
520 			     src->content ? content_thread : vidmix_thread,
521 			     src);
522 	if (err) {
523 		src->run = false;
524 	}
525 
526 	return err;
527 }
528 
529 
530 /**
531  * Stop vidmix source thread
532  *
533  * @param src    Video mixer source
534  */
535 void vidmix_source_stop(struct vidmix_source *src)
536 {
537 	if (!src)
538 		return;
539 
540 	if (src->run) {
541 		src->run = false;
542 		pthread_join(src->thread, NULL);
543 	}
544 }
545 
546 
547 /**
548  * Set video mixer output frame size
549  *
550  * @param src  Video mixer source
551  * @param sz   Size of output video frame
552  *
553  * @return 0 for success, otherwise error code
554  */
555 int vidmix_source_set_size(struct vidmix_source *src, const struct vidsz *sz)
556 {
557 	struct vidframe *frame;
558 	int err;
559 
560 	if (!src || !sz)
561 		return EINVAL;
562 
563 	if (src->frame_tx && vidsz_cmp(&src->frame_tx->size, sz))
564 		return 0;
565 
566 	err = vidframe_alloc(&frame, VID_FMT_YUV420P, sz);
567 	if (err)
568 		return err;
569 
570 	clear_frame(frame);
571 
572 	pthread_mutex_lock(&src->mutex);
573 	mem_deref(src->frame_tx);
574 	src->frame_tx = frame;
575 	pthread_mutex_unlock(&src->mutex);
576 
577 	return 0;
578 }
579 
580 
581 /**
582  * Set video mixer output frame rate
583  *
584  * @param src  Video mixer source
585  * @param fps  Output frame rate (frames per second)
586  */
587 void vidmix_source_set_rate(struct vidmix_source *src, unsigned fps)
588 {
589 	if (!src || !fps)
590 		return;
591 
592 	pthread_mutex_lock(&src->mutex);
593 	src->fint = 1000/fps;
594 	pthread_mutex_unlock(&src->mutex);
595 }
596 
597 
598 /**
599  * Set video mixer content hide
600  *
601  * @param src    Video mixer source
602  * @param hide   True to hide content, false to show
603  */
604 void vidmix_source_set_content_hide(struct vidmix_source *src, bool hide)
605 {
606 	if (!src)
607 		return;
608 
609 	pthread_mutex_lock(&src->mutex);
610 	src->content_hide = hide;
611 	src->clear = true;
612 	pthread_mutex_unlock(&src->mutex);
613 }
614 
615 
616 /**
617  * Toggle vidmix source selfview
618  *
619  * @param src    Video mixer source
620  */
621 void vidmix_source_toggle_selfview(struct vidmix_source *src)
622 {
623 	if (!src)
624 		return;
625 
626 	pthread_mutex_lock(&src->mutex);
627 	src->selfview = !src->selfview;
628 	src->clear = true;
629 	pthread_mutex_unlock(&src->mutex);
630 }
631 
632 
633 /**
634  * Set focus on selected participant source
635  *
636  * @param src        Video mixer source
637  * @param focus_src  Video mixer source to focus, NULL to clear focus state
638  * @param focus_full Full focus
639  */
640 void vidmix_source_set_focus(struct vidmix_source *src,
641 			     const struct vidmix_source *focus_src,
642 			     bool focus_full)
643 {
644 	if (!src)
645 		return;
646 
647 	pthread_mutex_lock(&src->mutex);
648 	src->focus_full = focus_full;
649 	src->focus = (void *)focus_src;
650 	src->clear = true;
651 	pthread_mutex_unlock(&src->mutex);
652 }
653 
654 
655 /**
656  * Set focus on selected participant
657  *
658  * @param src    Video mixer source
659  * @param pidx   Participant to focus, 0 to disable
660  */
661 void vidmix_source_set_focus_idx(struct vidmix_source *src, unsigned pidx)
662 {
663 	bool focus_full = false;
664 	void *focus = NULL;
665 
666 	if (!src)
667 		return;
668 
669 	if (pidx > 0) {
670 
671 		struct le *le;
672 		unsigned i;
673 
674 		pthread_rwlock_rdlock(&src->mix->rwlock);
675 
676 		for (le=src->mix->srcl.head, i=1; le; le=le->next) {
677 
678 			const struct vidmix_source *lsrc = le->data;
679 
680 			if (lsrc == src && !src->selfview)
681 				continue;
682 
683 			if (lsrc->content && src->content_hide)
684 				continue;
685 
686 			if (i++ == pidx) {
687 				focus = (void *)lsrc;
688 				break;
689 			}
690 		}
691 
692 		pthread_rwlock_unlock(&src->mix->rwlock);
693 	}
694 
695 	if (focus && focus == src->focus)
696 		focus_full = !src->focus_full;
697 
698 	pthread_mutex_lock(&src->mutex);
699 	src->focus_full = focus_full;
700 	src->focus = focus;
701 	src->clear = true;
702 	pthread_mutex_unlock(&src->mutex);
703 }
704 
705 
706 /**
707  * Put a video frame into the video mixer
708  *
709  * @param src   Video source
710  * @param frame Video frame
711  */
712 void vidmix_source_put(struct vidmix_source *src, const struct vidframe *frame)
713 {
714 	if (!src || !frame || frame->fmt != VID_FMT_YUV420P)
715 		return;
716 
717 	if (!src->frame_rx || !vidsz_cmp(&src->frame_rx->size, &frame->size)) {
718 
719 		struct vidframe *frm;
720 		int err;
721 
722 		err = vidframe_alloc(&frm, VID_FMT_YUV420P, &frame->size);
723 		if (err)
724 			return;
725 
726 		pthread_rwlock_wrlock(&src->mix->rwlock);
727 
728 		mem_deref(src->frame_rx);
729 		src->frame_rx = frm;
730 
731 		clear_all(src->mix);
732 
733 		pthread_rwlock_unlock(&src->mix->rwlock);
734 	}
735 
736 	vidframe_copy(src->frame_rx, frame);
737 }
738