UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
conference.cpp File Reference
#include <condition_variable>
#include <chrono>
#include <cstdint>
#include <map>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include <string_view>
#include "debug.h"
#include "host.h"
#include "lib_common.h"
#include "module.h"
#include "utils/color_out.h"
#include "utils/misc.h"
#include "utils/string_view_utils.hpp"
#include "video.h"
#include "video_codec.h"
#include "video_display.h"
#include <opencv2/core/mat.hpp>
#include <opencv2/imgproc.hpp>
#include "utils/profile_timer.hpp"
Include dependency graph for conference.cpp:

Go to the source code of this file.

Classes

struct  state_conference
 
struct  state_conference_common
 

Macros

#define FAIL_IF(x, msg)
 
#define MOD_NAME   "[conference] "
 

Functions

static void check_reconf (struct state_conference_common *s, struct video_desc desc)
 
static void display_conference_done (void *state)
 
static struct display * display_conference_fork (void *state)
 
static bool display_conference_get_property (void *state, int property, void *val, size_t *len)
 
static struct video_frame * display_conference_getf (void *state)
 
static void * display_conference_init (struct module *parent, const char *fmt, unsigned int flags)
 
static void display_conference_probe (struct device_info **available_cards, int *count, void(**deleter)(void *))
 
static void display_conference_put_audio_frame (void *state, const struct audio_frame *frame)
 
static bool display_conference_putf (void *state, struct video_frame *frame, long long flags)
 
static bool display_conference_reconfigure (void *state, struct video_desc desc)
 
static bool display_conference_reconfigure_audio (void *state, int quant_samples, int channels, int sample_rate)
 
static void display_conference_run (void *state)
 
static void display_conference_worker (std::shared_ptr< state_conference_common > s)
 
 REGISTER_HIDDEN_MODULE (conference, &display_conference_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION)
 
static void show_help (bool from_reflector=false)
 
static constexpr std::chrono::milliseconds SOURCE_TIMEOUT (500)
 

Variables

static const struct video_display_info display_conference_info
 
static constexpr unsigned int IN_QUEUE_MAX_BUFFER_LEN = 5
 

Detailed Description

Macro Definition Documentation

◆ FAIL_IF

#define FAIL_IF ( x,
msg )
Value:
do {\
if(x){\
log_msg(LOG_LEVEL_ERROR, msg);\
show_help();\
return INIT_NOERR;\
}\
} while(0)\
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define INIT_NOERR
Definition host.h:73

Referenced by display_conference_init().

◆ MOD_NAME

#define MOD_NAME   "[conference] "

Definition at line 77 of file conference.cpp.

Function Documentation

◆ check_reconf()

static void check_reconf ( struct state_conference_common * s,
struct video_desc desc )
static

Definition at line 539 of file conference.cpp.

540{
541 if (!video_desc_eq(desc, s->display_desc)) {
542 s->display_desc = desc;
543 log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "reconfiguring real display\n");
545 }
546}
struct video_desc desc
#define MOD_NAME
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_VERBOSE
Definition debug.h:63
struct video_desc display_desc
@ VIDEO_NORMAL
normal video (one tile)
Definition types.h:356
bool display_reconfigure(struct display *d, struct video_desc desc, enum video_mode video_mode)
Reconfigure display to new video format.
bool video_desc_eq(struct video_desc a, struct video_desc b)
Compares two video descriptions.

References desc, state_conference_common::display_desc, display_reconfigure(), LOG_LEVEL_VERBOSE, log_msg(), MOD_NAME, state_conference_common::real_display, video_desc_eq(), and VIDEO_NORMAL.

Here is the call graph for this function:

◆ display_conference_done()

static void display_conference_done ( void * state)
static

Definition at line 716 of file conference.cpp.

717{
718 auto s = static_cast<state_conference *>(state);
719
720 delete s;
721}
void ** state

References state.

◆ display_conference_fork()

static struct display * display_conference_fork ( void * state)
static

Definition at line 438 of file conference.cpp.

439{
440 auto s = ((struct state_conference *)state)->common;
441 struct display *out;
442 char fmt[2 + sizeof(void *) * 2 + 1] = "";
443 snprintf(fmt, sizeof fmt, "%p", state);
444
445 int rc = initialize_video_display(s->parent,
446 "conference", fmt, 0, NULL, &out);
447 if (rc == 0) return out; else return NULL;
448
449 return out;
450}
const char * fmt
int initialize_video_display(struct module *parent, const char *requested_display, const char *fmt, unsigned int flags, const char *postprocess, struct display **out)
Initializes video display.
This struct represents initialized video display state.
std::shared_ptr< state_conference_common > common

References fmt, initialize_video_display(), and state.

Referenced by display_conference_get_property().

Here is the call graph for this function:

◆ display_conference_get_property()

static bool display_conference_get_property ( void * state,
int property,
void * val,
size_t * len )
static

Definition at line 667 of file conference.cpp.

668{
669 auto s = ((struct state_conference *)state)->common;
671 ((struct multi_sources_supp_info *) val)->val = true;
673 ((struct multi_sources_supp_info *) val)->state = state;
674 *len = sizeof(struct multi_sources_supp_info);
675 return true;
676
677 } else if(property == DISPLAY_PROPERTY_CODECS) {
678 codec_t codecs[] = {UYVY};
679
680 memcpy(val, codecs, sizeof(codecs));
681
682 *len = sizeof(codecs);
683
684 return true;
685 }
686
687 return display_ctl_property(s->real_display.get(), property, val, len);
688}
int void * val
int void size_t * len
int property
static struct display * display_conference_fork(void *state)
bool display_ctl_property(struct display *d, int property, void *val, size_t *len)
Gets property from video display.
@ DISPLAY_PROPERTY_SUPPORTS_MULTI_SOURCES
@ DISPLAY_PROPERTY_CODECS
list of natively supported codecs - codec_t[]
struct display *(* fork_display)(void *state)
codec_t
Definition types.h:65
@ UYVY
YCbCr 422 8-bit - Cb Y0 Cr Y1.
Definition types.h:71
static struct @76 codecs[]

References codecs, display_conference_fork(), display_ctl_property(), DISPLAY_PROPERTY_CODECS, DISPLAY_PROPERTY_SUPPORTS_MULTI_SOURCES, len, property, state, UYVY, and val.

Here is the call graph for this function:

◆ display_conference_getf()

static struct video_frame * display_conference_getf ( void * state)
static

Definition at line 723 of file conference.cpp.

724{
726 auto s = static_cast<state_conference *>(state);
727
728 return vf_alloc_desc_data(s->desc);
729}
struct video_frame * vf_alloc_desc_data(struct video_desc desc)
#define PROFILE_FUNC

References PROFILE_FUNC, state, and vf_alloc_desc_data().

Here is the call graph for this function:

◆ display_conference_init()

static void * display_conference_init ( struct module * parent,
const char * fmt,
unsigned int flags )
static

Definition at line 466 of file conference.cpp.

467{
468 auto s = std::make_unique<state_conference>();
469 log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "init fmt: %s\n", fmt);
470
471 if(!fmt){
472 show_help();
473 return INIT_NOERR;
474 }
475
476 if (strcmp(fmt, "reflhelp") == 0) {
477 show_help(true);
478 return INIT_NOERR;
479 }
480
481 if (isdigit(fmt[0])){ // fork
482 struct state_conference *orig;
483 sscanf(fmt, "%p", &orig);
484 s->common = orig->common;
485 return s.release();
486 }
487
488 std::string requested_display = "gl";
489 std::string disp_conf;
490
491 s->common = std::make_shared<state_conference_common>(parent);
492
493 auto& desc = s->common->desc;
496 desc.tile_count = 1;
497 desc.fps = 0;
498
499 std::string_view param = fmt;
500 auto disp_cfg = tokenize(param, '#');
501 auto conf_cfg = tokenize(param, '#');
502
503#define FAIL_IF(x, msg) \
504 do {\
505 if(x){\
506 log_msg(LOG_LEVEL_ERROR, msg);\
507 show_help();\
508 return INIT_NOERR;\
509 }\
510 } while(0)\
511
512 requested_display = tokenize(disp_cfg, ':');
513 FAIL_IF(requested_display.empty(), "Requested display cannot be empty\n");
514 disp_conf = tokenize(disp_cfg, ':');
515 FAIL_IF(!parse_num(tokenize(conf_cfg, ':'), desc.width), "Failed to parse width\n");
516 FAIL_IF(!parse_num(tokenize(conf_cfg, ':'), desc.height), "Failed to parse height\n");
517
518 auto tok = tokenize(conf_cfg, ':');
519 FAIL_IF(!tok.empty() && !parse_num(tokenize(tok, ':'), s->common->req_fps), "Failed to parse fps\n");
520
521 desc.fps = s->common->req_fps;
522
523 tok = tokenize(conf_cfg, ':');
524 if(tok == "one_big"){
525 s->common->layout = Video_mixer::Layout::One_big;
526 }
527
528 int ret = initialize_video_display(parent, requested_display.c_str(), disp_conf.c_str(),
529 flags, nullptr, out_ptr(s->common->real_display));
530
531 if(ret != 0){
532 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to init real display\n");
533 return nullptr;
534 }
535
536 return s.release();
537}
const char unsigned int flags
static void show_help()
Definition wasapi.cpp:193
Definition misc.h:94
#define FAIL_IF(x, msg)
const char * parent
Definition settings.cpp:198
const char * param
Definition settings.cpp:195
std::string_view tokenize(std::string_view &str, char delim, char quot)
Tokenizer for string_view.
bool parse_num(std::string_view sv, T &res, int base=10)
unsigned int tile_count
Definition types.h:185
codec_t color_spec
Definition types.h:182
enum interlacing_t interlacing
Definition types.h:184
double fps
Definition types.h:183
unsigned int width
width of every tile (!)
Definition types.h:179
unsigned int height
height of every tile (!)
Definition types.h:180
@ PROGRESSIVE
progressive frame
Definition types.h:164

References video_desc::color_spec, state_conference::common, desc, FAIL_IF, flags, fmt, video_desc::fps, video_desc::height, INIT_NOERR, initialize_video_display(), video_desc::interlacing, LOG_LEVEL_ERROR, LOG_LEVEL_VERBOSE, log_msg(), MOD_NAME, param, parent, parse_num(), PROGRESSIVE, show_help(), video_desc::tile_count, tokenize(), UYVY, and video_desc::width.

Here is the call graph for this function:

◆ display_conference_probe()

static void display_conference_probe ( struct device_info ** available_cards,
int * count,
void(** deleter )(void *) )
static

Definition at line 758 of file conference.cpp.

758 {
760 *available_cards = nullptr;
761 *count = 0;
762}
#define UNUSED(x)
int void(** deleter)(void *))
int count
Definition hd-rum.c:44

References count, deleter, and UNUSED.

◆ display_conference_put_audio_frame()

static void display_conference_put_audio_frame ( void * state,
const struct audio_frame * frame )
static

Definition at line 699 of file conference.cpp.

700{
701 UNUSED(state);
702 UNUSED(frame);
703}
struct video_frame * frame

References frame, state, and UNUSED.

◆ display_conference_putf()

static bool display_conference_putf ( void * state,
struct video_frame * frame,
long long flags )
static

Definition at line 731 of file conference.cpp.

732{
733 auto s = static_cast<state_conference *>(state)->common;
734
735 if (flags == PUTF_DISCARD) {
736 vf_free(frame);
737 } else {
738 std::unique_lock<std::mutex> lg(s->incoming_frames_lock);
739 if (s->incoming_frames.size() >= IN_QUEUE_MAX_BUFFER_LEN) {
740 log_msg(LOG_LEVEL_WARNING, "[conference] queue full!\n");
741 if(flags != PUTF_BLOCKING){
742 vf_free(frame);
743 return 1;
744 }
745 }
746 s->incoming_frame_consumed.wait(lg,
747 [s]{
748 return s->incoming_frames.size() < IN_QUEUE_MAX_BUFFER_LEN;
749 });
750 s->incoming_frames.emplace(frame);
751 lg.unlock();
752 s->new_incoming_frame_cv.notify_one();
753 }
754
755 return true;
756}
void vf_free(struct video_frame *buf)
Frees video_frame structure.
static constexpr unsigned int IN_QUEUE_MAX_BUFFER_LEN
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
#define PUTF_DISCARD
#define PUTF_BLOCKING

References flags, frame, IN_QUEUE_MAX_BUFFER_LEN, LOG_LEVEL_WARNING, log_msg(), PUTF_BLOCKING, PUTF_DISCARD, state, and vf_free().

Here is the call graph for this function:

◆ display_conference_reconfigure()

static bool display_conference_reconfigure ( void * state,
struct video_desc desc )
static

Definition at line 690 of file conference.cpp.

691{
692 struct state_conference *s = (struct state_conference *) state;
693
694 s->desc = desc;
695
696 return true;
697}
struct video_desc desc

References desc, state_conference::desc, and state.

◆ display_conference_reconfigure_audio()

static bool display_conference_reconfigure_audio ( void * state,
int quant_samples,
int channels,
int sample_rate )
static

Definition at line 705 of file conference.cpp.

707{
708 UNUSED(state);
712
713 return false;
714}
int int int sample_rate
int int channels
int quant_samples

References channels, quant_samples, sample_rate, state, and UNUSED.

◆ display_conference_run()

static void display_conference_run ( void * state)
static

Definition at line 655 of file conference.cpp.

656{
658 auto s = static_cast<state_conference *>(state)->common;
659
660 std::thread worker = std::thread(display_conference_worker, s);
661
662 display_run_mainloop(s->real_display.get());
663
664 worker.join();
665}
static void display_conference_worker(std::shared_ptr< state_conference_common > s)
void display_run_mainloop(struct display *d)
Display mainloop function.
static void * worker(void *args)
Definition alsa.c:190

References display_conference_worker(), display_run_mainloop(), PROFILE_FUNC, state, and worker().

Here is the call graph for this function:

◆ display_conference_worker()

static void display_conference_worker ( std::shared_ptr< state_conference_common > s)
static

We initially set next_frame_time to infinity (or a very long time from now) to avoid uselessly outputing the same frame twice in a row if we haven't received any new input, because then any new input would have to wait up to 1 frametime adding needless latency.

Definition at line 548 of file conference.cpp.

548 {
550 Video_mixer mixer(s->desc.width, s->desc.height, s->layout);
551
558 auto next_frame_time = clock::now() + std::chrono::hours(1); //workaround for gcc bug 58931
559 auto last_frame_time = clock::time_point::min();
560 for(;;){
561 struct message *msg;
562 while ((msg = check_message(s->mod.get()))) {
563 auto msg_univ = reinterpret_cast<struct msg_universal *>(msg);
564 std::string_view msg_text = msg_univ->text;
565 auto key = tokenize(msg_text, '=');
566 auto val = tokenize(msg_text, '=');
567
568 log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Received message: %s\n", msg_univ->text);
569 struct response *r;
570 if (key == "layout") {
571 Video_mixer::Layout new_layout = layout_from_name(val);
572
573 if(new_layout == Video_mixer::Layout::Invalid){
574 log_msg(LOG_LEVEL_ERROR, "Unknown layout %s\n", std::string(val).c_str());
575 r = new_response(RESPONSE_BAD_REQUEST, "Unknown layout!");
576 } else {
577 log_msg(LOG_LEVEL_NOTICE, "Setting layout to %s\n", std::string(val).c_str());
578 mixer.set_layout(new_layout);
579 r = new_response(RESPONSE_OK, NULL);
580 }
581
582 } else if (key == "primary_ssrc"){
583 uint32_t parsed_ssrc = 0;
584 if(!parse_num(val, parsed_ssrc, 16)){
585 log_msg(LOG_LEVEL_ERROR, "Unable to parse ssrc!\n");
586 r = new_response(RESPONSE_BAD_REQUEST, "Unable to parse ssrc!");
587 } else {
588 log_msg(LOG_LEVEL_NOTICE, "Setting primary ssrc to %x\n", parsed_ssrc);
589 mixer.set_primary_ssrc(parsed_ssrc);
590 r = new_response(RESPONSE_OK, NULL);
591 }
592
593 } else if (key == "info"){
594 auto ssrcs = mixer.get_participant_ssrc_list();
595 log_msg(LOG_LEVEL_NOTICE, "Conference layout: %s\n", layout_get_name(mixer.get_layout()));
596 log_msg(LOG_LEVEL_NOTICE, "%zu participants \n", ssrcs.size());
597 for(const auto& p : ssrcs){
598 log_msg(LOG_LEVEL_NOTICE, "%s %x\n", p == mixer.get_primary_ssrc() ? "*" : " ", p);
599 }
600 r = new_response(RESPONSE_OK, NULL);
601 } else {
602 r = new_response(RESPONSE_BAD_REQUEST, "Wrong message!");
603 }
604 free_message(msg, r);
605 }
606
608
609 auto wait_p = [s]{ return !s->incoming_frames.empty(); };
610 std::unique_lock<std::mutex> lock(s->incoming_frames_lock);
611 bool have_frame = s->new_incoming_frame_cv.wait_until(lock, next_frame_time, wait_p);
612 if(have_frame){
613 frame = std::move(s->incoming_frames.front());
614 s->incoming_frames.pop();
615 }
616 lock.unlock();
617
618 if(have_frame){
619 s->incoming_frame_consumed.notify_one();
620 if(!frame){
621 display_put_frame(s->real_display.get(), nullptr, PUTF_BLOCKING);
622 break;
623 }
624 if(s->req_fps <= 0){
625 s->desc.fps = std::max(s->desc.fps, frame->fps);
626 }
627 mixer.process_frame(std::move(frame));
628 }
629
630 auto now = clock::now();
631 if(next_frame_time <= now){
632 check_reconf(s.get(), s->desc);
633 auto disp_frame = display_get_frame(s->real_display.get());
634
635 mixer.get_mixed(disp_frame);
636
637 display_put_frame(s->real_display.get(), disp_frame, PUTF_BLOCKING);
638
639 last_frame_time = next_frame_time;
640 next_frame_time = now + std::chrono::hours(1);
641 } else {
642 if(!have_frame)
643 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Woke up without new frame and before render time. This should not happen.\n");
644 using namespace std::chrono_literals;
645 next_frame_time = last_frame_time + std::chrono::duration_cast<clock::duration>(1s / s->desc.fps);
646 if(next_frame_time <= now){
647 next_frame_time = now;
648 }
649 }
650 }
651
652 return;
653}
static void check_reconf(struct state_conference_common *s, struct video_desc desc)
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
bool display_put_frame(struct display *d, struct video_frame *frame, long long timeout_ns)
Puts filled video frame. After calling this function, video frame cannot be used.
struct video_frame * display_get_frame(struct display *d)
Returns video framebuffer which will be written to.
void free_message(struct message *msg, struct response *r)
struct response * new_response(int status, const char *text)
struct message * check_message(struct module *mod)
#define RESPONSE_OK
Definition messaging.h:57
#define RESPONSE_BAD_REQUEST
Definition messaging.h:60
struct msghdr msg
char text[8192]
Definition messaging.h:157
double fps
Definition types.h:308
std::unique_ptr< video_frame, frame_deleter > unique_frame
Definition pipewire.cpp:78
const char * key
Definition cineform.cpp:125

References check_message(), check_reconf(), display_get_frame(), display_put_frame(), video_frame::fps, frame, free_message(), key, LOG_LEVEL_ERROR, LOG_LEVEL_NOTICE, LOG_LEVEL_WARNING, log_msg(), MOD_NAME, msg, new_response(), parse_num(), PROFILE_FUNC, PUTF_BLOCKING, RESPONSE_BAD_REQUEST, RESPONSE_OK, msg_universal::text, tokenize(), and val.

Referenced by display_conference_run().

Here is the call graph for this function:

◆ REGISTER_HIDDEN_MODULE()

REGISTER_HIDDEN_MODULE ( conference ,
& display_conference_info,
LIBRARY_CLASS_VIDEO_DISPLAY ,
VIDEO_DISPLAY_ABI_VERSION  )

◆ show_help()

static void show_help ( bool from_reflector = false)
static

Definition at line 452 of file conference.cpp.

452 {
453 if (from_reflector) {
454 color_printf(TBOLD("conference")
455 " options:\n");
456 color_printf("\t" TBOLD(
457 "-r <width>:<height>[:<fps>]")
458 "\n");
459 return;
460 }
461 printf("Conference display\n");
462 printf("Usage:\n");
463 printf("\t-d conference:<display_config>#<width>:<height>:[fps]:[layout]\n");
464}
int color_printf(const char *format,...)
#define TBOLD(x)
Definition color_out.h:70

References color_printf(), and TBOLD.

Here is the call graph for this function:

◆ SOURCE_TIMEOUT()

static constexpr std::chrono::milliseconds SOURCE_TIMEOUT ( 500 )
staticconstexpr

Variable Documentation

◆ display_conference_info

const struct video_display_info display_conference_info
static
Initial value:
= {
}
static void display_conference_probe(struct device_info **available_cards, int *count, void(**deleter)(void *))
static struct video_frame * display_conference_getf(void *state)
static bool display_conference_get_property(void *state, int property, void *val, size_t *len)
static bool display_conference_putf(void *state, struct video_frame *frame, long long flags)
static bool display_conference_reconfigure_audio(void *state, int quant_samples, int channels, int sample_rate)
static void display_conference_run(void *state)
static void display_conference_put_audio_frame(void *state, const struct audio_frame *frame)
static void * display_conference_init(struct module *parent, const char *fmt, unsigned int flags)
static void display_conference_done(void *state)
static bool display_conference_reconfigure(void *state, struct video_desc desc)
#define DISPLAY_NO_GENERIC_FPS_INDICATOR

Definition at line 764 of file conference.cpp.

◆ IN_QUEUE_MAX_BUFFER_LEN

unsigned int IN_QUEUE_MAX_BUFFER_LEN = 5
staticconstexpr

Definition at line 410 of file conference.cpp.