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 <QtMultimedia/private/qtmultimediaglobal_p.h> |
41 | #include "camerabinvideoencoder.h" |
42 | #include "camerabinsession.h" |
43 | #include "camerabincontainer.h" |
44 | #include <private/qgstutils_p.h> |
45 | |
46 | #include <QtCore/qdebug.h> |
47 | |
48 | QT_BEGIN_NAMESPACE |
49 | |
50 | CameraBinVideoEncoder::CameraBinVideoEncoder(CameraBinSession *session) |
51 | :QVideoEncoderSettingsControl(session) |
52 | , m_session(session) |
53 | #if QT_CONFIG(gstreamer_encodingprofiles) |
54 | , m_codecs(QGstCodecsInfo::VideoEncoder) |
55 | #endif |
56 | { |
57 | } |
58 | |
59 | CameraBinVideoEncoder::~CameraBinVideoEncoder() |
60 | { |
61 | } |
62 | |
63 | QList<QSize> CameraBinVideoEncoder::supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous) const |
64 | { |
65 | if (continuous) |
66 | *continuous = false; |
67 | |
68 | QPair<int,int> rate = rateAsRational(settings.frameRate()); |
69 | |
70 | //select the closest supported rational rate to settings.frameRate() |
71 | |
72 | return m_session->supportedResolutions(rate, continuous, mode: QCamera::CaptureVideo); |
73 | } |
74 | |
75 | QList< qreal > CameraBinVideoEncoder::supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous) const |
76 | { |
77 | if (continuous) |
78 | *continuous = false; |
79 | |
80 | QList< qreal > res; |
81 | |
82 | const auto rates = m_session->supportedFrameRates(frameSize: settings.resolution(), continuous); |
83 | for (const auto &rate : rates) { |
84 | if (rate.second > 0) |
85 | res << qreal(rate.first)/rate.second; |
86 | } |
87 | |
88 | return res; |
89 | } |
90 | |
91 | QStringList CameraBinVideoEncoder::supportedVideoCodecs() const |
92 | { |
93 | #if QT_CONFIG(gstreamer_encodingprofiles) |
94 | return m_codecs.supportedCodecs(); |
95 | #else |
96 | return QStringList(); |
97 | #endif |
98 | } |
99 | |
100 | QString CameraBinVideoEncoder::videoCodecDescription(const QString &codecName) const |
101 | { |
102 | #if QT_CONFIG(gstreamer_encodingprofiles) |
103 | return m_codecs.codecDescription(codec: codecName); |
104 | #else |
105 | Q_UNUSED(codecName) |
106 | return QString(); |
107 | #endif |
108 | } |
109 | |
110 | QVideoEncoderSettings CameraBinVideoEncoder::videoSettings() const |
111 | { |
112 | return m_videoSettings; |
113 | } |
114 | |
115 | void CameraBinVideoEncoder::setVideoSettings(const QVideoEncoderSettings &settings) |
116 | { |
117 | if (m_videoSettings != settings) { |
118 | m_actualVideoSettings = settings; |
119 | m_videoSettings = settings; |
120 | emit settingsChanged(); |
121 | } |
122 | } |
123 | |
124 | QVideoEncoderSettings CameraBinVideoEncoder::actualVideoSettings() const |
125 | { |
126 | return m_actualVideoSettings; |
127 | } |
128 | |
129 | void CameraBinVideoEncoder::setActualVideoSettings(const QVideoEncoderSettings &settings) |
130 | { |
131 | m_actualVideoSettings = settings; |
132 | } |
133 | |
134 | void CameraBinVideoEncoder::resetActualSettings() |
135 | { |
136 | m_actualVideoSettings = m_videoSettings; |
137 | } |
138 | |
139 | |
140 | QPair<int,int> CameraBinVideoEncoder::rateAsRational(qreal frameRate) const |
141 | { |
142 | if (frameRate > 0.001) { |
143 | //convert to rational number |
144 | QList<int> denumCandidates; |
145 | denumCandidates << 1 << 2 << 3 << 5 << 10 << 25 << 30 << 50 << 100 << 1001 << 1000; |
146 | |
147 | qreal error = 1.0; |
148 | int num = 1; |
149 | int denum = 1; |
150 | |
151 | for (int curDenum : qAsConst(t&: denumCandidates)) { |
152 | int curNum = qRound(d: frameRate*curDenum); |
153 | qreal curError = qAbs(t: qreal(curNum)/curDenum - frameRate); |
154 | |
155 | if (curError < error) { |
156 | error = curError; |
157 | num = curNum; |
158 | denum = curDenum; |
159 | } |
160 | |
161 | if (curError < 1e-8) |
162 | break; |
163 | } |
164 | |
165 | return QPair<int,int>(num,denum); |
166 | } |
167 | |
168 | return QPair<int,int>(); |
169 | } |
170 | |
171 | #if QT_CONFIG(gstreamer_encodingprofiles) |
172 | |
173 | GstEncodingProfile *CameraBinVideoEncoder::createProfile() |
174 | { |
175 | QString codec = m_actualVideoSettings.codec(); |
176 | GstCaps *caps = !codec.isEmpty() ? gst_caps_from_string(string: codec.toLatin1()) : nullptr; |
177 | |
178 | if (!caps) |
179 | return nullptr; |
180 | |
181 | QString preset = m_actualVideoSettings.encodingOption(QStringLiteral("preset" )).toString(); |
182 | GstEncodingVideoProfile *profile = gst_encoding_video_profile_new( |
183 | format: caps, |
184 | preset: !preset.isEmpty() ? preset.toLatin1().constData() : NULL, //preset |
185 | NULL, //restriction |
186 | presence: 1); //presence |
187 | |
188 | gst_caps_unref(caps); |
189 | |
190 | gst_encoding_video_profile_set_pass(prof: profile, pass: 0); |
191 | gst_encoding_video_profile_set_variableframerate(prof: profile, TRUE); |
192 | |
193 | return (GstEncodingProfile *)profile; |
194 | } |
195 | |
196 | #endif |
197 | |
198 | void CameraBinVideoEncoder::applySettings(GstElement *encoder) |
199 | { |
200 | GObjectClass * const objectClass = G_OBJECT_GET_CLASS(encoder); |
201 | const char * const name = qt_gst_element_get_factory_name(element: encoder); |
202 | |
203 | const int bitRate = m_actualVideoSettings.bitRate(); |
204 | if (bitRate == -1) { |
205 | // Bit rate is invalid, don't evaluate the remaining conditions. |
206 | } else if (g_object_class_find_property(oclass: objectClass, property_name: "bitrate" )) { |
207 | g_object_set(G_OBJECT(encoder), first_property_name: "bitrate" , bitRate, NULL); |
208 | } else if (g_object_class_find_property(oclass: objectClass, property_name: "target-bitrate" )) { |
209 | g_object_set(G_OBJECT(encoder), first_property_name: "target-bitrate" , bitRate, NULL); |
210 | } |
211 | |
212 | if (qstrcmp(str1: name, str2: "theoraenc" ) == 0) { |
213 | static const int qualities[] = { 8, 16, 32, 45, 60 }; |
214 | g_object_set(G_OBJECT(encoder), first_property_name: "quality" , qualities[m_actualVideoSettings.quality()], NULL); |
215 | } else if (qstrncmp(str1: name, str2: "avenc_" , len: 6) == 0) { |
216 | if (g_object_class_find_property(oclass: objectClass, property_name: "pass" )) { |
217 | static const int modes[] = { 0, 2, 512, 1024 }; |
218 | g_object_set(G_OBJECT(encoder), first_property_name: "pass" , modes[m_actualVideoSettings.encodingMode()], NULL); |
219 | } |
220 | if (g_object_class_find_property(oclass: objectClass, property_name: "quantizer" )) { |
221 | static const double qualities[] = { 20, 8.0, 3.0, 2.5, 2.0 }; |
222 | g_object_set(G_OBJECT(encoder), first_property_name: "quantizer" , qualities[m_actualVideoSettings.quality()], NULL); |
223 | } |
224 | } else if (qstrncmp(str1: name, str2: "omx" , len: 3) == 0) { |
225 | if (!g_object_class_find_property(oclass: objectClass, property_name: "control-rate" )) { |
226 | } else switch (m_actualVideoSettings.encodingMode()) { |
227 | case QMultimedia::ConstantBitRateEncoding: |
228 | g_object_set(G_OBJECT(encoder), first_property_name: "control-rate" , 2, NULL); |
229 | break; |
230 | case QMultimedia::AverageBitRateEncoding: |
231 | g_object_set(G_OBJECT(encoder), first_property_name: "control-rate" , 1, NULL); |
232 | break; |
233 | default: |
234 | g_object_set(G_OBJECT(encoder), first_property_name: "control-rate" , 0, NULL); |
235 | } |
236 | } |
237 | } |
238 | |
239 | QT_END_NAMESPACE |
240 | |