UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
blend.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2014-2023 CESNET, z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
43#include "config.h"
44#include "config_unix.h"
45#include "config_win32.h"
46#include "debug.h"
47#include "lib_common.h"
48#include "video.h"
49#include "video_display.h"
50
51#include <cinttypes>
52#include <condition_variable>
53#include <chrono>
54#include <iostream>
55#include <list>
56#include <map>
57#include <memory>
58#include <mutex>
59#include <queue>
60#include <thread>
61#include <unordered_map>
62
63using namespace std;
64
65static constexpr int TRANSITION_COUNT = 10;
66static constexpr int BUFFER_LEN = 5;
67static constexpr chrono::milliseconds SOURCE_TIMEOUT(500);
68static constexpr unsigned int IN_QUEUE_MAX_BUFFER_LEN = 5;
69static constexpr int SKIP_FIRST_N_FRAMES_IN_STREAM = 5;
70
71static void display_blend_run(void *state);
72
76
77 for (auto && ssrc_map : frames) {
78 for (auto && frame : ssrc_map.second) {
80 }
81 }
82 thread_id.join();
83 }
86
87 uint32_t current_ssrc;
88 uint32_t old_ssrc;
89
91
92 queue<struct video_frame *> incoming_queue;
93 condition_variable in_queue_decremented_cv;
94 map<uint32_t, list<struct video_frame *> > frames;
95 unordered_map<uint32_t, chrono::system_clock::time_point> disabled_ssrc;
96
97 mutex lock;
98 condition_variable cv;
99
100 struct module *parent;
101 thread thread_id;
102};
103
105 shared_ptr<struct state_blend_common> common;
107};
108
109static struct display *display_blend_fork(void *state)
110{
111 shared_ptr<struct state_blend_common> s = ((struct state_blend *)state)->common;
112 struct display *out;
113 char fmt[2 + sizeof(void *) * 2 + 1] = "";
114 snprintf(fmt, sizeof fmt, "%p", state);
115
116 int rc = initialize_video_display(s->parent,
117 "blend", fmt, 0, NULL, &out);
118 if (rc == 0) return out; else return NULL;
119
120 return out;
121}
122
123static void *display_blend_init(struct module *parent, const char *fmt, unsigned int flags)
124{
125 char *fmt_copy = NULL;
126 const char *requested_display = "";
127 const char *cfg = "";
128 int ret;
129
130 if (fmt == nullptr || strlen(fmt) == 0 || "help"s == fmt) {
131 cout << "blend is a helper display to combine (blend) multiple incoming streams.\n"
132 "Please do not use directly, intended for internal purposes!\n";
133 return nullptr;
134 }
135
136 auto *s = new state_blend();
137
138 if (isdigit(fmt[0]) != 0) { // fork
139 struct state_blend *orig = nullptr;
140 sscanf(fmt, "%p", &orig);
141 s->common = orig->common;
142 return s;
143 }
144 fmt_copy = strdup(fmt);
145 requested_display = fmt_copy;
146 char *delim = strchr(fmt_copy, ':');
147 if (delim != nullptr) {
148 *delim = '\0';
149 cfg = delim + 1;
150 }
151 s->common = shared_ptr<state_blend_common>(new state_blend_common());
152 ret = initialize_video_display(parent, requested_display, cfg, flags, NULL, &s->common->real_display);
153 assert(ret == 0 && "Unable to initialize real display for blend");
154 free(fmt_copy);
155
156 display_run_new_thread(s->common->real_display);
157
158 s->common->parent = parent;
159 s->common->thread_id = thread(display_blend_run, s);
160
161 return s;
162}
163
164static void check_reconf(struct state_blend_common *s, struct video_desc desc)
165{
166 if (!video_desc_eq(desc, s->display_desc)) {
167 s->display_desc = desc;
168 fprintf(stderr, "RECONFIGURED\n");
170 }
171}
172
173static void display_blend_run(void *state)
174{
175 shared_ptr<struct state_blend_common> s = ((struct state_blend *)state)->common;
176 bool prefill = false;
177 int skipped = 0;
178
179 while (1) {
180 struct video_frame *frame;
181 {
182 unique_lock<mutex> lg(s->lock);
183 s->cv.wait(lg, [s]{return s->incoming_queue.size() > 0;});
184 frame = s->incoming_queue.front();
185 s->incoming_queue.pop();
186 s->in_queue_decremented_cv.notify_one();
187 }
188
189 if (!frame) {
190 display_put_frame(s->real_display, NULL, PUTF_BLOCKING);
191 break;
192 }
193
194 chrono::system_clock::time_point now = chrono::system_clock::now();
195 auto it = s->disabled_ssrc.find(frame->ssrc);
196 if (it != s->disabled_ssrc.end()) {
197 it->second = now;
198 vf_free(frame);
199 continue;
200 }
201
202 it = s->disabled_ssrc.begin();
203 while (it != s->disabled_ssrc.end()) {
204 if (chrono::duration_cast<chrono::milliseconds>(now - it->second) > SOURCE_TIMEOUT) {
205 verbose_msg("Source 0x%08" PRIx32 " timeout. Deleting from blend display.\n", it->first);
206 s->disabled_ssrc.erase(it++);
207 } else {
208 ++it;
209 }
210 }
211
212 if (frame->ssrc != s->current_ssrc && frame->ssrc != s->old_ssrc) {
213 if (skipped >= SKIP_FIRST_N_FRAMES_IN_STREAM) {
214 s->old_ssrc = s->current_ssrc; // if != 0, we will be in transition state
215 s->current_ssrc = frame->ssrc;
216 prefill = true;
217 skipped = 0;
218 } else {
219 skipped++;
220 continue;
221 }
222 }
223
224 s->frames[frame->ssrc].push_back(frame);
225
226 if (s->frames[s->current_ssrc].size() >= BUFFER_LEN) {
227 prefill = false;
228 }
229
230 // we may receive two streams concurrently, therefore we use the timing according to
231 // the later one
232 if (frame->ssrc != s->current_ssrc) {
233 continue;
234 }
235
236 if (s->old_ssrc != 0) {
237 if (prefill) {
238 auto & ssrc_list = s->frames[s->old_ssrc];
239 if (ssrc_list.empty()) {
240 fprintf(stderr, "SMOLIK1!\n");
241 } else {
242 frame = ssrc_list.front();
243 ssrc_list.pop_front();
244
246
247 struct video_frame *real_display_frame = display_get_frame(s->real_display);
248 memcpy(real_display_frame->tiles[0].data, frame->tiles[0].data, frame->tiles[0].data_len);
249 vf_free(frame);
250 real_display_frame->ssrc = s->current_ssrc;
251 display_put_frame(s->real_display, real_display_frame, PUTF_BLOCKING);
252 }
253 } else {
254 auto & old_list = s->frames[s->old_ssrc];
255 auto & new_list = s->frames[s->current_ssrc];
256
257 s->transition += 1;
258
259 if (old_list.empty() || new_list.empty()) {
260 fprintf(stderr, "SMOLIK2!\n");
261 // ok here, we do not have nothing to mix, therefore cancel smooth transition
262 s->transition = TRANSITION_COUNT;
263 } else {
264 struct video_frame *old_frame, *new_frame;
265
266 old_frame = old_list.front();
267 old_list.pop_front();
268 new_frame = new_list.front();
269 new_list.pop_front();
270
271 struct video_desc old_desc = video_desc_from_frame(old_frame),
272 new_desc = video_desc_from_frame(new_frame);
273
274 check_reconf(s.get(), new_desc);
275
276 struct video_frame *real_display_frame = display_get_frame(s->real_display);
277
278 if (video_desc_eq(old_desc, new_desc)) {
279 for (unsigned int i = 0; i < new_frame->tiles[0].data_len; ++i) {
280 int old_val = ((unsigned char *) old_frame->tiles[0].data)[i];
281 int new_val = ((unsigned char *) new_frame->tiles[0].data)[i];
282 ((unsigned char *) real_display_frame->tiles[0].data)[i] =
283 ((new_val * s->transition) + (old_val * (TRANSITION_COUNT - s->transition))) / TRANSITION_COUNT;
284 }
285 } else {
286 // new desc is different than old desc!
287 fprintf(stderr, "SMOLIK4!\n");
288 memcpy(real_display_frame->tiles[0].data, new_frame->tiles[0].data, new_frame->tiles[0].data_len);
289 }
290 vf_free(old_frame);
291 vf_free(new_frame);
292 real_display_frame->ssrc = s->current_ssrc;
293 display_put_frame(s->real_display, real_display_frame, PUTF_BLOCKING);
294 }
295 }
296 } else {
297 if (!prefill) {
298 if (s->frames[s->current_ssrc].empty()) {
299 // this should not happen, heh ?
300 fprintf(stderr, "SMOLIK3!\n");
301 } else {
302 frame = s->frames[s->current_ssrc].front();
303 s->frames[s->current_ssrc].pop_front();
304
306
307 struct video_frame *real_display_frame = display_get_frame(s->real_display);
308 memcpy(real_display_frame->tiles[0].data, frame->tiles[0].data, frame->tiles[0].data_len);
309 vf_free(frame);
310 real_display_frame->ssrc = s->current_ssrc;
311 display_put_frame(s->real_display, real_display_frame, PUTF_BLOCKING);
312 }
313 }
314 }
315
316 if (s->old_ssrc != 0 && s->transition >= TRANSITION_COUNT) {
317 for (auto && frame : s->frames[s->old_ssrc]) {
318 vf_free(frame);
319 }
320
321 s->frames.erase(s->old_ssrc);
322
323 s->disabled_ssrc[s->old_ssrc] = chrono::system_clock::now();
324 s->old_ssrc = 0u;
325 s->transition = 0;
326 }
327 }
328
329 display_join(s->real_display);
330}
331
332static void display_blend_done(void *state)
333{
334 struct state_blend *s = (struct state_blend *)state;
335 delete s;
336}
337
339{
340 struct state_blend *s = (struct state_blend *)state;
341
342 return vf_alloc_desc_data(s->desc);
343}
344
345static bool display_blend_putf(void *state, struct video_frame *frame, long long flags)
346{
347 shared_ptr<struct state_blend_common> s = ((struct state_blend *)state)->common;
348
349 if (flags == PUTF_DISCARD) {
350 vf_free(frame);
351 } else {
352 unique_lock<mutex> lg(s->lock);
353 if (s->incoming_queue.size() >= IN_QUEUE_MAX_BUFFER_LEN) {
354 fprintf(stderr, "blend: queue full!\n");
355 }
356 if (flags != PUTF_BLOCKING && s->incoming_queue.size() >= IN_QUEUE_MAX_BUFFER_LEN) {
357 vf_free(frame);
358 return 1;
359 }
360 s->in_queue_decremented_cv.wait(lg, [s]{return s->incoming_queue.size() < IN_QUEUE_MAX_BUFFER_LEN;});
361 s->incoming_queue.push(frame);
362 lg.unlock();
363 s->cv.notify_one();
364 }
365
366 return true;
367}
368
369static bool display_blend_get_property(void *state, int property, void *val, size_t *len)
370{
371 shared_ptr<struct state_blend_common> s = ((struct state_blend *)state)->common;
373 ((struct multi_sources_supp_info *) val)->val = true;
375 ((struct multi_sources_supp_info *) val)->state = state;
376 *len = sizeof(struct multi_sources_supp_info);
377 return true;
378 }
379 return display_ctl_property(s->real_display, property, val, len);
380}
381
383{
384 struct state_blend *s = (struct state_blend *) state;
385
386 s->desc = desc;
387
388 return true;
389}
390
391static void display_blend_put_audio_frame(void *state, const struct audio_frame *frame)
392{
393 UNUSED(state);
394 UNUSED(frame);
395}
396
398 int sample_rate)
399{
400 UNUSED(state);
404
405 return false;
406}
407
408static void display_blend_probe(struct device_info **available_cards, int *count, void (**deleter)(void *)) {
410 *available_cards = nullptr;
411 *count = 0;
412}
413
427
429
#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_desc video_desc_from_frame(const struct video_frame *frame)
Returns struct video_desc from video frame.
struct video_frame * vf_alloc_desc_data(struct video_desc desc)
static bool display_blend_get_property(void *state, int property, void *val, size_t *len)
Definition blend.cpp:369
static constexpr unsigned int IN_QUEUE_MAX_BUFFER_LEN
Definition blend.cpp:68
static constexpr int BUFFER_LEN
Definition blend.cpp:66
static struct display * display_blend_fork(void *state)
Definition blend.cpp:109
static constexpr chrono::milliseconds SOURCE_TIMEOUT(500)
static void display_blend_done(void *state)
Definition blend.cpp:332
static bool display_blend_putf(void *state, struct video_frame *frame, long long flags)
Definition blend.cpp:345
static void display_blend_probe(struct device_info **available_cards, int *count, void(**deleter)(void *))
Definition blend.cpp:408
static bool display_blend_reconfigure(void *state, struct video_desc desc)
Definition blend.cpp:382
static void check_reconf(struct state_blend_common *s, struct video_desc desc)
Definition blend.cpp:164
static constexpr int TRANSITION_COUNT
Definition blend.cpp:65
static constexpr int SKIP_FIRST_N_FRAMES_IN_STREAM
Definition blend.cpp:69
static bool display_blend_reconfigure_audio(void *state, int quant_samples, int channels, int sample_rate)
Definition blend.cpp:397
static void display_blend_run(void *state)
Definition blend.cpp:173
static void display_blend_put_audio_frame(void *state, const struct audio_frame *frame)
Definition blend.cpp:391
static void * display_blend_init(struct module *parent, const char *fmt, unsigned int flags)
Definition blend.cpp:123
static struct video_frame * display_blend_getf(void *state)
Definition blend.cpp:338
static const struct video_display_info display_blend_info
Definition blend.cpp:414
#define verbose_msg(...)
Definition debug.h:90
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.
bool display_ctl_property(struct display *d, int property, void *val, size_t *len)
Gets property from video display.
void display_run_new_thread(struct display *d)
#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.
void display_join(struct display *d)
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
int count
Definition hd-rum.c:44
@ LIBRARY_CLASS_VIDEO_DISPLAY
Definition lib_common.h:79
#define REGISTER_HIDDEN_MODULE(name, info, lclass, abi)
Definition lib_common.h:156
const char * parent
Definition settings.cpp:198
struct used to comunicate with audio capture/playback device
Definition types.h:99
This struct represents initialized video display state.
struct display *(* fork_display)(void *state)
uint32_t old_ssrc
Definition blend.cpp:88
map< uint32_t, list< struct video_frame * > > frames
Definition blend.cpp:94
struct display * real_display
Definition blend.cpp:84
condition_variable in_queue_decremented_cv
Definition blend.cpp:93
condition_variable cv
Definition blend.cpp:98
uint32_t current_ssrc
Definition blend.cpp:87
unordered_map< uint32_t, chrono::system_clock::time_point > disabled_ssrc
Definition blend.cpp:95
struct module * parent
Definition blend.cpp:100
queue< struct video_frame * > incoming_queue
Definition blend.cpp:92
struct video_desc display_desc
Definition blend.cpp:85
struct video_desc desc
Definition blend.cpp:106
shared_ptr< struct state_blend_common > common
Definition blend.cpp:105
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
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
uint32_t ssrc
Definition types.h:330
@ VIDEO_NORMAL
normal video (one tile)
Definition types.h:356
This is an umbrella header for video functions.
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.