Loading...
Searching...
No Matches
HWTransfer.hpp
1#pragma once
2#include <Gfx/Graph/decoders/ColorSpace.hpp>
3#include <Gfx/Graph/decoders/GPUVideoDecoder.hpp>
4#include <Gfx/Graph/decoders/NV12.hpp>
5#include <Gfx/Graph/decoders/P010.hpp>
6#include <Gfx/Graph/decoders/YUV420.hpp>
7#include <Gfx/Graph/decoders/YUV420P10.hpp>
8#include <Gfx/Graph/decoders/YUV422.hpp>
9#include <Gfx/Graph/decoders/YUV422P10.hpp>
10#include <Gfx/Graph/decoders/YUV444.hpp>
11#include <Gfx/Graph/decoders/YUV444P10.hpp>
12
13#include <Video/GpuFormats.hpp>
14
15extern "C" {
16#include <libavformat/avformat.h>
17#include <libavutil/pixdesc.h>
18#if __has_include(<libavutil/hwcontext.h>)
19#include <libavutil/hwcontext.h>
20#endif
21}
22
23namespace score::gfx
24{
25
38{
39 Video::ImageFormat& decoder;
40 AVPixelFormat m_swFormat{AV_PIX_FMT_NONE};
41 std::unique_ptr<GPUVideoDecoder> m_delegate;
42 AVFrame* m_swFrame{};
43
44 explicit HWTransferDecoder(
45 Video::ImageFormat& d, AVPixelFormat swFormat = AV_PIX_FMT_NV12)
46 : decoder{d}
47 , m_swFormat{swFormat}
48 {
49 m_swFrame = av_frame_alloc();
50 }
51
52 static bool canDelegateFormat(AVPixelFormat fmt)
53 {
54 switch(fmt)
55 {
56 case AV_PIX_FMT_NV12:
57 case AV_PIX_FMT_NV21:
58 case AV_PIX_FMT_P010LE:
59 case AV_PIX_FMT_YUV420P:
60 case AV_PIX_FMT_YUVJ420P:
61 case AV_PIX_FMT_YUV420P10LE:
62 case AV_PIX_FMT_YUV422P:
63 case AV_PIX_FMT_YUVJ422P:
64 case AV_PIX_FMT_YUV422P10LE:
65 case AV_PIX_FMT_YUV444P:
66 case AV_PIX_FMT_YUVJ444P:
67 case AV_PIX_FMT_YUV444P10LE:
68 return true;
69 default:
70 return false;
71 }
72 }
73
74 std::unique_ptr<GPUVideoDecoder> createDelegateForFormat(AVPixelFormat fmt)
75 {
76 switch(fmt)
77 {
78 case AV_PIX_FMT_NV12:
79 return std::make_unique<NV12Decoder>(decoder, false);
80 case AV_PIX_FMT_NV21:
81 return std::make_unique<NV12Decoder>(decoder, true);
82 case AV_PIX_FMT_P010LE:
83 return std::make_unique<P010Decoder>(decoder);
84 case AV_PIX_FMT_YUV420P:
85 case AV_PIX_FMT_YUVJ420P:
86 return std::make_unique<YUV420Decoder>(decoder);
87 case AV_PIX_FMT_YUV420P10LE:
88 return std::make_unique<YUV420P10Decoder>(decoder);
89 case AV_PIX_FMT_YUV422P:
90 case AV_PIX_FMT_YUVJ422P:
91 return std::make_unique<YUV422Decoder>(decoder);
92 case AV_PIX_FMT_YUV422P10LE:
93 return std::make_unique<YUV422P10Decoder>(decoder);
94 case AV_PIX_FMT_YUV444P:
95 case AV_PIX_FMT_YUVJ444P:
96 return std::make_unique<YUV444Decoder>(decoder);
97 case AV_PIX_FMT_YUV444P10LE:
98 return std::make_unique<YUV444P10Decoder>(decoder);
99 default:
100 return std::make_unique<NV12Decoder>(decoder, false);
101 }
102 }
103
104 ~HWTransferDecoder() override
105 {
106 if(m_swFrame)
107 av_frame_free(&m_swFrame);
108
109 // Clear delegate's samplers to prevent double-free:
110 // our samplers vector shares the same pointers as the delegate's,
111 // and the base class GPUVideoDecoder::release() already cleaned them.
112 if(m_delegate)
113 m_delegate->samplers.clear();
114 }
115
116 std::pair<QShader, QShader> init(RenderList& r) override
117 {
118 if(!m_delegate)
119 {
120 decoder.pixel_format = m_swFormat;
121 m_delegate = createDelegateForFormat(m_swFormat);
122 }
123
124 auto ret = m_delegate->init(r);
125 samplers = m_delegate->samplers;
126 return ret;
127 }
128
129 void exec(RenderList& r, QRhiResourceUpdateBatch& res, AVFrame& frame) override
130 {
131#if LIBAVUTIL_VERSION_MAJOR >= 57
132 if(!Video::formatIsHardwareDecoded(static_cast<AVPixelFormat>(frame.format)))
133 {
134 // Already a software frame (e.g. after format change), delegate directly
135 if(m_delegate)
136 m_delegate->exec(r, res, frame);
137 return;
138 }
139
140 // Transfer HW frame → CPU.
141 // If the native sw_format is one we can't directly render (e.g. AYUV from
142 // ProRes 4444+alpha), request a format we handle instead. FFmpeg will
143 // convert internally during the transfer.
144 av_frame_unref(m_swFrame);
145 if(canDelegateFormat(m_swFormat))
146 m_swFrame->format = AV_PIX_FMT_NONE; // Use native sw_format
147 else
148 m_swFrame->format = AV_PIX_FMT_NV12; // Request a supported fallback
149 int ret = av_hwframe_transfer_data(m_swFrame, &frame, 0);
150 if(ret < 0)
151 {
152 qDebug() << "HWTransferDecoder: av_hwframe_transfer_data failed:" << ret;
153 return;
154 }
155 m_swFrame->pts = frame.pts;
156 m_swFrame->pkt_dts = frame.pkt_dts;
157
158 auto sw_fmt = static_cast<AVPixelFormat>(m_swFrame->format);
159
160 // If the software format changed (first frame, or dynamic change), rebuild delegate
161 if(sw_fmt != m_swFormat)
162 {
163 m_swFormat = sw_fmt;
164 decoder.pixel_format = sw_fmt;
165 decoder.width = m_swFrame->width;
166 decoder.height = m_swFrame->height;
167
168 // Format changed — rebuild delegate with correct textures/shaders.
169 // This should rarely happen since we pre-set sw_format at construction.
170 if(m_delegate)
171 {
172 m_delegate->samplers.clear();
173 m_delegate.reset();
174 }
175 samplers.clear();
176
177 m_delegate = createDelegateForFormat(sw_fmt);
178 if(m_delegate)
179 {
180 m_delegate->init(r);
181 samplers = m_delegate->samplers;
182 }
183 }
184
185 if(m_delegate)
186 m_delegate->exec(r, res, *m_swFrame);
187#endif
188 }
189};
190
191}
Processes and renders a video frame on the GPU.
Definition GPUVideoDecoder.hpp:90
List of nodes to be rendered to an output.
Definition RenderList.hpp:19
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:12
Definition VideoInterface.hpp:26
Fallback hardware decoder that transfers HW frames to CPU via av_hwframe_transfer_data(),...
Definition HWTransfer.hpp:38
void exec(RenderList &r, QRhiResourceUpdateBatch &res, AVFrame &frame) override
Decode and upload a video frame to the GPU.
Definition HWTransfer.hpp:129
std::pair< QShader, QShader > init(RenderList &r) override
Initialize a GPUVideoDecoder.
Definition HWTransfer.hpp:116