Added a fallback to ffmpeg parser in case of audio properties are not properly decoded, ref #781
This commit is contained in:
@@ -20,21 +20,22 @@
|
||||
#include "AudioFile.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <unordered_map>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/error.h>
|
||||
#include <libavutil/log.h>
|
||||
}
|
||||
|
||||
#include "core/ILogger.hpp"
|
||||
#include "core/String.hpp"
|
||||
|
||||
#include "audio/AudioTypes.hpp"
|
||||
#include "audio/IAudioFileInfo.hpp"
|
||||
#include "audio/Exception.hpp"
|
||||
|
||||
namespace lms::audio::ffmpeg
|
||||
{
|
||||
@@ -50,25 +51,23 @@ namespace lms::audio::ffmpeg
|
||||
return "Unknown error";
|
||||
}
|
||||
|
||||
class AudioFileException : public AudioFileParsingException
|
||||
class AvException : public Exception
|
||||
{
|
||||
public:
|
||||
AudioFileException(int avError)
|
||||
: AudioFileParsingException{ averror_to_string(avError) }
|
||||
AvException(int avError)
|
||||
: Exception{ averror_to_string(avError) }
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
void getMetaDataFromDictionnary(AVDictionary* dictionnary, AudioFile::MetadataMap& res)
|
||||
void extractMetaDataFromDictionnary(AVDictionary* dictionnary, AudioFile::MetadataMap& res)
|
||||
{
|
||||
if (!dictionnary)
|
||||
return;
|
||||
|
||||
AVDictionaryEntry* tag = NULL;
|
||||
while ((tag = ::av_dict_get(dictionnary, "", tag, AV_DICT_IGNORE_SUFFIX)))
|
||||
{
|
||||
const AVDictionaryEntry* tag{ NULL };
|
||||
while ((tag = av_dict_iterate(dictionnary, tag)))
|
||||
res[core::stringUtils::stringToUpper(tag->key)] = tag->value;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<ContainerType> avdemuxerToContainerType(std::string_view name)
|
||||
@@ -107,58 +106,112 @@ namespace lms::audio::ffmpeg
|
||||
{
|
||||
switch (codec)
|
||||
{
|
||||
case AV_CODEC_ID_MP3:
|
||||
return CodecType::MP3;
|
||||
case AV_CODEC_ID_AAC:
|
||||
return CodecType::AAC;
|
||||
case AV_CODEC_ID_VORBIS:
|
||||
return CodecType::Vorbis;
|
||||
case AV_CODEC_ID_WMAV1:
|
||||
return CodecType::WMA1;
|
||||
case AV_CODEC_ID_WMAV2:
|
||||
return CodecType::WMA2;
|
||||
case AV_CODEC_ID_WMAPRO:
|
||||
return CodecType::WMA9Pro;
|
||||
case AV_CODEC_ID_WMALOSSLESS:
|
||||
return CodecType::WMA9Lossless;
|
||||
case AV_CODEC_ID_FLAC:
|
||||
return CodecType::FLAC;
|
||||
case AV_CODEC_ID_AC3:
|
||||
return CodecType::AC3;
|
||||
case AV_CODEC_ID_ALAC:
|
||||
return CodecType::ALAC;
|
||||
case AV_CODEC_ID_WAVPACK:
|
||||
return CodecType::WavPack;
|
||||
case AV_CODEC_ID_MUSEPACK7:
|
||||
return CodecType::MPC7;
|
||||
case AV_CODEC_ID_MUSEPACK8:
|
||||
return CodecType::MPC8;
|
||||
case AV_CODEC_ID_APE:
|
||||
return CodecType::APE;
|
||||
case AV_CODEC_ID_MP4ALS:
|
||||
return CodecType::MP4ALS;
|
||||
case AV_CODEC_ID_OPUS:
|
||||
return CodecType::Opus;
|
||||
case AV_CODEC_ID_SHORTEN:
|
||||
return CodecType::Shorten;
|
||||
case AV_CODEC_ID_DSD_LSBF:
|
||||
case AV_CODEC_ID_DSD_LSBF_PLANAR:
|
||||
case AV_CODEC_ID_DSD_MSBF:
|
||||
case AV_CODEC_ID_DSD_MSBF_PLANAR:
|
||||
return CodecType::DSD;
|
||||
case AV_CODEC_ID_EAC3:
|
||||
return CodecType::EAC3;
|
||||
case AV_CODEC_ID_FLAC:
|
||||
return CodecType::FLAC;
|
||||
case AV_CODEC_ID_MP3:
|
||||
return CodecType::MP3;
|
||||
case AV_CODEC_ID_MP4ALS:
|
||||
return CodecType::MP4ALS;
|
||||
case AV_CODEC_ID_MUSEPACK7:
|
||||
return CodecType::MPC7;
|
||||
case AV_CODEC_ID_MUSEPACK8:
|
||||
return CodecType::MPC8;
|
||||
case AV_CODEC_ID_OPUS:
|
||||
return CodecType::Opus;
|
||||
case AV_CODEC_ID_SHORTEN:
|
||||
return CodecType::Shorten;
|
||||
case AV_CODEC_ID_VORBIS:
|
||||
return CodecType::Vorbis;
|
||||
case AV_CODEC_ID_WAVPACK:
|
||||
return CodecType::WavPack;
|
||||
case AV_CODEC_ID_WMALOSSLESS:
|
||||
return CodecType::WMA9Lossless;
|
||||
case AV_CODEC_ID_WMAPRO:
|
||||
return CodecType::WMA9Pro;
|
||||
case AV_CODEC_ID_WMAV1:
|
||||
return CodecType::WMA1;
|
||||
case AV_CODEC_ID_WMAV2:
|
||||
return CodecType::WMA2;
|
||||
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
core::LiteralString avLogLevelToStr(int level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case AV_LOG_TRACE:
|
||||
return "trace";
|
||||
case AV_LOG_DEBUG:
|
||||
return "debug";
|
||||
case AV_LOG_VERBOSE:
|
||||
return "verbose";
|
||||
case AV_LOG_INFO:
|
||||
return "info";
|
||||
case AV_LOG_WARNING:
|
||||
return "warning";
|
||||
case AV_LOG_ERROR:
|
||||
return "error";
|
||||
case AV_LOG_FATAL:
|
||||
return "fatal";
|
||||
case AV_LOG_PANIC:
|
||||
return "panic";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void avLogCallback(void*, int level, const char* fmt, va_list vl)
|
||||
{
|
||||
if (!core::Service<core::logging::ILogger>::get()->isSeverityActive(core::logging::Severity::DEBUG))
|
||||
return;
|
||||
|
||||
if (level > AV_LOG_WARNING)
|
||||
return;
|
||||
|
||||
std::array<char, 256> buffer{ 0 };
|
||||
std::vsnprintf(buffer.data(), buffer.size(), fmt, vl);
|
||||
|
||||
LMS_LOG(AUDIO, DEBUG, "ffmpeg [" << avLogLevelToStr(level) << "] " << buffer.data());
|
||||
}
|
||||
|
||||
class AvInitializer
|
||||
{
|
||||
public:
|
||||
AvInitializer()
|
||||
{
|
||||
::av_log_set_callback(avLogCallback);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
AudioFile::AudioFile(const std::filesystem::path& p)
|
||||
: _p{ p }
|
||||
{
|
||||
static AvInitializer init;
|
||||
|
||||
int error{ avformat_open_input(&_context, _p.c_str(), nullptr, nullptr) };
|
||||
if (error < 0)
|
||||
{
|
||||
LMS_LOG(AUDIO, ERROR, "Cannot open " << _p << ": " << averror_to_string(error));
|
||||
throw AudioFileException{ error };
|
||||
throw AvException{ error };
|
||||
}
|
||||
|
||||
error = avformat_find_stream_info(_context, nullptr);
|
||||
@@ -166,7 +219,7 @@ namespace lms::audio::ffmpeg
|
||||
{
|
||||
LMS_LOG(AUDIO, ERROR, "Cannot find stream information on " << _p << ": " << averror_to_string(error));
|
||||
avformat_close_input(&_context);
|
||||
throw AudioFileException{ error };
|
||||
throw AvException{ error };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,11 +246,11 @@ namespace lms::audio::ffmpeg
|
||||
return info;
|
||||
}
|
||||
|
||||
AudioFile::MetadataMap AudioFile::getMetaData() const
|
||||
AudioFile::MetadataMap AudioFile::extractMetaData() const
|
||||
{
|
||||
MetadataMap res;
|
||||
|
||||
getMetaDataFromDictionnary(_context->metadata, res);
|
||||
extractMetaDataFromDictionnary(_context->metadata, res);
|
||||
|
||||
// HACK for OGG files
|
||||
// If we did not find tags, search metadata in streams
|
||||
@@ -205,7 +258,7 @@ namespace lms::audio::ffmpeg
|
||||
{
|
||||
for (std::size_t i{}; i < _context->nb_streams; ++i)
|
||||
{
|
||||
getMetaDataFromDictionnary(_context->streams[i]->metadata, res);
|
||||
extractMetaDataFromDictionnary(_context->streams[i]->metadata, res);
|
||||
|
||||
if (!res.empty())
|
||||
break;
|
||||
@@ -266,7 +319,7 @@ namespace lms::audio::ffmpeg
|
||||
return false;
|
||||
}
|
||||
|
||||
void AudioFile::visitAttachedPictures(std::function<void(const Picture&, const MetadataMap&)> func) const
|
||||
void AudioFile::visitAttachedPictures(std::function<void(const PictureView&, const MetadataMap&)> func) const
|
||||
{
|
||||
static const std::unordered_map<int, std::string> codecMimeMap{
|
||||
{ AV_CODEC_ID_BMP, "image/bmp" },
|
||||
@@ -286,14 +339,14 @@ namespace lms::audio::ffmpeg
|
||||
|
||||
if (avstream->codecpar == nullptr)
|
||||
{
|
||||
LMS_LOG(AUDIO, ERROR, "Skipping stream " << i << " since no codecpar is set");
|
||||
LMS_LOG(AUDIO, WARNING, "Skipping stream " << i << " since no codecpar is set");
|
||||
continue;
|
||||
}
|
||||
|
||||
MetadataMap metadata;
|
||||
getMetaDataFromDictionnary(avstream->metadata, metadata);
|
||||
extractMetaDataFromDictionnary(avstream->metadata, metadata);
|
||||
|
||||
Picture picture;
|
||||
PictureView picture;
|
||||
|
||||
auto itMime = codecMimeMap.find(avstream->codecpar->codec_id);
|
||||
if (itMime != codecMimeMap.end())
|
||||
@@ -303,10 +356,10 @@ namespace lms::audio::ffmpeg
|
||||
else
|
||||
{
|
||||
picture.mimeType = "application/octet-stream";
|
||||
LMS_LOG(AUDIO, ERROR, "CODEC ID " << avstream->codecpar->codec_id << " not handled in mime type conversion");
|
||||
LMS_LOG(AUDIO, WARNING, "AVCodecID" << avstream->codecpar->codec_id << " (" << ::avcodec_get_name(avstream->codecpar->codec_id) << ") not handled in mime type conversion");
|
||||
}
|
||||
|
||||
const AVPacket& pkt{ avstream->attached_pic };
|
||||
const ::AVPacket& pkt{ avstream->attached_pic };
|
||||
|
||||
picture.data = std::span{ reinterpret_cast<const std::byte*>(pkt.data), static_cast<std::size_t>(pkt.size) };
|
||||
func(picture, metadata);
|
||||
@@ -325,7 +378,7 @@ namespace lms::audio::ffmpeg
|
||||
|
||||
if (!avstream->codecpar)
|
||||
{
|
||||
LMS_LOG(AUDIO, ERROR, "Skipping stream " << streamIndex << " since no codecpar is set");
|
||||
LMS_LOG(AUDIO, WARNING, "Skipping stream " << streamIndex << " since no codecpar is set");
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ extern "C"
|
||||
|
||||
namespace lms::audio::ffmpeg
|
||||
{
|
||||
struct Picture
|
||||
struct PictureView
|
||||
{
|
||||
std::string mimeType;
|
||||
std::span<const std::byte> data; // valid as long as IAudioFile exists
|
||||
@@ -75,12 +75,12 @@ namespace lms::audio::ffmpeg
|
||||
|
||||
const std::filesystem::path& getPath() const;
|
||||
ContainerInfo getContainerInfo() const;
|
||||
MetadataMap getMetaData() const;
|
||||
MetadataMap extractMetaData() const;
|
||||
std::vector<StreamInfo> getStreamInfo() const;
|
||||
std::optional<StreamInfo> getBestStreamInfo() const;
|
||||
std::optional<std::size_t> getBestStreamIndex() const;
|
||||
bool hasAttachedPictures() const;
|
||||
void visitAttachedPictures(std::function<void(const Picture&, const MetadataMap&)> func) const;
|
||||
void visitAttachedPictures(std::function<void(const PictureView&, const MetadataMap&)> func) const;
|
||||
|
||||
private:
|
||||
std::optional<StreamInfo> getStreamInfo(std::size_t streamIndex) const;
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#include "AudioFileInfo.hpp"
|
||||
|
||||
#include "core/ILogger.hpp"
|
||||
|
||||
#include "audio/AudioTypes.hpp"
|
||||
#include "audio/IAudioFileInfo.hpp"
|
||||
|
||||
@@ -30,65 +32,88 @@ namespace lms::audio::ffmpeg
|
||||
{
|
||||
namespace
|
||||
{
|
||||
AudioProperties computeAudioProperties(const AudioFile& audioFile)
|
||||
std::optional<AudioProperties> computeAudioProperties(const AudioFile& audioFile)
|
||||
{
|
||||
AudioProperties audioProperties;
|
||||
std::optional<AudioProperties> audioProperties;
|
||||
|
||||
const auto containerInfo{ audioFile.getContainerInfo() };
|
||||
const auto bestStreamInfo{ audioFile.getBestStreamInfo() };
|
||||
if (!bestStreamInfo)
|
||||
throw AudioFileParsingException{ "Cannot find best audio stream" };
|
||||
{
|
||||
LMS_LOG(AUDIO, DEBUG, "Cannot find best audio stream in " << audioFile.getPath());
|
||||
return audioProperties;
|
||||
}
|
||||
|
||||
if (!containerInfo.container)
|
||||
throw AudioFileParsingException{ "Unhandled container type '" + containerInfo.containerName + "'" };
|
||||
{
|
||||
LMS_LOG(AUDIO, DEBUG, "Unhandled container '" << containerInfo.containerName << "' in " << audioFile.getPath());
|
||||
return audioProperties;
|
||||
}
|
||||
|
||||
if (!bestStreamInfo->codec)
|
||||
throw AudioFileParsingException{ "Unhandled codec type '" + bestStreamInfo->codecName + "'" };
|
||||
{
|
||||
LMS_LOG(AUDIO, DEBUG, "Unhandled codec '" << bestStreamInfo->codecName << "' in " << audioFile.getPath());
|
||||
return audioProperties;
|
||||
}
|
||||
|
||||
if (!bestStreamInfo->bitrate || *bestStreamInfo->bitrate == 0)
|
||||
throw AudioFileParsingException{ "Cannot determine bitrate" };
|
||||
{
|
||||
LMS_LOG(AUDIO, DEBUG, "Cannot determine bitrate in " << audioFile.getPath());
|
||||
return audioProperties;
|
||||
}
|
||||
|
||||
if (!bestStreamInfo->channelCount || *bestStreamInfo->channelCount == 0)
|
||||
throw AudioFileParsingException{ "Cannot determine channel count" };
|
||||
{
|
||||
LMS_LOG(AUDIO, DEBUG, "Cannot determine channel count in " << audioFile.getPath());
|
||||
return audioProperties;
|
||||
}
|
||||
|
||||
if (!bestStreamInfo->sampleRate || *bestStreamInfo->sampleRate == 0)
|
||||
throw AudioFileParsingException{ "Cannot determine sample rate" };
|
||||
{
|
||||
LMS_LOG(AUDIO, DEBUG, "Cannot determine sample rate in " << audioFile.getPath());
|
||||
return audioProperties;
|
||||
}
|
||||
|
||||
audioProperties.container = *containerInfo.container;
|
||||
audioProperties.duration = containerInfo.duration;
|
||||
audioProperties.codec = *bestStreamInfo->codec;
|
||||
audioProperties.bitrate = *bestStreamInfo->bitrate;
|
||||
audioProperties.channelCount = *bestStreamInfo->channelCount;
|
||||
audioProperties.sampleRate = *bestStreamInfo->sampleRate;
|
||||
audioProperties.bitsPerSample = bestStreamInfo->bitsPerSample;
|
||||
audioProperties.emplace();
|
||||
|
||||
audioProperties->container = *containerInfo.container;
|
||||
audioProperties->duration = containerInfo.duration;
|
||||
audioProperties->codec = *bestStreamInfo->codec;
|
||||
audioProperties->bitrate = *bestStreamInfo->bitrate;
|
||||
audioProperties->channelCount = *bestStreamInfo->channelCount;
|
||||
audioProperties->sampleRate = *bestStreamInfo->sampleRate;
|
||||
audioProperties->bitsPerSample = bestStreamInfo->bitsPerSample;
|
||||
|
||||
return audioProperties;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
AudioFileInfo::AudioFileInfo(const std::filesystem::path& filePath, bool enableExtraDebugLogs)
|
||||
AudioFileInfo::AudioFileInfo(const std::filesystem::path& filePath, const AudioFileInfoParseOptions& parseOptions)
|
||||
: _audioFile{ std::make_unique<AudioFile>(filePath) }
|
||||
, _audioProperties{ std::make_unique<AudioProperties>(computeAudioProperties(*_audioFile)) }
|
||||
, _tagReader{ std::make_unique<TagReader>(*_audioFile, enableExtraDebugLogs) }
|
||||
, _imageReader{ std::make_unique<ImageReader>(*_audioFile) }
|
||||
, _audioProperties{ computeAudioProperties(*_audioFile) }
|
||||
{
|
||||
if (parseOptions.readTags)
|
||||
_tagReader = std::make_unique<TagReader>(*_audioFile, parseOptions.enableExtraDebugLogs);
|
||||
|
||||
if (parseOptions.readImages)
|
||||
_imageReader = std::make_unique<ImageReader>(*_audioFile);
|
||||
}
|
||||
|
||||
AudioFileInfo::~AudioFileInfo() = default;
|
||||
|
||||
const AudioProperties& AudioFileInfo::getAudioProperties() const
|
||||
const AudioProperties* AudioFileInfo::getAudioProperties() const
|
||||
{
|
||||
return *_audioProperties;
|
||||
return _audioProperties.has_value() ? &_audioProperties.value() : nullptr;
|
||||
}
|
||||
|
||||
const IImageReader& AudioFileInfo::getImageReader() const
|
||||
const IImageReader* AudioFileInfo::getImageReader() const
|
||||
{
|
||||
return *_imageReader;
|
||||
return _imageReader.get();
|
||||
}
|
||||
|
||||
const ITagReader& AudioFileInfo::getTagReader() const
|
||||
const ITagReader* AudioFileInfo::getTagReader() const
|
||||
{
|
||||
return *_tagReader;
|
||||
return _tagReader.get();
|
||||
}
|
||||
|
||||
} // namespace lms::audio::ffmpeg
|
||||
|
||||
@@ -20,9 +20,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
#include "audio/AudioTypes.hpp"
|
||||
#include "audio/IAudioFileInfo.hpp"
|
||||
#include "audio/IAudioFileInfoParser.hpp"
|
||||
|
||||
namespace lms::audio::ffmpeg
|
||||
{
|
||||
@@ -33,18 +35,19 @@ namespace lms::audio::ffmpeg
|
||||
class AudioFileInfo final : public IAudioFileInfo
|
||||
{
|
||||
public:
|
||||
AudioFileInfo(const std::filesystem::path& filePath, bool enableExtraDebugLogs);
|
||||
~AudioFileInfo();
|
||||
AudioFileInfo(const std::filesystem::path& filePath, const AudioFileInfoParseOptions& parseOptions);
|
||||
~AudioFileInfo() override;
|
||||
|
||||
AudioFileInfo(const AudioFileInfo&) = delete;
|
||||
AudioFileInfo& operator=(const AudioFileInfo&) = delete;
|
||||
|
||||
private:
|
||||
const AudioProperties& getAudioProperties() const override;
|
||||
const IImageReader& getImageReader() const override;
|
||||
const ITagReader& getTagReader() const override;
|
||||
const AudioProperties* getAudioProperties() const override;
|
||||
const IImageReader* getImageReader() const override;
|
||||
const ITagReader* getTagReader() const override;
|
||||
|
||||
std::unique_ptr<AudioFile> _audioFile;
|
||||
std::unique_ptr<AudioProperties> _audioProperties;
|
||||
const std::optional<AudioProperties> _audioProperties;
|
||||
std::unique_ptr<TagReader> _tagReader;
|
||||
std::unique_ptr<ImageReader> _imageReader;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "AudioFileInfoParser.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "AudioFileInfo.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace lms::audio::ffmpeg
|
||||
{
|
||||
std::unique_ptr<IAudioFileInfo> AudioFileInfoParser::parse(const std::filesystem::path& p, const AudioFileInfoParseOptions& parseOptions) const
|
||||
{
|
||||
return std::make_unique<AudioFileInfo>(p, parseOptions);
|
||||
}
|
||||
|
||||
std::span<const std::filesystem::path> AudioFileInfoParser::getSupportedExtensions() const
|
||||
{
|
||||
return utils::getSupportedExtensions();
|
||||
}
|
||||
} // namespace lms::audio::ffmpeg
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "audio/IAudioFileInfoParser.hpp"
|
||||
|
||||
namespace lms::audio::ffmpeg
|
||||
{
|
||||
class AudioFileInfoParser : public IAudioFileInfoParser
|
||||
{
|
||||
private:
|
||||
std::unique_ptr<IAudioFileInfo> parse(const std::filesystem::path& p, const AudioFileInfoParseOptions& parseOptions = AudioFileInfoParseOptions{}) const override;
|
||||
std::span<const std::filesystem::path> getSupportedExtensions() const override;
|
||||
};
|
||||
} // namespace lms::audio::ffmpeg
|
||||
@@ -40,7 +40,7 @@ namespace lms::audio::ffmpeg
|
||||
return std::any_of(std::cbegin(metadata), std::cend(metadata), [&](const auto& keyValue) { return core::stringUtils::stringCaseInsensitiveContains(keyValue.second, keyword); });
|
||||
} };
|
||||
|
||||
_audioFile.visitAttachedPictures([&](const Picture& picture, const AudioFile::MetadataMap& metaData) {
|
||||
_audioFile.visitAttachedPictures([&](const PictureView& picture, const AudioFile::MetadataMap& metaData) {
|
||||
Image image;
|
||||
image.data = picture.data;
|
||||
image.mimeType = picture.mimeType;
|
||||
@@ -52,5 +52,4 @@ namespace lms::audio::ffmpeg
|
||||
visitor(image);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace lms::audio::ffmpeg
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace lms::audio::ffmpeg
|
||||
|
||||
TagReader::TagReader(const AudioFile& audioFile, bool enableExtraDebugLogs)
|
||||
: _audioFile{ audioFile }
|
||||
, _metaDataMap{ audioFile.getMetaData() }
|
||||
, _metaDataMap{ audioFile.extractMetaData() }
|
||||
{
|
||||
if (enableExtraDebugLogs && core::Service<core::logging::ILogger>::get()->isSeverityActive(core::logging::Severity::DEBUG))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user