File indexing completed on 2025-03-16 12:49:35
0001 /* 0002 SPDX-FileCopyrightText: 2012-2014 Vishesh Handa <me@vhanda.in> 0003 0004 Code adapted from Strigi FFmpeg Analyzer - 0005 SPDX-FileCopyrightText: 2010 Evgeny Egorochkin <phreedom.stdin@gmail.com> 0006 SPDX-FileCopyrightText: 2011 Tirtha Chatterjee <tirtha.p.chatterjee@gmail.com> 0007 0008 SPDX-License-Identifier: LGPL-2.1-or-later 0009 */ 0010 0011 0012 #include "ffmpegextractor.h" 0013 #include "kfilemetadata_debug.h" 0014 0015 #include "config-kfilemetadata.h" 0016 0017 #ifdef __cplusplus 0018 #define __STDC_CONSTANT_MACROS 0019 #ifdef _STDINT_H 0020 #undef _STDINT_H 0021 #endif 0022 # include <stdint.h> 0023 #endif 0024 0025 extern "C" { 0026 #include <libavformat/avformat.h> 0027 #include <libavutil/dict.h> 0028 #include <libavcodec/avcodec.h> 0029 } 0030 0031 using namespace KFileMetaData; 0032 0033 FFmpegExtractor::FFmpegExtractor(QObject* parent) 0034 : ExtractorPlugin(parent) 0035 { 0036 } 0037 0038 const QStringList supportedMimeTypes = { 0039 QStringLiteral("video/mp2t"), 0040 QStringLiteral("video/mp4"), 0041 QStringLiteral("video/mpeg"), 0042 QStringLiteral("video/ogg"), 0043 QStringLiteral("video/quicktime"), 0044 QStringLiteral("video/vnd.avi"), 0045 QStringLiteral("video/webm"), 0046 QStringLiteral("video/x-flv"), 0047 QStringLiteral("video/x-matroska"), 0048 QStringLiteral("video/x-ms-asf"), 0049 QStringLiteral("video/x-ms-wmv"), 0050 QStringLiteral("video/x-msvideo"), 0051 }; 0052 0053 QStringList FFmpegExtractor::mimetypes() const 0054 { 0055 return supportedMimeTypes; 0056 } 0057 0058 void FFmpegExtractor::extract(ExtractionResult* result) 0059 { 0060 AVFormatContext* fmt_ctx = nullptr; 0061 0062 #if LIBAVFORMAT_VERSION_MAJOR < 58 0063 av_register_all(); 0064 #endif 0065 0066 QByteArray arr = result->inputUrl().toUtf8(); 0067 0068 fmt_ctx = avformat_alloc_context(); 0069 if (int ret = avformat_open_input(&fmt_ctx, arr.data(), nullptr, nullptr)) { 0070 qCWarning(KFILEMETADATA_LOG) << "avformat_open_input error: " << ret; 0071 return; 0072 } 0073 0074 int ret = avformat_find_stream_info(fmt_ctx, nullptr); 0075 if (ret < 0) { 0076 qCWarning(KFILEMETADATA_LOG) << "avform_find_stream_info error: " << ret; 0077 return; 0078 } 0079 0080 result->addType(Type::Video); 0081 0082 if (result->inputFlags() & ExtractionResult::ExtractMetaData) { 0083 int totalSecs = fmt_ctx->duration / AV_TIME_BASE; 0084 int bitrate = fmt_ctx->bit_rate; 0085 0086 result->add(Property::Duration, totalSecs); 0087 result->add(Property::BitRate, bitrate); 0088 0089 const int index_stream = av_find_default_stream_index(fmt_ctx); 0090 if (index_stream >= 0) { 0091 AVStream* stream = fmt_ctx->streams[index_stream]; 0092 0093 const AVCodecParameters* codec = stream->codecpar; 0094 0095 if (codec->codec_type == AVMEDIA_TYPE_VIDEO) { 0096 result->add(Property::Width, codec->width); 0097 result->add(Property::Height, codec->height); 0098 0099 AVRational avSampleAspectRatio = av_guess_sample_aspect_ratio(fmt_ctx, stream, nullptr); 0100 AVRational avDisplayAspectRatio; 0101 av_reduce(&avDisplayAspectRatio.num, &avDisplayAspectRatio.den, 0102 codec->width * avSampleAspectRatio.num, 0103 codec->height * avSampleAspectRatio.den, 0104 1024*1024); 0105 double displayAspectRatio = avDisplayAspectRatio.num; 0106 if (avDisplayAspectRatio.den) { 0107 displayAspectRatio /= avDisplayAspectRatio.den; 0108 } 0109 if (displayAspectRatio) { 0110 result->add(Property::AspectRatio, displayAspectRatio); 0111 } 0112 0113 AVRational avFrameRate = av_guess_frame_rate(fmt_ctx, stream, nullptr); 0114 double frameRate = avFrameRate.num; 0115 if (avFrameRate.den) { 0116 frameRate /= avFrameRate.den; 0117 } 0118 if (frameRate) { 0119 result->add(Property::FrameRate, frameRate); 0120 } 0121 } 0122 } 0123 0124 AVDictionary* dict = fmt_ctx->metadata; 0125 // In Ogg, the internal comment metadata headers are attached to a single content stream. 0126 // By convention, it's the first logical bitstream occuring. 0127 if (!dict && fmt_ctx->nb_streams > 0) { 0128 dict = fmt_ctx->streams[0]->metadata; 0129 } 0130 0131 AVDictionaryEntry* entry; 0132 0133 entry = av_dict_get(dict, "title", nullptr, 0); 0134 if (entry) { 0135 result->add(Property::Title, QString::fromUtf8(entry->value)); 0136 } 0137 0138 0139 entry = av_dict_get(dict, "author", nullptr, 0); 0140 if (entry) { 0141 result->add(Property::Author, QString::fromUtf8(entry->value)); 0142 } 0143 0144 entry = av_dict_get(dict, "copyright", nullptr, 0); 0145 if (entry) { 0146 result->add(Property::Copyright, QString::fromUtf8(entry->value)); 0147 } 0148 0149 entry = av_dict_get(dict, "comment", nullptr, 0); 0150 if (entry) { 0151 result->add(Property::Comment, QString::fromUtf8(entry->value)); 0152 } 0153 0154 entry = av_dict_get(dict, "album", nullptr, 0); 0155 if (entry) { 0156 result->add(Property::Album, QString::fromUtf8(entry->value)); 0157 } 0158 0159 entry = av_dict_get(dict, "genre", nullptr, 0); 0160 if (entry) { 0161 result->add(Property::Genre, QString::fromUtf8(entry->value)); 0162 } 0163 0164 entry = av_dict_get(dict, "track", nullptr, 0); 0165 if (entry) { 0166 QString value = QString::fromUtf8(entry->value); 0167 0168 bool ok = false; 0169 int track = value.toInt(&ok); 0170 if (ok && track) { 0171 result->add(Property::TrackNumber, track); 0172 } 0173 } 0174 0175 entry = av_dict_get(dict, "year", nullptr, 0); 0176 if (entry) { 0177 int year = QString::fromUtf8(entry->value).toInt(); 0178 result->add(Property::ReleaseYear, year); 0179 } 0180 } 0181 0182 avformat_close_input(&fmt_ctx); 0183 } 0184 0185 #include "moc_ffmpegextractor.cpp"