UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
conference.cpp
Go to the documentation of this file.
1
6/*
7 * Copyright (c) 2014-2024
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, is permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * 3. Neither the name of CESNET nor the names of its contributors may be
22 * used to endorse or promote products derived from this software without
23 * specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
27 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
28 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
29 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
35 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
36 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39#include <condition_variable>
40#include <chrono>
41#include <cstdint>
42#include <map>
43#include <memory>
44#include <mutex>
45#include <queue>
46#include <thread>
47#include <string_view>
48
49#include "debug.h"
50#include "host.h"
51#include "lib_common.h"
52#include "module.h"
53#include "utils/color_out.h"
54#include "utils/misc.h"
56#include "video.h"
57#include "video_codec.h"
58#include "video_display.h"
59
60#pragma GCC diagnostic push
61#pragma GCC diagnostic ignored "-Wcast-align"
62#pragma GCC diagnostic ignored "-Wcast-qual"
63#ifdef HAVE_OPENCV2_OPENCV_HPP
64#include <opencv2/opencv.hpp>
65#else
66#include <opencv2/core/mat.hpp>
67#include <opencv2/imgproc.hpp>
68#endif
69#pragma GCC diagnostic pop
70
71#ifdef __SSSE3__
72#include "tmmintrin.h"
73#endif
74
76
77#define MOD_NAME "[conference] "
78
79namespace{
80using clock = std::chrono::steady_clock;
81
82struct frame_deleter{ void operator()(video_frame *f){ vf_free(f); } };
83using unique_frame = std::unique_ptr<video_frame, frame_deleter>;
84
85struct Participant{
86 void to_cv_frame();
87 void frame_recieved(unique_frame &&f);
88 void set_pos_keep_aspect(int x, int y, int w, int h);
89
91 clock::time_point last_time_recieved;
92 unsigned src_w = 0;
93 unsigned src_h = 0;
94
95 unsigned x = 0;
96 unsigned y = 0;
97 unsigned width = 0;
98 unsigned height = 0;
99
100 cv::Mat luma;
101 cv::Mat chroma;
102};
103
104void Participant::frame_recieved(unique_frame &&f){
105 frame = std::move(f);
106 last_time_recieved = clock::now();
107
108 src_w = frame->tiles[0].width;
109 src_h = frame->tiles[0].height;
110}
111
112void Participant::set_pos_keep_aspect(int x, int y, int w, int h){
113 double fx = static_cast<double>(w) / src_w;
114 double fy = static_cast<double>(h) / src_h;
115
116 if(fx > fy){
117 height = h;
118 width = src_w * fy;
119 x += (w - width) / 2;
120 } else {
121 height = src_h * fx;
122 width = w;
123 y += (h - height) / 2;
124 }
125
126 this->x = x;
127 this->y = y;
128}
129
130void Participant::to_cv_frame(){
131 if(!frame)
132 return;
133
134 assert(frame->color_spec == UYVY);
135 assert(frame->tile_count == 1);
136
138
139 auto& frame_tile = frame->tiles[0];
140 luma.create(cv::Size(frame_tile.width, frame_tile.height), CV_8UC1);
141 chroma.create(cv::Size(frame_tile.width / 2, frame_tile.height), CV_8UC2);
142
143 assert(luma.isContinuous() && chroma.isContinuous());
144 unsigned char *src = reinterpret_cast<unsigned char *>(frame_tile.data);
145 unsigned char *luma_dst = luma.ptr(0);
146 unsigned char *chroma_dst = chroma.ptr(0);
147
148 unsigned src_len = frame_tile.data_len;
149
150#ifdef __SSSE3__
151 __m128i uv_shuff = _mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, 14, 12, 10, 8, 6, 4, 2, 0);
152 __m128i y_shuff = _mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, 15, 13, 11, 9, 7, 5, 3, 1);
153 while(src_len >= 32){
154 __m128i uyvy = _mm_lddqu_si128((__m128i *)(void *) src);
155 src += 16;
156
157 __m128i y_low = _mm_shuffle_epi8(uyvy, y_shuff);
158 __m128i uv_low = _mm_shuffle_epi8(uyvy, uv_shuff);
159
160 uyvy = _mm_lddqu_si128((__m128i *)(void *) src);
161 src += 16;
162
163 __m128i y_high = _mm_shuffle_epi8(uyvy, y_shuff);
164 __m128i uv_high = _mm_shuffle_epi8(uyvy, uv_shuff);
165
166 _mm_store_si128((__m128i *)(void *) luma_dst, _mm_or_si128(y_low, _mm_bslli_si128(y_high, 8)));
167 luma_dst += 16;
168 _mm_store_si128((__m128i *)(void *) chroma_dst, _mm_or_si128(uv_low, _mm_bslli_si128(uv_high, 8)));
169 chroma_dst += 16;
170
171 src_len -= 32;
172 }
173#endif
174
175 while(src_len >= 4){
176 *chroma_dst++ = *src++;
177 *luma_dst++ = *src++;
178 *chroma_dst++ = *src++;
179 *luma_dst++ = *src++;
180
181 src_len -= 4;
182 }
183}
184
185class Video_mixer{
186public:
187 enum class Layout{ Invalid, Tiled, One_big };
188
189 Video_mixer(int width, int height, Layout l = Layout::Tiled);
190
191 void process_frame(unique_frame&& f);
192 void get_mixed(video_frame *result);
193
194 void set_layout(Layout new_layout) { layout = new_layout; recompute_layout(); }
195 Layout get_layout() const { return layout; }
196
197 void set_primary_ssrc(uint32_t ssrc) { primary_ssrc = ssrc; recompute_layout(); }
198 uint32_t get_primary_ssrc() const { return primary_ssrc; }
199
200 std::vector<uint32_t> get_participant_ssrc_list() const;
201
202private:
203 void recompute_layout();
204 void tiled_layout();
205 void one_big_layout();
206
207 unsigned width;
208 unsigned height;
209 Layout layout = Layout::Tiled;
210
211 uint32_t primary_ssrc = 0;
212
213 cv::Mat mixed_luma;
214 cv::Mat mixed_chroma;
215
216 std::map<uint32_t, Participant> participants;
217};
218
219Video_mixer::Video_mixer(int width, int height, Layout layout):
220 width(width),
221 height(height),
222 layout(layout)
223{
224 mixed_luma.create(cv::Size(width, height), CV_8UC1);
225 mixed_chroma.create(cv::Size(width / 2, height), CV_8UC2);
226}
227
228void Video_mixer::tiled_layout(){
229 const unsigned rows = (unsigned) ceil(sqrt(participants.size()));
230 const unsigned tile_width = width / rows;
231 const unsigned tile_height = height / rows;
232
233 int pos = 0;
234 for(auto& [ssrc, t]: participants){
235 (void) ssrc;
236 t.set_pos_keep_aspect((pos % rows) * tile_width,
237 (pos / rows) * tile_height,
238 tile_width, tile_height);
239
240 pos++;
241 }
242}
243
244void Video_mixer::one_big_layout(){
245 const unsigned small_height = height / 4;
246 const unsigned big_height = small_height * 3;
247 const unsigned tile_width = width / 4;
248
249 int pos = 0;
250 for(auto& [ssrc, t]: participants){
251 if(ssrc == primary_ssrc){
252 t.set_pos_keep_aspect(0, 0, width, big_height);
253 continue;
254 }
255
256 if(pos * tile_width > width)
257 continue;
258
259 t.set_pos_keep_aspect(pos * tile_width, big_height, tile_width, small_height);
260 pos++;
261 }
262}
263
264void Video_mixer::recompute_layout(){
265 mixed_luma.setTo(16);
266 mixed_chroma.setTo(128);
267
268 if(!primary_ssrc && !participants.empty()){
269 primary_ssrc = participants.begin()->first;
270 }
271
272 if(participants.size() == 1){
273 participants.begin()->second.set_pos_keep_aspect(0, 0, width, height);
274 return;
275 }
276
277 switch(layout){
278 case Layout::Tiled:
279 tiled_layout();
280 break;
281 case Layout::One_big:
282 one_big_layout();
283 break;
284 case Layout::Invalid:
285 assert("Invalid layout" && false);
286 break;
287 }
288}
289
290void Video_mixer::process_frame(unique_frame&& f){
291 auto iter = participants.find(f->ssrc);
292 auto& p = participants[f->ssrc];
293 p.frame_recieved(std::move(f));
294
295 if(iter == participants.end()){
296 recompute_layout();
297 }
298}
299
300void Video_mixer::get_mixed(video_frame *result){
302
303 auto now = clock::now();
304
305 bool recompute = false;
306 for(auto it = participants.begin(); it != participants.end();){
307 auto& p = it->second;
308 if(now - p.last_time_recieved > std::chrono::seconds(2)){
309 if(it->first == primary_ssrc)
310 primary_ssrc = 0;
311
312 it = participants.erase(it);
313 recompute = true;
314 continue;
315 }
316 it++;
317 }
318 if(recompute)
319 recompute_layout();
320
321 for(auto&& [ssrc, p] : participants){
322 (void) ssrc;
323 p.to_cv_frame();
324
325 PROFILE_DETAIL("resize participant");
326 cv::Size l_size(p.width, p.height);
327 cv::Size c_size(p.width / 2, p.height);
328 cv::resize(p.luma, mixed_luma(cv::Rect(p.x, p.y, p.width, p.height)), l_size, 0, 0);
329 cv::resize(p.chroma, mixed_chroma(cv::Rect(p.x / 2, p.y, p.width / 2, p.height)), c_size, 0, 0);
330 PROFILE_DETAIL("");
331 }
332
333 unsigned char *dst = reinterpret_cast<unsigned char *>(result->tiles[0].data);
334 unsigned char *chroma_src = mixed_chroma.ptr(0);
335 unsigned char *luma_src = mixed_luma.ptr(0);
336 assert(mixed_luma.isContinuous() && mixed_chroma.isContinuous());
337 PROFILE_DETAIL("Convert to ug frame");
338 unsigned dst_len = result->tiles[0].data_len;
339
340#ifdef __SSSE3__
341 __m128i y_shuff = _mm_set_epi8(7, -1, 6, -1, 5, -1, 4, -1, 3, -1, 2, -1, 1, -1, 0, -1);
342 __m128i uv_shuff = _mm_set_epi8(-1, 7, -1, 6, -1, 5, -1, 4, -1, 3, -1, 2, -1, 1, -1, 0);
343 while(dst_len >= 32){
344 __m128i luma = _mm_load_si128((__m128i const*)(const void *) luma_src);
345 luma_src += 16;
346 __m128i chroma = _mm_load_si128((__m128i const*)(const void *) chroma_src);
347 chroma_src += 16;
348
349 __m128i res = _mm_or_si128(_mm_shuffle_epi8(luma, y_shuff), _mm_shuffle_epi8(chroma, uv_shuff));
350 _mm_storeu_si128((__m128i *)(void *) dst, res);
351 dst += 16;
352
353 luma = _mm_bsrli_si128(luma, 8);
354 chroma = _mm_bsrli_si128(chroma, 8);
355
356 res = _mm_or_si128(_mm_shuffle_epi8(luma, y_shuff), _mm_shuffle_epi8(chroma, uv_shuff));
357 _mm_storeu_si128((__m128i *)(void *) dst, res);
358 dst += 16;
359
360 dst_len -= 32;
361 }
362#endif
363
364 while(dst_len >= 4){
365 *dst++ = *chroma_src++;
366 *dst++ = *luma_src++;
367 *dst++ = *chroma_src++;
368 *dst++ = *luma_src++;
369
370 dst_len -= 4;
371 }
372}
373
374std::vector<uint32_t> Video_mixer::get_participant_ssrc_list() const{
375 std::vector<uint32_t> ret;
376 ret.reserve(participants.size());
377 for(const auto& i : participants){
378 ret.push_back(i.first);
379 }
380 return ret;
381}
382
383struct display_deleter{ void operator()(display *d){ display_done(d); } };
384using unique_disp = std::unique_ptr<display, display_deleter>;
385
386struct { const char *name; Video_mixer::Layout layout; } layout_name_map[] = {
387 {"tiled", Video_mixer::Layout::Tiled},
388 {"one_big", Video_mixer::Layout::One_big},
389};
390
391Video_mixer::Layout layout_from_name(std::string_view name){
392 for(const auto& i : layout_name_map){
393 if(name == i.name)
394 return i.layout;
395 }
396 return Video_mixer::Layout::Invalid;
397}
398
399const char *layout_get_name(Video_mixer::Layout layout){
400 for(const auto& i : layout_name_map){
401 if(layout == i.layout)
402 return i.name;
403 }
404 return "invalid_layout";
405}
406
407}//anon namespace
408
409[[maybe_unused]] static constexpr std::chrono::milliseconds SOURCE_TIMEOUT(500);
410static constexpr unsigned int IN_QUEUE_MAX_BUFFER_LEN = 5;
411
416
417 struct module *parent = nullptr;
419
420 double req_fps = 0;
421 struct video_desc desc = {};
422 Video_mixer::Layout layout = Video_mixer::Layout::Tiled;
423
424 unique_disp real_display;
425 struct video_desc display_desc = {};
426
428 std::condition_variable incoming_frame_consumed;
429 std::condition_variable new_incoming_frame_cv;
430 std::queue<unique_frame> incoming_frames;
431};
432
434 std::shared_ptr<state_conference_common> common;
436};
437
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}
451
452static void show_help(bool from_reflector = false){
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}
465
466static void *display_conference_init(struct module *parent, const char *fmt, unsigned int flags)
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}
538
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}
547
548static void display_conference_worker(std::shared_ptr<state_conference_common> s){
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}
654
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}
666
667static bool display_conference_get_property(void *state, int property, void *val, size_t *len)
668{
669 auto s = ((struct state_conference *)state)->common;
671 ((struct multi_sources_supp_info *) val)->val = true;
672 ((struct multi_sources_supp_info *) val)->fork_display = display_conference_fork;
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}
689
691{
692 struct state_conference *s = (struct state_conference *) state;
693
694 s->desc = desc;
695
696 return true;
697}
698
700{
701 UNUSED(state);
702 UNUSED(frame);
703}
704
706 int sample_rate)
707{
708 UNUSED(state);
712
713 return false;
714}
715
717{
718 auto s = static_cast<state_conference *>(state);
719
720 delete s;
721}
722
724{
726 auto s = static_cast<state_conference *>(state);
727
728 return vf_alloc_desc_data(s->desc);
729}
730
731static bool display_conference_putf(void *state, struct video_frame *frame, long long flags)
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}
757
758static void display_conference_probe(struct device_info **available_cards, int *count, void (**deleter)(void *)) {
760 *available_cards = nullptr;
761 *count = 0;
762}
763
777
779
#define UNUSED(x)
void ** state
int void * val
struct video_desc desc
int int int sample_rate
int int channels
struct video_frame * frame
const char * fmt
int void size_t * len
const char unsigned int flags
int void(** deleter)(void *))
int property
int quant_samples
void vf_free(struct video_frame *buf)
Frees video_frame structure.
struct video_frame * vf_alloc_desc_data(struct video_desc desc)
#define MOD_NAME
Definition audio.cpp:110
static constexpr unsigned int IN_QUEUE_MAX_BUFFER_LEN
Definition blend.cpp:68
static constexpr chrono::milliseconds SOURCE_TIMEOUT(500)
static void check_reconf(struct state_blend_common *s, struct video_desc desc)
Definition blend.cpp:164
static void show_help()
Definition wasapi.cpp:193
Definition misc.h:94
int color_printf(const char *format,...)
#define TBOLD(x)
Definition color_out.h:70
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)
#define FAIL_IF(x, msg)
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 struct display * display_conference_fork(void *state)
static const struct video_display_info display_conference_info
static void display_conference_put_audio_frame(void *state, const struct audio_frame *frame)
static void display_conference_worker(std::shared_ptr< state_conference_common > s)
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)
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_VERBOSE
Definition debug.h:63
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
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.
void display_run_mainloop(struct display *d)
Display mainloop function.
bool display_ctl_property(struct display *d, int property, void *val, size_t *len)
Gets property from video display.
#define PUTF_DISCARD
#define PUTF_BLOCKING
#define VIDEO_DISPLAY_ABI_VERSION
#define DISPLAY_NO_GENERIC_FPS_INDICATOR
void display_done(struct display *d)
This function performs cleanup after done.
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.
struct video_frame * display_get_frame(struct display *d)
Returns video framebuffer which will be written to.
@ DISPLAY_PROPERTY_SUPPORTS_MULTI_SOURCES
@ DISPLAY_PROPERTY_CODECS
list of natively supported codecs - codec_t[]
@ MODULE_CLASS_DATA
Definition module.h:86
int count
Definition hd-rum.c:44
#define INIT_NOERR
Definition host.h:73
@ LIBRARY_CLASS_VIDEO_DISPLAY
Definition lib_common.h:79
#define REGISTER_HIDDEN_MODULE(name, info, lclass, abi)
Definition lib_common.h:156
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
static void * worker(void *args)
Definition alsa.c:190
#define PROFILE_FUNC
#define PROFILE_DETAIL(name)
struct msghdr msg
const char * parent
Definition settings.cpp:198
const char * param
Definition settings.cpp:195
const char * name
Definition settings.cpp:193
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)
struct used to comunicate with audio capture/playback device
Definition types.h:99
This struct represents initialized video display state.
void operator()(video_frame *f)
Definition pipewire.cpp:77
char text[8192]
Definition messaging.h:157
struct video_desc display_desc
std::queue< unique_frame > incoming_frames
std::condition_variable incoming_frame_consumed
std::condition_variable new_incoming_frame_cv
state_conference_common(struct module *parent)
std::mutex incoming_frames_lock
struct video_desc desc
std::shared_ptr< state_conference_common > common
unsigned int width
Definition types.h:280
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
unsigned int data_len
length of the data
Definition types.h:288
unsigned int height
Definition types.h:281
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
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
Definition types.h:345
double fps
Definition types.h:308
unsigned int tile_count
Definition types.h:344
codec_t color_spec
Definition types.h:306
@ VIDEO_NORMAL
normal video (one tile)
Definition types.h:356
@ PROGRESSIVE
progressive frame
Definition types.h:164
codec_t
Definition types.h:65
@ UYVY
YCbCr 422 8-bit - Cb Y0 Cr Y1.
Definition types.h:71
This is an umbrella header for video functions.
std::unique_ptr< video_frame, frame_deleter > unique_frame
Definition pipewire.cpp:78
const char * key
Definition cineform.cpp:125
static struct @76 codecs[]
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.