diff options
author | Stéphane L <contact@slepin.fr> | 2019-01-17 09:25:40 +0100 |
---|---|---|
committer | Daniel Schürmann <daschuer@mixxx.org> | 2021-01-08 23:51:53 +0100 |
commit | 029b40539380119bcd80a28e53607b2530d1b548 (patch) | |
tree | 8178e8b2dee2f374ecf5274921c4027a572f4c56 /src/encoder | |
parent | a1cc267f91826830ee4e1cab8318a44641e445e2 (diff) |
fdk-aac encoder
Diffstat (limited to 'src/encoder')
-rw-r--r-- | src/encoder/encoder.cpp | 32 | ||||
-rw-r--r-- | src/encoder/encoder.h | 14 | ||||
-rw-r--r-- | src/encoder/encoderfdkaac.cpp | 395 | ||||
-rw-r--r-- | src/encoder/encoderfdkaac.h | 218 | ||||
-rw-r--r-- | src/encoder/encoderfdkaacsettings.cpp | 54 | ||||
-rw-r--r-- | src/encoder/encoderfdkaacsettings.h | 28 |
6 files changed, 729 insertions, 12 deletions
diff --git a/src/encoder/encoder.cpp b/src/encoder/encoder.cpp index 81716bea37..315eac0aca 100644 --- a/src/encoder/encoder.cpp +++ b/src/encoder/encoder.cpp @@ -1,5 +1,7 @@ #include "encoder/encoder.h" +#include <QList> + #include "preferences/usersettings.h" #include "recording/defs_recording.h" // TODO(XXX): __FFMPEGFILE_ENCODERS__ is currently undefined because @@ -23,10 +25,11 @@ #ifdef __OPUS__ #include "encoder/encoderopus.h" -#endif #include "encoder/encoderopussettings.h" +#endif -#include <QList> +#include "encoder/encoderfdkaac.h" +#include "encoder/encoderfdkaacsettings.h" EncoderFactory EncoderFactory::factory; @@ -42,10 +45,12 @@ EncoderFactory::EncoderFactory() { m_formats.append(Encoder::Format("FLAC", ENCODING_FLAC, true)); m_formats.append(Encoder::Format("MP3", ENCODING_MP3, false)); m_formats.append(Encoder::Format("OGG Vorbis", ENCODING_OGG, false)); - #ifdef __OPUS__ m_formats.append(Encoder::Format("Opus", ENCODING_OPUS, false)); #endif + m_formats.append(Encoder::Format("AAC", ENCODING_AAC, false, "m4a")); + m_formats.append(Encoder::Format("HE-AAC", ENCODING_HEAAC, false, "m4a")); + m_formats.append(Encoder::Format("HE-AACv2", ENCODING_HEAACV2, false, "m4a")); } const QList<Encoder::Format> EncoderFactory::getFormats() const @@ -104,25 +109,28 @@ EncoderPointer EncoderFactory::createEncoder( pEncoder = std::make_shared<EncoderVorbis>(pCallback); #endif pEncoder->setEncoderSettings(*pSettings); - } #ifdef __OPUS__ - else if (pSettings && pSettings->getFormat() == ENCODING_OPUS) { + } else if (pSettings && pSettings->getFormat() == ENCODING_OPUS) { pEncoder = std::make_shared<EncoderOpus>(pCallback); pEncoder->setEncoderSettings(*pSettings); - } #endif - else { + } else if (pSettings && + (pSettings->getFormat() == ENCODING_AAC || + pSettings->getFormat() == ENCODING_HEAAC || + pSettings->getFormat() == ENCODING_HEAACV2)) { + pEncoder = std::make_shared<EncoderFdkAac>(pCallback); + pEncoder->setEncoderSettings(*pSettings); + } else { qWarning() << "Unsupported format requested! " << QString(pSettings ? pSettings->getFormat() : QString("NULL")); DEBUG_ASSERT(false); - pEncoder = std::make_shared<EncoderWave>(pCallback);; + pEncoder = std::make_shared<EncoderWave>(pCallback); } return pEncoder; } EncoderRecordingSettingsPointer EncoderFactory::getEncoderRecordingSettings(Encoder::Format format, - UserSettingsPointer pConfig) const -{ + UserSettingsPointer pConfig) const { if (format.internalName == ENCODING_WAVE) { return std::make_shared<EncoderWaveSettings>(pConfig, format.internalName); } else if (format.internalName == ENCODING_AIFF) { @@ -135,6 +143,10 @@ EncoderRecordingSettingsPointer EncoderFactory::getEncoderRecordingSettings(Enco return std::make_shared<EncoderVorbisSettings>(pConfig); } else if (format.internalName == ENCODING_OPUS) { return std::make_shared<EncoderOpusSettings>(pConfig); + } else if (format.internalName == ENCODING_AAC || + format.internalName == ENCODING_HEAAC || + format.internalName == ENCODING_HEAACV2) { + return std::make_shared<EncoderFdkAacSettings>(pConfig, format.internalName); } else { qWarning() << "Unsupported format requested! " << format.internalName; DEBUG_ASSERT(false); diff --git a/src/encoder/encoder.h b/src/encoder/encoder.h index bc47f0cc81..e440ca9575 100644 --- a/src/encoder/encoder.h +++ b/src/encoder/encoder.h @@ -11,12 +11,22 @@ class Encoder { public: class Format { public: - Format(const QString& labelIn, const QString& nameIn, bool losslessIn) - : label(labelIn), internalName(nameIn), lossless(losslessIn) { + Format(const QString& labelIn, + const QString& nameIn, + bool losslessIn, + const QString& fileExtIn = QString::null) + : label(labelIn), + internalName(nameIn), + lossless(losslessIn), + fileExtension(fileExtIn) { + if (fileExtension == QString::null) { + fileExtension = label; + } } QString label; QString internalName; bool lossless; + QString fileExtension; }; Encoder() {} diff --git a/src/encoder/encoderfdkaac.cpp b/src/encoder/encoderfdkaac.cpp new file mode 100644 index 0000000000..235dacf8e8 --- /dev/null +++ b/src/encoder/encoderfdkaac.cpp @@ -0,0 +1,395 @@ +#include "encoder/encoderfdkaac.h" + +#include <QDir> +#include <QStandardPaths> +#include <QString> +#include <QStringList> + +#include "engine/sidechain/enginesidechain.h" +#include "recording/defs_recording.h" +#include "util/logger.h" +#include "util/sample.h" + +namespace { +// recommended in encoder documentation, section 2.4.1 +const int kOutBufferBits = 6144; +const mixxx::Logger kLogger("EncoderFdkAac"); +} // namespace + +EncoderFdkAac::EncoderFdkAac(EncoderCallback* pCallback) + : aacEncOpen(nullptr), + aacEncClose(nullptr), + aacEncEncode(nullptr), + aacEncInfo(nullptr), + aacEncoder_SetParam(nullptr), + m_aacAot(AOT_AAC_LC), + m_bitrate(0), + m_channels(0), + m_samplerate(0), + m_pCallback(pCallback), + m_library(nullptr), + m_pInputFifo(nullptr), + m_pFifoChunkBuffer(nullptr), + m_readRequired(0), + m_aacEnc(), + m_pAacDataBuffer(nullptr), + m_aacInfo() { + // Load shared library + // Code import from encodermp3.cpp + QStringList libnames; + QString libname = ""; +#ifdef __LINUX__ + libnames << "fdk-aac"; +#elif __WINDOWS__ + // Give top priority to libfdk-aac copied + // into Mixxx's installation folder + libnames << "libfdk-aac-1.dll"; + + // Fallback and user-friendly method: use libfdk-aac + // provided with B.U.T.T installed in + // a standard location + QString buttFdkAacPath = buttWindowsFdkAac(); + if (!buttFdkAacPath.isNull()) { + kLogger.debug() << "Found libfdk-aac at" << buttFdkAacPath; + libnames << buttFdkAacPath; + } + + // Last resort choices: try versions with unusual names + libnames << "libfdk-aac.dll"; + libnames << "libfdkaac.dll"; +#elif __APPLE__ + // Using Homebrew ('brew install fdk-aac' command): + libnames << "/usr/local/lib/libfdk-aac.dylib"; + // Using MacPorts ('sudo port install libfdk-aac' command): + libnames << "/opt/local/lib/libfdk-aac.dylib"; +#endif + + for (const auto& libname : libnames) { + m_library = new QLibrary(libname, 0); + if (m_library->load()) { + kLogger.debug() << "Successfully loaded encoder library " << libname; + break; + } else { + kLogger.warning() << "Failed to load " << libname << ", " << m_library->errorString(); + } + + delete m_library; + m_library = nullptr; + } + + if (!m_library || !m_library->isLoaded()) { + ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties(); + props->setType(DLG_WARNING); + props->setTitle(QObject::tr("Encoder")); + + // TODO(Palakis): write installation guide on Mixxx's wiki + // and include link in message below + QString missingCodec = QObject::tr( + "<html>Mixxx cannot record or stream in AAC " + "or AAC+ without the FDK-AAC encoder. Due to licensing issues, " + "we cannot distribute this with Mixxx. " + "In order to record or stream in AAC or AAC+, you need to " + "download <b>libfdk-aac</b> " + "and install it on your system."); + +#ifdef __LINUX__ + missingCodec = missingCodec.arg("linux"); +#elif __WINDOWS__ + missingCodec = missingCodec.arg("windows"); +#elif __APPLE__ + missingCodec = missingCodec.arg("mac_osx"); +#endif + props->setText(missingCodec); + props->setKey(missingCodec); + ErrorDialogHandler::instance()->requestErrorDialog(props); + return; + } + + aacEncGetLibInfo = (aacEncGetLibInfo_)m_library->resolve("aacEncGetLibInfo"); + aacEncOpen = (aacEncOpen_)m_library->resolve("aacEncOpen"); + aacEncClose = (aacEncClose_)m_library->resolve("aacEncClose"); + aacEncEncode = (aacEncEncode_)m_library->resolve("aacEncEncode"); + aacEncInfo = (aacEncInfo_)m_library->resolve("aacEncInfo"); + aacEncoder_SetParam = (aacEncoder_SetParam_)m_library->resolve("aacEncoder_SetParam"); + + // Check if all function pointers aren't null. + // Otherwise, the version of libfdk-aac loaded doesn't comply with the official distribution + // Shouldn't happen on Linux, mainly on Windows. + if (!aacEncGetLibInfo || + !aacEncOpen || + !aacEncClose || + !aacEncEncode || + !aacEncInfo || + !aacEncoder_SetParam) { + m_library->unload(); + delete m_library; + m_library = nullptr; + + kLogger.debug() << "aacEncGetLibInfo:" << aacEncGetLibInfo; + kLogger.debug() << "aacEncOpen:" << aacEncOpen; + kLogger.debug() << "aacEncClose:" << aacEncClose; + kLogger.debug() << "aacEncEncode:" << aacEncEncode; + kLogger.debug() << "aacEncInfo:" << aacEncInfo; + kLogger.debug() << "aacEncoder_SetParam:" << aacEncoder_SetParam; + + ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties(); + props->setType(DLG_WARNING); + props->setTitle(QObject::tr("Encoder")); + QString key = QObject::tr( + "<html>Mixxx has detected that you use a modified version of libfdk-aac. " + "See <a href='http://mixxx.org/wiki/doku.php/internet_broadcasting'>Mixxx Wiki</a> " + "for more information.</html>"); + props->setText(key); + props->setKey(key); + ErrorDialogHandler::instance()->requestErrorDialog(props); + return; + } + + kLogger.debug() << "Loaded libfdk-aac"; +} + +EncoderFdkAac::~EncoderFdkAac() { + if (m_library && m_library->isLoaded()) { + aacEncClose(&m_aacEnc); + + flush(); + m_library->unload(); + delete m_library; + kLogger.debug() << "Unloaded libfdk-aac"; + } + + delete[] m_pAacDataBuffer; + delete m_pFifoChunkBuffer; + delete m_pInputFifo; +} + +QString EncoderFdkAac::buttWindowsFdkAac() { + // Return %APPDATA%/Local path + QString appData = QStandardPaths::writableLocation( + QStandardPaths::AppLocalDataLocation); + appData = QFileInfo(appData).absolutePath() + "/.."; + + // Candidate paths for a butt installation + QStringList searchPaths; + searchPaths << "C:/Program Files"; + searchPaths << "C:/Program Files (x86)"; + searchPaths << appData; + + // Try to find a butt installation in one of the + // potential paths above + for (QString topPath : searchPaths) { + QDir folder(topPath); + if (!folder.exists()) { + continue; + } + + // Typical name for a butt installation folder + // is "butt-x.x.x" so list subfolders beginning with "butt" + QStringList nameFilters("butt*"); + QStringList subfolders = + folder.entryList(nameFilters, QDir::Dirs, QDir::Name); + + // If a butt installation is found, try + // to find libfdk-aac in it + for (QString subName : subfolders) { + if (!folder.cd(subName)) { + continue; + } + + kLogger.debug() + << "Found potential B.U.T.T installation at" + << (topPath + "/" + subName); + + QString libFile = "libfdk-aac-1.dll"; + if (folder.exists(libFile)) { + // Found a libfdk-aac here. + // Return the full path of the .dll file. + return folder.absoluteFilePath(libFile); + } + + folder.cdUp(); + } + } + + return QString::null; +} + +void EncoderFdkAac::setEncoderSettings(const EncoderSettings& settings) { + if (settings.getFormat() == ENCODING_AAC) { + // MPEG-4 AAC-LC + m_aacAot = AOT_AAC_LC; + } else if (settings.getFormat() == ENCODING_HEAAC) { + // MPEG-4 HE-AAC + m_aacAot = AOT_SBR; + } else if (settings.getFormat() == ENCODING_HEAACV2) { + // MPEG-4 HE-AACv2 + m_aacAot = AOT_PS; + } else { + // Fallback to AAC-LC in case + // of unknown value + m_aacAot = AOT_AAC_LC; + } + + // TODO(Palakis): support more bitrate configurations + m_bitrate = settings.getQuality(); + switch (settings.getChannelMode()) { + case EncoderSettings::ChannelMode::MONO: + m_channels = 1; + break; + case EncoderSettings::ChannelMode::STEREO: + m_channels = 2; + break; + case EncoderSettings::ChannelMode::AUTOMATIC: + m_channels = 2; + break; + } +} + +int EncoderFdkAac::initEncoder(int samplerate, QString& errorMessage) { + (void)errorMessage; + m_samplerate = samplerate; + + if (!m_library) { + kLogger.warning() << "initEncoder failed: fdk-aac library not loaded"; + return -1; + } + + // This initializes the encoder handle but not the encoder itself. + // Actual encoder init is done below. + aacEncOpen(&m_aacEnc, 0, m_channels); + m_pAacDataBuffer = new unsigned char[kOutBufferBits * m_channels](); + + // AAC Object Type: specifies "mode": AAC-LC, HE-AAC, HE-AACv2, DAB AAC, etc... + aacEncoder_SetParam(m_aacEnc, AACENC_AOT, m_aacAot); + + // Input audio samplerate + aacEncoder_SetParam(m_aacEnc, AACENC_SAMPLERATE, m_samplerate); + // Input and output audio channel count + aacEncoder_SetParam(m_aacEnc, AACENC_CHANNELMODE, m_channels); + // Input audio channel order (fixed to 1 for traditional WAVE ordering: L, R, ...) + aacEncoder_SetParam(m_aacEnc, AACENC_CHANNELORDER, 1); + + // Output bitrate in bits per second + // m_bitrate is in kilobits per second, conversion needed + aacEncoder_SetParam(m_aacEnc, AACENC_BITRATE, m_bitrate * 1000); + // Transport type (2 = ADTS) + aacEncoder_SetParam(m_aacEnc, AACENC_TRANSMUX, 2); + // Enable the AAC Afterburner, which increases audio quality + // at the cost of increased CPU and memory usage. + // Fraunhofer recommends to enable this if increased CPU and memory + // consumption is not a problem. + // TODO(Palakis): is this an issue even with 12-year old computers + // and notebooks? + aacEncoder_SetParam(m_aacEnc, AACENC_AFTERBURNER, 1); + + // Actual encoder init, validates settings provided above + int result = aacEncEncode(m_aacEnc, nullptr, nullptr, nullptr, nullptr); + if (result != AACENC_OK) { + kLogger.warning() << "aac encoder init failed! error code:" << result; + return -1; + } + + aacEncInfo(m_aacEnc, &m_aacInfo); + m_readRequired = m_aacInfo.frameLength * m_channels; + + // Size the input FIFO buffer with twice the maximum possible sample count that can be + // processed at once, to avoid skipping frames or waiting for the required sample count + // and encode at a regular pace. + // This is set to the buffer size of the sidechain engine because + // Recording (which uses this engine) sends more samples at once to the encoder than + // the Live Broadcasting implementation + m_pInputFifo = new FIFO<SAMPLE>(EngineSideChain::SIDECHAIN_BUFFER_SIZE * 2); + + m_pFifoChunkBuffer = new SAMPLE[m_readRequired * sizeof(SAMPLE)](); + return 0; +} + +void EncoderFdkAac::encodeBuffer(const CSAMPLE* samples, const int sampleCount) { + if (!m_pInputFifo) { + return; + } + + int writeRequired = sampleCount; + int writeAvailable = m_pInputFifo->writeAvailable(); + if (writeRequired > writeAvailable) { + kLogger.warning() << "FIFO buffer too small, loosing samples!" + << "required:" << writeRequired + << "; available: " << writeAvailable; + } + + int writeCount = math_min(writeRequired, writeAvailable); + if (writeCount > 0) { + // fdk-aac doesn't support float samples, so convert + // to integers instead + SAMPLE convertedSamples[writeCount]; + SampleUtil::convertFloat32ToS16(convertedSamples, samples, writeCount); + m_pInputFifo->write(convertedSamples, writeCount); + } + + processFIFO(); +} + +void EncoderFdkAac::processFIFO() { + if (!m_pInputFifo || !m_pFifoChunkBuffer) { + return; + } + + while (m_pInputFifo->readAvailable() >= m_readRequired) { + m_pInputFifo->read(m_pFifoChunkBuffer, m_readRequired); + + // fdk-aac only accept pointers for most buffer settings. + // Declare settings here and point to them below. + int inSampleSize = sizeof(SAMPLE); + int inDataSize = m_readRequired * inSampleSize; + int inDataDescription = IN_AUDIO_DATA; + + int outElemSize = sizeof(unsigned char); + int outDataSize = kOutBufferBits * m_channels * outElemSize; + int outDataDescription = OUT_BITSTREAM_DATA; + + // Input Buffer + AACENC_BufDesc inputBuf; + inputBuf.numBufs = 1; + inputBuf.bufs = (void**)&m_pFifoChunkBuffer; + inputBuf.bufSizes = &inDataSize; + inputBuf.bufElSizes = &inSampleSize; + inputBuf.bufferIdentifiers = &inDataDescription; + + AACENC_InArgs inputDesc; + inputDesc.numInSamples = m_readRequired; + inputDesc.numAncBytes = 0; + + // Output (result) Buffer + AACENC_BufDesc outputBuf; + outputBuf.numBufs = 1; + outputBuf.bufs = (void**)&m_pAacDataBuffer; + outputBuf.bufSizes = &outDataSize; + outputBuf.bufElSizes = &outElemSize; + outputBuf.bufferIdentifiers = &outDataDescription; + + // Populated by aacEncEncode + AACENC_OutArgs outputDesc; + + int result = aacEncEncode(m_aacEnc, &inputBuf, &outputBuf, &inputDesc, &outputDesc); + if (result != AACENC_OK) { + kLogger.warning() << "aacEncEncode failed! error code:" << result; + return; + } + + int sampleDiff = inputDesc.numInSamples - outputDesc.numInSamples; + if (sampleDiff > 0) { + kLogger.warning() << "encoder ignored" << sampleDiff << "samples!"; + } + + m_pCallback->write(nullptr, m_pAacDataBuffer, 0, outputDesc.numOutBytes); + } +} + +void EncoderFdkAac::updateMetaData(const QString& artist, const QString& title, const QString& album) { + (void)artist, (void)title, (void)album; +} + +void EncoderFdkAac::flush() { + // At this point there may still be samples in the FIFO buffer. + processFIFO(); +} diff --git a/src/encoder/encoderfdkaac.h b/src/encoder/encoderfdkaac.h new file mode 100644 index 0000000000..03d5cbb0d0 --- /dev/null +++ b/src/encoder/encoderfdkaac.h @@ -0,0 +1,218 @@ +// encoderfdkaac.h +// Created on Aug 15 2017 by Palakis + +#ifndef ENCODER_ENCODERFDKAAC_H +#define ENCODER_ENCODERFDKAAC_H + +#include <QLibrary> +#include <QString> + +#include "encoder/encoder.h" +#include "util/fifo.h" + +class EncoderFdkAac : public Encoder { + public: + EncoderFdkAac(EncoderCallback* pCallback); + virtual ~EncoderFdkAac(); + + int initEncoder(int samplerate, QString& errorMessage) override; + void encodeBuffer(const CSAMPLE* samples, const int sampleCount) override; + void updateMetaData(const QString& artist, const QString& title, const QString& album) override; + void flush() override; + void setEncoderSettings(const EncoderSettings& settings) override; + + private: + QString buttWindowsFdkAac(); + + // libfdk-aac common AOTs + static const int AOT_AAC_LC = 2; // AAC-LC + static const int AOT_SBR = 5; // HE-AAC + static const int AOT_PS = 29; // HE-AACv2 + + // libfdk-aac types and structs + typedef signed int INT; + typedef unsigned int UINT; + typedef signed short SHORT; + typedef unsigned short USHORT; + typedef signed char SCHAR; + typedef unsigned char UCHAR; + + typedef enum { + AACENC_OK = 0x0000, + + AACENC_INVALID_HANDLE = 0x0020, + AACENC_MEMORY_ERROR = 0x0021, + AACENC_UNSUPPORTED_PARAMETER = 0x0022, + AACENC_INVALID_CONFIG = 0x0023, + + AACENC_INIT_ERROR = 0x0040, + AACENC_INIT_AAC_ERROR = 0x0041, + AACENC_INIT_SBR_ERROR = 0x0042, + AACENC_INIT_TP_ERROR = 0x0043, + AACENC_INIT_META_ERROR = 0x0044, + + AACENC_ENCODE_ERROR = 0x0060, + + AACENC_ENCODE_EOF = 0x0080, + } AACENC_ERROR; + + typedef enum { + AACENC_AOT = 0x0100, + AACENC_BITRATE = 0x0101, + AACENC_BITRATEMODE = 0x0102, + AACENC_SAMPLERATE = 0x0103, + AACENC_SBR_MODE = 0x0104, + AACENC_GRANULE_LENGTH = 0x0105, + AACENC_CHANNELMODE = 0x0106, + AACENC_CHANNELORDER = 0x0107, + AACENC_SBR_RATIO = 0x0108, + AACENC_AFTERBURNER = 0x0200, + AACENC_BANDWIDTH = 0x0203, + AACENC_PEAK_BITRATE = 0x0207, + AACENC_TRANSMUX = 0x0300, + AACENC_HEADER_PERIOD = 0x0301, + AACENC_SIGNALING_MODE = 0x0302, + AACENC_TPSUBFRAMES = 0x0303, + AACENC_AUDIOMUXVER = 0x0304, + AACENC_PROTECTION = 0x0306, + AACENC_ANCILLARY_BITRATE = 0x0500, + AACENC_METADATA_MODE = 0x0600, + AACENC_CONTROL_STATE = 0xFF00, + AACENC_NONE = 0xFFFF + } AACENC_PARAM; + + typedef enum { + IN_AUDIO_DATA = 0, + IN_ANCILLRY_DATA = 1, // as is in fdk-aac + IN_METADATA_SETUP = 2, + OUT_BITSTREAM_DATA = 3, + OUT_AU_SIZES = 4 + } AACENC_BufferIdentifier; + + typedef enum { + FDK_NONE = 0, + FDK_TOOLS = 1, + FDK_SYSLIB = 2, + FDK_AACDEC = 3, + FDK_AACENC = 4, + FDK_SBRDEC = 5, + FDK_SBRENC = 6, + FDK_TPDEC = 7, + FDK_TPENC = 8, + FDK_MPSDEC = 9, + FDK_MPEGFILEREAD = 10, + FDK_MPEGFILEWRITE = 11, + FDK_MP2DEC = 12, + FDK_DABDEC = 13, + FDK_DABPARSE = 14, + FDK_DRMDEC = 15, + FDK_DRMPARSE = 16, + FDK_AACLDENC = 17, + FDK_MP2ENC = 18, + FDK_MP3ENC = 19, + FDK_MP3DEC = 20, + FDK_MP3HEADPHONE = 21, + FDK_MP3SDEC = 22, + FDK_MP3SENC = 23, + FDK_EAEC = 24, + FDK_DABENC = 25, + FDK_DMBDEC = 26, + FDK_FDREVERB = 27, + FDK_DRMENC = 28, + FDK_METADATATRANSCODER = 29, + FDK_AC3DEC = 30, + FDK_PCMDMX = 31, + + FDK_MODULE_LAST + } FDK_MODULE_ID; + + typedef struct AACENCODER* HANDLE_AACENCODER; + typedef struct { + UINT maxOutBufBytes; + UINT maxAncBytes; + UINT inBufFillLevel; + UINT inputChannels; + UINT frameLength; + UINT encoderDelay; + UCHAR confBuf[64]; + UINT confSize; + } AACENC_InfoStruct; + typedef struct { + INT numBufs; + void** bufs; + INT* bufferIdentifiers; + INT* bufSizes; + INT* bufElSizes; + } AACENC_BufDesc; + typedef struct { + INT numInSamples; + INT numAncBytes; + } AACENC_InArgs; + typedef struct { + INT numOutBytes; + INT numInSamples; + INT numAncBytes; + } AACENC_OutArgs; + typedef struct { + const char* title; + const char* build_date; + const char* build_time; + FDK_MODULE_ID module_id; + INT version; + UINT flags; + char versionStr[32]; + } LIB_INFO; + + // libfdk-aac functions prototypes + typedef AACENC_ERROR (*aacEncGetLibInfo_)(LIB_INFO*); + + typedef AACENC_ERROR (*aacEncOpen_)( + HANDLE_AACENCODER*, + const UINT, + const UINT); + + typedef AACENC_ERROR (*aacEncClose_)(HANDLE_AACENCODER*); + + typedef AACENC_ERROR (*aacEncEncode_)( + const HANDLE_AACENCODER, + const AACENC_BufDesc*, + const AACENC_BufDesc*, + const AACENC_InArgs*, + AACENC_OutArgs*); + + typedef AACENC_ERROR (*aacEncInfo_)( + const HANDLE_AACENCODER, + AACENC_InfoStruct*); + + typedef AACENC_ERROR (*aacEncoder_SetParam_)( + const HANDLE_AACENCODER, + const AACENC_PARAM, + const UINT); + + // libfdk-aac function pointers + aacEncGetLibInfo_ aacEncGetLibInfo; + aacEncOpen_ aacEncOpen; + aacEncClose_ aacEncClose; + aacEncEncode_ aacEncEncode; + aacEncInfo_ aacEncInfo; + aacEncoder_SetParam_ aacEncoder_SetParam; + + // Instance methods + void processFIFO(); + + // Instance attributes + int m_aacAot; + int m_bitrate; + int m_channels; + int m_samplerate; + EncoderCallback* m_pCallback; + QLibrary* m_library; + FIFO<SAMPLE>* m_pInputFifo; + SAMPLE* m_pFifoChunkBuffer; + int m_readRequired; + HANDLE_AACENCODER m_aacEnc; + unsigned char* m_pAacDataBuffer; + AACENC_InfoStruct m_aacInfo; +}; + +#endif // ENCODER_ENCODERFDKAAC_H diff --git a/src/encoder/encoderfdkaacsettings.cpp b/src/encoder/encoderfdkaacsettings.cpp new file mode 100644 index 0000000000..c2b0048676 --- /dev/null +++ b/src/encoder/encoderfdkaacsettings.cpp @@ -0,0 +1,54 @@ +#include "encoder/encoderfdkaacsettings.h" + +#include "recording/defs_recording.h" +#include "util/logger.h" + +namespace { +const int kDefaultQualityIndex = 6; +const char* kQualityKey = "FdkAac_Quality"; +const mixxx::Logger kLogger("EncoderFdkAacSettings"); +} // namespace + +EncoderFdkAacSettings::EncoderFdkAacSettings( + UserSettingsPointer pConfig, + QString format) + : m_pConfig(pConfig), + m_format(std::move(format)) { + m_qualList.append(32); + m_qualList.append(48); + m_qualList.append(64); + m_qualList.append(80); + m_qualList.append(96); + m_qualList.append(112); // stereo + m_qualList.append(128); // stereo + m_qualList.append(160); // stereo + m_qualList.append(192); // stereo + m_qualList.append(224); // stereo + m_qualList.append(256); // stereo + m_qualList.append(320); // stereo +} + +EncoderFdkAacSettings::~EncoderFdkAacSettings() { +} + +QList<int> EncoderFdkAacSettings::getQualityValues() const { + return m_qualList; +} + +int EncoderFdkAacSettings::getQuality() const { + return m_qualList.at(getQualityIndex()); +} + +int EncoderFdkAacSettings::getQualityIndex() const { + int qualityIndex = m_pConfig->getValue( + ConfigKey(RECORDING_PREF_KEY, kQualityKey), kDefaultQualityIndex); + if (qualityIndex >= 0 && qualityIndex < m_qualList.size()) { + return qualityIndex; + } else { + kLogger.warning() + << "Invalid qualityIndex:" + << qualityIndex << "(Max is:" << m_qualList.size() << "). Ignoring it" + << "and returning default, which is:" << kDefaultQualityIndex; + } + return kDefaultQualityIndex; +} diff --git a/src/encoder/encoderfdkaacsettings.h b/src/encoder/encoderfdkaacsettings.h new file mode 100644 index 0000000000..df3a21d169 --- /dev/null +++ b/src/encoder/encoderfdkaacsettings.h @@ -0,0 +1,28 @@ +#pragma once + +#include <QList> + +#include "encoder/encoder.h" +#include "encoder/encodersettings.h" + +class EncoderFdkAacSettings : public EncoderRecordingSettings { + public: + EncoderFdkAacSettings(UserSettingsPointer pConfig, QString format); + virtual ~EncoderFdkAacSettings(); + + // Returns the list of quality values that it supports, to assign them to the slider + QList<int> getQualityValues() const override; + // Returns the current quality value + int getQuality() const override; + int getQualityIndex() const override; + + // Returns the format of this encoder settings. + QString getFormat() const override { + return m_format; + } + + private: + QList<int> m_qualList; + UserSettingsPointer m_pConfig; + QString m_format; +}; |