UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
preview.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2022 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#ifdef HAVE_CONFIG_H
39#include "config.h"
40#include "config_unix.h"
41#include "config_win32.h"
42#endif /* HAVE_CONFIG_H */
43
44#include <vector>
45#include <queue>
46#include <mutex>
47#include <atomic>
48#include <condition_variable>
49#include <thread>
50#include <chrono>
51
52#include "capture_filter.h"
53
54#include "debug.h"
55#include "lib_common.h"
56#include "utils/color_out.h"
57#include "utils/fs.h"
58#include "utils/misc.h"
59#include "utils/macros.h"
61#include "video.h"
62#include "video_codec.h"
63#include "tools/ipc_frame.h"
65#include "tools/ipc_frame_ug.h"
66
67#define MOD_NAME "[capture filter preview] "
68
69#define DEFAULT_PREVIEW_FILENAME "ug_preview_cap_unix"
70
71#define DEFAULT_SCALE_W 960
72#define DEFAULT_SCALE_H 540
73
74struct module;
75
76static int init(struct module *parent, const char *cfg, void **state);
77static void done(void *state);
78static struct video_frame *filter(void *state, struct video_frame *in);
79
80using ipc_frame_conv_func_t = bool (*)(struct Ipc_frame *dst,
81 const struct video_frame *src,
83 unsigned scale_factor);
84
85using clk = std::chrono::steady_clock;
86
88 std::mutex mut;
89 std::condition_variable frame_submitted_cv;
90 std::atomic<bool> should_exit = false;
91
92 std::vector<Ipc_frame_uniq> free_frames;
93 std::queue<Ipc_frame_uniq> frame_queue;
94
97
99
100 clk::duration frame_time;
101 clk::time_point next_frame = clk::now();
102
103 std::thread worker_thread;
104};
105
106static void worker(struct state_preview_filter *s, std::string path){
109
110 for(;;){
111 if(!writer){
112 if(s->should_exit)
113 break;
114
115 writer.reset(ipc_frame_writer_new(path.c_str()));
116 if(!writer){
117 log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "Unable to init ipc writer for path %s\n", path.c_str());
118 sleep(1);
119 continue;
120 }
121 }
122
123 {
124 std::unique_lock<std::mutex> lock(s->mut);
125 s->frame_submitted_cv.wait(lock,
126 [=]{ return !s->frame_queue.empty(); });
127 frame = std::move(s->frame_queue.front());
128 s->frame_queue.pop();
129 }
130
131 if(!frame)
132 break;
133
134 if(!ipc_frame_writer_write(writer.get(), frame.get())){;
135 writer.reset();
136 }
137
138 std::lock_guard<std::mutex> lock(s->mut);
139 s->free_frames.push_back(std::move(frame));
140 }
141}
142
143static void show_help(){
144 col() << "preview capture filter\n";
145 col() << "usage:\n";
146 col() << TBOLD(TRED("\t--capture-filter preview") "[:path=<path>][:target_size=<w>x<h>]\n\n");
147 col() << "options:\n";
148 col() << TBOLD("\tpath=<path>") << "\tpath to unix socket to connect to. Default is \""
150 col() << TBOLD("\ttarget_size=<w>x<h>")<< "\tScales the video frame so that the total number of pixel is around <w>x<h>. If -1x-1 is passed, no scaling takes place."
151 << " Defaults are " TOSTRING(DEFAULT_SCALE_W) "x" TOSTRING(DEFAULT_SCALE_H) ".\n";
152 col() << TBOLD("\thq") << "\tUse higher quality downscale\n";
153}
154
155static int init(struct module *parent, const char *cfg, void **state){
156 UNUSED(parent);
157 auto s = std::make_unique<state_preview_filter>();
158
159 s->free_frames.emplace_back(ipc_frame_new());
160 s->free_frames.emplace_back(ipc_frame_new());
161
162 std::string socket_path = get_temp_dir();
163 socket_path += DEFAULT_PREVIEW_FILENAME;
164
165 std::string_view cfg_sv = cfg ? cfg : "";
166 while(!cfg_sv.empty()){
167 auto tok = tokenize(cfg_sv, ':', '"');
168 auto key = tokenize(tok, '=');
169 auto val = tokenize(tok, '=');
170
171 if(key == "help"){
172 show_help();
173 return 1;
174 } else if(key == "path"){
175 socket_path = val;
176 } else if(key == "key"){
177 socket_path = get_temp_dir();
178 socket_path += DEFAULT_PREVIEW_FILENAME;
179 socket_path += val;
180 } else if(key == "target_size"){
181 parse_num(tokenize(val, 'x'), s->target_width);
182 parse_num(tokenize(val, 'x'), s->target_height);
183 } else if(key == "hq"){
184 s->ipc_conv = ipc_frame_from_ug_frame_hq;
185 } else if(key == "rate_limit"){
186 double limit = 1;
188 s->frame_time = std::chrono::duration_cast<clk::duration>(std::chrono::duration<double>(1 / limit));
189 } else {
190 log_msg(LOG_LEVEL_ERROR, "Invalid option\n");
191 return -1;
192 }
193 }
194
195 s->worker_thread = std::thread(worker, s.get(), socket_path);
196
197 *state = s.release();
198
199 return 0;
200}
201
202static void done(void *state){
203 auto s = static_cast<state_preview_filter *> (state);
204
205 {
206 std::lock_guard<std::mutex> lock(s->mut);
207 s->frame_queue.push(nullptr);
208 s->should_exit = true;
209 }
210 s->frame_submitted_cv.notify_one();
211 s->worker_thread.join();
212
213 delete s;
214}
215
216static struct video_frame *filter(void *state, struct video_frame *in){
218
219 auto now = clk::now();
220 if(now < s->next_frame)
221 return in;
222
223 Ipc_frame_uniq ipc_frame;
224 {
225 std::lock_guard<std::mutex> lock(s->mut);
226 if(!s->free_frames.empty()){
227 ipc_frame = std::move(s->free_frames.back());
228 s->free_frames.pop_back();
229 }
230 }
231
232 if(!ipc_frame)
233 return in;
234
235 assert(in->tile_count == 1);
236 const tile *tile = &in->tiles[0];
237
240
241 if(s->ipc_conv(ipc_frame.get(), in, RGB, scale)){
242 std::lock_guard<std::mutex> lock(s->mut);
243 s->frame_queue.push(std::move(ipc_frame));
244 s->frame_submitted_cv.notify_one();
245 } else {
246 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Unable to convert\n");
247 }
248
249 s->next_frame = std::max(s->next_frame + s->frame_time, now);
250
251 return in;
252}
253
254
256 .init = init,
257 .done = done,
258 .filter = filter,
259};
260
262
#define UNUSED(x)
void ** state
int void * val
struct video_frame * frame
#define CAPTURE_FILTER_ABI_VERSION
#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_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_VERBOSE
Definition debug.h:63
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
static void * writer(void *arg)
Definition hd-rum.c:129
Ipc_frame * ipc_frame_new()
Definition ipc_frame.cpp:48
std::unique_ptr< Ipc_frame, Ipc_frame_deleter > Ipc_frame_uniq
Definition ipc_frame.h:40
bool ipc_frame_from_ug_frame_hq(struct Ipc_frame *dst, const struct video_frame *src, codec_t codec, unsigned scale_factor)
Same as ipc_frame_from_ug_frame, but convert pixfmt first and scale later.
bool ipc_frame_from_ug_frame(struct Ipc_frame *dst, const struct video_frame *src, codec_t codec, unsigned scale_factor)
Make an ipc frame from ultragrid frame.
int ipc_frame_get_scale_factor(int src_w, int src_h, int target_w, int target_h)
bool ipc_frame_writer_write(struct Ipc_frame_writer *writer, const struct Ipc_frame *f)
Ipc_frame_writer * ipc_frame_writer_new(const char *path)
std::unique_ptr< Ipc_frame_writer, Ipc_frame_writer_deleter > Ipc_frame_writer_uniq
@ LIBRARY_CLASS_CAPTURE_FILTER
Definition lib_common.h:75
#define REGISTER_HIDDEN_MODULE(name, info, lclass, abi)
Definition lib_common.h:156
#define TOSTRING(A)
Definition macros.h:49
static const struct capture_filter_info capture_filter_preview
Definition preview.cpp:255
static struct video_frame * filter(void *state, struct video_frame *in)
Definition preview.cpp:216
#define DEFAULT_SCALE_H
Definition preview.cpp:72
bool(*)(struct Ipc_frame *dst, const struct video_frame *src, codec_t codec, unsigned scale_factor) ipc_frame_conv_func_t
Definition preview.cpp:80
static void worker(struct state_preview_filter *s, std::string path)
Definition preview.cpp:106
#define DEFAULT_PREVIEW_FILENAME
Definition preview.cpp:69
static void show_help()
Definition preview.cpp:143
#define MOD_NAME
Definition preview.cpp:67
#define DEFAULT_SCALE_W
Definition preview.cpp:71
static void done(void *state)
Definition preview.cpp:202
std::chrono::steady_clock clk
Definition preview.cpp:85
static int init(struct module *parent, const char *cfg, void **state)
Definition preview.cpp:155
static const char * get_temp_dir(void)
const char * parent
Definition settings.cpp:198
const char * limit
Definition settings.cpp:199
std::string_view tokenize(std::string_view &str, char delim, char quot)
Tokenizer for string_view.
bool parse_num(std::string_view sv, T &res, int base=10)
clk::time_point next_frame
Definition preview.cpp:101
std::condition_variable frame_submitted_cv
Definition preview.cpp:89
std::vector< Ipc_frame_uniq > free_frames
Definition preview.cpp:92
std::queue< Ipc_frame_uniq > frame_queue
Definition preview.cpp:93
std::atomic< bool > should_exit
Definition preview.cpp:90
ipc_frame_conv_func_t ipc_conv
Definition preview.cpp:98
std::thread worker_thread
Definition preview.cpp:103
clk::duration frame_time
Definition preview.cpp:100
Struct tile is an area of video_frame.
Definition types.h:279
unsigned int width
Definition types.h:280
unsigned int height
Definition types.h:281
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
unsigned int tile_count
Definition types.h:344
codec_t
Definition types.h:65
@ RGB
RGB 8-bit (packed into 24-bit word)
Definition types.h:80
This is an umbrella header for video functions.
const char * key
Definition cineform.cpp:125