UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
pano_gl.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2010-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#ifdef HAVE_CONFIG_H
38#include "config.h"
39#include "config_unix.h"
40#include "config_win32.h"
41#endif
42
43#include <cassert>
44#include <chrono>
45#include <condition_variable>
46#include <mutex>
47#include <queue>
48
49#include "debug.h"
50#include "host.h"
51#include "keyboard_control.h" // K_*
52#include "lib_common.h"
53#include "messaging.h"
54#include "module.h"
55#include "video.h"
56#include "video_display.h"
58
59#include "opengl_utils.hpp"
60#include "opengl_panorama.hpp"
61#include "sdl_window.hpp"
63
64#define MAX_BUFFER_SIZE 1
65#define MOD_NAME "[pano gl] "
66
67struct state_vr{
69
70 bool running = false;
71 bool fs = false;
72
75
78
80
81 int threshold_fps = 60; //Target fps for redraws caused by interaction
82
83 SDL_TimerID redraw_timer = 0;
84 bool redraw_needed = false;
85
86 std::chrono::steady_clock::time_point last_frame;
87
88 std::mutex lock;
89 std::condition_variable frame_consumed_cv;
90 std::queue<video_frame *> free_frame_queue;
91};
92
93static void * display_panogl_init(struct module *parent, const char *fmt, unsigned int flags) {
95 UNUSED(fmt);
97 state_vr *s = new state_vr();
98 s->sdl_frame_event = SDL_RegisterEvents(1);
99 s->sdl_redraw_event = SDL_RegisterEvents(1);
100
101 return s;
102}
103
104static void draw(state_vr *s){
106 auto now = std::chrono::steady_clock::now();
107 s->last_frame = now;
108
109 glClear(GL_COLOR_BUFFER_BIT);
111
112 SDL_GL_SwapWindow(s->window.sdl_window);
113}
114
115static Uint32 redraw_callback(Uint32 interval, void *param){
116 int event_id = *static_cast<int *>(param);
117 SDL_Event event;
118 event.type = event_id;
119 SDL_PushEvent(&event);
120
121 return interval;
122}
123
124static void redraw(state_vr *s,
125 bool video_framerate_over_threshold = false,
126 bool triggered_by_frame = false)
127{
128 if(video_framerate_over_threshold && triggered_by_frame){
129 /* If video framerate is over the threshold we draw the
130 * incoming frames immediately. Since this draw already
131 * contains changes caused by interaction (e.g. moving the
132 * camera) we cancel the redraw timer
133 */
134 if(s->redraw_timer){
135 SDL_RemoveTimer(s->redraw_timer);
136 s->redraw_timer = 0;
137 s->redraw_needed = false;
138 }
139 draw(s);
140 } else {
141 if(!s->redraw_timer){
142 /* Redrawing due to interaction is delayed, so that if
143 * a fresh video frame arrives very soon after the
144 * interaction it can be drawn together with the
145 * changes caused by interaction
146 */
147 s->redraw_timer = SDL_AddTimer(1000 / s->threshold_fps,
149
150 if(triggered_by_frame)
151 draw(s);
152 }
153 s->redraw_needed = !triggered_by_frame;
154 }
155}
156
157static void handle_window_event(state_vr *s, SDL_Event *event){
158 if(event->window.event == SDL_WINDOWEVENT_RESIZED){
159 glViewport(0, 0, event->window.data1, event->window.data2);
160 s->window.width = event->window.data1;
161 s->window.height = event->window.data2;
162 redraw(s);
163 }
164}
165
166static void handle_keyboard_event(state_vr *s, SDL_Event *event){
167 if(event->key.type == SDL_KEYDOWN){
168 switch(event->key.keysym.sym){
169 case SDLK_f:
170 s->fs = !s->fs;
171 SDL_SetWindowFullscreen(s->window.sdl_window, s->fs ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
172 redraw(s);
173 break;
174 case SDLK_q:
175 exit_uv(0);
176 break;
177 default:
178 break;
179 }
180 }
181}
182
183static void handle_user_event(state_vr *s, SDL_Event *event){
184 if(event->type == s->sdl_frame_event){
185 std::unique_lock<std::mutex> lk(s->lock);
186 s->buffered_frames_count -= 1;
187 lk.unlock();
188 s->frame_consumed_cv.notify_one();
189
190 video_frame *frame = static_cast<video_frame *>(event->user.data1);
191
192 if(frame){
194
195 lk.lock();
197 s->free_frame_queue.push(frame);
198 lk.unlock();
199 } else {
200 //poison
201 s->running = false;
202 return;
203 }
204 redraw(s, frame->fps >= s->threshold_fps, true);
205 } else if(event->type == s->sdl_redraw_event){
206 if(s->redraw_needed){
207 draw(s);
208 s->redraw_needed = false;
209 } else {
210 SDL_RemoveTimer(s->redraw_timer);
211 s->redraw_timer = 0;
212 }
213 }
214}
215
216static void display_panogl_run(void *state) {
217 state_vr *s = static_cast<state_vr *>(state);
218
219 draw(s);
220
221 s->running = true;
222 while(s->running){
223 SDL_Event event;
224 if (!SDL_WaitEvent(&event)) {
225 continue;
226 }
227
228 switch(event.type){
229 case SDL_MOUSEMOTION:
230 if(event.motion.state & SDL_BUTTON_LMASK){
231 s->scene.rotate(event.motion.xrel / 8.f,
232 event.motion.yrel / 8.f);
233
234 redraw(s);
235 }
236 break;
237 case SDL_MOUSEWHEEL:
238 s->scene.fov -= event.wheel.y;
239 redraw(s);
240 break;
241 case SDL_WINDOWEVENT:
242 handle_window_event(s, &event);
243 break;
244 case SDL_KEYDOWN:
245 handle_keyboard_event(s, &event);
246 break;
247 case SDL_QUIT:
248 exit_uv(0);
249 break;
250 default:
251 if(event.type >= SDL_USEREVENT)
252 handle_user_event(s, &event);
253 break;
254 }
255 }
256}
257
258static void display_panogl_done(void *state) {
259 state_vr *s = static_cast<state_vr *>(state);
260
261 delete s;
262}
263
264static struct video_frame * display_panogl_getf(void *state) {
265 struct state_vr *s = static_cast<state_vr *>(state);
266
267 std::lock_guard<std::mutex> lock(s->lock);
268
269 while (s->free_frame_queue.size() > 0) {
270 struct video_frame *buffer = s->free_frame_queue.front();
271 s->free_frame_queue.pop();
273 return buffer;
274 } else {
275 vf_free(buffer);
276 }
277 }
278
280}
281
282static bool display_panogl_putf(void *state, struct video_frame *frame, long long nonblock) {
284 struct state_vr *s = static_cast<state_vr *>(state);
285
286 if (nonblock == PUTF_DISCARD) {
287 vf_free(frame);
288 return true;
289 }
290
291 std::unique_lock<std::mutex> lk(s->lock);
293 && frame != NULL) {
294 vf_free(frame);
295 printf("1 frame(s) dropped!\n");
296 return false;
297 }
298 s->frame_consumed_cv.wait(lk, [s]{return s->buffered_frames_count < MAX_BUFFER_SIZE;});
299 s->buffered_frames_count += 1;
300 lk.unlock();
301 SDL_Event event;
302 event.type = s->sdl_frame_event;
303 event.user.data1 = frame;
304 PROFILE_DETAIL("push frame event");
305 SDL_PushEvent(&event);
306
307 return true;
308}
309
311 state_vr *s = static_cast<state_vr *>(state);
312
313 s->current_desc = desc;
314 return true;
315}
316
317static bool display_panogl_get_property(void *state, int property, void *val, size_t *len) {
318 struct state_vr *s = static_cast<state_vr *>(state);
319 enum interlacing_t supported_il_modes[] = {PROGRESSIVE};
320 int rgb_shift[] = {0, 8, 16};
321
322 switch (property) {
324 auto codecs = s->scene.get_codecs();
325 size_t codecs_size = codecs.size() * sizeof(codec_t);
326 if(codecs_size <= *len) {
327 memcpy(val, codecs.data(), codecs_size);
328 } else {
329 return false;
330 }
331
332 *len = codecs_size;
333 break;
334 }
336 if(sizeof(rgb_shift) > *len) {
337 return false;
338 }
339 memcpy(val, rgb_shift, sizeof(rgb_shift));
340 *len = sizeof(rgb_shift);
341 break;
343 *(int *) val = PITCH_DEFAULT;
344 *len = sizeof(int);
345 break;
347 if(sizeof(supported_il_modes) <= *len) {
348 memcpy(val, supported_il_modes, sizeof(supported_il_modes));
349 } else {
350 return false;
351 }
352 *len = sizeof(supported_il_modes);
353 break;
354 default:
355 return false;
356 }
357 return true;
358}
359
360static void display_panogl_probe(struct device_info **available_cards, int *count, void (**deleter)(void *)) {
362 *count = 1;
363 *available_cards = (struct device_info *) calloc(1, sizeof(struct device_info));
364 strcpy((*available_cards)[0].dev, "");
365 strcpy((*available_cards)[0].name, "Panorama Gl SW display");
366 (*available_cards)[0].repeatable = true;
367}
368
382
#define UNUSED(x)
void ** state
struct video_frame long long nonblock
int void * val
struct video_desc desc
struct video_frame * frame
const char * fmt
int void size_t * len
const char unsigned int flags
int void(** deleter)(void *))
int property
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_data(struct video_desc desc)
#define PITCH_DEFAULT
default pitch, i. e. respective linesize
#define PUTF_DISCARD
#define PUTF_BLOCKING
#define VIDEO_DISPLAY_ABI_VERSION
@ 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_SUPPORTED_IL_MODES
display supported interlacing modes - enum interlacing_t[]
@ 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
@ 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
static Uint32 redraw_callback(Uint32 interval, void *param)
Definition pano_gl.cpp:115
static void display_panogl_run(void *state)
Definition pano_gl.cpp:216
static void display_panogl_done(void *state)
Definition pano_gl.cpp:258
static bool display_panogl_reconfigure(void *state, struct video_desc desc)
Definition pano_gl.cpp:310
static bool display_panogl_putf(void *state, struct video_frame *frame, long long nonblock)
Definition pano_gl.cpp:282
static void handle_keyboard_event(state_vr *s, SDL_Event *event)
Definition pano_gl.cpp:166
static bool display_panogl_get_property(void *state, int property, void *val, size_t *len)
Definition pano_gl.cpp:317
static void handle_window_event(state_vr *s, SDL_Event *event)
Definition pano_gl.cpp:157
static void redraw(state_vr *s, bool video_framerate_over_threshold=false, bool triggered_by_frame=false)
Definition pano_gl.cpp:124
static void draw(state_vr *s)
Definition pano_gl.cpp:104
static const struct video_display_info display_panogl_info
Definition pano_gl.cpp:369
#define MOD_NAME
Definition pano_gl.cpp:65
static struct video_frame * display_panogl_getf(void *state)
Definition pano_gl.cpp:264
#define MAX_BUFFER_SIZE
Definition pano_gl.cpp:64
static void handle_user_event(state_vr *s, SDL_Event *event)
Definition pano_gl.cpp:183
static void * display_panogl_init(struct module *parent, const char *fmt, unsigned int flags)
Definition pano_gl.cpp:93
static void display_panogl_probe(struct device_info **available_cards, int *count, void(**deleter)(void *))
Definition pano_gl.cpp:360
#define PROFILE_FUNC
#define PROFILE_DETAIL(name)
const char * parent
Definition settings.cpp:198
const char * param
Definition settings.cpp:195
const char * name
Definition settings.cpp:193
void rotate(float dx, float dy)
void render(int width, int height)
void put_frame(video_frame *f, bool pbo_frame=false)
std::vector< codec_t > get_codecs()
SDL_Window * sdl_window
char dev[1024]
Definition types.h:383
std::chrono::steady_clock::time_point last_frame
Definition pano_gl.cpp:86
std::mutex lock
Definition pano_gl.cpp:88
unsigned sdl_frame_event
Definition pano_gl.cpp:73
bool redraw_needed
Definition pano_gl.cpp:84
std::queue< video_frame * > free_frame_queue
Definition pano_gl.cpp:90
int buffered_frames_count
Definition pano_gl.cpp:77
unsigned sdl_redraw_event
Definition pano_gl.cpp:74
std::condition_variable frame_consumed_cv
Definition pano_gl.cpp:89
video_desc current_desc
Definition pano_gl.cpp:76
SDL_TimerID redraw_timer
Definition pano_gl.cpp:83
PanoramaScene scene
Definition pano_gl.cpp:79
int threshold_fps
Definition pano_gl.cpp:81
Sdl_window window
Definition pano_gl.cpp:68
bool fs
Definition pano_gl.cpp:71
bool running
Definition pano_gl.cpp:70
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
double fps
Definition types.h:308
interlacing_t
Definition types.h:163
@ PROGRESSIVE
progressive frame
Definition types.h:164
codec_t
Definition types.h:65
This is an umbrella header for video functions.
static struct @76 codecs[]
void vf_recycle(struct video_frame *buf)
This function should be called before returning a frame back to a frame pool. Currently this is used ...
bool video_desc_eq(struct video_desc a, struct video_desc b)
Compares two video descriptions.