Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qffmpegrecordingengine.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 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
7#include "private/qmultimediautils_p.h"
8
9#include <qdebug.h>
11#include "qffmpegaudioinput_p.h"
12#include <private/qplatformcamera_p.h>
16#include "qffmpegmuxer_p.h"
17#include <qloggingcategory.h>
18
20
21static Q_LOGGING_CATEGORY(qLcFFmpegEncoder, "qt.multimedia.ffmpeg.encoder");
22
23namespace QFFmpeg
24{
25
27 std::unique_ptr<EncodingFormatContext> context)
28 : m_settings(settings), m_formatContext(std::move(context)), m_muxer(new Muxer(this))
29{
30 Q_ASSERT(m_formatContext);
31 Q_ASSERT(m_formatContext->isAVIOOpen());
32}
33
37
38void RecordingEngine::addAudioInput(QFFmpegAudioInput *input)
39{
41
42 if (input->device.isNull()) {
44 QLatin1StringView("Audio device is null"));
45 return;
46 }
47
48 if (!input->device.preferredFormat().isValid()) {
51 QLatin1StringView("Audio device has invalid preferred format"));
52 return;
53 }
54
55 auto audioEncoder = new AudioEncoder(*this, input, m_settings);
56 m_audioEncoders.push_back(audioEncoder);
57 addMediaFrameHandler(input, &QFFmpegAudioInput::newAudioBuffer, audioEncoder,
59 input->setRunning(true);
60}
61
62void RecordingEngine::addVideoSource(QPlatformVideoSource *source, const QVideoFrame &firstFrame)
63{
64 QVideoFrameFormat frameFormat =
65 firstFrame.isValid() ? firstFrame.surfaceFormat() : source->frameFormat();
66
67 Q_ASSERT(frameFormat.isValid());
68
69 std::optional<AVPixelFormat> hwPixelFormat = source->ffmpegHWPixelFormat()
70 ? AVPixelFormat(*source->ffmpegHWPixelFormat())
71 : std::optional<AVPixelFormat>{};
72
73 qCDebug(qLcFFmpegEncoder) << "adding video source" << source->metaObject()->className() << ":"
74 << "pixelFormat=" << frameFormat.pixelFormat()
75 << "frameSize=" << frameFormat.frameSize()
76 << "frameRate=" << frameFormat.streamFrameRate()
77 << "ffmpegHWPixelFormat=" << (hwPixelFormat ? *hwPixelFormat : AV_PIX_FMT_NONE);
78
79 auto veUPtr = std::make_unique<VideoEncoder>(*this, m_settings, frameFormat, hwPixelFormat);
80 if (!veUPtr->isValid()) {
82 QLatin1StringView("Cannot initialize encoder"));
83 return;
84 }
85
86 auto ve = veUPtr.release();
88 m_videoEncoders.append(ve);
89
90 if (firstFrame.isValid())
91 ve->addFrame(firstFrame);
92}
93
94void RecordingEngine::start()
95{
96 Q_ASSERT(m_initializer);
97 m_initializer.reset();
98
99 if (m_audioEncoders.empty() && m_videoEncoders.empty()) {
101 QLatin1StringView("No valid stream found for encoding"));
102 return;
103 }
104
105 qCDebug(qLcFFmpegEncoder) << "RecordingEngine::start!";
106
107 avFormatContext()->metadata = QFFmpegMetaData::toAVMetaData(m_metaData);
108
109 Q_ASSERT(!m_isHeaderWritten);
110
111 int res = avformat_write_header(avFormatContext(), nullptr);
112 if (res < 0) {
113 qWarning() << "could not write header, error:" << res << err2str(res);
115 QLatin1StringView("Cannot start writing the stream"));
116 return;
117 }
118
119 m_isHeaderWritten = true;
120
121 qCDebug(qLcFFmpegEncoder) << "stream header is successfully written";
122
123 m_muxer->start();
124
125 forEachEncoder([](QThread *thread) { thread->start(); });
126}
127
129 const std::vector<QPlatformVideoSource *> &videoSources)
130{
131 qCDebug(qLcFFmpegEncoder) << ">>>>>>>>>>>>>>> initialize";
132
133 m_initializer = std::make_unique<EncodingInitializer>(*this);
134 m_initializer->start(audioInput, videoSources);
135}
136
137RecordingEngine::EncodingFinalizer::EncodingFinalizer(RecordingEngine &recordingEngine)
138 : m_recordingEngine(recordingEngine)
139{
141}
142
143void RecordingEngine::EncodingFinalizer::run()
144{
145 m_recordingEngine.forEachEncoder(&EncoderThread::stopAndDelete);
146
147 if (m_recordingEngine.m_isHeaderWritten) {
148 const int res = av_write_trailer(m_recordingEngine.avFormatContext());
149 if (res < 0) {
150 const auto errorDescription = err2str(res);
151 qCWarning(qLcFFmpegEncoder) << "could not write trailer" << res << errorDescription;
153 QLatin1String("Cannot write trailer: ")
154 + errorDescription);
155 }
156 }
157 // else ffmpeg might crash
158
159 // close AVIO before emitting finalizationDone.
160 m_recordingEngine.m_formatContext->closeAVIO();
161
162 qCDebug(qLcFFmpegEncoder) << " done finalizing.";
163 emit m_recordingEngine.finalizationDone();
164 auto recordingEnginePtr = &m_recordingEngine;
165 delete recordingEnginePtr;
166}
167
169{
170 qCDebug(qLcFFmpegEncoder) << ">>>>>>>>>>>>>>> finalize";
171
172 m_initializer.reset();
173
174 for (auto &conn : m_connections)
175 disconnect(conn);
176
177 auto *finalizer = new EncodingFinalizer(*this);
178 finalizer->start();
179}
180
182{
183 forEachEncoder(&EncoderThread::setPaused, paused);
184}
185
187{
188 m_metaData = metaData;
189}
190
192{
193 QMutexLocker locker(&m_timeMutex);
194 if (time > m_timeRecorded) {
195 m_timeRecorded = time;
197 }
198}
199
200template<typename... Args>
201void RecordingEngine::addMediaFrameHandler(Args &&...args)
202{
203 auto connection = connect(std::forward<Args>(args)..., Qt::DirectConnection);
204 m_connections.append(connection);
205}
206
207template <typename F, typename... Args>
208void RecordingEngine::forEachEncoder(F &&f, Args &&...args)
209{
210 for (AudioEncoder *audioEncoder : m_audioEncoders)
211 std::invoke(f, audioEncoder, args...);
212 for (VideoEncoder *videoEncoder : m_videoEncoders)
213 std::invoke(f, videoEncoder, args...);
214}
215}
216
218
219#include "moc_qffmpegrecordingengine_p.cpp"
void newAudioBuffer(const QAudioBuffer &buffer)
static AVDictionary * toAVMetaData(const QMediaMetaData &metaData)
void addBuffer(const QAudioBuffer &buffer)
void stopAndDelete()
Stops the thread and deletes this object.
virtual void setPaused(bool paused)
RecordingEngine(const QMediaEncoderSettings &settings, std::unique_ptr< EncodingFormatContext > context)
void initialize(QFFmpegAudioInput *audioInput, const std::vector< QPlatformVideoSource * > &videoSources)
void setMetaData(const QMediaMetaData &metaData)
void sessionError(QMediaRecorder::Error code, const QString &description)
void durationChanged(qint64 duration)
void streamInitializationError(QMediaRecorder::Error code, const QString &description)
void addFrame(const QVideoFrame &frame)
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtMultimedia
\inmodule QtCore
Definition qmutex.h:313
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1598
void deleteLater()
\threadsafe
Definition qobject.cpp:2435
void newVideoFrame(const QVideoFrame &)
void start(Priority=InheritPriority)
Definition qthread.cpp:996
void finished(QPrivateSignal)
The QVideoFrameFormat class specifies the stream format of a video presentation surface.
bool isValid() const
Identifies if a video surface format has a valid pixel format and frame size.
qreal streamFrameRate() const
Returns the frame rate of a video stream in frames per second.
QVideoFrameFormat::PixelFormat pixelFormat() const
Returns the pixel format of frames in a video stream.
QSize frameSize() const
Returns the dimensions of frames in a video stream.
The QVideoFrame class represents a frame of video data.
Definition qvideoframe.h:27
QVideoFrameFormat surfaceFormat() const
Returns the surface format of this video frame.
bool isValid() const
Identifies whether a video frame is valid.
#define this
Definition dialogs.cpp:9
QString err2str(int errnum)
Definition qffmpeg_p.h:64
Combined button and popup list for selecting options.
@ DirectConnection
static void * context
DBusConnection * connection
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
GLfloat GLfloat f
GLsizei GLsizei GLchar * source
GLuint res
GLenum GLenum GLenum input
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define emit
long long qint64
Definition qtypes.h:60
QSettings settings("MySoft", "Star Runner")
[0]
myObject disconnect()
[26]
QJSValueList args