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#define AVMediaType XAVMediaType
7extern "C" {
8#include <libavutil/hwcontext_videotoolbox.h>
9} // extern "C"
10#undef AVMediaType
11
12#include <QtCore/qloggingcategory.h>
13#include <QtCore/qsize.h>
14
15#include <QtMultimedia/private/qavfhelpers_p.h>
16#include <QtMultimedia/private/qvideoframe_p.h>
17
18#include <QtFFmpegMediaPluginImpl/private/qcvimagevideobuffer_p.h>
19#include <QtFFmpegMediaPluginImpl/private/qffmpegdarwinhwframehelpers_p.h>
20#define AVMediaType XAVMediaType
21#include <QtFFmpegMediaPluginImpl/private/qffmpegvideobuffer_p.h>
22#include <QtFFmpegMediaPluginImpl/private/qffmpeghwaccel_p.h>
23#undef AVMediaType
24
25#include <chrono>
26#include <optional>
27
28Q_STATIC_LOGGING_CATEGORY(qLcAvfSampleBufferDelegate, "qt.multimedia.avf.samplebufferdelegate");
29
30using namespace Qt::StringLiterals;
31
32QT_USE_NAMESPACE
33
34@implementation QT_MANGLE_NAMESPACE(QAVFSampleBufferDelegate) {
35@private
36 std::function<void(const QVideoFrame &)> frameHandler;
37 QFFmpeg::QAVFSampleBufferDelegateTransformProvider transformationProvider;
38 std::unique_ptr<QFFmpeg::HWAccel> m_accel;
39 std::chrono::microseconds startTime;
40 std::optional<std::chrono::microseconds> baseTime;
41 qreal frameRate;
42}
43
44- (instancetype)initWithFrameHandler:(std::function<void(const QVideoFrame &)>)handler
45{
46 if (!(self = [super init]))
47 return nil;
48
49 Q_ASSERT(handler);
50
51 frameHandler = std::move(handler);
52 return self;
53}
54
55- (void)discardFutureSamples
56{
57 frameHandler = nullptr;
58}
59
60- (void)setTransformationProvider:
61 (const QFFmpeg::QAVFSampleBufferDelegateTransformProvider &)provider
62{
63 transformationProvider = std::move(provider);
64}
65
66- (void)captureOutput:(AVCaptureOutput *)captureOutput
67 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
68 fromConnection:(AVCaptureConnection *)connection
69{
70 Q_UNUSED(captureOutput);
71
72 if (!frameHandler)
73 return;
74
75 CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
76 if (!imageBuffer || CFGetTypeID(imageBuffer) != CVPixelBufferGetTypeID()) {
77 qWarning() << "Cannot get image buffer from sample buffer";
78 return;
79 }
80
81 auto pixelBuffer = QAVFHelpers::QSharedCVPixelBuffer(
82 imageBuffer,
83 QAVFHelpers::QSharedCVPixelBuffer::RefMode::NeedsRef);
84
85 // The incoming buffer is recycled from a pool owned by AVFoundation, so we
86 // copy it into a free-standing CVPixelBuffer that we fully own before
87 // keeping it around in a QVideoFrame.
88 q23::expected<QAVFHelpers::QSharedCVPixelBuffer, QString> copyResult =
89 QFFmpeg::deepCopyCvPixelBuffer(pixelBuffer.get());
90 if (!copyResult) {
91 qCWarning(qLcAvfSampleBufferDelegate)
92 << "Failed to copy incoming pixel buffer:" << copyResult.error();
93 return;
94 }
95 pixelBuffer = std::move(*copyResult);
96
97 QSize incomingFrameSize = QAVFHelpers::CVPixelBufferSize(pixelBuffer.get());
98 Q_ASSERT(!incomingFrameSize.isEmpty());
99 CvPixelFormat incomingCvPixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer.get());
100
101 Q_ASSERT(m_accel);
102 m_accel->updateFramesContext(
103 av_map_videotoolbox_format_to_pixfmt(incomingCvPixelFormat),
104 incomingFrameSize);
105
106 std::chrono::microseconds frameTime =
107 QAVFHelpers::CMTimeToMicroseconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer));
108 if (!baseTime) {
109 baseTime = frameTime;
110 startTime = frameTime;
111 }
112
113 QVideoFrameFormat format = QAVFHelpers::videoFormatForImageBuffer(pixelBuffer.get());
114 if (!format.isValid()) {
115 qWarning() << "Cannot get get video format for image buffer"
116 << QAVFHelpers::CVPixelBufferSize(pixelBuffer.get());
117 return;
118 }
119
120 std::optional<QFFmpeg::QAVFSampleBufferDelegateTransform> transform;
121 if (transformationProvider) {
122 transform = transformationProvider(connection);
123 const VideoTransformation &surfaceTransform = transform.value().surfaceTransform;
124 format.setRotation(surfaceTransform.rotation);
125 format.setMirrored(surfaceTransform.mirroredHorizontallyAfterRotation);
126 }
127
128 format.setStreamFrameRate(frameRate);
129
130 QVideoFrame frame;
131 q23::expected<QVideoFrame, QString> frameResult = QFFmpeg::qVideoFrameFromCvPixelBuffer(
132 *m_accel,
133 startTime - *baseTime,
134 pixelBuffer,
135 format);
136 if (!frameResult)
137 qCWarning(qLcAvfSampleBufferDelegate) << frameResult.error();
138 else
139 frame = *frameResult;
140
141 if (!frame.isValid())
142 frame = QVideoFramePrivate::createFrame(
143 std::make_unique<QFFmpeg::CVImageVideoBuffer>(std::move(pixelBuffer)),
144 std::move(format));
145
146 if (transform.has_value()) {
147 const VideoTransformation &presentationTransform = transform.value().presentationTransform;
148 frame.setRotation(presentationTransform.rotation);
149 frame.setMirrored(presentationTransform.mirroredHorizontallyAfterRotation);
150 }
151
152 frame.setStartTime((startTime - *baseTime).count());
153 frame.setEndTime((frameTime - *baseTime).count());
154 startTime = frameTime;
155
156 frameHandler(frame);
157}
158
159// Sets the initial HWAccel. Should only be called once during
160// initialization.
161- (void)setHWAccel:(std::unique_ptr<QFFmpeg::HWAccel> &&)accel
162{
163 m_accel = std::move(accel);
164}
165
166- (void)setVideoFormatFrameRate:(qreal)rate
167{
168 frameRate = rate;
169}
170
171@end
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)