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
qffmpegmediarecorder.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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
5
6#include <QtCore/qdebug.h>
7#include <QtCore/qloggingcategory.h>
8#include <QtMultimedia/qaudiobuffer.h>
9#include <QtMultimedia/qaudiodevice.h>
10#include <QtMultimedia/qaudiosource.h>
11#include <QtMultimedia/private/qmediastoragelocation_p.h>
12#include <QtMultimedia/private/qplatformcamera_p.h>
13#include <QtMultimedia/private/qplatformsurfacecapture_p.h>
14
15#include "recordingengine/qffmpegrecordingengine_p.h"
17
18Q_STATIC_LOGGING_CATEGORY(qLcMediaEncoder, "qt.multimedia.ffmpeg.encoder");
19
20QT_BEGIN_NAMESPACE
21
22using namespace Qt::StringLiterals;
23
24QFFmpegMediaRecorder::QFFmpegMediaRecorder(QMediaRecorder *parent) : QPlatformMediaRecorder(parent)
25{
26}
27
29
31{
32 return true;
33}
34
35void QFFmpegMediaRecorder::handleSessionError(QMediaRecorder::Error code, const QString &description)
36{
37 updateError(code, description);
38 stop();
39}
40
41void QFFmpegMediaRecorder::record(QMediaEncoderSettings &settings)
42{
43 if (!m_session || state() != QMediaRecorder::StoppedState)
44 return;
45
46 auto videoSources = m_session->activeVideoSources();
47 auto audioInputs = m_session->activeAudioInputs();
48 const auto hasVideo = !videoSources.empty();
49 const auto hasAudio = !audioInputs.empty();
50
51 if (!hasVideo && !hasAudio) {
52 updateError(QMediaRecorder::ResourceError, QMediaRecorder::tr("No video or audio input"));
53 return;
54 }
55
56 if (outputDevice() && !outputLocation().isEmpty())
57 qCWarning(qLcMediaEncoder)
58 << "Both outputDevice and outputLocation has been set to QMediaRecorder";
59
60 if (outputDevice() && !outputDevice()->isWritable())
61 qCWarning(qLcMediaEncoder) << "Output device has been set but not it's not writable";
62
63 QString actualLocation;
64 auto formatContext = std::make_unique<QFFmpeg::EncodingFormatContext>(settings.fileFormat());
65
66 bool isAVIOOpen = false;
67 if (outputDevice() && outputDevice()->isWritable()) {
68 isAVIOOpen = formatContext->openAVIO(outputDevice());
69 } else {
70 actualLocation = findActualLocation(settings);
71 isAVIOOpen = formatContext->openAVIO(actualLocation);
72 }
73
74 qCInfo(qLcMediaEncoder).nospace()
75 << "Recording new media with muxer "
76 << formatContext->avFormatContext()->oformat->long_name << " to "
77 << (actualLocation.isNull() ? u"IO device"_s : actualLocation)
78 << " with format: " << settings.fileFormat() << ", " << settings.audioCodec() << ", "
79 << settings.videoCodec();
80
81 if (!isAVIOOpen) {
82 updateError(QMediaRecorder::LocationNotWritable,
83 QMediaRecorder::tr("Cannot open the output location for writing"));
84 return;
85 }
86
87 m_recordingEngine.reset(new RecordingEngine(settings, std::move(formatContext)));
88 m_recordingEngine->setMetaData(m_metaData);
89
90 connect(m_recordingEngine.get(), &QFFmpeg::RecordingEngine::durationChanged, this,
91 &QFFmpegMediaRecorder::newDuration);
92 connect(m_recordingEngine.get(), &QFFmpeg::RecordingEngine::finalizationDone, this,
93 &QFFmpegMediaRecorder::finalizationDone);
94 connect(m_recordingEngine.get(), &QFFmpeg::RecordingEngine::sessionError, this,
95 &QFFmpegMediaRecorder::handleSessionError);
96
98
99 auto handleStreamInitializationError = [this](QMediaRecorder::Error code,
100 const QString &description) {
101 qCWarning(qLcMediaEncoder) << "Stream initialization error:" << description;
102 updateError(code, description);
103 };
104
105 connect(m_recordingEngine.get(), &QFFmpeg::RecordingEngine::streamInitializationError, this,
106 handleStreamInitializationError);
107
108 durationChanged(0);
109 actualLocationChanged(QUrl::fromLocalFile(actualLocation));
110
111 qCDebug(qLcMediaEncoder) << "Starting recording engine";
112 if (m_recordingEngine->initialize(audioInputs, videoSources)) {
113 stateChanged(QMediaRecorder::RecordingState);
114 qCDebug(qLcMediaEncoder) << "Recording engine started";
115 } else {
116 // else an error has been already emitted
117 qCWarning(qLcMediaEncoder) << "Failed to start recording engine";
118 m_recordingEngine->disconnect(this);
119 m_recordingEngine.reset();
120 }
121}
122
124{
125 if (!m_session || state() != QMediaRecorder::RecordingState)
126 return;
127
128 Q_ASSERT(m_recordingEngine);
129 m_recordingEngine->setPaused(true);
130
131 stateChanged(QMediaRecorder::PausedState);
132}
133
135{
136 if (!m_session || state() != QMediaRecorder::PausedState)
137 return;
138
139 Q_ASSERT(m_recordingEngine);
140 m_recordingEngine->setPaused(false);
141
142 stateChanged(QMediaRecorder::RecordingState);
143}
144
146{
147 if (!m_session || state() == QMediaRecorder::StoppedState)
148 return;
149
150 qCDebug(qLcMediaEncoder) << "Stopping media recorder";
151
152 m_recordingEngine.reset();
153}
154
155void QFFmpegMediaRecorder::finalizationDone()
156{
157 stateChanged(QMediaRecorder::StoppedState);
158}
159
160void QFFmpegMediaRecorder::setMetaData(const QMediaMetaData &metaData)
161{
162 if (!m_session)
163 return;
164 m_metaData = metaData;
165}
166
167QMediaMetaData QFFmpegMediaRecorder::metaData() const
168{
169 return m_metaData;
170}
171
173{
174 auto *captureSession = session;
175 if (m_session == captureSession)
176 return;
177
178 if (m_session)
179 stop();
180
181 m_session = captureSession;
182 if (!m_session)
183 return;
184}
185
187{
188 const bool autoStop = mediaRecorder()->autoStop();
189 if (!m_recordingEngine || m_recordingEngine->autoStop() == autoStop)
190 return;
191
192 if (autoStop)
193 connect(m_recordingEngine.get(), &QFFmpeg::RecordingEngine::autoStopped, this,
195 else
196 disconnect(m_recordingEngine.get(), &QFFmpeg::RecordingEngine::autoStopped, this,
198
199 m_recordingEngine->setAutoStop(autoStop);
200}
201
202void QFFmpegMediaRecorder::RecordingEngineDeleter::operator()(
203 RecordingEngine *recordingEngine) const
204{
205 // ### all of the below should be done asynchronous. finalize() should do it's work in a thread
206 // to avoid blocking the UI in case of slow codecs
207 recordingEngine->finalize();
208}
209
210QT_END_NAMESPACE
211
212#include "moc_qffmpegmediarecorder_p.cpp"
void setCaptureSession(QFFmpegMediaCaptureSession *session)
void record(QMediaEncoderSettings &settings) override
bool isLocationWritable(const QUrl &sink) const override
~QFFmpegMediaRecorder() override
QMediaMetaData metaData() const override
void setMetaData(const QMediaMetaData &) override
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)