UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
ug_input.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 */
37
38#include <cassert>
39#include <chrono>
40#include <memory>
41#include <mutex>
42#include <queue>
43#include <string>
44#include <thread>
45
46#include "audio/audio.h"
47#include "audio/types.h"
48#include "debug.h"
49#include "host.h"
50#include "lib_common.h"
51#include "tv.h"
52#include "video.h"
53#include "utils/color_out.h"
54#include "video_capture.h"
55#include "video_display.h"
56#include "video_display/pipe.hpp" // frame_recv_delegate
57#include "video_rxtx.hpp"
59
60#define MOD_NAME "[ug_input] "
61static constexpr int MAX_QUEUE_SIZE = 2;
62
63using std::lock_guard;
64using std::map;
65using std::mutex;
66using std::pair;
67using std::queue;
68using std::stoi;
69using std::string;
70using std::thread;
71using std::unique_ptr;
72using std::chrono::duration;
73using std::chrono::duration_cast;
74using std::chrono::steady_clock;
75
77 mutex lock;
78 queue<pair<struct video_frame *, struct audio_frame *>> frame_queue;
80
81 void frame_arrived(struct video_frame *f, struct audio_frame *a);
83 unique_ptr<ultragrid_rtp_video_rxtx> video_rxtx;
85
86 virtual ~ug_input_state() {}
87
88 std::chrono::steady_clock::time_point t0;
89 int frames;
90
92};
93
95{
96 lock_guard<mutex> lk(lock);
97 if (frame_queue.size() < MAX_QUEUE_SIZE) {
98 frame_queue.push({f, a});
99 } else {
100 MSG(WARNING, "Dropping frame!\n");
103 }
104}
105
106static void
108{
109 printf("Usage:\n");
110 color_printf("\t" TBOLD(
111 TRED("-t ug_input") "[:<port>[:codec=<c>]] [-s embedded]") "\n");
112 printf("where:\n");
113 color_printf("\t" TBOLD("<port>") " - UG port to listen to\n");
114 color_printf("\t" TBOLD("<c>") " - enforce pixfmt to decode to\n");
115}
116
117static bool
118parse_fmt(char *fmt, uint16_t *port, codec_t *decode_to)
119{
120 char *tok = nullptr;
121 char *saveptr = nullptr;
122 while ((tok = strtok_r(fmt, ":", &saveptr)) != nullptr) {
123 fmt = nullptr;
124 const char *val = strchr(tok, '=') + 1;
125 if (isdigit(tok[0])) {
126 *port = stoi(tok);
127 } else if (IS_KEY_PREFIX(tok, "codec")) {
128 *decode_to = get_codec_from_name(val);
129 if (*decode_to == VIDEO_CODEC_NONE) {
130 MSG(ERROR, "Invalid codec: %s\n", val);
131 return false;
132 }
133 } else {
134 MSG(ERROR, "Invalid option: %s\n", tok);
135 return false;
136 }
137 }
138 return true;
139}
140
141static int vidcap_ug_input_init(struct vidcap_params *cap_params, void **state)
142{
143 uint16_t port = 5004;
144 codec_t decode_to = VIDEO_CODEC_NONE;
145
146 if (strcmp("help", vidcap_params_get_fmt(cap_params)) == 0) {
147 usage();
148 return VIDCAP_INIT_NOERR;
149 }
150 char *fmt_cpy = strdup(vidcap_params_get_fmt(cap_params));
151 const bool parse_ret = parse_fmt(fmt_cpy, &port, &decode_to);
152 free(fmt_cpy);
153 if (!parse_ret) {
154 return VIDCAP_INIT_FAIL;
155 }
156
157 auto *s = new ug_input_state();
158
159 char cfg[128] = "";
160 snprintf(cfg, sizeof cfg, "%p", s);
161 if (decode_to != VIDEO_CODEC_NONE) {
162 snprintf(cfg + strlen(cfg), sizeof cfg - strlen(cfg),
163 ":codec=%s", get_codec_name(decode_to));
164 }
165 int ret = initialize_video_display(vidcap_params_get_parent(cap_params), "pipe", cfg, 0, NULL, &s->display);
166 assert(ret == 0 && "Unable to initialize proxy display");
167
168 map<string, param_u> params;
169
170 // common
171 s->common.parent = vidcap_params_get_parent(cap_params);
172 params["exporter"].ptr = NULL;
173 params["compression"].str = "none";
174 params["rxtx_mode"].i = MODE_RECEIVER;
175
176 //RTP
177 params["common"].ptr = &s->common;
178 // should be localhost and RX TX ports the same (here dynamic) in order to work like a pipe
179 params["receiver"].str = "localhost";
180 params["rx_port"].i = port;
181 params["tx_port"].i = 0;
182 params["fec"].str = "none";
183 params["bitrate"].ll = 0;
184
185 // UltraGrid RTP
186 params["decoder_mode"].l = VIDEO_NORMAL;
187 params["display_device"].ptr = s->display;
188
189 s->video_rxtx = unique_ptr<ultragrid_rtp_video_rxtx>(dynamic_cast<ultragrid_rtp_video_rxtx *>(video_rxtx::create("ultragrid_rtp", params)));
190 assert (s->video_rxtx);
191
193 struct audio_options opt = {
194 .host = "localhost",
195 .recv_port = port + 2,
196 .send_port = 0,
197 .recv_cfg = "embedded",
198 .send_cfg = "none",
199 .proto = "ultragrid_rtp",
200 .proto_cfg = "",
201 .fec_cfg = "none",
202 .channel_map = NULL,
203 .scale = "none",
204 .echo_cancellation = false,
205 .codec_cfg = "PCM"
206 };
207 if (audio_init(&s->audio, &opt, &s->common) != 0) {
208 delete s;
209 return VIDCAP_INIT_FAIL;
210 }
211
212 struct additional_audio_data aux_aud_data = {
213 { s->display, display_put_audio_frame,
215
216 },
217 s->video_rxtx.get()
218 };
219 audio_register_aux_data(s->audio, aux_aud_data);
220
221 audio_start(s->audio);
222 }
223 s->t0 = steady_clock::now();
224
225 s->receiver_thread = thread(&video_rxtx::receiver_thread, s->video_rxtx.get());
226 display_run_new_thread(s->display);
227
228 *state = s;
229 return VIDCAP_INIT_OK;
230}
231
232static void vidcap_ug_input_done(void *state)
233{
234 auto s = (ug_input_state *) state;
235
236 audio_join(s->audio);
237 s->receiver_thread.join();
238
239 display_put_frame(s->display, NULL, 0);
240 display_join(s->display);
241 display_done(s->display);
242
243 s->video_rxtx->join();
244
245 while (!s->frame_queue.empty()) {
246 auto item = s->frame_queue.front();
247 s->frame_queue.pop();
250 }
251 audio_done(s->audio);
252
253 delete s;
254}
255
257{
258 auto s = (ug_input_state *) state;
259 *audio = NULL;
260 lock_guard<mutex> lk(s->lock);
261 if (s->frame_queue.empty()) {
262 return NULL;
263 } else {
264 auto item = s->frame_queue.front();
265 struct video_frame *frame = item.first;
266 *audio = item.second;
267 s->frame_queue.pop();
269
270 s->frames++;
271 auto curr_time = steady_clock::now();
272 double seconds = duration_cast<duration<double>>(curr_time - s->t0).count();
273 if (seconds >= 5.0) {
274 float fps = s->frames / seconds;
275 log_msg(LOG_LEVEL_INFO, "[ug_input] %d frames in %g seconds = %g FPS\n",
276 s->frames, seconds, fps);
277 s->t0 = curr_time;
278 s->frames = 0;
279 }
280
281 return frame;
282 }
283}
284
285static void vidcap_ug_input_probe(device_info **available_cards, int *count, void (**deleter)(void *))
286{
287 *deleter = free;
288 *count = 0;
289 *available_cards = nullptr;
290}
291
299
301
void ** state
int void * val
struct video_frame * frame
const char * fmt
struct audio_frame ** audio
int void(** deleter)(void *))
void vf_free(struct video_frame *buf)
Frees video_frame structure.
const char * get_codec_name(codec_t codec)
codec_t get_codec_from_name(const char *name)
This file contains definition of some common audio types.
#define AUDIO_FRAME_DISPOSE(frame)
Definition types.h:117
void audio_register_aux_data(struct state_audio *s, struct additional_audio_data data)
Definition audio.cpp:1191
void audio_join(struct state_audio *s)
Definition audio.cpp:502
void audio_done(struct state_audio *s)
Definition audio.cpp:512
int audio_init(struct state_audio **ret, const struct audio_options *opt, const struct common_opts *common)
Definition audio.cpp:272
void audio_start(struct state_audio *s)
Definition audio.cpp:463
int color_printf(const char *format,...)
#define TRED(x)
Definition color_out.h:75
#define TBOLD(x)
Definition color_out.h:70
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define MSG(l, fmt,...)
Definition debug.h:96
bool display_reconfigure_audio(struct display *d, int quant_samples, int channels, int sample_rate)
void display_put_audio_frame(struct display *d, const struct audio_frame *frame)
Puts audio data.
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)
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.
#define VIDCAP_INIT_NOERR
state not initialized, other action performed (typically help)
#define VIDCAP_INIT_OK
initialization successful
#define VIDCAP_NO_GENERIC_FPS_INDICATOR
#define VIDEO_CAPTURE_ABI_VERSION
#define VIDCAP_INIT_FAIL
error ocured
int count
Definition hd-rum.c:44
static struct @19 params[100]
#define MODE_RECEIVER
Definition host.h:103
#define COMMON_OPTS_INIT
Definition host.h:179
@ 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
#define IS_KEY_PREFIX(tok, key)
expands to true value if <k> from tok in format <k>=<v> is prefix of key
Definition macros.h:138
struct used to comunicate with audio capture/playback device
Definition types.h:99
This struct represents initialized video display state.
Definition hd-rum.c:27
struct state_audio * audio
Definition ug_input.cpp:84
std::chrono::steady_clock::time_point t0
Definition ug_input.cpp:88
queue< pair< struct video_frame *, struct audio_frame * > > frame_queue
Definition ug_input.cpp:78
struct display * display
Definition ug_input.cpp:79
virtual ~ug_input_state()
Definition ug_input.cpp:86
struct common_opts common
Definition ug_input.cpp:91
unique_ptr< ultragrid_rtp_video_rxtx > video_rxtx
Definition ug_input.cpp:83
thread receiver_thread
Definition ug_input.cpp:82
void frame_arrived(struct video_frame *f, struct audio_frame *a)
Definition ug_input.cpp:94
void(* dispose)(struct video_frame *)
Definition types.h:245
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
double fps
Definition types.h:308
struct video_frame_callbacks callbacks
Definition types.h:324
static video_rxtx * create(std::string const &name, std::map< std::string, param_u > const &)
static void * receiver_thread(void *arg)
@ VIDEO_NORMAL
normal video (one tile)
Definition types.h:356
codec_t
Definition types.h:65
@ VIDEO_CODEC_NONE
dummy color spec
Definition types.h:66
static int vidcap_ug_input_init(struct vidcap_params *cap_params, void **state)
Definition ug_input.cpp:141
static void vidcap_ug_input_probe(device_info **available_cards, int *count, void(**deleter)(void *))
Definition ug_input.cpp:285
static void vidcap_ug_input_done(void *state)
Definition ug_input.cpp:232
static void usage()
Definition ug_input.cpp:107
static bool parse_fmt(char *fmt, uint16_t *port, codec_t *decode_to)
Definition ug_input.cpp:118
static const struct video_capture_info vidcap_ug_input_info
Definition ug_input.cpp:292
static struct video_frame * vidcap_ug_input_grab(void *state, struct audio_frame **audio)
Definition ug_input.cpp:256
static constexpr int MAX_QUEUE_SIZE
Definition ug_input.cpp:61
This is an umbrella header for video functions.
struct module * vidcap_params_get_parent(const struct vidcap_params *params)
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
#define VIDEO_FRAME_DISPOSE(frame)
Definition video_frame.h:76