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/qsize.h>
13
14#include <QtMultimedia/private/qavfhelpers_p.h>
15#include <QtMultimedia/private/qvideoframe_p.h>
16
17#include <QtFFmpegMediaPluginImpl/private/qcvimagevideobuffer_p.h>
18#include <QtFFmpegMediaPluginImpl/private/qffmpegdarwinhwframehelpers_p.h>
19#define AVMediaType XAVMediaType
20#include <QtFFmpegMediaPluginImpl/private/qffmpegvideobuffer_p.h>
21#include <QtFFmpegMediaPluginImpl/private/qffmpeghwaccel_p.h>
22#undef AVMediaType
23
24#include <chrono>
25#include <optional>
26
27using namespace Qt::StringLiterals;
28
29QT_USE_NAMESPACE
30
31@implementation QT_MANGLE_NAMESPACE(QAVFSampleBufferDelegate) {
32@private
33 std::function<void(const QVideoFrame &)> frameHandler;
34 QFFmpeg::QAVFSampleBufferDelegateTransformProvider transformationProvider;
35 std::unique_ptr<QFFmpeg::HWAccel> m_accel;
36 std::chrono::microseconds startTime;
37 std::optional<std::chrono::microseconds> baseTime;
38 qreal frameRate;
39}
40
41- (instancetype)initWithFrameHandler:(std::function<void(const QVideoFrame &)>)handler
42{
43 if (!(self = [super init]))
44 return nil;
45
46 Q_ASSERT(handler);
47
48 frameHandler = std::move(handler);
49 return self;
50}
51
52- (void)discardFutureSamples
53{
54 frameHandler = nullptr;
55}
56
57- (void)setTransformationProvider:
58 (const QFFmpeg::QAVFSampleBufferDelegateTransformProvider &)provider
59{
60 transformationProvider = std::move(provider);
61}
62
63- (void)captureOutput:(AVCaptureOutput *)captureOutput
64 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
65 fromConnection:(AVCaptureConnection *)connection
66{
67 Q_UNUSED(captureOutput);
68
69 if (!frameHandler)
70 return;
71
72 CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
73 if (!imageBuffer || CFGetTypeID(imageBuffer) != CVPixelBufferGetTypeID()) {
74 qWarning() << "Cannot get image buffer from sample buffer";
75 return;
76 }
77
78 auto pixelBuffer = QAVFHelpers::QSharedCVPixelBuffer(
79 imageBuffer,
80 QAVFHelpers::QSharedCVPixelBuffer::RefMode::NeedsRef);
81
82 QSize incomingFrameSize {
83 static_cast<int>(CVPixelBufferGetWidth(pixelBuffer.get())),
84 static_cast<int>(CVPixelBufferGetHeight(pixelBuffer.get())) };
85 Q_ASSERT(!incomingFrameSize.isEmpty());
86 CvPixelFormat incomingCvPixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer.get());
87
88 Q_ASSERT(m_accel);
89 m_accel->updateFramesContext(
90 av_map_videotoolbox_format_to_pixfmt(incomingCvPixelFormat),
91 incomingFrameSize);
92
93 std::chrono::microseconds frameTime =
94 QAVFHelpers::CMTimeToMicroseconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer));
95 if (!baseTime) {
96 baseTime = frameTime;
97 startTime = frameTime;
98 }
99
100 QVideoFrameFormat format = QAVFHelpers::videoFormatForImageBuffer(pixelBuffer.get());
101 if (!format.isValid()) {
102 qWarning() << "Cannot get get video format for image buffer"
103 << CVPixelBufferGetWidth(pixelBuffer.get()) << 'x'
104 << CVPixelBufferGetHeight(pixelBuffer.get());
105 return;
106 }
107
108 std::optional<QFFmpeg::QAVFSampleBufferDelegateTransform> transform;
109 if (transformationProvider) {
110 transform = transformationProvider(connection);
111 const VideoTransformation &surfaceTransform = transform.value().surfaceTransform;
112 format.setRotation(surfaceTransform.rotation);
113 format.setMirrored(surfaceTransform.mirroredHorizontallyAfterRotation);
114 }
115
116 format.setStreamFrameRate(frameRate);
117
118 QVideoFrame frame = QFFmpeg::qVideoFrameFromCvPixelBuffer(
119 *m_accel,
120 startTime - *baseTime,
121 pixelBuffer,
122 format);
123 if (!frame.isValid())
124 frame = QVideoFramePrivate::createFrame(
125 std::make_unique<QFFmpeg::CVImageVideoBuffer>(std::move(pixelBuffer)),
126 std::move(format));
127
128 if (transform.has_value()) {
129 const VideoTransformation &presentationTransform = transform.value().presentationTransform;
130 frame.setRotation(presentationTransform.rotation);
131 frame.setMirrored(presentationTransform.mirroredHorizontallyAfterRotation);
132 }
133
134 frame.setStartTime((startTime - *baseTime).count());
135 frame.setEndTime((frameTime - *baseTime).count());
136 startTime = frameTime;
137
138 frameHandler(frame);
139}
140
141// Sets the initial HWAccel. Should only be called once during
142// initialization.
143- (void)setHWAccel:(std::unique_ptr<QFFmpeg::HWAccel> &&)accel
144{
145 m_accel = std::move(accel);
146}
147
148- (void)setVideoFormatFrameRate:(qreal)rate
149{
150 frameRate = rate;
151}
152
153@end