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
qffmpegencodinginitializer.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 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
8#include "qvideoframe.h"
9
10#include "private/qplatformvideoframeinput_p.h"
11#include "private/qplatformaudiobufferinput_p.h"
12#include "private/qplatformaudiobufferinput_p.h"
13
14QT_BEGIN_NAMESPACE
15
16namespace QFFmpeg {
17
18EncodingInitializer::EncodingInitializer(RecordingEngine &engine) : m_recordingEngine(engine) { }
19
20EncodingInitializer::~EncodingInitializer()
21{
22 for (QObject *source : m_pendingSources)
23 setEncoderInterface(source, nullptr);
24}
25
26bool EncodingInitializer::start(const std::vector<QAudioBufferSource *> &audioSources,
27 const std::vector<QPlatformVideoSource *> &videoSources)
28{
29 for (auto source : audioSources) {
30 if (auto audioInput = qobject_cast<QFFmpegAudioInput *>(source))
31 m_recordingEngine.addAudioInput(audioInput);
32 else if (auto audioBufferInput = qobject_cast<QPlatformAudioBufferInput *>(source))
33 addAudioBufferInput(audioBufferInput);
34 else
35 Q_ASSERT(!"Undefined source type");
36 }
37
38 for (auto source : videoSources)
39 addVideoSource(source);
40
41 return tryStartRecordingEngine();
42}
43
44void EncodingInitializer::addAudioBufferInput(QPlatformAudioBufferInput *input)
45{
46 Q_ASSERT(input);
47
48 if (input->audioFormat().isValid())
49 m_recordingEngine.addAudioBufferInput(input, {});
50 else
51 addPendingAudioBufferInput(input);
52}
53
54void EncodingInitializer::addPendingAudioBufferInput(QPlatformAudioBufferInput *input)
55{
56 addPendingSource(input);
57
58 connect(input, &QPlatformAudioBufferInput::destroyed, this, [this, input]() {
59 erasePendingSource(input, QStringLiteral("Audio source deleted"), true);
60 });
61
62 connect(input, &QPlatformAudioBufferInput::newAudioBuffer, this,
63 [this, input](const QAudioBuffer &buffer) {
64 if (buffer.isValid())
65 erasePendingSource(
66 input, [&]() { m_recordingEngine.addAudioBufferInput(input, buffer); });
67 else
68 erasePendingSource(input,
69 QStringLiteral("Audio source has sent the end frame"));
70 });
71}
72
73void EncodingInitializer::addVideoSource(QPlatformVideoSource *source)
74{
75 Q_ASSERT(source);
76 Q_ASSERT(source->isActive());
77
78 if (source->frameFormat().isValid())
79 m_recordingEngine.addVideoSource(source, {});
80 else if (source->hasError())
81 emitStreamInitializationError(QStringLiteral("Video source error: ")
82 + source->errorString());
83 else
84 addPendingVideoSource(source);
85}
86
87void EncodingInitializer::addPendingVideoSource(QPlatformVideoSource *source)
88{
89 addPendingSource(source);
90
91 connect(source, &QPlatformVideoSource::errorChanged, this, [this, source]() {
92 if (source->hasError())
93 erasePendingSource(source,
94 QStringLiteral("Videio source error: ") + source->errorString());
95 });
96
97 connect(source, &QPlatformVideoSource::destroyed, this, [this, source]() {
98 erasePendingSource(source, QStringLiteral("Source deleted"), true);
99 });
100
101 connect(source, &QPlatformVideoSource::activeChanged, this, [this, source]() {
102 if (!source->isActive())
103 erasePendingSource(source, QStringLiteral("Video source deactivated"));
104 });
105
106 connect(source, &QPlatformVideoSource::newVideoFrame, this,
107 [this, source](const QVideoFrame &frame) {
108 if (frame.isValid())
109 erasePendingSource(source,
110 [&]() { m_recordingEngine.addVideoSource(source, frame); });
111 else
112 erasePendingSource(source,
113 QStringLiteral("Video source has sent the end frame"));
114 });
115}
116
117bool EncodingInitializer::tryStartRecordingEngine()
118{
119 if (m_pendingSources.empty())
120 return m_recordingEngine.startEncoders();
121
122 // return true as no errors found, even though they can occur later on,
123 // upon the following encoders initializations.
124 return true;
125}
126
127void EncodingInitializer::emitStreamInitializationError(QString error)
128{
129 emit m_recordingEngine.streamInitializationError(
130 QMediaRecorder::ResourceError,
131 QStringLiteral("Video steam initialization error. ") + error);
132}
133
134void EncodingInitializer::addPendingSource(QObject *source)
135{
136 Q_ASSERT(m_pendingSources.count(source) == 0);
137
138 setEncoderInterface(source, this);
139 m_pendingSources.emplace(source);
140}
141
142template <typename F>
143void EncodingInitializer::erasePendingSource(QObject *source, F &&functionOrError, bool destroyed)
144{
145 const auto erasedCount = m_pendingSources.erase(source);
146 if (erasedCount == 0)
147 return; // got a queued event, just ignore it.
148
149 if (!destroyed) {
150 setEncoderInterface(source, nullptr);
151 disconnect(source, nullptr, this, nullptr);
152 }
153
154 if constexpr (std::is_invocable_v<F>)
155 functionOrError();
156 else
157 emitStreamInitializationError(functionOrError);
158
159 tryStartRecordingEngine();
160}
161
162bool EncodingInitializer::canPushFrame() const
163{
164 return true;
165}
166
167} // namespace QFFmpeg
168
169QT_END_NAMESPACE