Loading...
Searching...
No Matches
GPUVideoDecoder.hpp
1#pragma once
2
3#include <Gfx/Graph/Node.hpp>
4#include <Gfx/Graph/RenderList.hpp>
5#include <Gfx/Graph/RenderState.hpp>
6#include <Video/VideoInterface.hpp>
7
8#include <cstddef>
9
10extern "C" {
11#include <libavutil/frame.h>
12#include <libavutil/pixdesc.h>
13}
14
15#define SCORE_GFX_VIDEO_UNIFORMS \
16"layout(std140, binding = 0) uniform renderer_t {\n" \
17" mat4 clipSpaceCorrMatrix;\n" \
18" vec2 renderSize;\n" \
19"} renderer;\n" \
20"\n" \
21"layout(std140, binding = 2) uniform material_t {\n" \
22" vec2 scale;\n" \
23" vec2 texSz;\n" \
24" vec4 field;\n" \
25"} mat;\n" \
26SCORE_GFX_VIDEO_SAMPLE_TRANSFORM
27
51#define SCORE_GFX_VIDEO_SAMPLE_TRANSFORM \
52"vec2 score_tc(vec2 tc) {\n" \
53" float mode = mat.field.y;\n" \
54" if(mode < 0.5) return tc;\n" \
55"\n" \
56" float lines = max(mat.texSz.y, 2.0);\n" \
57" float halfLines = lines * 0.5;\n" \
58" float parity = mat.field.x;\n" \
59"\n" \
60" if(mode < 1.5) {\n" \
61" float y = floor(tc.y * lines);\n" \
62" float lineParity = mod(y, 2.0);\n" \
63" float row = floor(y * 0.5) + 0.5;\n" \
64" return vec2(tc.x, (row / halfLines) * 0.5 + lineParity * 0.5);\n" \
65" }\n" \
66"\n" \
67" float k = (tc.y * lines - parity) * 0.5;\n" \
68" k = clamp(k, 0.0, halfLines);\n" \
69" return vec2(tc.x, (k / halfLines) * 0.5 + parity * 0.5);\n" \
70"}\n"
71
72namespace score::gfx
73{
74
75
85inline float videoFieldMode(
86 Video::Interlacing interlacing, Video::Deinterlace deinterlace,
87 bool partnerValid) noexcept
88{
89 if(interlacing != Video::Interlacing::Fields)
90 return 0.f;
91 if(deinterlace == Video::Deinterlace::Bob || !partnerValid)
92 return 2.f;
93 return 1.f;
94}
95
96
100{
101 int log2ChromaW{1};
102 int log2ChromaH{1};
103 int bitDepth{8};
104 int numPlanes{2};
105 bool hasAlpha{};
106
107 bool is10bit() const noexcept { return bitDepth > 8; }
108
110 static PixelFormatInfo fromAVPixelFormat(AVPixelFormat fmt)
111 {
112 PixelFormatInfo info;
113 const AVPixFmtDescriptor* desc = av_pix_fmt_desc_get(fmt);
114 if(desc)
115 {
116 info.log2ChromaW = desc->log2_chroma_w;
117 info.log2ChromaH = desc->log2_chroma_h;
118 info.bitDepth = desc->comp[0].depth;
119 info.numPlanes = av_pix_fmt_count_planes(fmt);
120 info.hasAlpha = (desc->flags & AV_PIX_FMT_FLAG_ALPHA) != 0;
121 }
122 return info;
123 }
124
128 AVPixelFormat swFormat, AVPixelFormat codecparFormat,
129 int bitsPerRawSample)
130 {
131 PixelFormatInfo info;
132 // Prefer sw_format (from hw_frames_ctx) when available
133 AVPixelFormat fmt = (swFormat != AV_PIX_FMT_NONE) ? swFormat : codecparFormat;
134 if(fmt != AV_PIX_FMT_NONE)
135 info = fromAVPixelFormat(fmt);
136 // bits_per_raw_sample overrides depth when set (some containers report it)
137 if(bitsPerRawSample > info.bitDepth)
138 info.bitDepth = bitsPerRawSample;
139 return info;
140 }
141};
142
143// TODO the "model" nodes should have a first update step so that they
144// can share data across all renderers during a tick
145class VideoNode;
146
160class SCORE_PLUGIN_GFX_EXPORT GPUVideoDecoder
161{
162public:
164 virtual ~GPUVideoDecoder();
165
175 [[nodiscard]] virtual std::pair<QShader, QShader> init(RenderList& r) = 0;
176
180 virtual void exec(RenderList&, QRhiResourceUpdateBatch& res, AVFrame& frame) = 0;
181
185 virtual void release(RenderList&);
186
193 static QRhiTextureSubresourceUploadDescription
194 createTextureUpload(uint8_t* pixels, int w, int h, int bytesPerPixel, int stride);
195
202 static bool isTopField(const AVFrame& frame) noexcept
203 {
204#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(58, 0, 0)
205 return (frame.flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) != 0;
206#else
207 return frame.top_field_first != 0;
208#endif
209 }
210
221 {
222 int rows{};
223 int offset{};
224 };
225 static PlaneRows planeRows(
226 const Video::ImageFormat& fmt, const AVFrame& frame, int textureRows) noexcept
227 {
228 if(fmt.interlacing != Video::Interlacing::Fields)
229 return {textureRows, 0};
230
231 const int half = textureRows / 2;
232 return {half, isTopField(frame) ? 0 : half};
233 }
234
235 static QString vertexShader(bool invertY = false) noexcept;
236
237 std::vector<Sampler> samplers;
238
242 bool hasFrame{};
243
247 bool failed{};
248
256 bool formatChanged{};
257};
258
263{
264 // Writing nothing leaves the attachment undefined: NVIDIA hands back black,
265 // llvmpipe hands back white. Write the black the callers expect.
266 static const constexpr auto hashtag_no_filter = R"_(#version 450
267 layout(location = 0) out vec4 fragColor;
268 void main ()
269 {
270 fragColor = vec4(0.0, 0.0, 0.0, 1.0);
271 }
272 )_";
273
274 explicit EmptyDecoder() { }
275
276 std::pair<QShader, QShader> init(RenderList& r) override
277 {
278 return score::gfx::makeShaders(r.state, vertexShader(), hashtag_no_filter);
279 }
280
281 void exec(RenderList&, QRhiResourceUpdateBatch& res, AVFrame& frame) override { }
282};
283}
Processes and renders a video frame on the GPU.
Definition GPUVideoDecoder.hpp:161
virtual void exec(RenderList &, QRhiResourceUpdateBatch &res, AVFrame &frame)=0
Decode and upload a video frame to the GPU.
virtual std::pair< QShader, QShader > init(RenderList &r)=0
Initialize a GPUVideoDecoder.
static bool isTopField(const AVFrame &frame) noexcept
Whether this frame carries the EVEN lines of its picture.
Definition GPUVideoDecoder.hpp:202
List of nodes to be rendered to an output.
Definition RenderList.hpp:33
RenderState & state
RenderState corresponding to this RenderList.
Definition RenderList.hpp:274
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
float videoFieldMode(Video::Interlacing interlacing, Video::Deinterlace deinterlace, bool partnerValid) noexcept
Which mode score_tc should run, as a uniform value.
Definition GPUVideoDecoder.hpp:85
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:1359
STL namespace.
Definition VideoInterface.hpp:29
Default decoder when we do not know what to render.
Definition GPUVideoDecoder.hpp:263
void exec(RenderList &, QRhiResourceUpdateBatch &res, AVFrame &frame) override
Decode and upload a video frame to the GPU.
Definition GPUVideoDecoder.hpp:281
std::pair< QShader, QShader > init(RenderList &r) override
Initialize a GPUVideoDecoder.
Definition GPUVideoDecoder.hpp:276
The upload geometry for one plane of a possibly-fielded frame.
Definition GPUVideoDecoder.hpp:221
Definition GPUVideoDecoder.hpp:100
int log2ChromaW
Horizontal chroma subsampling: 0=4:4:4, 1=4:2:2/4:2:0.
Definition GPUVideoDecoder.hpp:101
static PixelFormatInfo fromAVPixelFormat(AVPixelFormat fmt)
Build from an AVPixelFormat.
Definition GPUVideoDecoder.hpp:110
int log2ChromaH
Vertical chroma subsampling: 0=4:4:4/4:2:2, 1=4:2:0.
Definition GPUVideoDecoder.hpp:102
int bitDepth
Per-component bit depth (8, 10, 12, 16)
Definition GPUVideoDecoder.hpp:103
bool hasAlpha
Format includes an alpha channel.
Definition GPUVideoDecoder.hpp:105
int numPlanes
Number of planes (2 for semi-planar, 3+ for planar)
Definition GPUVideoDecoder.hpp:104
static PixelFormatInfo fromCodecParameters(AVPixelFormat swFormat, AVPixelFormat codecparFormat, int bitsPerRawSample)
Definition GPUVideoDecoder.hpp:127