AAC ADTS Encoder (Pull)¶
This article explains how you can use TTranscoder::pull to encode a WAV file to AAC (Advanced Audio Coding) ADTS (Audio Data Transport Stream) elementary stream.
The code snippets in this article are from the enc_aac_adts_pull macOS sample.
Source Audio¶
For source we use the equinox-48KHz.wav file from the AVBlocks Assets repository. After downloading and unzipping you will find equinox-48KHz.wav in the aud subdirectory.
Code¶
This code takes a WAV file and encodes it to compressed AAC ADTS format using the pull method, which allows you to retrieve encoded samples one at a time.
Initialize AVBlocks¶
The first step in any AVBlocks application is to initialize the library. This must be done before using any other AVBlocks functionality. The TLibrary constructor initializes the library and loads the necessary codecs. The TLibrary destructor shuts the library down and releases any allocated memory, so the library is terminated when the TLibrary object goes out of scope at the end of main.
int main(int argc, char* argv[])
{
Options opt;
switch(prepareOptions(opt, argc, argv))
{
case Command: return 0;
case Error: return 1;
case Parsed: break;
}
TLibrary library;
return encode(opt) ? 0 : 1;
}
Configure Output Socket¶
The output socket defines the format of the encoded audio data. Unlike the run method, when using pull we don’t specify an output file in the socket. Instead, we configure only the stream format and parameters. The socket represents the output format configuration, while the pin represents the specific audio stream. The TAudioStreamInfo object specifies the audio format parameters - you can customize the number of channels or sampling rate with its channels and sampleRate methods.
TMediaSocket()
.streamType(StreamType::AAC)
.streamSubType(StreamSubType::AAC_ADTS)
.addPin(
TMediaPin()
.streamInfo(TAudioStreamInfo()
.streamType(StreamType::AAC)
.streamSubType(StreamSubType::AAC_ADTS)
)
)
Configure Transcoder and Encode with Pull¶
This is the main encoding function that uses the pull method to retrieve encoded samples. The pull method allows you to control when and how encoded data is retrieved, giving you more flexibility in processing. The transcoder reads from the input WAV file and encodes it to AAC ADTS format, but instead of writing directly to a file, it provides the encoded samples through the pull() method.
The allowDemoMode(true) call allows the transcoder to work even without a valid license (useful for testing, but not recommended for production). After opening the transcoder, we continuously pull encoded samples and write them to the output file. The pull loop continues until the transcoder returns false, which happens when all input has been processed.
The function checks the error code to determine if the encoding completed successfully (EOS - End of Stream) or if an actual error occurred.
bool encode(const Options& opt)
{
deleteFile(opt.outputFile.c_str());
std::ofstream outfile(opt.outputFile, std::ios_base::binary);
if (!outfile.is_open())
{
std::println(stderr, "Could not open file: {}", opt.outputFile);
return false;
}
try {
TTranscoder transcoder;
transcoder
.allowDemoMode(true)
.addInput(
TMediaSocket()
.file(opt.inputFile)
)
.addOutput(
// No file on the output socket — we pull samples manually
TMediaSocket()
.streamType(StreamType::AAC)
.streamSubType(StreamSubType::AAC_ADTS)
.addPin(
TMediaPin()
.streamInfo(TAudioStreamInfo()
.streamType(StreamType::AAC)
.streamSubType(StreamSubType::AAC_ADTS)
)
)
)
.open();
int32_t outputIndex = 0;
TMediaSample sample;
while (transcoder.pull(outputIndex, sample))
{
auto buf = sample.buffer();
outfile.write(reinterpret_cast<const char*>(buf.data()), buf.dataSize());
}
const auto error = transcoder.error();
bool success = (error.facility() == primo::error::ErrorFacility::Codec &&
error.code() == primo::codecs::CodecError::EOS);
if (!success)
printError("Transcoder pull", error);
transcoder.close();
if (success)
std::println("Output: {}", opt.outputFile);
return success;
} catch (const TAVBlocksException& ex) {
std::println(stderr, "AVBlocks error: {}", ex.what());
return false;
} catch (const std::exception& ex) {
std::println(stderr, "Error: {}", ex.what());
return false;
}
}
Complete C++ Code¶
Here’s the complete working example that demonstrates AAC ADTS encoding using the pull method. This code combines all the previous snippets into a functional program that can be compiled and run. The main function handles command-line argument parsing, initializes AVBlocks, performs the encoding operation using pull, and properly shuts down the library before exiting.
#include <primo/avblocks/avb++.h>
#include <print>
#include <fstream>
#include "options.h"
#include "util.h"
using namespace primo::codecs;
using namespace primo::avblocks::modern;
bool encode(const Options& opt)
{
deleteFile(opt.outputFile.c_str());
std::ofstream outfile(opt.outputFile, std::ios_base::binary);
if (!outfile.is_open())
{
std::println(stderr, "Could not open file: {}", opt.outputFile);
return false;
}
try {
TTranscoder transcoder;
transcoder
.allowDemoMode(true)
.addInput(
TMediaSocket()
.file(opt.inputFile)
)
.addOutput(
// No file on the output socket — we pull samples manually
TMediaSocket()
.streamType(StreamType::AAC)
.streamSubType(StreamSubType::AAC_ADTS)
.addPin(
TMediaPin()
.streamInfo(TAudioStreamInfo()
.streamType(StreamType::AAC)
.streamSubType(StreamSubType::AAC_ADTS)
)
)
)
.open();
int32_t outputIndex = 0;
TMediaSample sample;
while (transcoder.pull(outputIndex, sample))
{
auto buf = sample.buffer();
outfile.write(reinterpret_cast<const char*>(buf.data()), buf.dataSize());
}
const auto error = transcoder.error();
bool success = (error.facility() == primo::error::ErrorFacility::Codec &&
error.code() == primo::codecs::CodecError::EOS);
if (!success)
printError("Transcoder pull", error);
transcoder.close();
if (success)
std::println("Output: {}", opt.outputFile);
return success;
} catch (const TAVBlocksException& ex) {
std::println(stderr, "AVBlocks error: {}", ex.what());
return false;
} catch (const std::exception& ex) {
std::println(stderr, "Error: {}", ex.what());
return false;
}
}
int main(int argc, char* argv[])
{
Options opt;
switch(prepareOptions(opt, argc, argv))
{
case Command: return 0;
case Error: return 1;
case Parsed: break;
}
TLibrary library;
return encode(opt) ? 0 : 1;
}
How to Run¶
See the build instructions for macOS and the enc_aac_adts_pull example for details.
Command Line¶
./enc_aac_adts_pull --input <wav file> --output <aac file>
Examples¶
List options:
./bin/x64/enc_aac_adts_pull --help
Usage: enc_aac_adts_pull --input <wav file> --output <aac file>
-h, --help
-i, --input input WAV file
-o, --output output AAC file
The following example encodes input file ./assets/aud/equinox-48KHz.wav into output file equinox-48KHz.adts.aac:
mkdir -p ./output/enc_aac_adts_pull
./bin/x64/enc_aac_adts_pull \
--input ./assets/aud/equinox-48KHz.wav \
--output ./output/enc_aac_adts_pull/equinox-48KHz.adts.aac