Factorized some container/codec handling code + removed the no longer needed transcode output formats mka and webm
This commit is contained in:
@@ -35,7 +35,6 @@ extern "C"
|
||||
#include "core/ITraceLogger.hpp"
|
||||
#include "core/String.hpp"
|
||||
|
||||
#include "audio/AudioTypes.hpp"
|
||||
#include "audio/Exception.hpp"
|
||||
|
||||
namespace lms::audio::ffmpeg
|
||||
@@ -71,83 +70,83 @@ namespace lms::audio::ffmpeg
|
||||
res[core::stringUtils::stringToUpper(tag->key)] = tag->value;
|
||||
}
|
||||
|
||||
std::optional<ContainerType> avdemuxerToContainerType(std::string_view name)
|
||||
std::optional<core::media::ContainerType> avdemuxerToContainerType(std::string_view name)
|
||||
{
|
||||
if (name == "aiff")
|
||||
return ContainerType::AIFF;
|
||||
return core::media::ContainerType::AIFF;
|
||||
if (name == "ape")
|
||||
return ContainerType::APE;
|
||||
return core::media::ContainerType::APE;
|
||||
if (name.starts_with("asf"))
|
||||
return ContainerType::ASF;
|
||||
return core::media::ContainerType::ASF;
|
||||
if (name == "dsf")
|
||||
return ContainerType::DSF;
|
||||
return core::media::ContainerType::DSF;
|
||||
if (name == "flac")
|
||||
return ContainerType::FLAC;
|
||||
return core::media::ContainerType::FLAC;
|
||||
if (name.find("mp4") != std::string_view::npos)
|
||||
return ContainerType::MP4;
|
||||
return core::media::ContainerType::MP4;
|
||||
if (name.starts_with("mpc"))
|
||||
return ContainerType::MPC;
|
||||
return core::media::ContainerType::MPC;
|
||||
if (name == "mp3")
|
||||
return ContainerType::MPEG;
|
||||
return core::media::ContainerType::MPEG;
|
||||
if (name == "ogg")
|
||||
return ContainerType::Ogg;
|
||||
return core::media::ContainerType::Ogg;
|
||||
if (name == "shn")
|
||||
return ContainerType::Shorten;
|
||||
return core::media::ContainerType::Shorten;
|
||||
if (name == "tta")
|
||||
return ContainerType::TrueAudio;
|
||||
return core::media::ContainerType::TrueAudio;
|
||||
if (name == "wav")
|
||||
return ContainerType::WAV;
|
||||
return core::media::ContainerType::WAV;
|
||||
if (name == "wv")
|
||||
return ContainerType::WavPack;
|
||||
return core::media::ContainerType::WavPack;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<CodecType> avcodecToCodecType(AVCodecID codec)
|
||||
std::optional<core::media::CodecType> avcodecToCodecType(AVCodecID codec)
|
||||
{
|
||||
switch (codec)
|
||||
{
|
||||
case AV_CODEC_ID_AAC:
|
||||
return CodecType::AAC;
|
||||
return core::media::CodecType::AAC;
|
||||
case AV_CODEC_ID_AC3:
|
||||
return CodecType::AC3;
|
||||
return core::media::CodecType::AC3;
|
||||
case AV_CODEC_ID_ALAC:
|
||||
return CodecType::ALAC;
|
||||
return core::media::CodecType::ALAC;
|
||||
case AV_CODEC_ID_APE:
|
||||
return CodecType::APE;
|
||||
return core::media::CodecType::APE;
|
||||
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;
|
||||
return core::media::CodecType::DSD;
|
||||
case AV_CODEC_ID_EAC3:
|
||||
return CodecType::EAC3;
|
||||
return core::media::CodecType::EAC3;
|
||||
case AV_CODEC_ID_FLAC:
|
||||
return CodecType::FLAC;
|
||||
return core::media::CodecType::FLAC;
|
||||
case AV_CODEC_ID_MP3:
|
||||
return CodecType::MP3;
|
||||
return core::media::CodecType::MP3;
|
||||
case AV_CODEC_ID_MP4ALS:
|
||||
return CodecType::MP4ALS;
|
||||
return core::media::CodecType::MP4ALS;
|
||||
case AV_CODEC_ID_MUSEPACK7:
|
||||
return CodecType::MPC7;
|
||||
return core::media::CodecType::MPC7;
|
||||
case AV_CODEC_ID_MUSEPACK8:
|
||||
return CodecType::MPC8;
|
||||
return core::media::CodecType::MPC8;
|
||||
case AV_CODEC_ID_OPUS:
|
||||
return CodecType::Opus;
|
||||
return core::media::CodecType::Opus;
|
||||
case AV_CODEC_ID_SHORTEN:
|
||||
return CodecType::Shorten;
|
||||
return core::media::CodecType::Shorten;
|
||||
case AV_CODEC_ID_VORBIS:
|
||||
return CodecType::Vorbis;
|
||||
return core::media::CodecType::Vorbis;
|
||||
case AV_CODEC_ID_WAVPACK:
|
||||
return CodecType::WavPack;
|
||||
return core::media::CodecType::WavPack;
|
||||
case AV_CODEC_ID_WMALOSSLESS:
|
||||
return CodecType::WMA9Lossless;
|
||||
return core::media::CodecType::WMA9Lossless;
|
||||
case AV_CODEC_ID_WMAPRO:
|
||||
return CodecType::WMA9Pro;
|
||||
return core::media::CodecType::WMA9Pro;
|
||||
case AV_CODEC_ID_WMAV1:
|
||||
return CodecType::WMA1;
|
||||
return core::media::CodecType::WMA1;
|
||||
case AV_CODEC_ID_WMAV2:
|
||||
return CodecType::WMA2;
|
||||
return core::media::CodecType::WMA2;
|
||||
|
||||
default:
|
||||
return std::nullopt;
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "audio/AudioTypes.hpp"
|
||||
#include "core/media/CodecType.hpp"
|
||||
#include "core/media/ContainerType.hpp"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@@ -44,7 +45,7 @@ namespace lms::audio::ffmpeg
|
||||
|
||||
struct ContainerInfo
|
||||
{
|
||||
std::optional<ContainerType> container;
|
||||
std::optional<core::media::ContainerType> container;
|
||||
std::string containerName;
|
||||
|
||||
std::optional<std::size_t> bitrate;
|
||||
@@ -54,7 +55,7 @@ namespace lms::audio::ffmpeg
|
||||
struct StreamInfo
|
||||
{
|
||||
size_t index{};
|
||||
std::optional<CodecType> codec;
|
||||
std::optional<core::media::CodecType> codec;
|
||||
std::string codecName;
|
||||
|
||||
std::optional<size_t> bitrate;
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
#include "core/ILogger.hpp"
|
||||
|
||||
#include "audio/AudioTypes.hpp"
|
||||
#include "audio/AudioProperties.hpp"
|
||||
#include "audio/IAudioFileInfo.hpp"
|
||||
|
||||
#include "AudioFile.hpp"
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
#include "audio/AudioTypes.hpp"
|
||||
#include "audio/AudioProperties.hpp"
|
||||
#include "audio/IAudioFileInfo.hpp"
|
||||
#include "audio/IAudioFileInfoParser.hpp"
|
||||
|
||||
|
||||
@@ -45,9 +45,9 @@ namespace lms::audio::ffmpeg
|
||||
image.data = picture.data;
|
||||
image.mimeType = picture.mimeType;
|
||||
if (metaDataHasKeyword(metaData, "front"))
|
||||
image.type = Image::Type::FrontCover;
|
||||
image.type = core::media::ImageType::FrontCover;
|
||||
else if (metaDataHasKeyword(metaData, "back"))
|
||||
image.type = Image::Type::BackCover;
|
||||
image.type = core::media::ImageType::BackCover;
|
||||
|
||||
visitor(image);
|
||||
});
|
||||
|
||||
@@ -19,13 +19,14 @@
|
||||
|
||||
#include "Transcoder.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <filesystem>
|
||||
#include <iomanip>
|
||||
|
||||
#include "core/IChildProcessManager.hpp"
|
||||
#include "core/IConfig.hpp"
|
||||
#include "core/ILogger.hpp"
|
||||
#include "core/Service.hpp"
|
||||
#include "core/media/MimeType.hpp"
|
||||
|
||||
#include "audio/Exception.hpp"
|
||||
#include "audio/TranscodeTypes.hpp"
|
||||
@@ -42,18 +43,29 @@ namespace lms::audio::ffmpeg
|
||||
{
|
||||
#define LOG(severity, message) LMS_LOG(TRANSCODING, severity, "[" << _debugId << "] - " << message)
|
||||
|
||||
static std::atomic<size_t> globalId{};
|
||||
static std::filesystem::path ffmpegPath;
|
||||
|
||||
void Transcoder::init()
|
||||
namespace
|
||||
{
|
||||
ffmpegPath = core::Service<core::IConfig>::get()->getPath("ffmpeg-file", "/usr/bin/ffmpeg");
|
||||
if (!std::filesystem::exists(ffmpegPath))
|
||||
throw Exception{ "File '" + ffmpegPath.string() + "' does not exist!" };
|
||||
}
|
||||
class FFmpegPath
|
||||
{
|
||||
public:
|
||||
FFmpegPath()
|
||||
{
|
||||
path = core::Service<core::IConfig>::get()->getPath("ffmpeg-file", "/usr/bin/ffmpeg");
|
||||
if (!std::filesystem::exists(path))
|
||||
throw Exception{ "File '" + path.string() + "' does not exist!" };
|
||||
}
|
||||
|
||||
const std::filesystem::path& get() const { return path; }
|
||||
|
||||
private:
|
||||
std::filesystem::path path;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
std::atomic<std::size_t> Transcoder::_nextDebugId;
|
||||
|
||||
Transcoder::Transcoder(const TranscodeParameters& parameters)
|
||||
: _debugId{ globalId++ }
|
||||
: _debugId{ _nextDebugId++ }
|
||||
, _inputParams{ parameters.inputParameters }
|
||||
, _outputParams{ parameters.outputParameters }
|
||||
{
|
||||
@@ -64,8 +76,7 @@ namespace lms::audio::ffmpeg
|
||||
|
||||
void Transcoder::start()
|
||||
{
|
||||
if (ffmpegPath.empty())
|
||||
init();
|
||||
static FFmpegPath ffmpegPath;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -76,15 +87,16 @@ namespace lms::audio::ffmpeg
|
||||
}
|
||||
catch (const std::filesystem::filesystem_error& e)
|
||||
{
|
||||
// TODO store/raise e.code()
|
||||
throw Exception{ "File error '" + _inputParams.filePath.string() + "': " + e.what() };
|
||||
throw IOFileException{ _inputParams.filePath, "Failed to test file existence", e.code() };
|
||||
}
|
||||
|
||||
LOG(INFO, "Transcoding file " << _inputParams.filePath);
|
||||
|
||||
std::vector<std::string> args;
|
||||
|
||||
args.emplace_back(ffmpegPath.string());
|
||||
args.emplace_back(ffmpegPath.get().string());
|
||||
|
||||
// TODO some codecs have restrictions, take them into account (channel count, sample rate, etc.)
|
||||
|
||||
// Make sure:
|
||||
// - we do not produce anything in the stderr output
|
||||
@@ -114,7 +126,7 @@ namespace lms::audio::ffmpeg
|
||||
args.emplace_back("-1");
|
||||
}
|
||||
|
||||
// Skip video flows (including covers)
|
||||
// Skip video flows (including covers!)
|
||||
args.emplace_back("-vn");
|
||||
|
||||
// Output bitrates
|
||||
@@ -124,63 +136,68 @@ namespace lms::audio::ffmpeg
|
||||
args.emplace_back(std::to_string(*_outputParams.bitrate));
|
||||
}
|
||||
|
||||
// Codecs and formats
|
||||
if (_outputParams.format)
|
||||
{
|
||||
switch (*_outputParams.format)
|
||||
args.emplace_back("-f");
|
||||
|
||||
switch (_outputParams.format->container)
|
||||
{
|
||||
case OutputFormat::MP3:
|
||||
args.emplace_back("-f");
|
||||
case core::media::ContainerType::FLAC:
|
||||
args.emplace_back("flac");
|
||||
break;
|
||||
case core::media::ContainerType::Ogg:
|
||||
args.emplace_back("ogg");
|
||||
break;
|
||||
case core::media::ContainerType::MPEG:
|
||||
args.emplace_back("mp3");
|
||||
break;
|
||||
|
||||
case OutputFormat::OGG_OPUS:
|
||||
args.emplace_back("-acodec");
|
||||
default:
|
||||
throw Exception{ "Unsupported container type " + std::string{ core::media::containerTypeToString(_outputParams.format->container).str() } };
|
||||
}
|
||||
|
||||
args.emplace_back("-acodec");
|
||||
|
||||
switch (_outputParams.format->codec)
|
||||
{
|
||||
case core::media::CodecType::MP3:
|
||||
args.emplace_back("libmp3lame");
|
||||
break;
|
||||
|
||||
case core::media::CodecType::Opus:
|
||||
args.emplace_back("libopus");
|
||||
args.emplace_back("-f");
|
||||
args.emplace_back("ogg");
|
||||
break;
|
||||
|
||||
case OutputFormat::MATROSKA_OPUS:
|
||||
args.emplace_back("-acodec");
|
||||
args.emplace_back("libopus");
|
||||
args.emplace_back("-f");
|
||||
args.emplace_back("matroska");
|
||||
break;
|
||||
|
||||
case OutputFormat::OGG_VORBIS:
|
||||
args.emplace_back("-acodec");
|
||||
case core::media::CodecType::Vorbis:
|
||||
args.emplace_back("libvorbis");
|
||||
args.emplace_back("-f");
|
||||
args.emplace_back("ogg");
|
||||
break;
|
||||
|
||||
case OutputFormat::WEBM_VORBIS:
|
||||
args.emplace_back("-acodec");
|
||||
args.emplace_back("libvorbis");
|
||||
args.emplace_back("-f");
|
||||
args.emplace_back("webm");
|
||||
case core::media::CodecType::FLAC:
|
||||
args.emplace_back("flac");
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Exception{ "Unhandled format (" + std::to_string(static_cast<int>(*_outputParams.format)) + ")" };
|
||||
throw Exception{ "Unhandled codec type " + std::string{ codecTypeToString(_outputParams.format->codec).str() } };
|
||||
}
|
||||
}
|
||||
|
||||
args.emplace_back("pipe:1");
|
||||
|
||||
LOG(DEBUG, "Dumping args (" << args.size() << ")");
|
||||
for (const std::string& arg : args)
|
||||
LOG(DEBUG, "Arg = '" << arg << "'");
|
||||
if (core::Service<core::logging::ILogger>::get()->isSeverityActive(core::logging::Severity::DEBUG))
|
||||
{
|
||||
LOG(DEBUG, "Dumping args (" << args.size() << ")");
|
||||
for (const std::string& arg : args)
|
||||
LOG(DEBUG, "Arg = '" << arg << "'");
|
||||
}
|
||||
|
||||
// Caution: stdin must have been closed before
|
||||
try
|
||||
{
|
||||
_childProcess = core::Service<core::IChildProcessManager>::get()->spawnChildProcess(ffmpegPath, args);
|
||||
_childProcess = core::Service<core::IChildProcessManager>::get()->spawnChildProcess(ffmpegPath.get(), args);
|
||||
}
|
||||
catch (core::ChildProcessException& exception)
|
||||
{
|
||||
throw Exception{ "Cannot execute '" + ffmpegPath.string() + "': " + exception.what() };
|
||||
throw Exception{ "Cannot execute '" + ffmpegPath.get().string() + "': " + exception.what() };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,25 +219,10 @@ namespace lms::audio::ffmpeg
|
||||
|
||||
std::string_view Transcoder::getOutputMimeType() const
|
||||
{
|
||||
// TODO: use input mime type
|
||||
if (_outputParams.format)
|
||||
{
|
||||
switch (*_outputParams.format)
|
||||
{
|
||||
case OutputFormat::MP3:
|
||||
return "audio/mpeg";
|
||||
case OutputFormat::OGG_OPUS:
|
||||
return "audio/opus";
|
||||
case OutputFormat::MATROSKA_OPUS:
|
||||
return "audio/x-matroska";
|
||||
case OutputFormat::OGG_VORBIS:
|
||||
return "audio/ogg";
|
||||
case OutputFormat::WEBM_VORBIS:
|
||||
return "audio/webm";
|
||||
}
|
||||
}
|
||||
return core::media::getMimeType(_outputParams.format->container, _outputParams.format->codec).str();
|
||||
|
||||
return "application/octet-stream"; // default, should not happen
|
||||
return core::media::getMimeType(_inputParams.audioProperties.container, _inputParams.audioProperties.codec).str();
|
||||
}
|
||||
|
||||
bool Transcoder::finished() const
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "audio/ITranscoder.hpp"
|
||||
|
||||
namespace lms::core
|
||||
@@ -47,7 +49,8 @@ namespace lms::audio::ffmpeg
|
||||
static void init();
|
||||
void start();
|
||||
|
||||
const std::size_t _debugId{};
|
||||
static std::atomic<std::size_t> _nextDebugId;
|
||||
const std::size_t _debugId;
|
||||
const TranscodeInputParameters _inputParams;
|
||||
const TranscodeOutputParameters _outputParams;
|
||||
std::unique_ptr<core::IChildProcess> _childProcess;
|
||||
|
||||
Reference in New Issue
Block a user