Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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
4#include <QtFFmpegMediaPluginImpl/private/qavfsamplebufferdelegate_p.h>
5
6#include <QtMultimedia/private/qavfhelpers_p.h>
7#include <QtMultimedia/private/qvideoframe_p.h>
8
9#include <QtFFmpegMediaPluginImpl/private/qcvimagevideobuffer_p.h>
10#define AVMediaType XAVMediaType
11#include <QtFFmpegMediaPluginImpl/private/qffmpegvideobuffer_p.h>
12#include <QtFFmpegMediaPluginImpl/private/qffmpeghwaccel_p.h>
13#undef AVMediaType
14
15#include <optional>
16
17QT_USE_NAMESPACE
18
19// Make sure this is compatible with the layout used in ffmpeg's hwcontext_videotoolbox
20static QFFmpeg::AVFrameUPtr allocHWFrame(
21 AVBufferRef *hwContext,
22 QAVFHelpers::QSharedCVPixelBuffer sharedPixBuf)
23{
24 Q_ASSERT(sharedPixBuf);
25
26 AVHWFramesContext *ctx = (AVHWFramesContext *)hwContext->data;
27 auto frame = QFFmpeg::makeAVFrame();
28 frame->hw_frames_ctx = av_buffer_ref(hwContext);
29 frame->extended_data = frame->data;
30
31 CVPixelBufferRef pixbuf = sharedPixBuf.release();
32 auto releasePixBufFn = [](void* opaquePtr, uint8_t *) {
33 CVPixelBufferRelease(static_cast<CVPixelBufferRef>(opaquePtr));
34 };
35 frame->buf[0] = av_buffer_create(nullptr, 0, releasePixBufFn, pixbuf, 0);
36
37 // It is convention to use 4th data plane for hardware frames.
38 frame->data[3] = (uint8_t *)pixbuf;
39 frame->width = ctx->width;
40 frame->height = ctx->height;
41 frame->format = AV_PIX_FMT_VIDEOTOOLBOX;
42 if (frame->width != (int)CVPixelBufferGetWidth(pixbuf)
43 || frame->height != (int)CVPixelBufferGetHeight(pixbuf)) {
44
45 // This can happen while changing camera format
46 return nullptr;
47 }
48 return frame;
49}
50
51@implementation QAVFSampleBufferDelegate {
52@private
53 std::function<void(const QVideoFrame &)> frameHandler;
54 QFFmpeg::QAVFSampleBufferDelegateTransformProvider transformationProvider;
55 AVBufferRef *hwFramesContext;
56 std::unique_ptr<QFFmpeg::HWAccel> m_accel;
57 qint64 startTime;
58 std::optional<qint64> baseTime;
59 qreal frameRate;
60}
61
62static QVideoFrame createHwVideoFrame(
63 QAVFSampleBufferDelegate &delegate,
64 const QAVFHelpers::QSharedCVPixelBuffer &imageBuffer,
65 QVideoFrameFormat format)
66{
67 Q_ASSERT(delegate.baseTime);
68
69 if (!delegate.m_accel)
70 return {};
71
72 auto avFrame = allocHWFrame(
73 delegate.m_accel->hwFramesContextAsBuffer(),
74 imageBuffer);
75 if (!avFrame)
76 return {};
77
78#ifdef USE_SW_FRAMES
79 {
80 auto swFrame = QFFmpeg::makeAVFrame();
81 /* retrieve data from GPU to CPU */
82 const int ret = av_hwframe_transfer_data(swFrame.get(), avFrame.get(), 0);
83 if (ret < 0) {
84 qWarning() << "Error transferring the data to system memory:" << ret;
85 } else {
86 avFrame = std::move(swFrame);
87 }
88 }
89#endif
90
91 avFrame->pts = delegate.startTime - *delegate.baseTime;
92
93 return QVideoFramePrivate::createFrame(std::make_unique<QFFmpegVideoBuffer>(std::move(avFrame)),
94 format);
95}
96
97- (instancetype)initWithFrameHandler:(std::function<void(const QVideoFrame &)>)handler
98{
99 if (!(self = [super init]))
100 return nil;
101
102 Q_ASSERT(handler);
103
104 frameHandler = std::move(handler);
105 hwFramesContext = nullptr;
106 startTime = 0;
107 frameRate = 0.;
108 return self;
109}
110
111- (void)discardFutureSamples
112{
113 frameHandler = nullptr;
114}
115
116- (void)setTransformationProvider:
117 (const QFFmpeg::QAVFSampleBufferDelegateTransformProvider &)provider
118{
119 transformationProvider = std::move(provider);
120}
121
122- (void)captureOutput:(AVCaptureOutput *)captureOutput
123 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
124 fromConnection:(AVCaptureConnection *)connection
125{
126 Q_UNUSED(captureOutput);
127
128 if (!frameHandler)
129 return;
130
131 // NB: on iOS captureOutput/connection can be nil (when recording a video -
132 // avfmediaassetwriter).
133
134 CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
135 if (!imageBuffer || CFGetTypeID(imageBuffer) != CVPixelBufferGetTypeID()) {
136 qWarning() << "Cannot get image buffer from sample buffer";
137 return;
138 }
139
140 auto pixelBuffer = QAVFHelpers::QSharedCVPixelBuffer(
141 imageBuffer,
142 QAVFHelpers::QSharedCVPixelBuffer::RefMode::NeedsRef);
143
144 const CMTime time = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
145 const qint64 frameTime = time.timescale ? time.value * 1000000 / time.timescale : 0;
146 if (!baseTime) {
147 baseTime = frameTime;
148 startTime = frameTime;
149 }
150
151 QVideoFrameFormat format = QAVFHelpers::videoFormatForImageBuffer(pixelBuffer.get());
152 if (!format.isValid()) {
153 qWarning() << "Cannot get get video format for image buffer"
154 << CVPixelBufferGetWidth(pixelBuffer.get()) << 'x'
155 << CVPixelBufferGetHeight(pixelBuffer.get());
156 return;
157 }
158
159 std::optional<QFFmpeg::QAVFSampleBufferDelegateTransform> transform;
160 if (transformationProvider) {
161 transform = transformationProvider(connection);
162 const VideoTransformation &surfaceTransform = transform.value().surfaceTransform;
163 format.setRotation(surfaceTransform.rotation);
164 format.setMirrored(surfaceTransform.mirroredHorizontallyAfterRotation);
165 }
166
167 format.setStreamFrameRate(frameRate);
168
169 auto frame = createHwVideoFrame(*self, pixelBuffer, format);
170 if (!frame.isValid())
171 frame = QVideoFramePrivate::createFrame(
172 std::make_unique<QFFmpeg::CVImageVideoBuffer>(std::move(pixelBuffer)),
173 std::move(format));
174
175 if (transform.has_value()) {
176 const VideoTransformation &presentationTransform = transform.value().presentationTransform;
177 frame.setRotation(presentationTransform.rotation);
178 frame.setMirrored(presentationTransform.mirroredHorizontallyAfterRotation);
179 }
180
181 frame.setStartTime(startTime - *baseTime);
182 frame.setEndTime(frameTime - *baseTime);
183 startTime = frameTime;
184
185 frameHandler(frame);
186}
187
188- (void)setHWAccel:(std::unique_ptr<QFFmpeg::HWAccel> &&)accel
189{
190 m_accel = std::move(accel);
191}
192
193- (void)setVideoFormatFrameRate:(qreal)rate
194{
195 frameRate = rate;
196}
197
198@end