UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
hd-rum-recompress.cpp
Go to the documentation of this file.
1
10/*
11 * Copyright (c) 2013-2023 CESNET, z. s. p. o.
12 * All rights reserved.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, is permitted provided that the following conditions
16 * are met:
17 *
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 *
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 *
25 * 3. Neither the name of CESNET nor the names of its contributors may be
26 * used to endorse or promote products derived from this software without
27 * specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
30 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
31 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
32 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
33 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
34 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
35 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
36 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
39 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
40 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 */
42#ifdef HAVE_CONFIG_H
43#include "config.h"
44#include "config_unix.h"
45#include "config_win32.h"
46#endif
47
48#include <cinttypes>
49#include <chrono>
50#include <memory>
51#include <thread>
52#include <string>
53
54
56
57#include "debug.h"
58#include "host.h"
59#include "utils/misc.h"
60#include "rtp/rtp.h"
61#include "tv.h"
62#include "video_compress.h"
63
66
67namespace {
68struct compress_state_deleter{
69 void operator()(struct compress_state *s){ module_done(CAST_MODULE(s)); }
70};
71}
72
73using namespace std;
74
78 std::string host, unsigned short rx_port,
79 unsigned short tx_port, const struct common_opts *common,
80 const char *fec, long long bitrate);
81
82 std::unique_ptr<ultragrid_rtp_video_rxtx> video_rxtx;
83 std::string host;
85
86 std::chrono::steady_clock::time_point t0{std::chrono::steady_clock::now()};
87 int frames;
88
89 bool active;
90};
91
93 std::string compress_cfg;
94 std::unique_ptr<compress_state, compress_state_deleter> compress;
95
96 std::mutex ports_mut;
97 std::vector<recompress_output_port> ports;
98
99 std::thread thread;
100};
101
103 struct module *parent;
104 std::mutex mut;
105 std::map<std::string, recompress_worker_ctx> workers;
106 std::vector<std::pair<std::string, int>> index_to_port;
107};
108
110 std::string host, unsigned short rx_port,
111 unsigned short tx_port, const struct common_opts *common,
112 const char *fec, long long bitrate) :
113 host(std::move(host)),
114 tx_port(tx_port),
115 frames(0),
116 active(true)
117{
118 std::map<std::string, param_u> params;
119
120 // common
121 params["compression"].str = "none";
122 params["rxtx_mode"].i = MODE_SENDER;
123
124 //RTP
125 params["common"].cptr = common;
126 params["receiver"].str = this->host.c_str();
127 params["rx_port"].i = rx_port;
128 params["tx_port"].i = tx_port;
129 params["fec"].str = fec;
130 params["bitrate"].ll = bitrate;
131
132 // UltraGrid RTP
133 params["decoder_mode"].l = VIDEO_NORMAL;
134 params["display_device"].ptr = NULL;
135
136 auto rxtx = video_rxtx::create("ultragrid_rtp", params);
137 if (this->host.find(':') != std::string::npos) {
138 rxtx->m_port_id = "[" + this->host + "]:" + to_string(tx_port);
139 } else {
140 rxtx->m_port_id = this->host + ":" + to_string(tx_port);
141 }
142
143 video_rxtx.reset(dynamic_cast<ultragrid_rtp_video_rxtx *>(rxtx));
144}
145
146static void recompress_port_write(recompress_output_port& port, shared_ptr<video_frame> frame)
147{
149
150 port.frames += 1;
151
152 auto now = chrono::steady_clock::now();
153
154 double seconds = chrono::duration_cast<chrono::duration<double>>(now - port.t0).count();
155 if(seconds > 5) {
156 double fps = port.frames / seconds;
157 log_msg(LOG_LEVEL_INFO, "[0x%08" PRIx32 "->%s:%d:0x%08" PRIx32 "] %d frames in %g seconds = %g FPS\n",
158 frame->ssrc,
159 port.host.c_str(), port.tx_port,
160 port.video_rxtx->get_ssrc(),
161 port.frames, seconds, fps);
162 port.t0 = now;
163 port.frames = 0;
164 }
165
166 port.video_rxtx->send(std::move(frame));
167}
168
171 assert(ctx->compress);
172
173 while(auto frame = compress_pop(ctx->compress.get())){
174 std::lock_guard<std::mutex> lock(ctx->ports_mut);
175 for(auto& port : ctx->ports){
176 if(port.active)
178 }
179 PROFILE_DETAIL("compress_pop");
180 }
181}
182
183static int move_port_to_worker(struct state_recompress *s, const char *compress,
185{
186 auto& worker = s->workers[compress];
187 if(!worker.compress){
188 worker.compress_cfg = compress;
189 int ret = compress_init(s->parent, compress, out_ptr(worker.compress));
190 if (ret != 0) {
191 s->workers.erase(compress);
192 return -1;
193 }
194
195 worker.thread = std::thread(recompress_worker, &worker);
196 }
197
198 std::lock_guard<std::mutex> lock(worker.ports_mut);
199 int index_in_worker = worker.ports.size();
200 worker.ports.push_back(std::move(port));
201
202 return index_in_worker;
203}
204
206 const char *host, const char *compress, unsigned short rx_port,
207 unsigned short tx_port, const struct common_opts *common,
208 const char *fec, long long bitrate)
209{
211
212 try{
213 port = recompress_output_port(host, rx_port, tx_port,
214 common, fec, bitrate);
215 } catch(...) {
216 return -1;
217 }
218
219 std::lock_guard<std::mutex> lock(s->mut);
220 int index_in_worker = move_port_to_worker(s, compress, std::move(port));
221 if(index_in_worker < 0)
222 return -1;
223
224 int index_of_port = s->index_to_port.size();
225 s->index_to_port.emplace_back(compress, index_in_worker);
226
227 return index_of_port;
228}
229
230static void extract_port(struct state_recompress *s,
231 const std::string& compress_cfg, int i,
232 recompress_output_port *move_to = nullptr)
233{
234 auto& worker = s->workers[compress_cfg];
235 {
236 std::unique_lock<std::mutex> lock(worker.ports_mut);
237 if(move_to)
238 *move_to = std::move(worker.ports[i]);
239 worker.ports.erase(worker.ports.begin() + i);
240
241 if(worker.ports.empty()){
242 //poison compress
243 compress_frame(worker.compress.get(), nullptr);
244 worker.thread.join();
245 s->workers.erase(compress_cfg);
246 }
247 }
248
249 for(auto& p : s->index_to_port){
250 if(p.first == compress_cfg && p.second > i)
251 p.second--;
252 }
253}
254
255void recompress_remove_port(struct state_recompress *s, int index){
256 std::lock_guard<std::mutex> lock(s->mut);
257 auto [compress_cfg, i] = s->index_to_port[index];
258
259 extract_port(s, compress_cfg, i);
260 s->index_to_port.erase(s->index_to_port.begin() + index);
261}
262
263uint32_t recompress_get_port_ssrc(struct state_recompress *s, int idx){
264 std::lock_guard<std::mutex> lock(s->mut);
265 auto [compress_cfg, i] = s->index_to_port[idx];
266
267 std::lock_guard<std::mutex> work_lock(s->workers[compress_cfg].ports_mut);
268 return s->workers[compress_cfg].ports[i].video_rxtx->get_ssrc();
269}
270
272 int index, bool active)
273{
274 std::lock_guard<std::mutex> lock(s->mut);
275 auto [compress_cfg, i] = s->index_to_port[index];
276
277 std::unique_lock<std::mutex> worker_lock(s->workers[compress_cfg].ports_mut);
278 s->workers[compress_cfg].ports[i].active = active;
279}
280
282 const char *new_compress)
283{
284 std::lock_guard<std::mutex> lock(s->mut);
285 auto [old_compress, i] = s->index_to_port[index];
286
287 if(old_compress == new_compress)
288 return true;
289
291 extract_port(s, old_compress, i, &port);
292 int index_in_worker = move_port_to_worker(s, new_compress, std::move(port));
293
294 if(index_in_worker < 0){
295 s->index_to_port.erase(s->index_to_port.begin() + index);
296 return false;
297 }
298
299 s->index_to_port[index] = {new_compress, index_in_worker};
300
301 return true;
302}
303
305 int ret = 0;
306 for(const auto& port : worker.ports){
307 if(port.active)
308 ret++;
309 }
310 return ret;
311}
312
314 std::lock_guard<std::mutex> lock(s->mut);
315 int ret = 0;
316 for(const auto& worker : s->workers){
317 ret += worker_get_num_active_ports(worker.second);
318 }
319
320 return ret;
321}
322
324 auto state = new state_recompress();
325 if(!state)
326 return nullptr;
327
328 state->parent = parent;
329
330 return state;
331}
332
333void recompress_process_async(state_recompress *s, std::shared_ptr<video_frame> frame){
335 std::lock_guard<std::mutex> lock(s->mut);
336 for(const auto& worker : s->workers){
337 if(worker_get_num_active_ports(worker.second) > 0)
338 compress_frame(worker.second.compress.get(), frame);
339 }
340}
341
343 {
344 std::lock_guard<std::mutex> lock(s->mut);
345 for(auto& worker : s->workers){
346 //poison compress
347 compress_frame(worker.second.compress.get(), nullptr);
348
349 worker.second.thread.join();
350 compress_done(worker.second.compress.get());
351 }
352 }
353 delete s;
354}
355
void ** state
struct video_frame * frame
Definition misc.h:94
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
void module_done(struct module *module_data)
Definition module.c:93
#define CAST_MODULE(x)
Definition module.h:197
shared_ptr< video_frame > compress_pop(struct compress_state *proxy)
void compress_frame(struct compress_state *proxy, shared_ptr< video_frame > frame)
int compress_init(struct module *parent, const char *config_string, struct compress_state **state)
This function initializes video compression.
void compress_done(struct compress_state *proxy)
Video compression cleanup function.
int recompress_get_num_active_ports(struct state_recompress *s)
static void recompress_port_write(recompress_output_port &port, shared_ptr< video_frame > frame)
void recompress_port_set_active(struct state_recompress *s, int index, bool active)
static int move_port_to_worker(struct state_recompress *s, const char *compress, recompress_output_port &&port)
uint32_t recompress_get_port_ssrc(struct state_recompress *s, int idx)
static void recompress_worker(struct recompress_worker_ctx *ctx)
static void extract_port(struct state_recompress *s, const std::string &compress_cfg, int i, recompress_output_port *move_to=nullptr)
void recompress_remove_port(struct state_recompress *s, int index)
static int worker_get_num_active_ports(const recompress_worker_ctx &worker)
int recompress_add_port(struct state_recompress *s, const char *host, const char *compress, unsigned short rx_port, unsigned short tx_port, const struct common_opts *common, const char *fec, long long bitrate)
struct state_recompress * recompress_init(struct module *parent)
void recompress_process_async(state_recompress *s, std::shared_ptr< video_frame > frame)
void recompress_done(struct state_recompress *s)
bool recompress_port_change_compress(struct state_recompress *s, int index, const char *new_compress)
int count
Definition hd-rum.c:44
static struct @19 params[100]
#define MODE_SENDER
Definition host.h:102
static void * worker(void *args)
Definition alsa.c:190
#define PROFILE_FUNC
#define PROFILE_DETAIL(name)
const char * parent
Definition settings.cpp:198
Video compress state.
Definition fec.h:51
pthread_mutex_t lock
Definition module.h:114
std::unique_ptr< ultragrid_rtp_video_rxtx > video_rxtx
std::chrono::steady_clock::time_point t0
recompress_output_port()=default
std::unique_ptr< compress_state, compress_state_deleter > compress
std::vector< recompress_output_port > ports
std::vector< std::pair< std::string, int > > index_to_port
std::map< std::string, recompress_worker_ctx > workers
struct module * parent
uint32_t ssrc
Definition types.h:330
static video_rxtx * create(std::string const &name, std::map< std::string, param_u > const &)
@ VIDEO_NORMAL
normal video (one tile)
Definition types.h:356
API for video compress drivers.