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