Loading...
Searching...
No Matches
YUYV422.hpp
1#pragma once
2#include <Gfx/Graph/decoders/ColorSpace.hpp>
3#include <Gfx/Graph/decoders/GPUVideoDecoder.hpp>
4extern "C" {
5#include <libavformat/avformat.h>
6}
7
8namespace score::gfx
9{
16{
17 static const constexpr auto frag = R"_(#version 450
18
19)_" SCORE_GFX_VIDEO_UNIFORMS R"_(
20
21layout(binding=3) uniform sampler2D u_tex;
22
23layout(location = 0) in vec2 v_texcoord;
24layout(location = 0) out vec4 fragColor;
25
26%2
27
28vec4 processTexture(vec4 tex) {
29 vec4 processed = convert_to_rgb(tex);
30 { %1 }
31 return processed;
32}
33
34void main() {
35 // YUYV packs Y0 U Y1 V into RGBA as x=Y0 y=U z=Y1 w=V
36 // Input texture is half the width of the output (one RGBA texel = 2 pixels)
37 float colIndex = floor(v_texcoord.x * mat.texSz.x);
38 float oddCol = mod(colIndex, 2.0);
39
40 // Offset by half an input pixel to sample the correct texel center
41 vec2 dxInput = 0.5 * vec2(1.0 / mat.texSz.x, 0.0);
42
43 float oddY = texture(u_tex, v_texcoord - dxInput).z;
44 float evenY = texture(u_tex, v_texcoord + dxInput).x;
45 float y = mix(evenY, oddY, oddCol);
46
47 vec2 uv = texture(u_tex, score_tc(v_texcoord)).%3;
48
49 fragColor = processTexture(vec4(y, uv.x, uv.y, 1.));
50}
51)_";
52
54 explicit YUYV422Decoder(Video::ImageFormat& d, bool swapChroma = false)
55 : decoder{d}
56 , yvyu{swapChroma}
57 {
58 }
59
60 Video::ImageFormat& decoder;
61 bool yvyu{};
62 std::pair<QShader, QShader> init(RenderList& r) override
63 {
64 auto& rhi = *r.state.rhi;
65
66 const auto w = decoder.width, h = decoder.height;
67 // Y
68 {
69 auto tex = rhi.newTexture(QRhiTexture::RGBA8, {w / 2, h}, 1, QRhiTexture::Flag{});
70 tex->create();
71
72 auto sampler = rhi.newSampler(
73 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
74 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
75 sampler->create();
76 samplers.push_back({sampler, tex});
77 }
78
80 r.state, vertexShader(),
81 QString(frag).arg("").arg(colorMatrix(decoder)).arg(yvyu ? "wy" : "yw"));
82 }
83
84 void exec(RenderList&, QRhiResourceUpdateBatch& res, AVFrame& frame) override
85 {
86 setYPixels(res, frame.data[0], frame.linesize[0]);
87 }
88
89 void
90 setYPixels(QRhiResourceUpdateBatch& res, uint8_t* pixels, int stride) const noexcept
91 {
92 const auto w = decoder.width, h = decoder.height;
93 auto y_tex = samplers[0].texture;
94
95 // Texture is RGBA8 at {w/2, h}: 4 bytes per texel = one YUYV macropixel
96 QRhiTextureUploadEntry entry{0, 0, createTextureUpload(pixels, w / 2, h, 4, stride)};
97
98 QRhiTextureUploadDescription desc{entry};
99 res.uploadTexture(y_tex, desc);
100 }
101};
102
109{
110 static const constexpr auto frag = R"_(#version 450
111
112)_" SCORE_GFX_VIDEO_UNIFORMS R"_(
113
114layout(binding=3) uniform sampler2D u_tex;
115
116layout(location = 0) in vec2 v_texcoord;
117layout(location = 0) out vec4 fragColor;
118
119%2
120
121vec4 processTexture(vec4 tex) {
122 vec4 processed = convert_to_rgb(tex);
123 { %1 }
124 return processed;
125}
126
127void main() {
128 // UYVY packs U0 Y0 V0 Y1 into RGBA as x=U0 y=Y0 z=V0 w=Y1
129 // Input texture is half the width of the output (one RGBA texel = 2 pixels)
130 float colIndex = floor(v_texcoord.x * mat.texSz.x);
131 float oddCol = mod(colIndex, 2.0);
132
133 // The sampling seam is applied ONCE, to the base coordinate, and the taps
134 // are offset from there. score_tc only ever moves y and these offsets are
135 // horizontal, so the order does not matter -- but applying it per tap would
136 // run the parity arithmetic three times for one answer.
137 vec2 tc = score_tc(v_texcoord);
138
139 // Offset by half an input pixel to sample the correct texel center
140 vec2 dxInput = 0.5 * vec2(1.0 / mat.texSz.x, 0.0);
141
142 float oddY = texture(u_tex, tc - dxInput).w;
143 float evenY = texture(u_tex, tc + dxInput).y;
144 float y = mix(evenY, oddY, oddCol);
145
146 vec2 uv = texture(u_tex, tc).%3;
147
148 fragColor = processTexture(vec4(y, uv.x, uv.y, 1.));
149}
150)_";
151
153 UYVY422Decoder(Video::ImageFormat& d, bool swapChroma = false)
154 : decoder{d}
155 , vyuy{swapChroma}
156 {
157 }
158
159 Video::ImageFormat& decoder;
160 bool vyuy{};
161 std::pair<QShader, QShader> init(RenderList& r) override
162 {
163 auto& rhi = *r.state.rhi;
164
165 const auto w = decoder.width, h = decoder.height;
166 // Y
167 {
168 auto tex = rhi.newTexture(QRhiTexture::RGBA8, {w / 2, h}, 1, QRhiTexture::Flag{});
169 tex->create();
170
171 auto sampler = rhi.newSampler(
172 QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
173 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
174 sampler->create();
175 samplers.push_back({sampler, tex});
176 }
177
179 r.state, vertexShader(),
180 QString(frag).arg("").arg(colorMatrix(decoder)).arg(vyuy ? "zx" : "xz"));
181 }
182
183 void exec(RenderList&, QRhiResourceUpdateBatch& res, AVFrame& frame) override
184 {
185 auto y_tex = samplers[0].texture;
186
187 auto pixels = frame.data[0];
188 auto stride = frame.linesize[0];
189 // Texture is RGBA8 at {w/2, h}: 4 bytes per texel = one UYVY macropixel.
190 // A fielded source (NDI's "fastest" hands over UYVY fields) fills half of
191 // it per frame, top half for field 0, bottom half for field 1.
192 const int texW = decoder.width / 2;
193 const auto [rows, offset] = planeRows(decoder, frame, decoder.height);
194 auto sub = createTextureUpload(pixels, texW, rows, 4, stride);
195 sub.setSourceSize({texW, rows});
196 sub.setDestinationTopLeft({0, offset});
197
198 QRhiTextureUploadDescription desc{QRhiTextureUploadEntry{0, 0, sub}};
199 res.uploadTexture(y_tex, desc);
200 }
201};
202
203}
Processes and renders a video frame on the GPU.
Definition GPUVideoDecoder.hpp:161
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:36
RenderState & state
RenderState corresponding to this RenderList.
Definition RenderList.hpp:299
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
std::pair< QShader, QShader > makeShaders(const RenderState &v, QString vert, QString frag, int multiViewCount)
Get a pair of compiled vertex / fragment shaders from GLSL 4.5 sources.
Definition score-plugin-gfx/Gfx/Graph/Utils.cpp:1707
Definition VideoInterface.hpp:29
Decodes UYVY422 video, mostly used for NDI.
Definition YUYV422.hpp:109
void exec(RenderList &, QRhiResourceUpdateBatch &res, AVFrame &frame) override
Decode and upload a video frame to the GPU.
Definition YUYV422.hpp:183
UYVY422Decoder(Video::ImageFormat &d, bool swapChroma=false)
VYUY is UYVY with the two chroma taps exchanged.
Definition YUYV422.hpp:153
std::pair< QShader, QShader > init(RenderList &r) override
Initialize a GPUVideoDecoder.
Definition YUYV422.hpp:161
Decodes YUYV422 video.
Definition YUYV422.hpp:16
YUYV422Decoder(Video::ImageFormat &d, bool swapChroma=false)
YVYU is YUYV with the two chroma taps exchanged; nothing else differs.
Definition YUYV422.hpp:54
std::pair< QShader, QShader > init(RenderList &r) override
Initialize a GPUVideoDecoder.
Definition YUYV422.hpp:62
void exec(RenderList &, QRhiResourceUpdateBatch &res, AVFrame &frame) override
Decode and upload a video frame to the GPU.
Definition YUYV422.hpp:84