1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qgstreamerrecordercontrol.h"
41#include "qgstreameraudioencode.h"
42#include "qgstreamervideoencode.h"
43#include "qgstreamermediacontainercontrol.h"
44#include <QtCore/QDebug>
45#include <QtGui/qdesktopservices.h>
46
47QGstreamerRecorderControl::QGstreamerRecorderControl(QGstreamerCaptureSession *session)
48 :QMediaRecorderControl(session),
49 m_session(session),
50 m_state(QMediaRecorder::StoppedState),
51 m_status(QMediaRecorder::UnloadedStatus)
52{
53 connect(asender: m_session, SIGNAL(stateChanged(QGstreamerCaptureSession::State)), SLOT(updateStatus()));
54 connect(asender: m_session, SIGNAL(error(int,QString)), SLOT(handleSessionError(int,QString)));
55 connect(asender: m_session, SIGNAL(durationChanged(qint64)), SIGNAL(durationChanged(qint64)));
56 connect(asender: m_session, SIGNAL(mutedChanged(bool)), SIGNAL(mutedChanged(bool)));
57 connect(asender: m_session, SIGNAL(volumeChanged(qreal)), SIGNAL(volumeChanged(qreal)));
58 m_hasPreviewState = m_session->captureMode() != QGstreamerCaptureSession::Audio;
59}
60
61QGstreamerRecorderControl::~QGstreamerRecorderControl()
62{
63}
64
65QUrl QGstreamerRecorderControl::outputLocation() const
66{
67 return m_session->outputLocation();
68}
69
70bool QGstreamerRecorderControl::setOutputLocation(const QUrl &sink)
71{
72 m_outputLocation = sink;
73 m_session->setOutputLocation(sink);
74 return true;
75}
76
77
78QMediaRecorder::State QGstreamerRecorderControl::state() const
79{
80 return m_state;
81}
82
83QMediaRecorder::Status QGstreamerRecorderControl::status() const
84{
85 static QMediaRecorder::Status statusTable[3][3] = {
86 //Stopped recorder state:
87 { QMediaRecorder::LoadedStatus, QMediaRecorder::FinalizingStatus, QMediaRecorder::FinalizingStatus },
88 //Recording recorder state:
89 { QMediaRecorder::StartingStatus, QMediaRecorder::RecordingStatus, QMediaRecorder::PausedStatus },
90 //Paused recorder state:
91 { QMediaRecorder::StartingStatus, QMediaRecorder::RecordingStatus, QMediaRecorder::PausedStatus }
92 };
93
94 QMediaRecorder::State sessionState = QMediaRecorder::StoppedState;
95
96 switch ( m_session->state() ) {
97 case QGstreamerCaptureSession::RecordingState:
98 sessionState = QMediaRecorder::RecordingState;
99 break;
100 case QGstreamerCaptureSession::PausedState:
101 sessionState = QMediaRecorder::PausedState;
102 break;
103 case QGstreamerCaptureSession::PreviewState:
104 case QGstreamerCaptureSession::StoppedState:
105 sessionState = QMediaRecorder::StoppedState;
106 break;
107 }
108
109 return statusTable[m_state][sessionState];
110}
111
112void QGstreamerRecorderControl::updateStatus()
113{
114 QMediaRecorder::Status newStatus = status();
115 if (m_status != newStatus) {
116 m_status = newStatus;
117 emit statusChanged(status: m_status);
118 // If stop has been called and session state became stopped.
119 if (m_status == QMediaRecorder::LoadedStatus)
120 emit stateChanged(state: m_state);
121 }
122}
123
124void QGstreamerRecorderControl::handleSessionError(int code, const QString &description)
125{
126 emit error(error: code, errorString: description);
127 stop();
128}
129
130qint64 QGstreamerRecorderControl::duration() const
131{
132 return m_session->duration();
133}
134
135void QGstreamerRecorderControl::setState(QMediaRecorder::State state)
136{
137 switch (state) {
138 case QMediaRecorder::StoppedState:
139 stop();
140 break;
141 case QMediaRecorder::PausedState:
142 pause();
143 break;
144 case QMediaRecorder::RecordingState:
145 record();
146 break;
147 }
148}
149
150void QGstreamerRecorderControl::record()
151{
152 if (m_state == QMediaRecorder::RecordingState)
153 return;
154
155 m_state = QMediaRecorder::RecordingState;
156
157 if (m_outputLocation.isEmpty()) {
158 QString container = m_session->mediaContainerControl()->containerExtension();
159 if (container.isEmpty())
160 container = "raw";
161
162 m_session->setOutputLocation(QUrl(generateFileName(dir: defaultDir(), ext: container)));
163 }
164
165 m_session->dumpGraph(fileName: "before-record");
166 if (!m_hasPreviewState || m_session->state() != QGstreamerCaptureSession::StoppedState) {
167 m_session->setState(QGstreamerCaptureSession::RecordingState);
168 } else
169 emit error(error: QMediaRecorder::ResourceError, errorString: tr(s: "Service has not been started"));
170
171 m_session->dumpGraph(fileName: "after-record");
172
173 emit stateChanged(state: m_state);
174 updateStatus();
175
176 emit actualLocationChanged(location: m_session->outputLocation());
177}
178
179void QGstreamerRecorderControl::pause()
180{
181 if (m_state == QMediaRecorder::PausedState)
182 return;
183
184 m_state = QMediaRecorder::PausedState;
185
186 m_session->dumpGraph(fileName: "before-pause");
187 if (!m_hasPreviewState || m_session->state() != QGstreamerCaptureSession::StoppedState) {
188 m_session->setState(QGstreamerCaptureSession::PausedState);
189 } else
190 emit error(error: QMediaRecorder::ResourceError, errorString: tr(s: "Service has not been started"));
191
192 emit stateChanged(state: m_state);
193 updateStatus();
194}
195
196void QGstreamerRecorderControl::stop()
197{
198 if (m_state == QMediaRecorder::StoppedState)
199 return;
200
201 m_state = QMediaRecorder::StoppedState;
202
203 if (!m_hasPreviewState) {
204 m_session->setState(QGstreamerCaptureSession::StoppedState);
205 } else {
206 if (m_session->state() != QGstreamerCaptureSession::StoppedState)
207 m_session->setState(QGstreamerCaptureSession::PreviewState);
208 }
209
210 updateStatus();
211}
212
213void QGstreamerRecorderControl::applySettings()
214{
215 //Check the codecs are compatible with container,
216 //and choose the compatible codecs/container if omitted
217 QGstreamerAudioEncode *audioEncodeControl = m_session->audioEncodeControl();
218 QGstreamerVideoEncode *videoEncodeControl = m_session->videoEncodeControl();
219 QGstreamerMediaContainerControl *mediaContainerControl = m_session->mediaContainerControl();
220
221 bool needAudio = m_session->captureMode() & QGstreamerCaptureSession::Audio;
222 bool needVideo = m_session->captureMode() & QGstreamerCaptureSession::Video;
223
224 QStringList containerCandidates;
225 if (mediaContainerControl->containerFormat().isEmpty())
226 containerCandidates = mediaContainerControl->supportedContainers();
227 else
228 containerCandidates << mediaContainerControl->containerFormat();
229
230
231 QStringList audioCandidates;
232 if (needAudio) {
233 QAudioEncoderSettings audioSettings = audioEncodeControl->audioSettings();
234 if (audioSettings.codec().isEmpty())
235 audioCandidates = audioEncodeControl->supportedAudioCodecs();
236 else
237 audioCandidates << audioSettings.codec();
238 }
239
240 QStringList videoCandidates;
241 if (needVideo) {
242 QVideoEncoderSettings videoSettings = videoEncodeControl->videoSettings();
243 if (videoSettings.codec().isEmpty())
244 videoCandidates = videoEncodeControl->supportedVideoCodecs();
245 else
246 videoCandidates << videoSettings.codec();
247 }
248
249 QString container;
250 QString audioCodec;
251 QString videoCodec;
252
253 for (const QString &containerCandidate : qAsConst(t&: containerCandidates)) {
254 QSet<QString> supportedTypes = mediaContainerControl->supportedStreamTypes(container: containerCandidate);
255
256 audioCodec.clear();
257 videoCodec.clear();
258
259 if (needAudio) {
260 bool found = false;
261 for (const QString &audioCandidate : qAsConst(t&: audioCandidates)) {
262 QSet<QString> audioTypes = audioEncodeControl->supportedStreamTypes(codecName: audioCandidate);
263 if (audioTypes.intersects(other: supportedTypes)) {
264 found = true;
265 audioCodec = audioCandidate;
266 break;
267 }
268 }
269 if (!found)
270 continue;
271 }
272
273 if (needVideo) {
274 bool found = false;
275 for (const QString &videoCandidate : qAsConst(t&: videoCandidates)) {
276 QSet<QString> videoTypes = videoEncodeControl->supportedStreamTypes(codecName: videoCandidate);
277 if (videoTypes.intersects(other: supportedTypes)) {
278 found = true;
279 videoCodec = videoCandidate;
280 break;
281 }
282 }
283 if (!found)
284 continue;
285 }
286
287 container = containerCandidate;
288 break;
289 }
290
291 if (container.isEmpty()) {
292 emit error(error: QMediaRecorder::FormatError, errorString: tr(s: "Not compatible codecs and container format."));
293 } else {
294 mediaContainerControl->setContainerFormat(container);
295
296 if (needAudio) {
297 QAudioEncoderSettings audioSettings = audioEncodeControl->audioSettings();
298 audioSettings.setCodec(audioCodec);
299 audioEncodeControl->setAudioSettings(audioSettings);
300 }
301
302 if (needVideo) {
303 QVideoEncoderSettings videoSettings = videoEncodeControl->videoSettings();
304 videoSettings.setCodec(videoCodec);
305 videoEncodeControl->setVideoSettings(videoSettings);
306 }
307 }
308}
309
310
311bool QGstreamerRecorderControl::isMuted() const
312{
313 return m_session->isMuted();
314}
315
316qreal QGstreamerRecorderControl::volume() const
317{
318 return m_session->volume();
319}
320
321void QGstreamerRecorderControl::setMuted(bool muted)
322{
323 m_session->setMuted(muted);
324}
325
326void QGstreamerRecorderControl::setVolume(qreal volume)
327{
328 m_session->setVolume(volume);
329}
330
331QDir QGstreamerRecorderControl::defaultDir() const
332{
333 QStringList dirCandidates;
334
335 if (m_session->captureMode() & QGstreamerCaptureSession::Video)
336 dirCandidates << QStandardPaths::writableLocation(type: QStandardPaths::MoviesLocation);
337 else
338 dirCandidates << QStandardPaths::writableLocation(type: QStandardPaths::MusicLocation);
339
340 dirCandidates << QDir::home().filePath(fileName: "Documents");
341 dirCandidates << QDir::home().filePath(fileName: "My Documents");
342 dirCandidates << QDir::homePath();
343 dirCandidates << QDir::currentPath();
344 dirCandidates << QDir::tempPath();
345
346 for (const QString &path : qAsConst(t&: dirCandidates)) {
347 QDir dir(path);
348 if (dir.exists() && QFileInfo(path).isWritable())
349 return dir;
350 }
351
352 return QDir();
353}
354
355QString QGstreamerRecorderControl::generateFileName(const QDir &dir, const QString &ext) const
356{
357
358 int lastClip = 0;
359 const auto list = dir.entryList(nameFilters: QStringList() << QString("clip_*.%1").arg(a: ext));
360 for (const QString &fileName : list) {
361 int imgNumber = fileName.midRef(position: 5, n: fileName.size()-6-ext.length()).toInt();
362 lastClip = qMax(a: lastClip, b: imgNumber);
363 }
364
365 QString name = QString("clip_%1.%2").arg(a: lastClip+1,
366 fieldWidth: 4, //fieldWidth
367 base: 10,
368 fillChar: QLatin1Char('0')).arg(a: ext);
369
370 return dir.absoluteFilePath(fileName: name);
371}
372

source code of qtmultimedia/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp