Commit 517d88d7 authored by Amaury Bouchra Pilet's avatar Amaury Bouchra Pilet Committed by Nicolas Fella
Browse files

Use embedded cover in MKV (and others) video files

Only MP4 was supported, using TagLib. Now using ffmpeg, which supports MP4, MKV and others.
Relying on Matroska specification's attachment names to select best cover (landscape preferred) if several are available.
parent 9e0e0c0d
Pipeline #220322 passed with stage
in 37 seconds
......@@ -25,7 +25,6 @@ include(FeatureSummary)
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core Gui)
find_package(KF5 ${KF_MIN_VERSION} REQUIRED COMPONENTS KIO I18n Config)
find_package(FFmpeg COMPONENTS AVCODEC AVFORMAT SWSCALE)
find_package(Taglib REQUIRED)
include_directories(
${FFMPEG_INCLUDE_DIRS}
......@@ -57,7 +56,7 @@ ecm_qt_declare_logging_category(ffmpegthumbs
kconfig_add_kcfg_files(ffmpegthumbs ffmpegthumbnailersettings5.kcfgc)
target_link_libraries(ffmpegthumbs Qt::Core Qt::Gui KF5::KIOWidgets KF5::KIOCore KF5::I18n KF5::ConfigCore KF5::ConfigGui ${AVUTIL_LIBRARIES} ${AVFILTER_LIBRARIES} ${AVFORMAT_LIBRARIES} ${AVCODEC_LIBRARIES} ${SWSCALE_LIBRARIES} Taglib::Taglib )
target_link_libraries(ffmpegthumbs Qt::Core Qt::Gui KF5::KIOWidgets KF5::KIOCore KF5::I18n KF5::ConfigCore KF5::ConfigGui ${AVUTIL_LIBRARIES} ${AVFILTER_LIBRARIES} ${AVFORMAT_LIBRARIES} ${AVCODEC_LIBRARIES} ${SWSCALE_LIBRARIES})
install(FILES ffmpegthumbnailersettings5.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR})
install(TARGETS ffmpegthumbs DESTINATION ${KDE_INSTALL_PLUGINDIR})
......
......@@ -11,8 +11,6 @@
#include <limits>
#include <mp4file.h>
#include <QCheckBox>
#include <QFormLayout>
#include <QImage>
......@@ -21,6 +19,8 @@
#include <KLocalizedString>
extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/dict.h>
#include <libavutil/log.h>
}
......@@ -115,23 +115,55 @@ bool FFMpegThumbnailer::create(const QString& path, int width, int /*height*/, Q
// Try reading thumbnail embedded into video file
QByteArray ba = path.toLocal8Bit();
TagLib::MP4::File f(ba.data(), false);
AVFormatContext* ct = avformat_alloc_context();
AVPacket* pic = nullptr;
// No matter the seqIdx, we have to know if the video has an embedded cover, even if we then don't return
// it. We could cache it to avoid repeating this for higher seqIdx values, but this should be fast enough
// to not be noticeable and caching adds unnecessary complexity.
if (f.isValid()) {
TagLib::MP4::Tag* tag = f.tag();
TagLib::MP4::ItemListMap itemsListMap = tag->itemListMap();
TagLib::MP4::Item coverItem = itemsListMap["covr"];
TagLib::MP4::CoverArtList coverArtList = coverItem.toCoverArtList();
if (!coverArtList.isEmpty()) {
TagLib::MP4::CoverArt coverArt = coverArtList.front();
img.loadFromData((const uchar *)coverArt.data().data(),
coverArt.data().size());
if (ct && !avformat_open_input(&ct,ba.data(), nullptr, nullptr)) {
// Using an priority system based on size or filename (matroska specification) to select the most suitable picture
int bestPrio = 0;
for (size_t i = 0; i < ct->nb_streams; ++i) {
if (ct->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC) {
int prio = 0;
AVDictionaryEntry* fname = av_dict_get(ct->streams[i]->metadata, "filename", nullptr ,0);
if (fname) {
QString filename(fname->value);
QString noextname = filename.section('.', 0);
// Prefer landscape and larger
if (noextname == "cover_land") {
prio = std::numeric_limits<int>::max();
}
else if (noextname == "small_cover_land") {
prio = std::numeric_limits<int>::max()-1;
}
else if (noextname == "cover") {
prio = std::numeric_limits<int>::max()-2;
}
else if (noextname == "small_cover") {
prio = std::numeric_limits<int>::max()-3;
}
else {
prio = ct->streams[i]->attached_pic.size;
}
}
else {
prio = ct->streams[i]->attached_pic.size;
}
if (prio > bestPrio) {
pic = &(ct->streams[i]->attached_pic);
bestPrio = prio;
}
}
}
}
if (pic) {
img.loadFromData(pic->data, pic->size);
}
avformat_close_input(&ct);
if (!img.isNull()) {
// Video file has an embedded thumbnail -> return it for seqIdx=0 and shift the regular
......
......@@ -21,7 +21,7 @@ kconfig_add_kcfg_files(ffmpegthumbtest ${CMAKE_CURRENT_SOURCE_DIR}/../ffmpegthum
target_include_directories(ffmpegthumbtest PRIVATE ..)
target_link_libraries(ffmpegthumbtest Qt::Core Qt::Gui KF5::KIOWidgets KF5::KIOCore KF5::I18n KF5::ConfigCore KF5::ConfigGui ${AVUTIL_LIBRARIES} ${AVFILTER_LIBRARIES} ${AVFORMAT_LIBRARIES} ${AVCODEC_LIBRARIES} ${SWSCALE_LIBRARIES} Taglib::Taglib)
target_link_libraries(ffmpegthumbtest Qt::Core Qt::Gui KF5::KIOWidgets KF5::KIOCore KF5::I18n KF5::ConfigCore KF5::ConfigGui ${AVUTIL_LIBRARIES} ${AVFILTER_LIBRARIES} ${AVFORMAT_LIBRARIES} ${AVCODEC_LIBRARIES} ${SWSCALE_LIBRARIES})
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment