libaac-next/source/examples/aacenc/aacenc.cpp

217 lines
7.8 KiB
C++
Raw Permalink Normal View History

2025-08-05 21:27:47 +07:00
#include <aacnext/libaac.h>
2025-08-07 21:33:29 +07:00
#include <cstdio>
2025-08-06 08:54:16 +07:00
#include "argparse.h"
#include "audiofile.h"
2025-08-06 09:05:06 +07:00
#include "indicator.h"
2025-08-07 21:33:29 +07:00
#define MINIMP4_IMPLEMENTATION
#include "minimp4.h"
2025-08-06 09:05:06 +07:00
using namespace indicators;
2025-08-05 21:27:47 +07:00
2025-08-07 21:33:29 +07:00
static int doWrite(int64_t offset, const void *buffer, size_t size, void *token)
{
FILE *f = (FILE*)token;
fseek(f, offset, SEEK_SET);
return fwrite(buffer, 1, size, f) != size;
}
2025-08-05 21:27:47 +07:00
int main(int argc, char *argv[]) {
argparse::ArgumentParser ap("aacenc");
2025-08-06 08:54:16 +07:00
AudioFile<float> audioFile;
AACSettings aac = {};
2025-08-05 21:27:47 +07:00
ap.add_argument("input");
ap.add_argument("output");
2025-08-07 21:39:53 +07:00
ap.add_argument("-a", "--adts").help("ADTS format").flag();
2025-08-06 08:54:16 +07:00
ap.add_argument("-b", "--bitrate").help("Bit rate").default_value(128).scan<'i', int>();
ap.add_argument("-c", "--cutoff").help("Cutoff frequency").default_value(0).scan<'i', int>();
2025-08-07 21:33:29 +07:00
ap.add_argument("-e", "--enhanced-sbr").help("Enhanced SBR").flag();
2025-08-06 08:54:16 +07:00
ap.add_argument("-f", "--frame-size").help("Frame size").default_value(0).scan<'i', int>();
ap.add_argument("-i", "--iq").help("Inverse quantization").default_value(2).scan<'i', int>();
ap.add_argument("-p", "--profile").help("AAC profile").default_value(0).scan<'i', int>();
ap.add_argument("-t").help("TNS").flag();
2025-08-05 21:27:47 +07:00
try {
ap.parse_args(argc, argv);
} catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << ap;
return 1;
2025-08-06 08:54:16 +07:00
}
2025-08-07 21:33:29 +07:00
auto adts = ap.get<bool>("-a");
2025-08-06 08:54:16 +07:00
auto inFile = ap.get<std::string>("input");
auto outFile = ap.get<std::string>("output");
auto bitRate = ap.get<int>("-b");
auto cutOff = ap.get<int>("-c");
2025-08-07 21:33:29 +07:00
auto eSBR = ap.get<bool>("-e");
2025-08-06 08:54:16 +07:00
auto frameSize = ap.get<int>("-f");
auto inverseQuant = ap.get<int>("-i");
auto profile = ap.get<int>("-p");
auto tns = ap.get<bool>("-t");
if (!audioFile.load(inFile)) {
return 1;
}
2025-08-07 21:33:29 +07:00
FILE * outData = fopen(outFile.c_str(), "wb");
if (!outData) {
2025-08-06 08:54:16 +07:00
std::cerr << "Unable to open " << outFile << ": " << std::strerror(errno) << std::endl;
return 1;
}
std::cout << "Input file: " << inFile << std::endl;
std::cout << "Output file: " << outFile << std::endl;
audioFile.printSummary();
std::cout << "AAC encode configuration:" << std::endl;
std::cout << "Bitrate: " << bitRate << "kbps" << std::endl;
std::cout << "Cutoff: " << cutOff << std::endl;
2025-08-07 21:33:29 +07:00
std::cout << "Enhanced SBR: " << (eSBR ? "true" : "false") << std::endl;
2025-08-06 08:54:16 +07:00
std::cout << "Frame Size: " << frameSize << std::endl;
std::cout << "Inverse quantization: " << inverseQuant << std::endl;
std::cout << "Profile: " << profile << std::endl;
std::cout << "TNS: " << (tns ? "true" : "false") << std::endl;
2025-08-07 21:33:29 +07:00
std::cout << "ADTS: " << (adts ? "true" : "false") << std::endl;
2025-08-06 08:54:16 +07:00
2025-08-07 21:33:29 +07:00
aac.adts = adts;
2025-08-06 08:54:16 +07:00
aac.bitrate = bitRate * 1000;
aac.bitsPerSamples = 32;
aac.cutoff = cutOff;
aac.frameSize = frameSize;
aac.iq = inverseQuant;
aac.noChannels = audioFile.getNumChannels();
aac.profile = (profiles)profile;
aac.sampleRate = audioFile.getSampleRate();
2025-08-07 21:33:29 +07:00
aac.eSBR = eSBR;
2025-08-06 08:54:16 +07:00
aac.tns = tns;
auto aacEncode = aac_encode_open(aac);
if (aacEncode == NULL) {
std::cerr << "Failed opening AAC encoder" << std::endl;
return 1;
}
2025-08-07 21:33:29 +07:00
MP4E_mux_t *mux;
int audio_track_id;
uint32_t tick_duration = ((aacEncode->no_samples/audioFile.getNumChannels()) * 90000)/audioFile.getSampleRate();
std::cout << "Block size: " << (aacEncode->no_samples/audioFile.getNumChannels()) << std::endl;
2025-08-08 15:15:00 +07:00
std::cout << "Delay: " << (aacEncode->inputDelay) << std::endl;
2025-08-07 21:33:29 +07:00
if (!adts) {
2025-08-07 21:57:43 +07:00
mux = MP4E_open(0, 0, outData, doWrite);
2025-08-07 22:12:40 +07:00
MP4E_set_text_comment(mux, "generated by libaac-next");
2025-08-07 21:33:29 +07:00
MP4E_track_t tr;
tr.track_media_kind = e_audio;
tr.language[0] = 'u';
tr.language[1] = 'n';
tr.language[2] = 'd';
tr.language[3] = 0;
tr.object_type_indication = MP4_OBJECT_TYPE_AUDIO_ISO_IEC_14496_3;
tr.time_scale = 90000;
tr.default_duration = 0;
2025-08-07 22:16:31 +07:00
tr.u.a.channelcount = audioFile.getNumChannels();
2025-08-07 21:33:29 +07:00
audio_track_id = MP4E_add_track(mux, &tr);
MP4E_set_dsi(mux, audio_track_id, aacEncode->asc, aacEncode->ascSize);
2025-08-07 21:39:53 +07:00
printf("Extradata: ");
for (uint32_t i = 0; i < aacEncode->ascSize; i++) {
printf("%02x ", aacEncode->asc[i]);
}
printf("\n");
2025-08-07 21:33:29 +07:00
}
2025-08-06 08:54:16 +07:00
size_t numSamples = audioFile.getNumSamplesPerChannel();
size_t sampleReadOffset = 0;
float *samples = new float[aacEncode->no_samples];
2025-08-08 09:20:53 +07:00
uint8_t *aacBuf = new uint8_t[aacEncode->max_out_bytes];
2025-08-06 08:54:16 +07:00
uint32_t aacWriteSize;
2025-08-06 09:05:06 +07:00
show_console_cursor(false);
ProgressBar bar{
option::BarWidth{50},
option::Start{"["},
option::Fill{"="},
option::Lead{">"},
option::Remainder{" "},
option::End{"]"},
option::PostfixText{"Processing samples"},
option::ShowPercentage{true},
option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
};
2025-08-06 08:54:16 +07:00
for (size_t sampleOffset = 0; sampleOffset < numSamples; sampleOffset++) {
for (unsigned int ch = 0; ch < aac.noChannels; ch++) {
samples[sampleReadOffset++] = audioFile.samples[ch][sampleOffset];
}
if (sampleReadOffset >= aacEncode->no_samples) {
2025-08-06 09:05:06 +07:00
bar.set_progress(((double)sampleOffset / (double)numSamples) * 100);
2025-08-08 09:20:53 +07:00
auto ret = aac_encode(aacEncode, (unsigned char *)samples, sampleReadOffset * 4, aacBuf, &aacWriteSize);
2025-08-06 08:54:16 +07:00
if (ret == -1) {
2025-08-06 09:05:06 +07:00
show_console_cursor(true);
2025-08-06 08:54:16 +07:00
std::cerr << "Failed encoding to AAC encoder" << std::endl;
return 1;
}
2025-08-07 21:33:29 +07:00
if (adts) {
fwrite(aacBuf, 1, aacWriteSize, outData);
} else {
if (MP4E_STATUS_OK != MP4E_put_sample(mux, audio_track_id, aacBuf, aacWriteSize, tick_duration, MP4E_SAMPLE_RANDOM_ACCESS)) {
show_console_cursor(true);
std::cerr << "Failed muxing to MP4 muxer: " << ret << std::endl;
return 1;
}
}
2025-08-06 08:54:16 +07:00
sampleReadOffset = 0;
}
}
2025-08-06 09:05:06 +07:00
bar.set_progress(100);
show_console_cursor(true);
2025-08-06 08:54:16 +07:00
if (sampleReadOffset > 0) {
2025-08-08 09:20:53 +07:00
auto ret = aac_encode(aacEncode, (unsigned char *)samples, sampleReadOffset * 4, aacBuf, &aacWriteSize);
2025-08-06 08:54:16 +07:00
if (ret == -1) {
std::cerr << "Failed encoding to AAC encoder (last)" << std::endl;
return 1;
} else if (ret == 0) {
std::cerr << "Flush" << std::endl;
2025-08-08 09:20:53 +07:00
auto ret = aac_encode(aacEncode, NULL, 0, aacBuf, &aacWriteSize);
2025-08-06 08:54:16 +07:00
if (ret == -1) {
std::cerr << "Failed encoding to AAC encoder (last flush)" << std::endl;
return 1;
}
}
2025-08-07 21:33:29 +07:00
if (adts) {
fwrite(aacBuf, 1, aacWriteSize, outData);
} else {
if (MP4E_STATUS_OK != MP4E_put_sample(mux, audio_track_id, aacBuf, aacWriteSize, tick_duration, MP4E_SAMPLE_RANDOM_ACCESS)) {
std::cerr << "Failed muxing to MP4 muxer" << std::endl;
return 1;
}
}
2025-08-06 08:54:16 +07:00
}
for (int i = 0; i < 2; i++) {
2025-08-08 09:20:53 +07:00
auto ret = aac_encode(aacEncode, NULL, 0, aacBuf, &aacWriteSize);
2025-08-06 08:54:16 +07:00
if (ret == -1) {
std::cerr << "Failed encoding to AAC encoder (padding)" << std::endl;
return 1;
}
2025-08-07 21:33:29 +07:00
if (adts) {
fwrite(aacBuf, 1, aacWriteSize, outData);
} else {
if (MP4E_STATUS_OK != MP4E_put_sample(mux, audio_track_id, aacBuf, aacWriteSize, tick_duration, MP4E_SAMPLE_RANDOM_ACCESS)) {
std::cerr << "Failed muxing to MP4 muxer" << std::endl;
return 1;
}
}
2025-08-06 08:54:16 +07:00
}
2025-08-07 21:33:29 +07:00
if (!adts) MP4E_close(mux);
fclose(outData);
aac_encode_close(aacEncode);
2025-08-05 21:27:47 +07:00
}