Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qavfsamplebufferdelegate.mm
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#define AVMediaType XAVMediaType
7
8#include "qffmpeghwaccel_p.h"
9#include "qavfhelpers_p.h"
11
12#undef AVMediaType
13
14#include <optional>
15
17
18static void releaseHwFrame(void * /*opaque*/, uint8_t *data)
19{
20 CVPixelBufferRelease(CVPixelBufferRef(data));
21}
22
23namespace {
24
25class CVImageVideoBuffer : public QAbstractVideoBuffer
26{
27public:
28 CVImageVideoBuffer(CVImageBufferRef imageBuffer)
29 : QAbstractVideoBuffer(QVideoFrame::NoHandle), m_buffer(imageBuffer)
30 {
31 CVPixelBufferRetain(imageBuffer);
32 }
33
34 ~CVImageVideoBuffer()
35 {
36 CVImageVideoBuffer::unmap();
37 CVPixelBufferRelease(m_buffer);
38 }
39
40 CVImageVideoBuffer::MapData map(QVideoFrame::MapMode mode) override
41 {
42 MapData mapData;
43
44 if (m_mode == QVideoFrame::NotMapped) {
45 CVPixelBufferLockBaseAddress(
46 m_buffer, mode == QVideoFrame::ReadOnly ? kCVPixelBufferLock_ReadOnly : 0);
47 m_mode = mode;
48 }
49
50 mapData.nPlanes = CVPixelBufferGetPlaneCount(m_buffer);
51 Q_ASSERT(mapData.nPlanes <= 3);
52
53 if (!mapData.nPlanes) {
54 // single plane
55 mapData.bytesPerLine[0] = CVPixelBufferGetBytesPerRow(m_buffer);
56 mapData.data[0] = static_cast<uchar *>(CVPixelBufferGetBaseAddress(m_buffer));
57 mapData.size[0] = CVPixelBufferGetDataSize(m_buffer);
58 mapData.nPlanes = mapData.data[0] ? 1 : 0;
59 return mapData;
60 }
61
62 // For a bi-planar or tri-planar format we have to set the parameters correctly:
63 for (int i = 0; i < mapData.nPlanes; ++i) {
64 mapData.bytesPerLine[i] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, i);
65 mapData.size[i] = mapData.bytesPerLine[i] * CVPixelBufferGetHeightOfPlane(m_buffer, i);
66 mapData.data[i] = static_cast<uchar *>(CVPixelBufferGetBaseAddressOfPlane(m_buffer, i));
67 }
68
69 return mapData;
70 }
71
72 QVideoFrame::MapMode mapMode() const override { return m_mode; }
73
74 void unmap() override
75 {
76 if (m_mode != QVideoFrame::NotMapped) {
77 CVPixelBufferUnlockBaseAddress(
78 m_buffer, m_mode == QVideoFrame::ReadOnly ? kCVPixelBufferLock_ReadOnly : 0);
80 }
81 }
82
83private:
84 CVImageBufferRef m_buffer;
86};
87
88}
89
90// Make sure this is compatible with the layout used in ffmpeg's hwcontext_videotoolbox
91static QFFmpeg::AVFrameUPtr allocHWFrame(AVBufferRef *hwContext, const CVPixelBufferRef &pixbuf)
92{
93 AVHWFramesContext *ctx = (AVHWFramesContext *)hwContext->data;
95 frame->hw_frames_ctx = av_buffer_ref(hwContext);
96 frame->extended_data = frame->data;
97
98 frame->buf[0] = av_buffer_create((uint8_t *)pixbuf, 1, releaseHwFrame, NULL, 0);
99 frame->data[3] = (uint8_t *)pixbuf;
100 CVPixelBufferRetain(pixbuf);
101 frame->width = ctx->width;
102 frame->height = ctx->height;
103 frame->format = AV_PIX_FMT_VIDEOTOOLBOX;
104 if (frame->width != (int)CVPixelBufferGetWidth(pixbuf)
105 || frame->height != (int)CVPixelBufferGetHeight(pixbuf)) {
106
107 // This can happen while changing camera format
108 return nullptr;
109 }
110 return frame;
111}
112
114@private
115 std::function<void(const QVideoFrame &)> frameHandler;
116 AVBufferRef *hwFramesContext;
117 std::unique_ptr<QFFmpeg::HWAccel> m_accel;
119 std::optional<qint64> baseTime;
121}
122
123static QVideoFrame createHwVideoFrame(QAVFSampleBufferDelegate &delegate,
124 CVImageBufferRef imageBuffer, QVideoFrameFormat format)
125{
126 Q_ASSERT(delegate.baseTime);
127
128 if (!delegate.m_accel)
129 return {};
130
131 auto avFrame = allocHWFrame(delegate.m_accel->hwFramesContextAsBuffer(), imageBuffer);
132 if (!avFrame)
133 return {};
134
135#ifdef USE_SW_FRAMES
136 {
137 auto swFrame = QFFmpeg::makeAVFrame();
138 /* retrieve data from GPU to CPU */
139 const int ret = av_hwframe_transfer_data(swFrame.get(), avFrame.get(), 0);
140 if (ret < 0) {
141 qWarning() << "Error transferring the data to system memory:" << ret;
142 } else {
143 avFrame = std::move(swFrame);
144 }
145 }
146#endif
147
148 avFrame->pts = delegate.startTime - *delegate.baseTime;
149
150 return QVideoFrame(new QFFmpegVideoBuffer(std::move(avFrame)), format);
151}
152
153- (instancetype)initWithFrameHandler:(std::function<void(const QVideoFrame &)>)handler
154{
155 if (!(self = [super init]))
156 return nil;
157
158 Q_ASSERT(handler);
159
160 frameHandler = std::move(handler);
161 hwFramesContext = nullptr;
162 startTime = 0;
163 frameRate = 0.;
164 return self;
165}
166
167- (void)captureOutput:(AVCaptureOutput *)captureOutput
168 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
169 fromConnection:(AVCaptureConnection *)connection
170{
172 Q_UNUSED(captureOutput);
173
174 // NB: on iOS captureOutput/connection can be nil (when recording a video -
175 // avfmediaassetwriter).
176
177 CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
178
179 if (!imageBuffer) {
180 qWarning() << "Cannot get image buffer from sample buffer";
181 return;
182 }
183
184 const CMTime time = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
185 const qint64 frameTime = time.timescale ? time.value * 1000000 / time.timescale : 0;
186 if (!baseTime) {
187 baseTime = frameTime;
188 startTime = frameTime;
189 }
190
192 if (!format.isValid()) {
193 qWarning() << "Cannot get get video format for image buffer"
194 << CVPixelBufferGetWidth(imageBuffer) << 'x'
195 << CVPixelBufferGetHeight(imageBuffer);
196 return;
197 }
198
199 format.setStreamFrameRate(frameRate);
200
201 auto frame = createHwVideoFrame(*self, imageBuffer, format);
202 if (!frame.isValid())
203 frame = QVideoFrame(new CVImageVideoBuffer(imageBuffer), format);
204
205 frame.setStartTime(startTime - *baseTime);
206 frame.setEndTime(frameTime - *baseTime);
207 startTime = frameTime;
208
209 frameHandler(frame);
210}
211
212- (void)setHWAccel:(std::unique_ptr<QFFmpeg::HWAccel> &&)accel
213{
214 m_accel = std::move(accel);
215}
216
217- (void)setVideoFormatFrameRate:(qreal)rate
218{
219 frameRate = rate;
220}
221
222@end
The QAbstractVideoBuffer class is an abstraction for video data. \inmodule QtMultimedia.
The QVideoFrameFormat class specifies the stream format of a video presentation surface.
The QVideoFrame class represents a frame of video data.
Definition qvideoframe.h:27
MapMode
Enumerates how a video buffer's data is mapped to system memory.
Definition qvideoframe.h:37
int width
the width of the widget excluding any window frame
Definition qwidget.h:114
int height
the height of the widget excluding any window frame
Definition qwidget.h:115
EGLContext ctx
QMap< QString, QString > map
[6]
QVideoFrameFormat videoFormatForImageBuffer(CVImageBufferRef buffer, bool openGL=false)
AVFrameUPtr makeAVFrame()
Definition qffmpeg_p.h:136
std::unique_ptr< AVFrame, AVDeleter< decltype(&av_frame_free), &av_frame_free > > AVFrameUPtr
Definition qffmpeg_p.h:134
qint64 startTime
static QFFmpeg::AVFrameUPtr allocHWFrame(AVBufferRef *hwContext, const CVPixelBufferRef &pixbuf)
static QT_USE_NAMESPACE void releaseHwFrame(void *, uint8_t *data)
AVBufferRef * hwFramesContext
std::unique_ptr< QFFmpeg::HWAccel > m_accel
std::optional< qint64 > baseTime
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection * connection
#define qWarning
Definition qlogging.h:166
return ret
GLenum mode
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLsizei GLsizei GLenum format
GLuint GLenum * rate
static QAbstractVideoBuffer::MapData mapData(const camera_frame_nv12_t &frame, unsigned char *baseAddress)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
#define Q_UNUSED(x)
unsigned char uchar
Definition qtypes.h:32
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
QFrame frame
[0]