1 /******************************************************************************
2 QtAV: Multimedia framework based on Qt and FFmpeg
3 Copyright (C) 2012-2019 Wang Bin <wbsecg1@gmail.com>
4
5 * This file is part of QtAV
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 ******************************************************************************/
21
22 #include "QtAV/QtAV_Global.h"
23 #include <QtCore/QLibraryInfo>
24 #include <QtCore/QObject>
25 #include <QtCore/QRegExp>
26 #include "QtAV/version.h"
27 #include "QtAV/private/AVCompat.h"
28 #include "utils/internal.h"
29 #include "utils/Logger.h"
30
QtAV_Version()31 unsigned QtAV_Version()
32 {
33 return QTAV_VERSION;
34 }
35
QtAV_Version_String()36 QString QtAV_Version_String()
37 {
38 // vs<2015: C2308: concatenating mismatched strings for QStringLiteral("a" "b")
39 return QString::fromLatin1(QTAV_VERSION_STR);
40 }
41
42 #define QTAV_VERSION_STR_LONG QTAV_VERSION_STR "(" __DATE__ ", " __TIME__ ")"
43
QtAV_Version_String_Long()44 QString QtAV_Version_String_Long()
45 {
46 return QString::fromLatin1(QTAV_VERSION_STR_LONG);
47 }
48
49 namespace QtAV {
50
51 namespace Internal {
52 // disable logging for release. you can manually enable it.
53 #if defined(QT_NO_DEBUG)// && !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) && !defined(Q_OS_WINRT)
54 static QtAV::LogLevel gLogLevel = QtAV::LogOff;
55 #else
56 static QtAV::LogLevel gLogLevel = QtAV::LogAll;
57 #endif
58 static bool gLogLevelSet = false;
isLogLevelSet()59 bool isLogLevelSet() { return gLogLevelSet;}
60 static int gAVLogLevel = AV_LOG_INFO;
61 } //namespace Internal
62
63 //TODO: auto add new depend libraries information
aboutFFmpeg_PlainText()64 QString aboutFFmpeg_PlainText()
65 {
66 return aboutFFmpeg_HTML().remove(QRegExp(QStringLiteral("<[^>]*>")));
67 }
68
69 namespace Internal {
70 typedef struct depend_component {
71 const char* lib;
72 unsigned build_version;
73 unsigned rt_version;
74 const char *config;
75 const char *license;
76 } depend_component;
77
get_qt_version()78 static unsigned get_qt_version() {
79 int major = 0, minor = 0, patch = 0;
80 if (sscanf(qVersion(), "%d.%d.%d", &major, &minor, &patch) != 3)
81 qWarning("Can not recognize Qt runtime version");
82 return QT_VERSION_CHECK(major, minor, patch);
83 }
84
get_depend_component(const depend_component * info=0)85 static const depend_component* get_depend_component(const depend_component* info = 0)
86 {
87 // DO NOT use QStringLiteral here because the install script use strings to search "Qt-" in the library. QStringLiteral will place it in .ro and strings can not find it
88 static const QByteArray qt_license(QLibraryInfo::licensee().prepend(QLatin1String("Qt-" QT_VERSION_STR " licensee: ")).toUtf8());
89 #if QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)
90 static const char* qt_build_info = get_qt_version() >= QT_VERSION_CHECK(5, 3, 0) ? QLibraryInfo::build() : "";
91 #else
92 static const char* qt_build_info = "";
93 #endif
94 static const depend_component components[] = {
95 { "Qt", QT_VERSION, get_qt_version(), qt_build_info, qt_license.constData() },
96 //TODO: auto check loaded libraries
97 #define FF_COMPONENT(name, NAME) #name, LIB##NAME##_VERSION_INT, name##_version(), name##_configuration(), name##_license()
98 { FF_COMPONENT(avutil, AVUTIL) },
99 { FF_COMPONENT(avcodec, AVCODEC) },
100 { FF_COMPONENT(avformat, AVFORMAT) },
101 #if QTAV_HAVE(AVFILTER)
102 { FF_COMPONENT(avfilter, AVFILTER) },
103 #endif //QTAV_HAVE(AVFILTER)
104 #if QTAV_HAVE(AVDEVICE)
105 { FF_COMPONENT(avdevice, AVDEVICE) },
106 #endif //QTAV_HAVE(AVDEVICE)
107 #if QTAV_HAVE(AVRESAMPLE)
108 { FF_COMPONENT(avresample, AVRESAMPLE) },
109 #endif //QTAV_HAVE(AVRESAMPLE)
110 #if QTAV_HAVE(SWRESAMPLE)
111 { FF_COMPONENT(swresample, SWRESAMPLE) },
112 #endif //QTAV_HAVE(SWRESAMPLE)
113 { FF_COMPONENT(swscale, SWSCALE) },
114 #undef FF_COMPONENT
115 { 0, 0, 0, 0, 0 }
116 };
117 if (!info)
118 return &components[0];
119 // invalid input ptr
120 if (((ptrdiff_t)info - (ptrdiff_t)(&components[0]))%sizeof(depend_component))
121 return 0;
122 const depend_component *next = info;
123 next++;
124 if (!next->lib)
125 return 0;
126 return next;
127 }
128
print_library_info()129 void print_library_info()
130 {
131 qDebug() << aboutQtAV_PlainText().toUtf8().constData();
132 const depend_component* info = Internal::get_depend_component(0);
133 while (info) {
134 if (!qstrcmp(info->lib, "avutil"))
135 qDebug("FFmpeg/Libav configuration: %s", info->config);
136 qDebug("Build with %s-%u.%u.%u"
137 , info->lib
138 , QTAV_VERSION_MAJOR(info->build_version)
139 , QTAV_VERSION_MINOR(info->build_version)
140 , QTAV_VERSION_PATCH(info->build_version)
141 );
142 unsigned rt_version = info->rt_version;
143 if (info->build_version != rt_version) {
144 qWarning("Warning: %s runtime version %u.%u.%u mismatch!"
145 , info->lib
146 , QTAV_VERSION_MAJOR(rt_version)
147 , QTAV_VERSION_MINOR(rt_version)
148 , QTAV_VERSION_PATCH(rt_version)
149 );
150 }
151 info = Internal::get_depend_component(info);
152 }
153 }
154
155 } //namespace Internal
156
aboutFFmpeg_HTML()157 QString aboutFFmpeg_HTML()
158 {
159 QString text = QStringLiteral("<h3>FFmpeg/Libav</h3>\n");
160 const Internal::depend_component* info = Internal::get_depend_component(0);
161 while (info) {
162 text += QStringLiteral("<h4>%1: %2-%3.%4.%5</h4>\n")
163 .arg(QObject::tr("Build version"))
164 .arg(QLatin1String(info->lib))
165 .arg(QTAV_VERSION_MAJOR(info->build_version))
166 .arg(QTAV_VERSION_MINOR(info->build_version))
167 .arg(QTAV_VERSION_PATCH(info->build_version))
168 ;
169 unsigned rt_version = info->rt_version;
170 if (info->build_version != rt_version) {
171 text += QStringLiteral("<h4 style='color:#ff0000;'>%1: %2.%3.%4</h4>\n")
172 .arg(QObject::tr("Runtime version"))
173 .arg(QTAV_VERSION_MAJOR(rt_version))
174 .arg(QTAV_VERSION_MINOR(rt_version))
175 .arg(QTAV_VERSION_PATCH(rt_version))
176 ;
177 }
178 text += QStringLiteral("<p>%1</p>\n<p>%2</p>\n").arg(QString::fromUtf8(info->config)).arg(QString::fromUtf8(info->license));
179 info = Internal::get_depend_component(info);
180 }
181 return text;
182 }
183
aboutQtAV_PlainText()184 QString aboutQtAV_PlainText()
185 {
186 return aboutQtAV_HTML().remove(QRegExp(QStringLiteral("<[^>]*>")));
187 }
188
aboutQtAV_HTML()189 QString aboutQtAV_HTML()
190 {
191 static QString about = QString::fromLatin1("<img src='qrc:/QtAV.svg'><h3>QtAV " QTAV_VERSION_STR_LONG "</h3>\n"
192 "<p>%1</p><p>%2</p><p>%3 </p>"
193 "<p>Copyright (C) 2012-2019 Wang Bin (aka. Lucas Wang) <a href='mailto:wbsecg1@gmail.com'>wbsecg1@gmail.com</a></p>\n"
194 "<p>%4: <a href='http://qtav.org/donate.html'>http://qtav.org/donate.html</a></p>\n"
195 "<p>%5: <a href='https://github.com/wang-bin/QtAV'>https://github.com/wang-bin/QtAV</a></p>\n"
196 "<p>%6: <a href='http://qtav.org'>http://qtav.org</a></p>"
197 ).arg(QObject::tr("Multimedia framework base on Qt and FFmpeg.\n"))
198 .arg(QObject::tr("Distributed under the terms of LGPLv2.1 or later.\n"))
199 .arg(QObject::tr("Shanghai, China"))
200 .arg(QObject::tr("Donate"))
201 .arg(QObject::tr("Source"))
202 .arg(QObject::tr("Home page"));
203 return about;
204 }
205
setLogLevel(LogLevel value)206 void setLogLevel(LogLevel value)
207 {
208 Internal::gLogLevelSet = true;
209 Internal::gLogLevel = value;
210 }
211
logLevel()212 LogLevel logLevel()
213 {
214 return (LogLevel)Internal::gLogLevel;
215 }
216
setFFmpegLogHandler(void (* callback)(void *,int,const char *,va_list))217 void setFFmpegLogHandler(void (*callback)(void *, int, const char *, va_list))
218 {
219 // libav does not check null callback
220 if (!callback)
221 callback = av_log_default_callback;
222 av_log_set_callback(callback);
223 }
224
setFFmpegLogLevel(const QByteArray & level)225 void setFFmpegLogLevel(const QByteArray &level)
226 {
227 if (level.isEmpty())
228 return;
229 bool ok = false;
230 const int value = level.toInt(&ok);
231 if ((ok && value == 0) || level == "off" || level == "quiet")
232 Internal::gAVLogLevel = AV_LOG_QUIET;
233 else if (level == "panic")
234 Internal::gAVLogLevel = AV_LOG_PANIC;
235 else if (level == "fatal")
236 Internal::gAVLogLevel = AV_LOG_FATAL;
237 else if (level == "error")
238 Internal::gAVLogLevel = AV_LOG_ERROR;
239 else if (level.startsWith("warn"))
240 Internal::gAVLogLevel = AV_LOG_WARNING;
241 else if (level == "info")
242 Internal::gAVLogLevel = AV_LOG_INFO;
243 else if (level == "verbose")
244 Internal::gAVLogLevel = AV_LOG_VERBOSE;
245 else if (level == "debug")
246 Internal::gAVLogLevel = AV_LOG_DEBUG;
247 #ifdef AV_LOG_TRACE
248 else if (level == "trace")
249 Internal::gAVLogLevel = AV_LOG_TRACE;
250 #endif
251 else
252 Internal::gAVLogLevel = AV_LOG_INFO;
253 av_log_set_level(Internal::gAVLogLevel);
254 }
255
qtav_ffmpeg_log_callback(void * ctx,int level,const char * fmt,va_list vl)256 static void qtav_ffmpeg_log_callback(void* ctx, int level,const char* fmt, va_list vl)
257 {
258 // AV_LOG_DEBUG is used by ffmpeg developers
259 if (level > Internal::gAVLogLevel)
260 return;
261 AVClass *c = ctx ? *(AVClass**)ctx : 0;
262 QString qmsg = QString().sprintf("[FFmpeg:%s] ", c ? c->item_name(ctx) : "?") + QString().vsprintf(fmt, vl);
263 qmsg = qmsg.trimmed();
264 if (level > AV_LOG_WARNING)
265 qDebug() << qPrintable(qmsg);
266 else if (level > AV_LOG_PANIC)
267 qWarning() << qPrintable(qmsg);
268 }
269
avformatOptions()270 QString avformatOptions()
271 {
272 static QString opts;
273 if (!opts.isEmpty())
274 return opts;
275 void* obj = const_cast<void*>(reinterpret_cast<const void*>(avformat_get_class()));
276 opts = Internal::optionsToString((void*)&obj);
277 opts.append(ushort('\n'));
278 #if AVFORMAT_STATIC_REGISTER
279 const AVInputFormat *i = NULL;
280 void* it = NULL;
281 while ((i = av_demuxer_iterate(&it))) {
282 #else
283 AVInputFormat *i = NULL;
284 av_register_all(); // MUST register all input/output formats
285 while ((i = av_iformat_next(i))) {
286 #endif
287 QString opt(Internal::optionsToString((void*)&i->priv_class).trimmed());
288 if (opt.isEmpty())
289 continue;
290 opts.append(QStringLiteral("options for input format %1:\n%2\n\n")
291 .arg(QLatin1String(i->name))
292 .arg(opt));
293 }
294 #if AVFORMAT_STATIC_REGISTER
295 const AVOutputFormat *o = NULL;
296 it = NULL;
297 while ((o = av_muxer_iterate(&it))) {
298 #else
299 av_register_all(); // MUST register all input/output formats
300 AVOutputFormat *o = NULL;
301 while ((o = av_oformat_next(o))) {
302 #endif
303 QString opt(Internal::optionsToString((void*)&o->priv_class).trimmed());
304 if (opt.isEmpty())
305 continue;
306 opts.append(QStringLiteral("options for output format %1:\n%2\n\n")
307 .arg(QLatin1String(o->name))
308 .arg(opt));
309 }
310 return opts;
311 }
312
313 QString avcodecOptions()
314 {
315 static QString opts;
316 if (!opts.isEmpty())
317 return opts;
318 void* obj = const_cast<void*>(reinterpret_cast<const void*>(avcodec_get_class()));
319 opts = Internal::optionsToString((void*)&obj);
320 opts.append(ushort('\n'));
321 const AVCodec* c = NULL;
322 #if AVCODEC_STATIC_REGISTER
323 void* it = NULL;
324 while ((c = av_codec_iterate(&it))) {
325 #else
326 avcodec_register_all();
327 while ((c = av_codec_next(c))) {
328 #endif
329 QString opt(Internal::optionsToString((void*)&c->priv_class).trimmed());
330 if (opt.isEmpty())
331 continue;
332 opts.append(QStringLiteral("Options for codec %1:\n%2\n\n").arg(QLatin1String(c->name)).arg(opt));
333 }
334 return opts;
335 }
336
337 #if 0
338 const QStringList& supportedInputMimeTypes()
339 {
340 static QStringList mimes;
341 if (!mimes.isEmpty())
342 return mimes;
343 av_register_all(); // MUST register all input/output formats
344 AVOutputFormat *i = av_oformat_next(NULL);
345 QStringList list;
346 while (i) {
347 list << QString(i->mime_type).split(QLatin1Char(','), QString::SkipEmptyParts);
348 i = av_oformat_next(i);
349 }
350 foreach (const QString& v, list) {
351 mimes.append(v.trimmed());
352 }
353 mimes.removeDuplicates();
354 return mimes;
355 }
356
357 static QStringList s_audio_mimes, s_video_mimes, s_subtitle_mimes;
358 static void init_supported_codec_info() {
359 const AVCodecDescriptor* cd = avcodec_descriptor_next(NULL);
360 while (cd) {
361 QStringList list;
362 if (cd->mime_types) {
363 for (int i = 0; cd->mime_types[i]; ++i) {
364 list.append(QString(cd->mime_types[i]).trimmed());
365 }
366 }
367 switch (cd->type) {
368 case AVMEDIA_TYPE_AUDIO:
369 s_audio_mimes << list;
370 break;
371 case AVMEDIA_TYPE_VIDEO:
372 s_video_mimes << list;
373 case AVMEDIA_TYPE_SUBTITLE:
374 s_subtitle_mimes << list;
375 default:
376 break;
377 }
378 cd = avcodec_descriptor_next(cd);
379 }
380 s_audio_mimes.removeDuplicates();
381 s_video_mimes.removeDuplicates();
382 s_subtitle_mimes.removeDuplicates();
383 }
384 const QStringList& supportedAudioMimeTypes()
385 {
386 if (s_audio_mimes.isEmpty())
387 init_supported_codec_info();
388 return s_audio_mimes;
389 }
390
391 const QStringList& supportedVideoMimeTypes()
392 {
393 if (s_video_mimes.isEmpty())
394 init_supported_codec_info();
395 return s_video_mimes;
396 }
397 // TODO: subtitleprocessor support
398 const QStringList& supportedSubtitleMimeTypes()
399 {
400 if (s_subtitle_mimes.isEmpty())
401 init_supported_codec_info();
402 return s_subtitle_mimes;
403 }
404 #endif
405
406
407 /*
408 * AVColorSpace:
409 * libav11 libavutil54.3.0 pixfmt.h, ffmpeg2.1*libavutil52.48.101 frame.h
410 * ffmpeg2.5 pixfmt.h. AVFrame.colorspace
411 * earlier versions: avcodec.h, avctx.colorspace
412 */
413 ColorSpace colorSpaceFromFFmpeg(AVColorSpace cs)
414 {
415 switch (cs) {
416 // from ffmpeg: order of coefficients is actually GBR
417 case AVCOL_SPC_RGB: return ColorSpace_GBR;
418 case AVCOL_SPC_BT709: return ColorSpace_BT709;
419 case AVCOL_SPC_BT470BG: return ColorSpace_BT601;
420 case AVCOL_SPC_SMPTE170M: return ColorSpace_BT601;
421 default: return ColorSpace_Unknown;
422 }
423 }
424
425 ColorRange colorRangeFromFFmpeg(AVColorRange cr)
426 {
427 switch (cr) {
428 case AVCOL_RANGE_MPEG: return ColorRange_Limited;
429 case AVCOL_RANGE_JPEG: return ColorRange_Full;
430 default: return ColorRange_Unknown;
431 }
432 }
433
434 namespace
435 {
436 static const struct RegisterMetaTypes {
437 RegisterMetaTypes() {
438 qRegisterMetaType<QtAV::MediaStatus>("QtAV::MediaStatus");
439 }
440 } _registerMetaTypes;
441 }
442
443 // TODO: static link. move all into 1
444 namespace {
445 class InitFFmpegLog {
446 public:
447 InitFFmpegLog() {
448 setFFmpegLogHandler(qtav_ffmpeg_log_callback);
449 QtAV::setFFmpegLogLevel(qgetenv("QTAV_FFMPEG_LOG").toLower());
450 }
451 };
452 InitFFmpegLog fflog;
453 }
454 }
455
456 // Initialize Qt Resource System when the library is built
457 // statically
458 static void initResources() {
459 Q_INIT_RESOURCE(shaders);
460 Q_INIT_RESOURCE(QtAV);
461 }
462
463 namespace {
464 class ResourceLoader {
465 public:
466 ResourceLoader() { initResources(); }
467 };
468
469 ResourceLoader QtAV_QRCLoader;
470 }
471