YUYV422.hpp
1 #pragma once
2 #include <Gfx/Graph/decoders/GPUVideoDecoder.hpp>
3 extern "C" {
4 #include <libavformat/avformat.h>
5 }
6 
7 namespace score::gfx
8 {
15 {
16  static const constexpr auto filter = R"_(#version 450
17 
18 )_" SCORE_GFX_VIDEO_UNIFORMS R"_(
19 
20 layout(binding=3) uniform sampler2D u_tex;
21 
22 layout(location = 0) in vec2 v_texcoord;
23 layout(location = 0) out vec4 fragColor;
24 
25 const vec3 R_cf = vec3(1.164383, 0.000000, 1.596027);
26 const vec3 G_cf = vec3(1.164383, -0.391762, -0.812968);
27 const vec3 B_cf = vec3(1.164383, 2.017232, 0.000000);
28 const vec3 offset = vec3(-0.0625, -0.5, -0.5);
29 
30 void main() {
31  vec4 tex = texture(u_tex, v_texcoord);
32  float y = tex.r;
33  float u = tex.g - 0.5;
34  float v = tex.a - 0.5;
35  fragColor.r = y + 1.13983 * v;
36  fragColor.g = y - 0.39465 * u - 0.58060 * v;
37  fragColor.b = y + 2.03211 * u;
38  fragColor.a = 1.0;
39 }
40 )_";
41 
43  : decoder{d}
44  {
45  }
46 
47  Video::ImageFormat& decoder;
48  std::pair<QShader, QShader> init(RenderList& r) override
49  {
50  auto& rhi = *r.state.rhi;
51 
52  const auto w = decoder.width, h = decoder.height;
53  // Y
54  {
55  auto tex = rhi.newTexture(QRhiTexture::RGBA8, {w / 2, h}, 1, QRhiTexture::Flag{});
56  tex->create();
57 
58  auto sampler = rhi.newSampler(
59  QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
60  QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
61  sampler->create();
62  samplers.push_back({sampler, tex});
63  }
64 
65  return score::gfx::makeShaders(r.state, vertexShader(), filter);
66  }
67 
68  void exec(RenderList&, QRhiResourceUpdateBatch& res, AVFrame& frame) override
69  {
70  setYPixels(res, frame.data[0], frame.linesize[0]);
71  }
72 
73  void
74  setYPixels(QRhiResourceUpdateBatch& res, uint8_t* pixels, int stride) const noexcept
75  {
76  const auto w = decoder.width, h = decoder.height;
77  auto y_tex = samplers[0].texture;
78 
79  QRhiTextureUploadEntry entry{0, 0, createTextureUpload(pixels, w, h, 2, stride)};
80 
81  QRhiTextureUploadDescription desc{entry};
82  res.uploadTexture(y_tex, desc);
83  }
84 };
85 
92 {
93  static const constexpr auto filter = R"_(#version 450
94 
95 )_" SCORE_GFX_VIDEO_UNIFORMS R"_(
96 
97 layout(binding=3) uniform sampler2D u_tex;
98 
99 layout(location = 0) in vec2 v_texcoord;
100 layout(location = 0) out vec4 fragColor;
101 
102 const mat4 bt601 = mat4(
103  1.164f, 1.164f, 1.164f, 0.0f,
104  0.000f, -0.392f, 2.017f, 0.0f,
105  1.596f, -0.813f, 0.000f, 0.0f,
106  -0.8708f, 0.5296f, -1.081f, 1.0f);
107 const mat4 bt709 = mat4(
108  1.164f, 1.164f, 1.164f, 0.0f,
109  0.000f, -0.534f, 2.115, 0.0f,
110  1.793f, -0.213f, 0.000f, 0.0f,
111  -0.5727f, 0.3007f, -1.1302, 1.0f);
112 
113 void main() {
114  // For U0 Y0 V0 Y1 macropixel, lookup Y0 or Y1 based on whether
115  // the original texture x coord is even or odd.
116  vec4 uyvy = texture(u_tex, v_texcoord);
117  float Y;
118  if (fract(floor(v_texcoord.x * renderer.renderSize.x + 0.5) / 2.0) > 0.0)
119  Y = uyvy.a; // odd so choose Y1
120  else
121  Y = uyvy.g; // even so choose Y0
122  float Cb = uyvy.r;
123  float Cr = uyvy.b;
124 
125  vec4 color = vec4(Y, Cb, Cr, 1.0);
126  fragColor = bt601 * color;
127 }
128 )_";
129 
131  : decoder{d}
132  {
133  }
134  Video::ImageFormat& decoder;
135  std::pair<QShader, QShader> init(RenderList& r) override
136  {
137  auto& rhi = *r.state.rhi;
138 
139  const auto w = decoder.width, h = decoder.height;
140  // Y
141  {
142  auto tex = rhi.newTexture(QRhiTexture::RGBA8, {w / 2, h}, 1, QRhiTexture::Flag{});
143  tex->create();
144 
145  auto sampler = rhi.newSampler(
146  QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
147  QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
148  sampler->create();
149  samplers.push_back({sampler, tex});
150  }
151 
152  return score::gfx::makeShaders(r.state, vertexShader(), filter);
153  }
154 
155  void exec(RenderList&, QRhiResourceUpdateBatch& res, AVFrame& frame) override
156  {
157  auto y_tex = samplers[0].texture;
158 
159  auto pixels = frame.data[0];
160  auto stride = frame.linesize[0];
161  QRhiTextureUploadEntry entry{
162  0, 0, createTextureUpload(pixels, frame.width, frame.height, 2, stride)};
163 
164  QRhiTextureUploadDescription desc{entry};
165  res.uploadTexture(y_tex, desc);
166  }
167 };
168 
169 }
Processes and renders a video frame on the GPU.
Definition: GPUVideoDecoder.hpp:43
static QRhiTextureSubresourceUploadDescription createTextureUpload(uint8_t *pixels, int w, int h, int bytesPerPixel, int stride)
Utility method to create a QRhiTextureSubresourceUploadDescription.
Definition: GPUVideoDecoder.cpp:22
List of nodes to be rendered to an output.
Definition: RenderList.hpp:19
RenderState & state
RenderState corresponding to this RenderList.
Definition: RenderList.hpp:89
Graphics rendering pipeline for ossia score.
Definition: PreviewWidget.hpp:12
std::pair< QShader, QShader > makeShaders(const RenderState &v, QString vert, QString frag)
Get a pair of compiled vertex / fragment shaders from GLSL 4.5 sources.
Definition: score-plugin-gfx/Gfx/Graph/Utils.cpp:334
Definition: VideoInterface.hpp:16
Decodes UYVY422 video, mostly used for NDI.
Definition: YUYV422.hpp:92
void exec(RenderList &, QRhiResourceUpdateBatch &res, AVFrame &frame) override
Decode and upload a video frame to the GPU.
Definition: YUYV422.hpp:155
std::pair< QShader, QShader > init(RenderList &r) override
Initialize a GPUVideoDecoder.
Definition: YUYV422.hpp:135
Decodes YUYV422 video.
Definition: YUYV422.hpp:15
void exec(RenderList &, QRhiResourceUpdateBatch &res, AVFrame &frame) override
Decode and upload a video frame to the GPU.
Definition: YUYV422.hpp:68
std::pair< QShader, QShader > init(RenderList &r) override
Initialize a GPUVideoDecoder.
Definition: YUYV422.hpp:48