UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
spout.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2018-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
39#include <array>
40#include <cctype>
41#include <chrono>
42#include <iostream>
43#include <memory>
44#include <SpoutLibrary.h>
45#include <synchapi.h>
46#include <string>
47
48#include "debug.h"
49#include "gl_context.h"
50#include "host.h"
51#include "lib_common.h"
52#include "spout_sender.h" // spout_set_log_level
53#include "utils/color_out.h"
54#include "utils/text.h" // urlencode, urldecode
55#include "video.h"
56#include "video_capture.h"
57
58#define DEFAULT_FPS 60.0
59#define DEFAULT_CODEC RGB
60
61#define MOD_NAME "[spout cap.] "
62
63using std::array;
64using std::cout;
65using std::shared_ptr;
66using std::stoi;
67using std::string;
68
74 shared_ptr<SPOUTLIBRARY> spout_state;
76 GLenum gl_format;
77
78 char server_name[256];
79
80 std::chrono::steady_clock::time_point last_frame_captured = std::chrono::steady_clock::now();
81 int frames;
82};
83
84static void usage()
85{
86 col() << "Usage:\n";
87 col() << "\t" << TBOLD(TRED("-t spout") << "[:name=<server_name>][:fps=<fps>][:codec=<codec>]") << "\n";
88 col() << "where\n";
89 col() << "\t" << TBOLD("name") << "\n\t\tSpout server name\n";
90 col() << "\t" << TBOLD("fps") << "\n\t\tFPS count (default: " << DEFAULT_FPS << ")\n";
91 col() << "\t" << TBOLD("codec") << "\n\t\tvideo codec (default: " << get_codec_name(DEFAULT_CODEC) << ")\n";
92 col() << "\nServers:\n";
93 auto receiver = shared_ptr<SPOUTLIBRARY>(GetSpout(), [](auto s) {s->Release();});
94 int count = receiver->GetSenderCount();
95
96 for (int i = 0; i < count; ++i) {
97 array<char, 256> name{};
98 if (!receiver->GetSender(i, name.data(), name.size())) {
99 LOG(LOG_LEVEL_ERROR) << "Cannot get name for server #" << i << "\n";
100 continue;
101 }
102 unsigned int width = 0;
103 unsigned int height = 0;
104 HANDLE dxShareHandle = 0;
105 DWORD dwFormat = 0;
106 if (!receiver->GetSenderInfo(name.data(), width, height, dxShareHandle, dwFormat)) {
107 LOG(LOG_LEVEL_ERROR) << "Cannot get server " << name.data() << "details\n";
108 }
109 col() << "\t" << TBOLD(<< name.data() <<) << ") width: " << width << ", height: " << height << "\n";
110 }
111}
112
113static int vidcap_spout_init(struct vidcap_params *params, void **state)
114{
117 }
118
120
121 double fps = DEFAULT_FPS;
123
124 char *opts = strdup(vidcap_params_get_fmt(params));
125 char *item, *save_ptr;
126 int ret = VIDCAP_INIT_OK;
127
128 item = strtok_r(opts, ":", &save_ptr);
129 while (item) {
130 if (strcmp(item, "help") == 0) {
131 usage();
132 ret = VIDCAP_INIT_NOERR;
133 break;
134 } else if (strstr(item, "name=") == item) {
135 char *name = item + strlen("name=");
136 if (strstr(name, "urlencoded=") == name) {
137 name += strlen("urlencoded=");
138 if (urldecode(s->server_name, sizeof s->server_name, name) == 0) {
139 LOG(LOG_LEVEL_WARNING) << MOD_NAME << "Improperly formatted name: " << item + strlen("name=") << "\n";
140 ret = VIDCAP_INIT_FAIL;
141 }
142 } else {
143 strncpy(s->server_name, item + strlen("name="), sizeof(s->server_name) - 1);
144 }
145 } else if (strstr(item, "fps=") == item) {
146 fps = atof(item + strlen("fps="));
147 } else if (strstr(item, "codec=") == item) {
148 codec = get_codec_from_name(item + strlen("codec="));
149 } else {
150 LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Unknown argument - " << item << "\n";
151 ret = VIDCAP_INIT_FAIL;
152 break;
153 }
154 item = strtok_r(NULL, ":", &save_ptr);
155 }
156
157 free(opts);
158
159 if (ret != VIDCAP_INIT_OK) {
160 delete s;
161 return ret;
162 }
163
164 switch (codec) {
165 case RGB:
166 s->gl_format = GL_RGB;
167 break;
168 case RGBA:
169 s->gl_format = GL_RGBA;
170 break;
171 default:
172 LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Unsupported codec: " << get_codec_name(codec) << "! Currently only RGB and RGBA are supported.\n";
173 delete s;
174 return VIDCAP_INIT_FAIL;
175 }
176
177
179 LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Unable to initialize GL context!\n";
180 delete s;
181 return VIDCAP_INIT_FAIL;
182 }
184
185 s->spout_state = shared_ptr<SPOUTLIBRARY>(GetSpout(), [](auto s) {s->Release();});
187 if (strlen(s->server_name) != 0) {
188 s->spout_state->SetReceiverName(s->server_name);
189 }
190
192 s->desc = video_desc{0, 0, codec, fps, PROGRESSIVE, 1};
193
194 MSG(INFO, "initialized successfully, waiting for input...\n");
195 *state = s;
196 return VIDCAP_INIT_OK;
197}
198
199static void vidcap_spout_done(void *state)
200{
202
204 s->spout_state->ReleaseReceiver();
206
207 delete s;
208}
209
210static struct video_frame *vidcap_spout_grab(void *state, struct audio_frame **audio)
211{
212 enum {
213 MS100 = 100,
214 };
216
218 struct video_frame *out = vf_alloc_desc_data(s->desc);
220
221 bool ret = s->spout_state->ReceiveImage((unsigned char *) out->tiles[0].data, s->gl_format);
222 if (!ret) {
223 vf_free(out);
225 MSG(WARNING, "No signal detected...\n");
226 Sleep(MS100);
227 return NULL;
228 }
229 if (s->spout_state->IsUpdated()) {
230 s->desc.width = s->spout_state->GetSenderWidth();
231 s->desc.height = s->spout_state->GetSenderHeight();
232 MSG(NOTICE,
233 "Server updated - name: %s, size: %dx%d, fps: "
234 "%g, codec: %s\n",
235 s->spout_state->GetSenderName(), s->desc.width,
236 s->desc.height, s->desc.fps,
238 vf_free(out);
240 return NULL;
241 }
243
244 // wait until this frame is due
245 decltype(s->last_frame_captured) t;
246 do {
247 t = std::chrono::steady_clock::now();
248 } while (std::chrono::duration_cast<std::chrono::duration<double>>(t - s->last_frame_captured).count() < 1 / out->fps);
249 s->last_frame_captured = t;
250
251 *audio = NULL;
252 return out;
253}
254
255static void vidcap_spout_probe(device_info **available_cards, int *count, void (**deleter)(void *))
256{
257 *deleter = free;
258 *count = 0;
259
260 auto receiver = shared_ptr<SPOUTLIBRARY>(GetSpout(), [](auto s) {s->Release();});
261
262 int sender_count = receiver->GetSenderCount();
263
264 *available_cards = (struct device_info *) calloc(sender_count, sizeof(struct device_info));
265 if (*available_cards == nullptr) {
266 return;
267 }
268 *count = sender_count;
269
270 for (int i = 0; i < *count; ++i) {
271 array<char, 256> name{};
272 auto& card = (*available_cards)[i];
273 if (!receiver->GetSender(i, name.data(), name.size())) {
274 LOG(LOG_LEVEL_VERBOSE) << MOD_NAME << "Cannot get name for server #" << i << "\n";
275 snprintf(card.dev, sizeof card.dev, ":device=%d", i);
276 snprintf(card.name, sizeof card.name, "Spout #%d", i);
277 } else {
278 snprintf(card.dev, sizeof card.dev, ":name=urlencoded=");
279 urlencode(card.dev + strlen(card.dev),
280 sizeof card.dev - strlen(card.dev),
281 name.data(), urlencode_rfc3986_eval, false);
282 snprintf(card.name, sizeof card.name, "Spout %s", name.data());
283 }
284 card.repeatable = true;
285 }
286}
287
295
297
void ** state
struct audio_frame ** audio
int void(** deleter)(void *))
void vf_free(struct video_frame *buf)
Frees video_frame structure.
struct video_frame * vf_alloc_desc_data(struct video_desc desc)
const char * get_codec_name(codec_t codec)
codec_t get_codec_from_name(const char *name)
#define TRED(x)
Definition color_out.h:75
#define TBOLD(x)
Definition color_out.h:70
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_VERBOSE
Definition debug.h:63
#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
bool init_gl_context(struct gl_context *context, int which)
initializes specified OpenGL context
Definition gl_context.c:69
void destroy_gl_context(struct gl_context *context)
Definition gl_context.c:223
void gl_context_make_current(struct gl_context *context)
Definition gl_context.c:236
#define GL_CONTEXT_ANY
Definition gl_context.h:72
#define VIDCAP_INIT_NOERR
state not initialized, other action performed (typically help)
#define VIDCAP_INIT_OK
initialization successful
#define VIDEO_CAPTURE_ABI_VERSION
#define VIDCAP_INIT_FAIL
error ocured
#define VIDCAP_INIT_AUDIO_NOT_SUPPORTED
card does not support audio
int count
Definition hd-rum.c:44
volatile int log_level
Definition debug.cpp:65
@ LIBRARY_CLASS_VIDEO_CAPTURE
Definition lib_common.h:78
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
const char * name
Definition settings.cpp:193
static int vidcap_spout_init(struct vidcap_params *params, void **state)
Definition spout.cpp:113
static void usage()
Definition spout.cpp:84
#define DEFAULT_CODEC
Definition spout.cpp:59
static const struct video_capture_info vidcap_spout_info
Definition spout.cpp:288
#define DEFAULT_FPS
Definition spout.cpp:58
#define MOD_NAME
Definition spout.cpp:61
static void vidcap_spout_done(void *state)
Definition spout.cpp:199
static void vidcap_spout_probe(device_info **available_cards, int *count, void(**deleter)(void *))
Definition spout.cpp:255
static struct video_frame * vidcap_spout_grab(void *state, struct audio_frame **audio)
Definition spout.cpp:210
void spout_set_log_level(void *s, int ug_level)
struct used to comunicate with audio capture/playback device
Definition types.h:99
Definition hd-rum.c:27
struct gl_context glc
Definition spout.cpp:75
char server_name[256]
Definition spout.cpp:78
struct video_desc desc
Definition spout.cpp:73
std::chrono::steady_clock::time_point last_frame_captured
Definition spout.cpp:80
shared_ptr< SPOUTLIBRARY > spout_state
Definition spout.cpp:74
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
codec_t color_spec
Definition types.h:182
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
void(* dispose)(struct video_frame *)
Definition types.h:245
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
struct video_frame_callbacks callbacks
Definition types.h:324
@ PROGRESSIVE
progressive frame
Definition types.h:164
codec_t
Definition types.h:65
@ RGB
RGB 8-bit (packed into 24-bit word)
Definition types.h:80
@ RGBA
RGBA 8-bit, big-endian.
Definition types.h:70
int urlencode_rfc3986_eval(int c)
Definition text.c:59
size_t urldecode(char *out, size_t max_len, const char *in)
Definition text.c:129
size_t urlencode(char *out, size_t max_len, const char *in, int(*eval_pass)(int c), bool space_plus_replace)
Definition text.c:81
This is an umbrella header for video functions.
const char * vidcap_params_get_fmt(const struct vidcap_params *params)
unsigned int vidcap_params_get_flags(const struct vidcap_params *params)
#define VIDCAP_FLAG_AUDIO_ANY