File indexing completed on 2024-05-12 15:37:05

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"