UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
opengl_conversions.cpp
Go to the documentation of this file.
1
6/*
7 * Copyright (c) 2023 CESNET, z. s. p. o.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, is permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * 3. Neither the name of CESNET nor the names of its contributors may be
22 * used to endorse or promote products derived from this software without
23 * specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
27 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
28 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
29 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
35 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
36 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39#include "color.h"
40#include "debug.h"
41#include "host.h"
42#include "video_codec.h"
44#include "gl_vdpau.hpp"
45
46#define MOD_NAME "[GL conversions] "
47
48namespace {
49
50static const char *vert_src = R"END(
51#version 330 core
52layout(location = 0) in vec2 vert_pos;
53layout(location = 1) in vec2 vert_uv;
54
55out vec2 UV;
56
57uniform vec2 scale_vec;
58
59void main(){
60 gl_Position = vec4(vert_pos, 0.0f, 1.0f);
61 UV = vert_uv;
62}
63)END";
64
65static const char *yuv_conv_frag_src = R"END(
66#version 330 core
67layout(location = 0) out vec4 color;
68in vec2 UV;
69uniform sampler2D tex;
70
71uniform float width;
72
73uniform float luma_scale = 1.1643f;
74uniform float r_cr = 1.7926f;
75uniform float g_cb = -0.2132f;
76uniform float g_cr = -0.5328f;
77uniform float b_cb = 2.1124f;
78
79void main(){
80 //The width could be odd, but the width of texture is always even
81 float textureWidth = float((int(width) + 1) / 2 * 2);
82 vec4 yuv;
83 yuv.rgba = texture(tex, vec2(UV.x / textureWidth * width, UV.y)).grba;
84 if(UV.x * width / 2.0 - floor(UV.x * width / 2.0) > 0.5)
85 yuv.r = yuv.a;
86
87 yuv.r = 1.1643 * (yuv.r - 0.0625);
88 yuv.g = yuv.g - 0.5;
89 yuv.b = yuv.b - 0.5;
90
91 color.r = yuv.r + r_cr * yuv.b;
92 color.g = yuv.r + g_cb * yuv.g + g_cr * yuv.b;
93 color.b = yuv.r + b_cb * yuv.g;
94 color.a = 1.0;
95}
96)END";
97
98static void load_yuv_coefficients(GlProgram& program){
99 const char *col = get_commandline_param("color");
100 if(!col)
101 return;
102
103 int color = std::stol(col, nullptr, 16) >> 4; // first nibble
104 if (color < 1 || color > 3){
105 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Wrong chromicities index " << color << "\n";
106 return;
107 }
108
109 double cs_coeffs[2*4] = { 0, 0, KR_709, KB_709, KR_2020, KB_2020, KR_P3, KB_P3 };
110 double kr = cs_coeffs[2 * color];
111 double kb = cs_coeffs[2 * color + 1];
112
113 glUseProgram(program.get());
114 GLuint loc = glGetUniformLocation(program.get(), "luma_scale");
115 glUniform1f(loc, Y_LIMIT_INV(8));
116 loc = glGetUniformLocation(program.get(), "r_cr");
117 glUniform1f(loc, R_CR(8, kr, kb));
118 loc = glGetUniformLocation(program.get(), "g_cr");
119 glUniform1f(loc, G_CR(8, kr, kb));
120 loc = glGetUniformLocation(program.get(), "g_cb");
121 glUniform1f(loc, G_CB(8, kr, kb));
122 loc = glGetUniformLocation(program.get(), "b_cb");
123 glUniform1f(loc, B_CB(8, kr, kb));
124}
125
126class Rendering_convertor : public Frame_convertor{
127public:
128 Rendering_convertor(const char* vert_src, const char* frag_src) :
129 program(vert_src, frag_src),
130 quad(Model::get_quad())
131 {
132 width_uniform_location = glGetUniformLocation(program.get(), "width");
133 }
134
135 virtual ~Rendering_convertor() { };
136
137 void attach_texture(Texture& tex) override final {
138 fbuf.attach_texture(tex);
139 }
140
141 void put_frame(video_frame *f, bool pbo_frame) override final{
142 glUseProgram(program.get());
143 glBindFramebuffer(GL_FRAMEBUFFER, fbuf.get());
144 glViewport(0, 0, f->tiles[0].width, f->tiles[0].height);
145 glClear(GL_COLOR_BUFFER_BIT);
146
147 prepare_input_tex(f, pbo_frame);
148
149 if(width_uniform_location != -1)
150 glUniform1f(width_uniform_location, f->tiles[0].width);
151
152 quad.render();
153
154 glBindFramebuffer(GL_FRAMEBUFFER, 0);
155 glUseProgram(0);
156 }
157
158protected:
159 virtual void prepare_input_tex(video_frame *f, bool pbo_frame) = 0;
160
161 GlProgram program;
162 Model quad;
163 Framebuffer fbuf;
164 GLint width_uniform_location = -1;
165 Texture input_tex;
166};
167
168class Loading_convertor : public Frame_convertor{
169public:
170
171 void attach_texture(Texture& tex) override final{
172 this->tex = &tex;
173 }
174
175protected:
176 Texture *tex = nullptr;
177};
178
179class Yuv_convertor : public Rendering_convertor{
180public:
181 Yuv_convertor() : Rendering_convertor(vert_src, yuv_conv_frag_src){
182 load_yuv_coefficients(program);
183 }
184
185private:
186 void prepare_input_tex(video_frame *f, bool pbo_frame) override final{
187 int w = (f->tiles[0].width + 1) / 2;
188 int h = f->tiles[0].height;
189
190 input_tex.load_frame(w, h, GL_RGBA,
191 GL_RGBA, GL_UNSIGNED_BYTE,
192 f, pbo_frame);
193 }
194};
195
198static const char * v210_to_rgb_fp = R"raw(
199#version 330
200
201layout(location = 0) out vec4 color;
202in vec2 UV;
203uniform sampler2D tex;
204
205uniform float width;
206
207uniform float luma_scale = 1.1643f;
208uniform float r_cr = 1.7926f;
209uniform float g_cb = -0.2132f;
210uniform float g_cr = -0.5328f;
211uniform float b_cb = 2.1124f;
212
213// YUV offset
214const vec3 yuvOffset = vec3(-0.0625, -0.5, -0.5);
215
216// RGB coefficients
217vec3 Rcoeff = vec3(luma_scale, 0.0, r_cr);
218vec3 Gcoeff = vec3(luma_scale, g_cb, g_cr);
219vec3 Bcoeff = vec3(luma_scale, b_cb, 0.0);
220
221// U Y V A | Y U Y A | V Y U A | Y V Y A
222
223int GROUP_FOR_INDEX(int i) {
224 return i / 4;
225}
226
227int SUBINDEX_FOR_INDEX(int i) {
228 return i % 4;
229}
230
231int _y(int i) {
232 return 2 * i + 1;
233}
234
235int _u(int i) {
236 return 4 * (i/2);
237}
238
239int _v(int i) {
240 return 4 * (i / 2) + 2;
241}
242
243int offset(int i) {
244 return i + (i / 3);
245}
246
247vec3 ycbcr2rgb(vec3 yuvToConvert) {
248 vec3 pix;
249 yuvToConvert += yuvOffset;
250 pix.r = dot(yuvToConvert, Rcoeff);
251 pix.g = dot(yuvToConvert, Gcoeff);
252 pix.b = dot(yuvToConvert, Bcoeff);
253 return pix;
254}
255
256void main(void) {
257 float imageWidthRaw; // v210 texture size
258 imageWidthRaw = float((int(width) + 47) / 48 * 32); // 720->480
259
260 // interpolate (0,1) texcoords to [0,719]
261 int texcoordDenormX;
262 texcoordDenormX = int(round(UV.x * width - .5));
263
264 // 0 1 1 2 3 3 4 5 5 6 7 7 etc.
265 int yOffset;
266 yOffset = offset(_y(texcoordDenormX));
267 int sourceColumnIndexY;
268 sourceColumnIndexY = GROUP_FOR_INDEX(yOffset);
269
270 // 0 0 1 1 2 2 4 4 5 5 6 6 etc.
271 int uOffset;
272 uOffset = offset(_u(texcoordDenormX));
273 int sourceColumnIndexU;
274 sourceColumnIndexU = GROUP_FOR_INDEX(uOffset);
275
276 // 0 0 2 2 3 3 4 4 6 6 7 7 etc.
277 int vOffset;
278 vOffset = offset(_v(texcoordDenormX));
279 int sourceColumnIndexV;
280 sourceColumnIndexV = GROUP_FOR_INDEX(vOffset);
281
282 // 1 0 2 1 0 2 1 0 2 etc.
283 int compY;
284 compY = SUBINDEX_FOR_INDEX(yOffset);
285
286 // 0 0 1 1 2 2 0 0 1 1 2 2 etc.
287 int compU;
288 compU = SUBINDEX_FOR_INDEX(uOffset);
289
290 // 2 2 0 0 1 1 2 2 0 0 1 1 etc.
291 int compV;
292 compV = SUBINDEX_FOR_INDEX(vOffset);
293
294 vec4 y;
295 vec4 u;
296 vec4 v;
297 y = texture(tex, vec2((float(sourceColumnIndexY) + .5) / imageWidthRaw, UV.y));
298 u = texture(tex, vec2((float(sourceColumnIndexU) + .5) / imageWidthRaw, UV.y));
299 v = texture(tex, vec2((float(sourceColumnIndexV) + .5) / imageWidthRaw, UV.y));
300
301 vec3 outColor = ycbcr2rgb(vec3(y[compY], u[compU], v[compV]));
302
303 color = vec4(outColor, 1.0);
304}
305)raw";
306
307class V210_convertor : public Rendering_convertor{
308public:
309 V210_convertor(): Rendering_convertor(vert_src, v210_to_rgb_fp)
310 {
311 load_yuv_coefficients(program);
312 }
313
314private:
315 void prepare_input_tex(video_frame *f, bool pbo_frame = false) override final{
316 int w = vc_get_linesize(f->tiles[0].width, v210) / 4;
317 int h = f->tiles[0].height;
318 input_tex.load_frame(w, h, GL_RGB10_A2,
319 GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV,
320 f, pbo_frame);
321 }
322};
323
324static const char * yuva_to_rgb_fp = R"raw(
325#version 330
326
327layout(location = 0) out vec4 color;
328in vec2 UV;
329uniform sampler2D tex;
330
331uniform float luma_scale = 1.1643f;
332uniform float r_cr = 1.7926f;
333uniform float g_cb = -0.2132f;
334uniform float g_cr = -0.5328f;
335uniform float b_cb = 2.1124f;
336void main()
337{
338 vec4 yuv;
339 yuv.rgba = texture(tex, UV).grba;
340 yuv.r = luma_scale * (yuv.r - 0.0625);
341 yuv.g = yuv.g - 0.5;
342 yuv.b = yuv.b - 0.5;
343 color.r = yuv.r + r_cr * yuv.b;
344 color.g = yuv.r + g_cb * yuv.g + g_cr * yuv.b;
345 color.b = yuv.r + b_cb * yuv.g;
346 color.a = yuv.a;
347}
348)raw";
349
350class Y416_convertor : public Rendering_convertor{
351public:
352 Y416_convertor(): Rendering_convertor(vert_src, yuva_to_rgb_fp)
353 {
354 load_yuv_coefficients(program);
355 }
356
357private:
358 void prepare_input_tex(video_frame *f, bool pbo_frame = false) override final{
359 int w = f->tiles[0].width;
360 int h = f->tiles[0].height;
361 input_tex.load_frame(w, h, GL_RGBA,
362 GL_RGBA, GL_UNSIGNED_SHORT,
363 f, pbo_frame);
364 }
365};
366
367class DXT1_convertor : public Loading_convertor{
368public:
369 void put_frame(video_frame *f, bool) override{
370 int w = (f->tiles[0].width + 3) / 4 * 4;
371 int h = (f->tiles[0].height + 3) / 4 * 4;
372 glBindTexture(GL_TEXTURE_2D, tex->get());
373 glCompressedTexImage2D(GL_TEXTURE_2D, 0,
374 GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
375 w, h, 0,
376 w * h / 2,
377 f->tiles[0].data);
378 }
379};
380
381static const char fp_display_dxt5ycocg[] = R"raw(
382#version 330
383
384layout(location = 0) out vec4 color;
385in vec2 UV;
386uniform sampler2D tex;
387
388void main()
389{
390 vec4 element = texture(tex, UV);
391 float scale = (element.z * ( 255.0 / 8.0 )) + 1.0;
392 float Co = (element.x - (0.5 * 256.0 / 255.0)) / scale;
393 float Cg = (element.y - (0.5 * 256.0 / 255.0)) / scale;
394 float Y = element.w;
395 color = vec4(Y + Co - Cg, Y + Cg, Y - Co - Cg, 1.0);
396}
397)raw";
398
399class DXT5_convertor : public Rendering_convertor{
400public:
401 DXT5_convertor(): Rendering_convertor(vert_src, fp_display_dxt5ycocg) { }
402
403private:
404 void prepare_input_tex(video_frame *f, bool) override final{
405 int w = (f->tiles[0].width + 3) / 4 * 4;
406 int h = (f->tiles[0].height + 3) / 4 * 4;
407 input_tex.allocate();
408 glBindTexture(GL_TEXTURE_2D, input_tex.get());
409 glCompressedTexImage2D(GL_TEXTURE_2D, 0,
410 GL_COMPRESSED_RGBA_S3TC_DXT5_EXT,
411 w, h, 0,
412 w * h,
413 f->tiles[0].data);
414 }
415};
416
417static const char *fp_display_dxt1_yuv = R"raw(
418#version 330
419
420layout(location = 0) out vec4 color;
421in vec2 UV;
422uniform sampler2D tex;
423
424void main(void) {
425 vec4 col = texture(tex, UV);
426
427 float Y = 1.1643 * (col[0] - 0.0625);
428 float U = (col[1] - 0.5);
429 float V = (col[2] - 0.5);
430
431 float R = Y + 1.7926 * V;
432 float G = Y - 0.2132 * U - 0.5328 * V;
433 float B = Y + 2.1124 * U;
434
435 color = vec4(R,G,B,1.0);
436}
437)raw";
438
439class DXT1_YUV_convertor : public Rendering_convertor{
440public:
441 DXT1_YUV_convertor(): Rendering_convertor(vert_src, fp_display_dxt1_yuv) { }
442
443private:
444 void prepare_input_tex(video_frame *f, bool) override final{
445 int w = f->tiles[0].width;
446 int h = f->tiles[0].height;
447 input_tex.allocate();
448 glBindTexture(GL_TEXTURE_2D, input_tex.get());
449 glCompressedTexImage2D(GL_TEXTURE_2D, 0,
450 GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
451 w, h, 0,
452 (w * h/16) * 8,
453 f->tiles[0].data);
454 }
455};
456
457class R10k_convertor : public Loading_convertor{
458public:
459 void put_frame(video_frame *f, bool = false) override{
460 int w = f->tiles[0].width;
461 int h = f->tiles[0].height;
462 uint32_t *dst = nullptr;
463 int data_len = w * h * 8;
464 char *tex_src_data = nullptr;
465 if(internal_pbo){
466 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, internal_pbo->get());
467 glBufferData(GL_PIXEL_UNPACK_BUFFER, data_len, 0, GL_STREAM_DRAW);
468 dst = (uint32_t *) glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
469 process_r10k(dst, reinterpret_cast<uint32_t *>(f->tiles[0].data), w, h);
470 tex_src_data = nullptr;
471 glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
472 } else {
473 scratchpad.resize(w * h * 8);
474 dst = reinterpret_cast<uint32_t *>(scratchpad.data());
475 tex_src_data = scratchpad.data();
476 process_r10k(dst, reinterpret_cast<uint32_t *>(f->tiles[0].data), w, h);
477 }
478
479 glBindTexture(GL_TEXTURE_2D, tex->get());
480 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, tex_src_data);
481
482 if(internal_pbo){
483 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
484 }
485 }
486
487private:
488 void process_r10k(uint32_t * __restrict out, const uint32_t *__restrict in, long width, long height) {
489 long line_padding_b = vc_get_linesize(width, R10k) - 4 * width;
490 for (long i = 0; i < height; i += 1) {
491 OPTIMIZED_FOR (long j = 0; j < width; j += 1) {
492 uint32_t x = *in++;
493 *out++ = /* output is x2b8g8r8 little-endian */
494 (x & 0xFFU) << 2U | (x & 0xC0'00U) >> 14U | // R
495 (x & 0x3F'00U) << 6U | (x & 0xF0'00'00) >> 10U | // G
496 (x & 0x0F'00'00U) << 10U | (x & 0xFC'00'00'00U) >> 6U; // B
497 }
498 in += line_padding_b / sizeof(uint32_t);
499 }
500 }
501 std::vector<char> scratchpad;
502};
503
504class RGB_convertor : public Loading_convertor{
505public:
506 void put_frame(video_frame *f, bool pbo_frame = false) override{
507 int w = f->tiles[0].width;
508 int h = f->tiles[0].height;
509 tex->load_frame(w, h, GL_RGB,
510 GL_RGB, GL_UNSIGNED_BYTE,
511 f, pbo_frame);
512 }
513};
514
515class RGBA_convertor : public Loading_convertor{
516public:
517 void put_frame(video_frame *f, bool pbo_frame = false) override{
518 int w = f->tiles[0].width;
519 int h = f->tiles[0].height;
520 tex->load_frame(w, h, GL_RGBA,
521 GL_RGBA, GL_UNSIGNED_BYTE,
522 f, pbo_frame);
523 }
524};
525
526static const char fp_passthrough[] = R"raw(
527#version 330
528
529layout(location = 0) out vec4 color;
530in vec2 UV;
531uniform sampler2D tex;
532
533void main()
534{
535 color = texture(tex, UV);
536}
537)raw";
538
539#ifdef HWACC_VDPAU
540class VDPAU_convertor : public Rendering_convertor{
541public:
542 VDPAU_convertor(): Rendering_convertor(vert_src, fp_passthrough) {
543 vdp.init();
544 }
545
546 ~VDPAU_convertor() {
547 vdp.uninit();
548 }
549
550private:
551 void prepare_input_tex(video_frame *f, bool = false) override{
552 vdp.loadFrame(reinterpret_cast<hw_vdpau_frame *>(f->tiles[0].data));
553 }
554
555 struct state_vdpau vdp;
556};
557#endif
558
559struct {
561 std::unique_ptr<Frame_convertor> (*construct_func)();
562} codec_convertor_map[]{
572#ifdef HWACC_VDPAU
574#endif
575};
576
577} //anon namespace
578
579std::unique_ptr<Frame_convertor> get_convertor_for_codec(codec_t codec){
580 for(const auto& i : codec_convertor_map){
581 if(i.codec == codec)
582 return i.construct_func();
583 }
584
585 return nullptr;
586}
587
588std::vector<codec_t> get_convertor_supported_codecs(){
589 std::vector<codec_t> ret;
590 ret.reserve(std::size(codec_convertor_map));
591 for(const auto& i : codec_convertor_map){
592 ret.push_back(i.codec);
593 }
594 return ret;
595}
int vc_get_linesize(unsigned int width, codec_t codec)
static std::unique_ptr< Frame_convertor > construct_unique()
GLuint get()
static Model get_quad()
#define KR_709
Definition color.h:82
#define KR_P3
Definition color.h:86
#define KB_2020
Definition color.h:85
#define KB_709
Definition color.h:83
#define G_CR(in_depth, kr, kb)
Definition color.h:145
#define B_CB(in_depth, kr, kb)
Definition color.h:147
#define KR_2020
Definition color.h:84
#define KB_P3
Definition color.h:87
#define R_CR(in_depth, kr, kb)
Definition color.h:141
#define Y_LIMIT_INV(in_depth)
Definition color.h:150
#define G_CB(in_depth, kr, kb)
Definition color.h:142
#define LOG(level)
Definition debug.h:313
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
static const char * fp_display_dxt1_yuv
Definition gl.cpp:283
static const char fp_display_dxt5ycocg[]
Definition gl.cpp:310
static const char * v210_to_rgb_fp
Definition gl.cpp:182
static const char * yuva_to_rgb_fp
Definition gl.cpp:163
const char * get_commandline_param(const char *key)
Definition host.cpp:894
#define OPTIMIZED_FOR
Definition macros.h:127
std::vector< codec_t > get_convertor_supported_codecs()
#define MOD_NAME
std::unique_ptr< Frame_convertor > get_convertor_for_codec(codec_t codec)
static const char * vert_src
static const char * frag_src
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 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
codec_t
Definition types.h:65
@ DXT1
S3 Texture Compression DXT1.
Definition types.h:77
@ R10k
RGB 10-bit packed - RGBX, big-endian (2 bit padding)
Definition types.h:73
@ 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
@ HW_VDPAU
VDPAU hardware surface.
Definition types.h:91
@ Y416
interleaved little-endian YCbCr 4444 16-bit - UYVA
Definition types.h:99
@ DXT5
S3 Texture Compression DXT5.
Definition types.h:79
@ RGBA
RGBA 8-bit, big-endian.
Definition types.h:70
@ v210
YCbCr 422 10-bit - xVY0U|xY2UY1|xUY3V|xY5VY4, little-endian (2 bit padding /'x'/),...
Definition types.h:75