UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
sage.cpp
Go to the documentation of this file.
1/*
2 * FILE: video_display/sage.cpp
3 * AUTHORS: Martin Benes <martinbenesh@gmail.com>
4 * Lukas Hejtmanek <xhejtman@ics.muni.cz>
5 * Petr Holub <hopet@ics.muni.cz>
6 * Milos Liska <xliska@fi.muni.cz>
7 * Jiri Matela <matela@ics.muni.cz>
8 * Dalibor Matura <255899@mail.muni.cz>
9 * Ian Wesley-Smith <iwsmith@cct.lsu.edu>
10 *
11 * Copyright (c) 2005-2023 CESNET z.s.p.o.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, is permitted provided that the following conditions
15 * are met:
16 *
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 *
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
23 *
24 * 3. All advertising materials mentioning features or use of this software
25 * must display the following acknowledgement:
26 *
27 * This product includes software developed by CESNET z.s.p.o.
28 *
29 * 4. Neither the name of CESNET nor the names of its contributors may be used
30 * to endorse or promote products derived from this software without specific
31 * prior written permission.
32 *
33 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
34 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
35 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
36 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
37 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
38 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
39 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
40 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
42 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
43 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
44 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 *
46 *
47 */
48
49#ifdef HAVE_CONFIG_H
50#include "config.h"
51#include "config_unix.h"
52#include "config_win32.h"
53#endif // HAVE_CONFIG_H
54
55#include "debug.h"
56#include "host.h"
57#include "lib_common.h"
58#include "video.h"
59#include "video_display.h"
60
61#include <GL/gl.h>
62
63#include <X11/Xlib.h>
64#include <sys/time.h>
65#include <assert.h>
66
67#include <semaphore.h>
68#include <signal.h>
69#include <pthread.h>
70
71#include <sail.h>
72#include <misc.h>
73
74#include <host.h>
75#include <tv.h>
76
77#include <video_codec.h>
78
79#define MAGIC_SAGE 0x3e960c47
80
81struct state_sage {
83 struct tile *tile;
85
86 /* Thread related information follows... */
87 pthread_t thread_id;
88 sem_t semaphore;
89
90 volatile unsigned int buffer_writable:1;
91 volatile unsigned int grab_waiting:1;
92 pthread_cond_t buffer_writable_cond;
93 pthread_mutex_t buffer_writable_lock;
94
95 /* For debugging... */
96 uint32_t magic;
98
100
101 const char *confName;
102 const char *fsIP;
103
105 struct timeval t, t0;
106
107 volatile bool should_exit;
108 bool is_tx;
110
111 pthread_t thread_id;
112};
113
116{
117 return 0;
118}
119
120static void display_sage_run(void *arg)
121{
122 struct state_sage *s = (struct state_sage *)arg;
123 s->magic = MAGIC_SAGE;
124
125 while (!s->should_exit) {
127
128 sem_wait(&s->semaphore);
129 if (s->should_exit)
130 break;
131
132 if (s->deinterlace) {
133 vc_deinterlace((unsigned char *) s->tile->data, vc_get_linesize(s->tile->width,
134 s->frame->color_spec), s->tile->height);
135 }
136
137 s->sage_state->swapBuffer(SAGE_NON_BLOCKING);
138 sageMessage msg;
139 if (s->sage_state->checkMsg(msg, false) > 0) {
140 switch (msg.getCode()) {
141 case APP_QUIT:
142 sage::printLog("Ultragrid: QUIT message");
143 exit_uv(1);
144 break;
145 }
146 }
147
148 s->tile->data = (char *) s->sage_state->getBuffer();
149
150 pthread_mutex_lock(&s->buffer_writable_lock);
151 s->buffer_writable = 1;
152 if(s->grab_waiting) {
153 pthread_cond_broadcast(&s->buffer_writable_cond);
154 }
155 pthread_mutex_unlock(&s->buffer_writable_lock);
156
157 s->frames++;
158
159 gettimeofday(&s->t, NULL);
160 double seconds = tv_diff(s->t, s->t0);
161 if (seconds >= 5) {
162 float fps = s->frames / seconds;
163 log_msg(LOG_LEVEL_INFO, "[SAGE] %d frames in %g seconds = %g FPS\n",
164 s->frames, seconds, fps);
165 s->t0 = s->t;
166 s->frames = 0;
167 }
168 }
169}
170
171static void *display_sage_init(struct module *parent, const char *fmt, unsigned int flags)
172{
173 UNUSED(fmt);
174 UNUSED(flags);
175 UNUSED(parent);
176 struct state_sage *s;
177
178 s = (struct state_sage *) calloc(1, sizeof(struct state_sage));
179 assert(s != NULL);
180
181 s->confName = NULL;
182
183 if(fmt) {
184 if(strcmp(fmt, "help") == 0) {
185 printf("SAGE usage:\n");
186 printf("\tuv -t sage[:config=<config_file>|:fs=<fsIP>][:codec=<fcc>][:d]\n");
187 printf("\t <config_file> - SAGE app config file, default \"ultragrid.conf\"\n");
188 printf("\t <fsIP> - FS manager IP address\n");
189 printf("\t <fcc> - FourCC of codec that will be used to transmit to SAGE\n");
190 printf("\t Supported options are UYVY, RGBA, RGB or DXT1\n");
191 printf("\t d - deinterlace output\n");
192 return INIT_NOERR;
193 } else {
194 char *tmp, *parse_str;
195 tmp = parse_str = strdup(fmt);
196 char *save_ptr = NULL;
197 char *item;
198
199 while((item = strtok_r(parse_str, ":", &save_ptr))) {
200 parse_str = NULL;
201 if(strncmp(item, "config=", strlen("config=")) == 0) {
202 s->confName = item + strlen("config=");
203 } else if(strncmp(item, "codec=", strlen("codec=")) == 0) {
204 uint32_t fourcc;
205 if(strlen(item + strlen("codec=")) != sizeof(fourcc)) {
206 fprintf(stderr, "Malformed FourCC code (wrong length).\n");
207 free(s); return NULL;
208 }
209 memcpy((void *) &fourcc, item + strlen("codec="), sizeof(fourcc));
212 fprintf(stderr, "Codec not found according to FourCC.\n");
213 free(s); return NULL;
214 }
215 if(s->requestedDisplayCodec != UYVY &&
219#ifdef SAGE_NATIVE_DXT5YCOCG
221#endif // SAGE_NATIVE_DXT5YCOCG
222 ) {
223 fprintf(stderr, "Entered codec is not nativelly supported by SAGE.\n");
224 free(s); return NULL;
225 }
226 } else if(strcmp(item, "tx") == 0) {
227 s->is_tx = true;
228 } else if(strncmp(item, "fs=", strlen("fs=")) == 0) {
229 s->fsIP = item + strlen("fs=");
230 } else if(strcmp(item, "d") == 0) {
231 s->deinterlace = true;
232 } else {
233 fprintf(stderr, "[SAGE] unrecognized configuration: %s\n",
234 item);
235 free(s);
236 return NULL;
237 }
238 }
239 free(tmp);
240 }
241 }
242
243 if (!s->is_tx) {
244 // read config file only if we are in dispaly mode (not sender mode)
245 struct stat sb;
246 if(s->confName) {
247 if(stat(s->confName, &sb)) {
248 perror("Unable to use SAGE config file");
249 free(s);
250 return NULL;
251 }
252 } else if(stat("ultragrid.conf", &sb) == 0) {
253 s->confName = "ultragrid.conf";
254 }
255 if(s->confName) {
256 printf("[SAGE] Using config file %s.\n", s->confName);
257 }
258 }
259
260 if(s->confName == NULL && s->fsIP == NULL) {
261 fprintf(stderr, "[SAGE] Unable to locate FS manager address. "
262 "Set either in config file or from command line.\n");
263 free(s);
264 return NULL;
265 }
266
267 s->magic = MAGIC_SAGE;
268
269 gettimeofday(&s->t0, NULL);
270
271 s->frames = 0;
272 s->frame = vf_alloc(1);
273 s->tile = vf_get_tile(s->frame, 0);
274
275 /* sage init */
276 //FIXME sem se musi propasovat ty spravne parametry argc argv
277 s->appID = 0;
278 s->nodeID = 1;
279
280 s->sage_state = NULL;
281
282 /* thread init */
283 sem_init(&s->semaphore, 0, 0);
284
285 s->buffer_writable = 1;
286 s->grab_waiting = 1;
287 pthread_mutex_init(&s->buffer_writable_lock, 0);
288 pthread_cond_init(&s->buffer_writable_cond, NULL);
289
290 /*if (pthread_create
291 (&(s->thread_id), NULL, display_thread_sage, (void *)s) != 0) {
292 perror("Unable to create display thread\n");
293 return NULL;
294 }*/
295
296 debug_msg("Window initialized %p\n", s);
297
298 pthread_create(&s->thread_id, NULL, display_sage_run, s);
299 return (void *)s;
300}
301
302static void display_sage_done(void *state)
303{
304 struct state_sage *s = (struct state_sage *)state;
305
306 assert(s->magic == MAGIC_SAGE);
307
308 pthread_join(s->thread_id, NULL);
309 sem_destroy(&s->semaphore);
310 pthread_cond_destroy(&s->buffer_writable_cond);
311 pthread_mutex_destroy(&s->buffer_writable_lock);
312 vf_free(s->frame);
313 if (s->sage_state)
314 s->sage_state->shutdown();
315 //delete s->sage_state;
316 free(s);
317}
318
320{
321 struct state_sage *s = (struct state_sage *)state;
322
323 assert(s->magic == MAGIC_SAGE);
324
325 pthread_mutex_lock(&s->buffer_writable_lock);
326 while (!s->buffer_writable) {
327 s->grab_waiting = TRUE;
328 pthread_cond_wait(&s->buffer_writable_cond,
330 s->grab_waiting = FALSE;
331 }
332 pthread_mutex_unlock(&s->buffer_writable_lock);
333
334 return s->frame;
335}
336
337static bool display_sage_putf(void *state, struct video_frame *frame, long long nonblock)
338{
339 int tmp;
340 struct state_sage *s = (struct state_sage *)state;
341
342 assert(s->magic == MAGIC_SAGE);
343 UNUSED(frame);
345
346 if(!frame) {
347 s->should_exit = true;
348 }
349
350 /* ...and signal the worker */
351 pthread_mutex_lock(&s->buffer_writable_lock);
352 s->buffer_writable = 0;
353 pthread_mutex_unlock(&s->buffer_writable_lock);
354
355 sem_post(&s->semaphore);
356#ifndef __APPLE__
357 sem_getvalue(&s->semaphore, &tmp);
358 if (tmp > 1)
359 printf("frame drop!\n");
360#endif
361 return true;
362}
363
364/*
365 * Either confName or fsIP should be NULL
366 */
367static sail *initSage(const char *confName, const char *fsIP, int appID, int nodeID, int width,
368 int height, codec_t codec)
369{
370 sail *sageInf; // sage sail object
371
372 sageInf = new sail;
373 sailConfig sailCfg;
374
375 // default values
376 if(fsIP) {
377 strncpy(sailCfg.fsIP, fsIP, SAGE_IP_LEN - 1);
378 }
379 sailCfg.fsPort = 20002;
380 strncpy(sailCfg.masterIP, "127.0.0.1", SAGE_IP_LEN - 1);
381 sailCfg.nwID = 1;
382 sailCfg.msgPort = 23010;
383 sailCfg.syncPort = 13010;
384 sailCfg.blockSize = 64;
385 sailCfg.winX = sailCfg.winY = 0;
386 sailCfg.winWidth = width;
387 sailCfg.winHeight = height;
388 sailCfg.streamType = SAGE_BLOCK_NO_SYNC;
389 sailCfg.protocol = SAGE_UDP;
390 sailCfg.asyncUpdate = false;
391
392 if(confName) {
393 sailCfg.init((char *) confName);
394 }
395 char appName[] = "ultragrid";
396 sailCfg.setAppName(appName);
397 sailCfg.rank = nodeID;
398 sailCfg.appID = appID;
399 sailCfg.resX = width;
400 sailCfg.resY = height;
401
402 sageRect renderImageMap;
403 renderImageMap.left = 0.0;
404 renderImageMap.right = 1.0;
405 renderImageMap.bottom = 0.0;
406 renderImageMap.top = 1.0;
407
408 sailCfg.imageMap = renderImageMap;
409 switch (codec) {
410 case DXT1:
411 sailCfg.pixFmt = PIXFMT_DXT;
412 break;
413#ifdef SAGE_NATIVE_DXT5YCOCG
414 case DXT5:
415 sailCfg.pixFmt = PIXFMT_DXT5YCOCG
416 break;
417#endif // SAGE_NATIVE_DXT5YCOCG
418 case RGBA:
419 sailCfg.pixFmt = PIXFMT_8888;
420 break;
421 case UYVY:
422 sailCfg.pixFmt = PIXFMT_YUV;
423 break;
424 case RGB:
425 sailCfg.pixFmt = PIXFMT_888;
426 break;
427 default:
428 abort();
429 }
430
431 if(codec == DXT1) {
432 sailCfg.rowOrd = BOTTOM_TO_TOP;
433#ifdef SAGE_NATIVE_DXT5YCOCG
434 } else if(codec == DXT5) {
435 sailCfg.rowOrd = BOTTOM_TO_TOP;
436#endif // SAGE_NATIVE_DXT5YCOCG
437 } else {
438 sailCfg.rowOrd = TOP_TO_BOTTOM;
439 }
440 sailCfg.master = true;
441
442 sageInf->init(sailCfg);
443
444 return sageInf;
445}
446
448{
449 struct state_sage *s = (struct state_sage *)state;
450
451 assert(s->magic == MAGIC_SAGE);
452 assert(desc.color_spec == RGBA || desc.color_spec == RGB || desc.color_spec == UYVY ||
454#ifdef SAGE_NATIVE_DXT5YCOCG
455 || desc.color_spec == DXT5
456#endif // SAGE_NATIVE_DXT5YCOCG
457 );
458
459 s->tile->width = desc.width;
460 s->tile->height = desc.height;
461 s->frame->fps = desc.fps;
464
465 // SAGE fix - SAGE threads apparently do not process signals correctly so we temporarily
466 // block all signals while creating SAGE
467 sigset_t mask, old_mask;
468 sigemptyset(&mask);
469 sigaddset(&mask, SIGINT);
470 sigaddset(&mask, SIGTERM);
471 sigaddset(&mask, SIGHUP);
472 pthread_sigmask(SIG_BLOCK, &mask, &old_mask);
473
474 if(s->sage_state) {
475 s->sage_state->shutdown();
476 //delete s->sage_state; // this used to cause crashes
477 }
478
479 s->sage_state = initSage(s->confName, s->fsIP, s->appID, s->nodeID,
480 s->tile->width, s->tile->height, desc.color_spec);
481
482 // calling thread should be able to process signals afterwards
483 pthread_sigmask(SIG_UNBLOCK, &old_mask, NULL);
484
485 s->tile->data = (char *) s->sage_state->getBuffer();
487
488 return true;
489}
490
491static bool display_sage_get_property(void *state, int property, void *val, size_t *len)
492{
493 struct state_sage *s = (struct state_sage *)state;
494 UNUSED(state);
496#ifdef SAGE_NATIVE_DXT5YCOCG
497 , DXT5
498#endif // SAGE_NATIVE_DXT5YCOCG
499 };
500 int rgb_shift[] = {0, 8, 16};
501
502 switch (property) {
505 if(sizeof(codec_t) <= *len) {
506 memcpy(val, &s->requestedDisplayCodec, sizeof(codec_t));
507 *len = sizeof(codec_t);
508 } else {
509 return false;
510 }
511 } else {
512 if(sizeof(codecs) <= *len) {
513 memcpy(val, codecs, sizeof(codecs));
514 *len = sizeof(codecs);
515 } else {
516 return false;
517 }
518 }
519 break;
521 if(sizeof(rgb_shift) > *len) {
522 return false;
523 }
524 memcpy(val, rgb_shift, sizeof(rgb_shift));
525 *len = sizeof(rgb_shift);
526 break;
528 *(int *) val = PITCH_DEFAULT;
529 *len = sizeof(int);
530 break;
531 default:
532 return false;
533 }
534 return true;
535}
536
537static void display_sage_put_audio_frame(void *state, const struct audio_frame *frame)
538{
539 UNUSED(state);
540 UNUSED(frame);
541}
542
544 int sample_rate)
545{
546 UNUSED(state);
550
551 return false;
552}
553
554static void display_sage_probe(struct device_info **available_cards, int *count, void (**deleter)(void *)) {
556 *count = 1;
557 *available_cards = (struct device_info *) calloc(1, sizeof(struct device_info));
558 strcpy((*available_cards)[0].dev, "");
559 strcpy((*available_cards)[0].name, "SAGE display wall");
560 (*available_cards)[0].repeatable = true;
561}
562
576
578
#define UNUSED(x)
void ** state
struct video_frame long long nonblock
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
struct video_frame * vf_alloc(int count)
Allocates blank video frame.
void vf_free(struct video_frame *buf)
Frees video_frame structure.
int vc_get_linesize(unsigned int width, codec_t codec)
#define TRUE
#define FALSE
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define debug_msg(...)
#define debug_msg(...) log_msg(LOG_LEVEL_DEBUG, "[pid/%d +%d %s] ", getpid(), LINE,...
Definition debug.h:92
#define PITCH_DEFAULT
default pitch, i. e. respective linesize
#define VIDEO_DISPLAY_ABI_VERSION
#define DISPLAY_NO_GENERIC_FPS_INDICATOR
@ DISPLAY_PROPERTY_BUF_PITCH
requested framebuffer pitch - int (bytes), may be PITCH_DEFAULT
@ DISPLAY_PROPERTY_RGB_SHIFT
red,green,blue shift - int[3] (bits)
@ DISPLAY_PROPERTY_CODECS
list of natively supported codecs - codec_t[]
int count
Definition hd-rum.c:44
void exit_uv(int status)
Definition host.cpp:625
#define INIT_NOERR
Definition host.h:73
@ LIBRARY_CLASS_VIDEO_DISPLAY
Definition lib_common.h:79
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
struct msghdr msg
const char * parent
Definition settings.cpp:198
const char * name
Definition settings.cpp:193
struct used to comunicate with audio capture/playback device
Definition types.h:99
char dev[1024]
Definition types.h:383
Definition hd-rum.c:27
int appID
Definition sage.cpp:97
struct video_frame * frame
Definition sage.cpp:82
bool is_tx
Definition sage.cpp:108
codec_t requestedDisplayCodec
Definition sage.cpp:84
int nodeID
Definition sage.cpp:97
struct timeval t t0
Definition sage.cpp:105
const char * confName
Definition sage.cpp:101
pthread_cond_t buffer_writable_cond
Definition sage.cpp:92
volatile unsigned int grab_waiting
Definition sage.cpp:91
pthread_mutex_t buffer_writable_lock
Definition sage.cpp:93
sem_t semaphore
Definition sage.cpp:88
sail * sage_state
Definition sage.cpp:99
const char * fsIP
Definition sage.cpp:102
volatile bool should_exit
Definition sage.cpp:107
uint32_t magic
Definition sage.cpp:96
int frames
Definition sage.cpp:104
bool deinterlace
Definition sage.cpp:109
volatile unsigned int buffer_writable
Definition sage.cpp:90
pthread_t thread_id
Definition sage.cpp:87
struct tile * tile
Definition sage.cpp:83
Struct tile is an area of video_frame.
Definition types.h:279
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
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
enum interlacing_t interlacing
Definition types.h:307
double fps
Definition types.h:308
codec_t color_spec
Definition types.h:306
double tv_diff(struct timeval curr_time, struct timeval prev_time)
Definition tv.c:97
codec_t
Definition types.h:65
@ DXT1
S3 Texture Compression DXT1.
Definition types.h:77
@ RGB
RGB 8-bit (packed into 24-bit word)
Definition types.h:80
@ UYVY
YCbCr 422 8-bit - Cb Y0 Cr Y1.
Definition types.h:71
@ VIDEO_CODEC_NONE
dummy color spec
Definition types.h:66
@ DXT5
S3 Texture Compression DXT5.
Definition types.h:79
@ RGBA
RGBA 8-bit, big-endian.
Definition types.h:70
This is an umbrella header for video functions.
void vc_deinterlace(unsigned char *src, long src_linesize, int lines)
Deinterlaces framebuffer.
codec_t get_codec_from_fcc(uint32_t fourcc)
static struct @76 codecs[]
static struct video_frame * display_sage_getf(void *state)
Definition sage.cpp:319
static int display_sage_handle_events(void)
Definition sage.cpp:115
static void display_sage_probe(struct device_info **available_cards, int *count, void(**deleter)(void *))
Definition sage.cpp:554
static void display_sage_put_audio_frame(void *state, const struct audio_frame *frame)
Definition sage.cpp:537
static void * display_sage_init(struct module *parent, const char *fmt, unsigned int flags)
Definition sage.cpp:171
static void display_sage_run(void *arg)
Definition sage.cpp:120
#define MAGIC_SAGE
Definition sage.cpp:79
static void display_sage_done(void *state)
Definition sage.cpp:302
static bool display_sage_reconfigure(void *state, struct video_desc desc)
Definition sage.cpp:447
static bool display_sage_putf(void *state, struct video_frame *frame, long long nonblock)
Definition sage.cpp:337
static bool display_sage_reconfigure_audio(void *state, int quant_samples, int channels, int sample_rate)
Definition sage.cpp:543
static const struct video_display_info display_sage_info
Definition sage.cpp:563
static sail * initSage(const char *confName, const char *fsIP, int appID, int nodeID, int width, int height, codec_t codec)
Definition sage.cpp:367
static bool display_sage_get_property(void *state, int property, void *val, size_t *len)
Definition sage.cpp:491
struct tile * vf_get_tile(struct video_frame *buf, int pos)
Returns pointer to n-th tile from video frame Equivalent to &video_frame::tiles[pos].