UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
ldgm.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2012-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 */
37
38#define WANT_MKDIR
39#include "config_unix.h"
40#include "config_win32.h"
41
42#include <cerrno>
43#include <climits>
44#include <cstdio>
45#include <cstdlib>
46#include <cstring>
47#include <iomanip>
48#include <iostream>
49#include <sstream>
50#include <sys/stat.h>
51#include <sys/types.h>
52#include <unistd.h>
53
54#include "debug.h"
55#include "host.h"
56#include "ldgm.h"
57#include "lib_common.h"
58
63#include "ldgm/matrix-gen/ldpc-matrix.h" // LDGM_MAX_K
64
65#include "rtp/rtp.h"
66#include "rtp/rtp_callback.h"
67#include "transmit.h"
68#include "utils/color_out.h"
69#include "video.h"
70
71using std::endl;
72using std::map;
73using std::ostringstream;
74using std::setprecision;
75using std::shared_ptr;
76using std::string;
77using std::unique_ptr;
78
79#define MOD_NAME "[ldgm] "
80
81typedef enum {
82 STD1500 = 1500,
83 JUMBO9000 = 9000,
85
86typedef struct {
89 double loss;
90 // result
91 int k, m, c;
92
94
95
96#define PCT2 2.0
97#define PCT5 5.0
98#define PCT10 10.0
99typedef double loss_t;
100
102 std::numeric_limits<double>::min(),
103 PCT2,
104 PCT5,
105 PCT10,
106};
107
108#define JPEG60_SIZE (144 * 1000)
109#define JPEG80_SIZE (177 * 1000)
110#define JPEG90_SIZE (217 * 1000)
111
112#define UNCOMPRESSED_SIZE (1920 * 1080 * 2)
113
115 // JPEG 60
116 { STD1500, JPEG60_SIZE, PCT2, 750, 120, 5 },
117 { STD1500, JPEG60_SIZE, PCT5, 1500, 450, 6 },
118 { STD1500, JPEG60_SIZE, PCT10, 1000, 500, 7 },
119 // JPEG 80
120 { STD1500, JPEG80_SIZE, PCT2, 1500, 240, 5 },
121 { STD1500, JPEG80_SIZE, PCT5, 1250, 375, 6 },
122 { STD1500, JPEG80_SIZE, PCT10, 1500, 750, 8 },
123 // JPEG 90
124 { STD1500, JPEG90_SIZE, PCT2, 1500, 240, 6 },
125 { STD1500, JPEG90_SIZE, PCT5, 1500, 450, 6 },
126 { STD1500, JPEG90_SIZE, PCT10, 1500, 750, 8 },
127
128 // uncompressed
129 { JUMBO9000, UNCOMPRESSED_SIZE, PCT2, 1500, 180, 5 },
130 { JUMBO9000, UNCOMPRESSED_SIZE, PCT5, 1000, 300, 6 },
131 { JUMBO9000, UNCOMPRESSED_SIZE, PCT10, 1000, 500, 7 },
132
133 { STD1500, UNCOMPRESSED_SIZE, PCT2, 1500, 250, 5 },
134 { STD1500, UNCOMPRESSED_SIZE, PCT5, 1500, 650, 6 },
135 { STD1500, UNCOMPRESSED_SIZE, PCT10, 1500, 1500, 8 },
136};
137
138#define MINIMAL_VALUE 64
140#define DEFAULT_K 512
141#define DEFAULT_M 384
142#define DEFAULT_C 5
143#define MIN_C 2 // reasonable minimum
144#define MAX_C 63 // from packet format
145#define MAX_K (1<<13) - 1
146
147static bool file_exists(char *filename);
148static void usage(void);
149
150static bool file_exists(char *filename)
151{
152 struct stat sb;
153 if (stat(filename, &sb)) {
154 perror("stat");
155 return false;
156 }
157
158 return true;
159}
160
161void ldgm::set_params(unsigned int k, unsigned int m, unsigned int c, unsigned int seed)
162{
163 m_k = k;
164 m_m = m;
165 m_c = c;
166 m_seed = seed;
167
168 m_coding_session->set_params(k, m, c);
169
170 char filename[512];
171 char path[256];
172
173 int res;
174
175#ifdef _WIN32
176 TCHAR tmpPath[MAX_PATH];
177 UINT ret = GetTempPath(MAX_PATH, tmpPath);
178 if(ret == 0 || ret > MAX_PATH) {
179 fprintf(stderr, "Unable to get temporary directory name.\n");
180 throw 1;
181 }
182 snprintf(path, 256, "%s\\ultragrid\\", tmpPath);
183#else
184 snprintf(path, 256, "/var/tmp/ultragrid-%d/", (int) getuid());
185#endif
186
187 res = mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
188 if(res != 0) {
189 if(errno != EEXIST) {
190 perror("mkdir");
191 fprintf(stderr, "[LDGM] Unable to create data directory.\n");
192 throw 1;
193 }
194 }
195 snprintf(filename, 512, "%s/ldgm_matrix-%u-%u-%u-%u.bin", path, k, m, c, seed);
196 if(!file_exists(filename)) {
197 int ret = generate_ldgm_matrix(filename, k, m, c, seed, 0);
198 if(ret != 0) {
199 fprintf(stderr, "[LDGM] Unable to initialize LDGM matrix.\n");
200 throw 1;
201 }
202 }
203
204 m_coding_session->set_pcMatrix(filename);
205
206 MSG(INFO, "Using LDGM with k=%u m=%u c=%u\n", m_k, m_m, m_c);
207}
208
209ADD_TO_PARAM("ldgm-device", "* ldgm-device={CPU|GPU}\n"
210 " specify whether use CPU or GPU for LDGM\n");
211
212void ldgm::init(unsigned int k, unsigned int m, unsigned int c, unsigned int seed)
213{
214 bool ldgm_device_gpu = false;
215
216 if (get_commandline_param("ldgm-device")) {
217 ldgm_device_gpu = strcasecmp(get_commandline_param("ldgm-device"), "GPU") == 0;
218 }
219
220 if (ldgm_device_gpu) {
221 LDGM_session_gpu *(*loader)();
222 loader = reinterpret_cast<LDGM_session_gpu *(*)()>(
223 const_cast<void *>(
224 load_library("ldgm_gpu",
226 if (!loader) {
227 throw string("GPU accelerated LDGM support is not compiled in");
228 } else {
229 m_coding_session = unique_ptr<LDGM_session>(loader());
230
231 }
232 } else {
233 m_coding_session = unique_ptr<LDGM_session>(new LDGM_session_cpu());
234 }
235
236 set_params(k, m, c, seed);
237}
238
239ldgm::ldgm(unsigned int k, unsigned int m, unsigned int c, unsigned int seed)
240{
241 init(k, m, c, seed);
242}
243
244bool ldgm::decode(char *frame, int size, char **out, int *out_size, const map<int, int> &packets) {
245 char *decoded;
246 decoded = m_coding_session->decode_frame(frame, size, out_size, packets);
247 if (*out_size > 0) {
248 *out = decoded;
249 return true;
250 } else {
251 return false;
252 }
253}
254
256// ENCODER
258static void usage() {
259 color_printf(TBOLD("LDGM") " usage:\n");
260 color_printf("\t" TBOLD(TRED("-f ldgm") "[:<k>:<m>[:c]]" " | " TRED("-f ldgm:<expected_loss>%%")) "\n");
261 printf("\nwhere:\n");
263 "\t" TBOLD("<k>") " - matrix width\n"
264 "\t" TBOLD("<m>") " - matrix height\n"
265 "\t" TBOLD("<c>") " - number of ones per column\n"
266 "\t\tthe bigger ratio m/k, the better correction (but also "
267 "needed bandwidth)\n"
268 "\t\tk,m should be in interval [%d, %d]; c in [%d, %d]\n"
269 "\t\tdefault: k = %d, m = %d, c = %d\n"
270 "\t" TBOLD("<expected_loss>") " - expected maximal loss "
271 TBOLD("per frame") " in percent\n"
272 "\t\t(including '%%'-sign)¹\n"
273 "\n",
275 DEFAULT_C);
277 "¹ Please note that there are only a few presets for FullHD "
278 "video\n"
279 "that will work correctly for JPEG and uncompressed, see wiki:\n"
280 "<https://github.com/CESNET/UltraGrid/wiki/FEC-Settings#ldgm>\n\n");
281}
282ldgm::ldgm(const char *cfg)
283{
284 unsigned int k = DEFAULT_K,
285 m = DEFAULT_M,
286 c = DEFAULT_C;
287
288 if(cfg && strlen(cfg) > 0) {
289 if(strcasecmp(cfg, "help") == 0) {
290 usage();
291 throw 0;
292 }
293 char *save_ptr = NULL;
294 char *item;
295 char *tmp = strdup(cfg);
296 item = strtok_r(tmp, ":", &save_ptr);
297 if(item) {
298 k = atoi(item);
299 }
300 item = strtok_r(NULL, ":", &save_ptr);
301 if(item) {
302 m = atoi(item);
303 item = strtok_r(NULL, ":", &save_ptr);
304 if(item) {
305 c = atoi(item);
306 }
307 } else {
308 throw string("[LDGM] Was set k value but not m.");
309 }
310 free(tmp);
311 }
312
313 if(c < MIN_C || c > MAX_C) {
314 ostringstream oss;
315 oss << "[LDGM] C value shoud be inside interval [" << MIN_C << ", " << MAX_C << "]";
316 throw oss.str();
317 }
318
319 if(k > MAX_K) {
320 ostringstream oss;
321 oss << "[LDGM] K value exceeds maximal value " << MAX_K;
322 throw oss.str();
323 }
324
325 if(k < MINIMAL_VALUE || m < MINIMAL_VALUE) {
326 ostringstream oss;
327 oss << "[LDGM] Either k or m is lower than minimal value " << MINIMAL_VALUE;
328 throw oss.str();
329 }
330
331 printf("[LDGM] Using values k = %u, m = %u, c = %u.\n", k, m, c);
332
333 init(k, m, c);
334}
335
336ldgm::ldgm(int packet_size, int frame_size, double max_expected_loss)
337{
338 packet_type_t packet_type;
339 int nearest = INT_MAX;
340 loss_t loss = -1.0;
341 int k, m, c;
342
343 assert(max_expected_loss >= 0.0 && max_expected_loss <= 100.0);
344
345 if(frame_size < 2000000 && packet_size >= (STD1500 + JUMBO9000) / 2) {
346 throw string("LDGM: with frames smaller than 2M you should use standard Ethernet frames.");
347 }
348
349 if(packet_size < (STD1500 + JUMBO9000) / 2) {
350 packet_type = STD1500;
351 } else {
352 packet_type = JUMBO9000;
353 }
354
355 for(unsigned int i = 1; i < sizeof(losses) / sizeof(loss_t); ++i) {
356 if(max_expected_loss >= losses[i - 1] && max_expected_loss <= losses[i]) {
357 loss = losses[i];
358 break;
359 }
360 }
361
362 if(loss == -1.0) {
363 ostringstream oss;
364 oss << "LDGM: Cannot provide predefined settings for correction of loss of " << setprecision(2)
365 << max_expected_loss << endl <<
366 "LDGM: You have to try and set LDGM parameters manually. You can inform us if you need better protection.";
367 throw oss.str();
368 }
369
370 printf("LDGM: Choosing maximal loss %2.2f percent.\n", loss);
371
372 for(unsigned int i = 0; i < sizeof(suggested_configurations) / sizeof(configuration_t); ++i) {
373 if(suggested_configurations[i].packet_type == packet_type &&
375 if(abs(suggested_configurations[i].frame_size - frame_size) < abs(nearest - frame_size)) {
380 }
381 }
382 }
383
384 if (nearest == INT_MAX) {
385 throw string("LDGM: Could not find matching configuration for your request.\n"
386 "Please, set LDGM parameters manually.");
387 }
388
389 double difference_from_frame_size = abs(nearest - frame_size) / (double) frame_size;
390 if(difference_from_frame_size > 0.2) {
391 LOG(LOG_LEVEL_WARNING) << "LDGM: Chosen LDGM setting for frame size that is " << difference_from_frame_size * 100.0 << "% " << (nearest - frame_size > 0 ? "higher" : "lower") << " than your frame size.\n";
392 LOG(LOG_LEVEL_WARNING) << "You may wish to set the parameters manually.\n";
393 }
394
395 if(difference_from_frame_size > 0.5) {
396 throw string("");
397 }
398
399 init(k, m, c);
400}
401
402static void
403check_packet_size(size_t len, int k)
404{
405 enum { SHMEM_SIZE = 48 * 1024 };
406 static bool printed = false;
407 if (printed) {
408 return;
409 }
410 const size_t packet_size = len / k + 4;
411 if (packet_size < SHMEM_SIZE) {
412 return;
413 }
414
415 printed = true;
416 const char *dev = get_commandline_param("ldgm-device");
417 if (dev == nullptr|| strcasecmp(dev, "GPU") != 0) {
418 return;
419 }
420 MSG(WARNING,
421 "Packet size is %zu B, which may exceed GPU memory, which is "
422 "usually %d.\nYou can try to increase the parameter K.\n",
423 packet_size, SHMEM_SIZE);
424}
425
426shared_ptr<video_frame> ldgm::encode(shared_ptr<video_frame> tx_frame)
427{
428 // We need to have copy of coding session shared pointer in order to exit
429 // gracefully even when destructed LDGM state first with some frame still
430 // existing.
431 std::shared_ptr<LDGM_session> coding_session = this->m_coding_session;
432 shared_ptr<video_frame> out(vf_alloc_desc(video_desc_from_frame(tx_frame.get())),
433 [coding_session = std::move(coding_session)](struct video_frame *frame) {
434 for (unsigned int i = 0; i < frame->tile_count; ++i) {
435 coding_session->free_out_buf(frame->tiles[i].data);
436 }
437 vf_free(frame);
438 });
439
440 for (unsigned int i = 0; i < tx_frame->tile_count; ++i) {
441 video_payload_hdr_t video_hdr;
442 format_video_header(tx_frame.get(), i, 0, video_hdr);
443
444 check_packet_size(tx_frame->tiles[i].data_len, m_k);
445
446 int out_size;
447 char *output = m_coding_session->encode_hdr_frame((char *) video_hdr, sizeof(video_hdr),
448 tx_frame->tiles[i].data, tx_frame->tiles[i].data_len, &out_size);
449
450 out->tiles[i].data = output;
451 out->tiles[i].data_len = out_size;
452 }
453
454 out->fec_params.type = FEC_LDGM;
455 out->fec_params.k = m_k;
456 out->fec_params.m = m_m;
457 out->fec_params.c = m_c;
458 out->fec_params.seed = m_seed;
459 out->fec_params.symbol_size = m_coding_session->get_packet_size();
460
461 return out;
462}
463
struct video_frame * frame
int void size_t * len
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(struct video_desc desc)
Allocates video frame accordig given desc.
int color_printf(const char *format,...)
#define TRED(x)
Definition color_out.h:75
#define TBOLD(x)
Definition color_out.h:70
u32 UINT
Definition cuda_dxt.cu:13
static __device__ vec2 abs(const vec2 &v)
Definition cuda_dxt.cu:118
#define MSG(l, fmt,...)
Definition debug.h:96
#define LOG(level)
Definition debug.h:313
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
const char * get_commandline_param(const char *key)
Definition host.cpp:894
#define ADD_TO_PARAM(param, doc)
Definition host.h:203
void usage()
Definition main.cpp:28
#define UNCOMPRESSED_SIZE
Definition ldgm.cpp:112
#define PCT5
Definition ldgm.cpp:97
loss_t losses[]
Definition ldgm.cpp:101
#define JPEG90_SIZE
Definition ldgm.cpp:110
double loss_t
Definition ldgm.cpp:99
#define DEFAULT_M
Definition ldgm.cpp:141
#define DEFAULT_C
Definition ldgm.cpp:142
packet_type_t
Definition ldgm.cpp:81
@ STD1500
Definition ldgm.cpp:82
@ JUMBO9000
Definition ldgm.cpp:83
static void check_packet_size(size_t len, int k)
Definition ldgm.cpp:403
#define JPEG60_SIZE
Definition ldgm.cpp:108
#define MAX_C
Definition ldgm.cpp:144
#define JPEG80_SIZE
Definition ldgm.cpp:109
const configuration_t suggested_configurations[]
Definition ldgm.cpp:114
#define MINIMAL_VALUE
Definition ldgm.cpp:138
static bool file_exists(char *filename)
Definition ldgm.cpp:150
#define PCT10
Definition ldgm.cpp:98
#define DEFAULT_K
Definition ldgm.cpp:140
#define MIN_C
Definition ldgm.cpp:143
static void usage(void)
Definition ldgm.cpp:258
#define MAX_K
Definition ldgm.cpp:145
#define PCT2
Definition ldgm.cpp:96
#define LDGM_GPU_API_VERSION
Definition ldgm.h:49
const void * load_library(const char *name, enum library_class cls, int abi_version)
@ LIBRARY_CLASS_UNDEFINED
Definition lib_common.h:74
int generate_ldgm_matrix(char *fname, unsigned int k, unsigned int m, unsigned int column_weight, unsigned int seed, unsigned int extend_rows)
uint32_t video_payload_hdr_t[6]
Definition rtp_types.h:107
packet_type_t packet_type
Definition ldgm.cpp:87
double loss
Definition ldgm.cpp:89
Definition hd-rum.c:27
std::shared_ptr< video_frame > encode(std::shared_ptr< video_frame >)
Definition ldgm.cpp:426
unsigned int m_seed
Definition ldgm.h:68
unsigned int m_c
Definition ldgm.h:67
void init(unsigned int k, unsigned int m, unsigned int c, unsigned int seed=DEFAULT_LDGM_SEED)
Definition ldgm.cpp:212
std::shared_ptr< LDGM_session > m_coding_session
Definition ldgm.h:66
unsigned int m_k
Definition ldgm.h:67
bool decode(char *in, int in_len, char **out, int *len, const std::map< int, int > &)
Definition ldgm.cpp:244
unsigned int m_m
Definition ldgm.h:67
ldgm(unsigned int k, unsigned int m, unsigned int c, unsigned int seed)
Definition ldgm.cpp:239
void set_params(unsigned int k, unsigned int m, unsigned int c, unsigned int seed)
Definition ldgm.cpp:161
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
static struct @51 loss[MAX_HISTORY]
void format_video_header(struct video_frame *frame, int tile_idx, int buffer_idx, uint32_t *video_hdr)
Definition transmit.cpp:445
@ FEC_LDGM
Definition types.h:203
This is an umbrella header for video functions.