9#include "private/qmultimediautils_p.h"
10#include "private/qplatformaudiobufferinput_p.h"
11#include "private/qplatformvideosource_p.h"
12#include "private/qplatformvideoframeinput_p.h"
21namespace ranges = QtMultimediaPrivate::ranges;
29 std::unique_ptr<EncodingFormatContext> context)
30 : m_settings(settings), m_formatContext(std::move(context)), m_muxer(
new Muxer(
this))
32 Q_ASSERT(m_formatContext);
33 Q_ASSERT(m_formatContext->isAVIOOpen());
38 Q_ASSERT(m_state == State::Finalizing);
44 Q_ASSERT(m_state == State::FormatsInitializing);
46 if (input->device.isNull()) {
47 emit streamInitializationError(QMediaRecorder::ResourceError,
48 QLatin1StringView(
"Audio device is null"));
52 const QAudioFormat format = input->device.preferredFormat();
54 if (!format.isValid()) {
55 emit streamInitializationError(
56 QMediaRecorder::FormatError,
57 QLatin1StringView(
"Audio device has invalid preferred format"));
62 connectEncoderToSource(audioEncoder, input);
65void RecordingEngine::addAudioBufferInput(QPlatformAudioBufferInput *input,
66 const QAudioBuffer &firstBuffer)
69 Q_ASSERT(m_state == State::FormatsInitializing);
71 const QAudioFormat format = firstBuffer.isValid() ? firstBuffer.format() : input->audioFormat();
76 if (firstBuffer.isValid())
77 audioEncoder->addBuffer(firstBuffer);
79 connectEncoderToSource(audioEncoder, input);
84 Q_ASSERT(format.isValid());
86 auto audioEncoder =
new AudioEncoder(*
this, format, m_settings);
88 m_audioEncoders.emplace_back(audioEncoder);
89 connect(audioEncoder, &EncoderThread::endOfSourceStream,
this,
91 connect(audioEncoder, &EncoderThread::initialized,
this,
92 &
RecordingEngine::handleEncoderInitialization, Qt::SingleShotConnection);
94 audioEncoder->setAutoStop(
true);
99void RecordingEngine::addVideoSource(QPlatformVideoSource *source,
const QVideoFrame &firstFrame)
101 Q_ASSERT(m_state == State::FormatsInitializing);
103 QVideoFrameFormat frameFormat =
104 firstFrame.isValid() ? firstFrame.surfaceFormat() : source->frameFormat();
106 Q_ASSERT(frameFormat.isValid());
108 std::optional<AVPixelFormat> hwPixelFormat = source->ffmpegHWPixelFormat()
109 ? AVPixelFormat(*source->ffmpegHWPixelFormat())
110 : std::optional<AVPixelFormat>{};
112 qCDebug(qLcFFmpegEncoder) <<
"adding video source" << source->metaObject()->className() <<
":"
113 <<
"pixelFormat=" << frameFormat.pixelFormat()
114 <<
"frameSize=" << frameFormat.frameSize()
115 <<
"frameRate=" << frameFormat.streamFrameRate()
116 <<
"ffmpegHWPixelFormat=" << (hwPixelFormat ? *hwPixelFormat : AV_PIX_FMT_NONE);
118 auto videoEncoder =
new VideoEncoder(*
this, m_settings, frameFormat, hwPixelFormat);
119 m_videoEncoders.emplace_back(videoEncoder);
121 videoEncoder->setAutoStop(
true);
123 connect(videoEncoder, &EncoderThread::endOfSourceStream,
this,
126 connect(videoEncoder, &EncoderThread::initialized,
this,
127 &
RecordingEngine::handleEncoderInitialization, Qt::SingleShotConnection);
130 if (firstFrame.isValid())
131 videoEncoder->addFrame(firstFrame);
133 connectEncoderToSource(videoEncoder, source);
138 Q_ASSERT(m_state == State::FormatsInitializing);
139 Q_ASSERT(m_formatsInitializer);
140 m_formatsInitializer.reset();
142 if (m_audioEncoders.empty() && m_videoEncoders.empty()) {
143 emit sessionError(QMediaRecorder::ResourceError,
144 QLatin1StringView(
"No valid stream found for encoding"));
148 m_state = State::EncodersInitializing;
158 const std::vector<QPlatformVideoSource *> &videoSources)
160 Q_ASSERT(m_state == State::None);
162 m_state = State::FormatsInitializing;
163 m_formatsInitializer = std::make_unique<EncodingInitializer>(*
this);
164 return m_formatsInitializer->start(audioSources, videoSources);
169 : m_recordingEngine(recordingEngine), m_writeTrailer(writeTrailer)
171 setObjectName(QStringLiteral(
"EncodingFinalizer"));
172 Q_ASSERT(m_recordingEngine.m_state == State::Finalizing);
173 connect(
this, &QThread::finished,
this, &QObject::deleteLater);
178 Q_ASSERT(m_recordingEngine.m_state == State::Finalizing);
180 m_recordingEngine.stopAndDeleteThreads();
182 if (m_writeTrailer) {
183 const int res = av_write_trailer(m_recordingEngine.avFormatContext());
185 qCWarning(qLcFFmpegEncoder) <<
"could not write trailer" << res << AVError(res);
186 emit m_recordingEngine.sessionError(QMediaRecorder::FormatError,
187 QLatin1String(
"Cannot write trailer: ")
194 m_recordingEngine.m_formatContext->closeAVIO();
196 qCDebug(qLcFFmpegEncoder) <<
"Media recording finalized";
197 emit m_recordingEngine.finalizationDone();
198 auto recordingEnginePtr = &m_recordingEngine;
199 delete recordingEnginePtr;
204 qCDebug(qLcFFmpegEncoder) <<
"Media recording finalizing";
206 Q_ASSERT(m_state == State::FormatsInitializing || m_state == State::EncodersInitializing
207 || m_state == State::Encoding);
209 Q_ASSERT((m_state == State::FormatsInitializing) == !!m_formatsInitializer);
211 m_formatsInitializer.reset();
214 if (m_state != State::Encoding)
215 forEachEncoder(&EncoderThread::startEncoding,
false);
217 const bool shouldWriteTrailer = m_state == State::Encoding;
218 m_state = State::Finalizing;
220 EncodingFinalizer *finalizer =
new EncodingFinalizer(*
this, shouldWriteTrailer);
226 forEachEncoder(&EncoderThread::setPaused, paused);
231 m_autoStop = autoStop;
232 forEachEncoder(&EncoderThread::setAutoStop, autoStop);
233 handleSourceEndOfStream();
238 m_metaData = metaData;
243 QMutexLocker locker(&m_timeMutex);
244 if (time > m_timeRecorded) {
245 m_timeRecorded = time;
246 emit durationChanged(time);
252 return allOfEncoders(&EncoderThread::isEndOfSourceStream);
263 Q_ASSERT(m_state == State::EncodersInitializing || m_state == State::Finalizing);
265 if (m_state == State::Finalizing)
268 ++m_initializedEncodersCount;
270 Q_ASSERT(m_initializedEncodersCount <= encodersCount());
272 if (m_initializedEncodersCount < encodersCount())
275 Q_ASSERT(allOfEncoders(&EncoderThread::isInitialized));
277 qCDebug(qLcFFmpegEncoder) <<
"Encoders initialized; writing header";
279 avFormatContext()->metadata = QFFmpegMetaData::toAVMetaData(m_metaData);
281 const int res = avformat_write_header(avFormatContext(),
nullptr);
283 qWarning() <<
"could not write header, error:" << res << AVError(res);
284 emit sessionError(QMediaRecorder::ResourceError,
285 QLatin1StringView(
"Cannot start writing the stream"));
289 qCDebug(qLcFFmpegEncoder) <<
"Stream header is successfully written";
291 m_state = State::Encoding;
293 forEachEncoder(&EncoderThread::startEncoding,
true);
298 m_audioEncoders.clear();
299 m_videoEncoders.clear();
303template <
typename F,
typename... Args>
306 for (
auto &audioEncoder : m_audioEncoders)
307 std::invoke(f, audioEncoder.get(), args...);
308 for (
auto &videoEncoder : m_videoEncoders)
309 std::invoke(f, videoEncoder.get(), args...);
315 auto predicate = [&f](
const auto &encoder) {
return std::invoke(f, encoder.get()); };
317 return ranges::all_of(m_audioEncoders, predicate)
318 && ranges::all_of(m_videoEncoders, predicate);
324#include "moc_qffmpegrecordingengine_p.cpp"
bool isEndOfSourceStreams() const
bool initialize(const std::vector< QAudioBufferSource * > &audioSources, const std::vector< QPlatformVideoSource * > &videoSources)
~RecordingEngine() override
void setAutoStop(bool autoStop)
QT_MANGLE_NAMESPACE(QMacScreenCaptureStreamDelegate) QMacScreenCaptureStreamDelegate
void disconnectEncoderFromSource(EncoderThread *encoder)
Combined button and popup list for selecting options.
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)