AAC ADTS Encoder (Push)¶
This article explains how you can use Transcoder::push to encode an AAC (Advanced Audio Coding) ADTS (Audio Data Transport Stream) audio stream from raw audio frames.
Source Audio¶
As audio input we use the equinox-48KHz.wav file from the AVBlocks Assets archive. After downloading and unzipping you will find equinox-48KHz.wav in the aud subdirectory.
Code¶
This code shows how you can encode raw uncompressed audio frames into an AAC ADTS stream. Two Transcoder objects are used, one to read the raw LPCM frames from a file, and another to encode the raw frames to AAC ADTS stream. The encoding is done via the Transcoder::push method.
Windows¶
Initialize AVBlocks¶
void encode_aac_adts_stream()
{
Library::initialize();
auto inputFile = primo::ustring("equinox-48KHz.wav");
auto outputFile = primo::ustring("equinox-48KHz.aac");
encode_aac_adts_stream(inputFile, outputFile);
Library::shutdown();
}
Configure Audio Reader Transcoder¶
p::ref<Transcoder> create_lpcm_source(const char_t* inputFile)
{
// Create MediaSocket describing the WAV input using MediaInfo
p::ref<MediaInfo> mediaInfo(Library::createMediaInfo());
mediaInfo->inputs()->at(0)->setFile(inputFile);
if (!mediaInfo->open())
throw std::runtime_error(std::string("Could not parse {}.") + primo::ustring(inputFile).str());
p::ref<MediaSocket> inputSocket(Library::createMediaSocket(mediaInfo.get()));
mediaInfo->close();
// Create AudioStreamInfo, MediaPin, and MediaSocket describing the LPCM output
// This is the same as the input, but no output file is set on the MediaSocket,
// because we want to pull frames one by one using Transcoder::pull
// AudioStreamInfo
p::ref<AudioStreamInfo> pcmAudio(Library::createAudioStreamInfo());
pcmAudio->setStreamType(StreamType::LPCM);
pcmAudio->setChannels(2);
pcmAudio->setSampleRate(48000);
pcmAudio->setBitsPerSample(16);
// MediaPin
p::ref<MediaPin> outputPin(Library::createMediaPin());
outputPin->setStreamInfo(pcmAudio.get());
// MediaSocket
p::ref<MediaSocket> outputSocket(Library::createMediaSocket());
outputSocket->setStreamType(StreamType::LPCM);
outputSocket->pins()->add(outputPin.get());
// Create Transcoder
p::ref<Transcoder> wavReader(Library::createTranscoder());
wavReader->inputs()->add(inputSocket.get());
wavReader->outputs()->add(outputSocket.get());
return wavReader;
}
Configure AAC Encoder Transcoder¶
p::ref<Transcoder> create_aac_encoder(const char_t* outputFile)
{
// Create AudioStreamInfo, MediaPin, and MediaSocket describing the LPCM input
// AudioStreamInfo
p::ref<AudioStreamInfo> pcmAudio(Library::createAudioStreamInfo());
pcmAudio->setStreamType(StreamType::LPCM);
pcmAudio->setSampleRate(48000);
pcmAudio->setChannels(2);
pcmAudio->setBitsPerSample(16);
pcmAudio->setBytesPerFrame((pcmAudio->bitsPerSample() / 8) * pcmAudio->channels());
// MediaPin
p::ref<MediaPin> inputPin(Library::createMediaPin());
inputPin->setStreamInfo(pcmAudio.get());
// MediaSocket
p::ref<MediaSocket> inputSocket(Library::createMediaSocket());
inputSocket->setStreamType(StreamType::LPCM);
inputSocket->pins()->add(inputPin.get());
// Create AudioStreamInfo, MediaPin, and MediaSocket describing the AAC / M4A output
// AudioStreamInfo
p::ref<AudioStreamInfo> aacAdtsAudio(Library::createAudioStreamInfo());
aacAdtsAudio->setStreamType(StreamType::AAC);
aacAdtsAudio->setStreamSubType(StreamSubType::AAC_ADTS);
aacAdtsAudio->setChannels(2);
aacAdtsAudio->setSampleRate(48000);
aacAdtsAudio->setBitsPerSample(16);
// MediaPin
p::ref<MediaPin> outputPin(Library::createMediaPin());
outputPin->setStreamInfo(aacAdtsAudio.get());
// MediaSocket
p::ref<MediaSocket> outputSocket(Library::createMediaSocket());
outputSocket->setStreamType(StreamType::AAC);
outputSocket->setStreamSubType(StreamSubType::AAC_ADTS);
outputSocket->setFile(outputFile);
outputSocket->pins()->add(outputPin.get());
// Transcoder
p::ref<Transcoder> aacEncoder(Library::createTranscoder());
aacEncoder->inputs()->add(inputSocket.get());
aacEncoder->outputs()->add(outputSocket.get());
return aacEncoder;
}
Open Transcoders¶
void encode_aac_adts_stream(const char_t* inputFile, const char_t* outputFile)
{
// Create a reader to simulate raw audio frames. In reality you will likely
// have a different raw audio source like for example some kind of audio capture device.
p::ref<Transcoder> lpcmReader = create_lpcm_source(inputFile);
// Create an AAC encoder. We will pass the raw audio frames to it to encode them as AAC / ADTS stream
p::ref<Transcoder> aacEncoder = create_aac_encoder(outputFile);
if (lpcmReader->open())
{
if (aacEncoder->open())
{
encode_aac_adts_stream(lpcmReader.get(), aacEncoder.get());
aacEncoder->close();
}
lpcmReader->close();
}
}
Call Transcoder::pull and Transcoder::push¶
void encode_aac_adts_stream(Transcoder* lpcmReader, Transcoder* aacEncoder)
{
int32_t inputIndex = 0;
p::ref<MediaSample> audioFrame(Library::createMediaSample());
while (true)
{
// Simulate a raw frame
// Each call to Transcoder::pull returns one audio frame.
if (!lpcmReader->pull(inputIndex, audioFrame.get()))
break;
// Pass the raw audio frame to Transcoder::push to encode it as AAC / ADTS
if (!aacEncoder->push(0, audioFrame.get()))
break;
}
aacEncoder->flush();
}
Complete C++ Code¶
#include <primo/avblocks/avb.h>
#include <primo/platform/reference++.h>
#include <primo/platform/ustring.h>
// link with AVBlocks64.lib
#pragma comment(lib, "./avblocks/lib/x64/AVBlocks64.lib")
#include <stdexcept>
#include <string>
#include <iostream>
namespace p = primo;
using namespace p::codecs;
using namespace p::avblocks;
p::ref<Transcoder> create_lpcm_source(const char_t* inputFile)
{
// Create MediaSocket describing the WAV input using MediaInfo
p::ref<MediaInfo> mediaInfo(Library::createMediaInfo());
mediaInfo->inputs()->at(0)->setFile(inputFile);
if (!mediaInfo->open())
throw std::runtime_error(std::string("Could not parse {}.") + primo::ustring(inputFile).str());
p::ref<MediaSocket> inputSocket(Library::createMediaSocket(mediaInfo.get()));
mediaInfo->close();
// Create AudioStreamInfo, MediaPin, and MediaSocket describing the LPCM output
// This is the same as the input, but no output file is set on the MediaSocket,
// because we want to pull frames one by one using Transcoder::pull
// AudioStreamInfo
p::ref<AudioStreamInfo> pcmAudio(Library::createAudioStreamInfo());
pcmAudio->setStreamType(StreamType::LPCM);
pcmAudio->setChannels(2);
pcmAudio->setSampleRate(48000);
pcmAudio->setBitsPerSample(16);
// MediaPin
p::ref<MediaPin> outputPin(Library::createMediaPin());
outputPin->setStreamInfo(pcmAudio.get());
// MediaSocket
p::ref<MediaSocket> outputSocket(Library::createMediaSocket());
outputSocket->setStreamType(StreamType::LPCM);
outputSocket->pins()->add(outputPin.get());
// Create Transcoder
p::ref<Transcoder> wavReader(Library::createTranscoder());
wavReader->inputs()->add(inputSocket.get());
wavReader->outputs()->add(outputSocket.get());
return wavReader;
}
p::ref<Transcoder> create_aac_encoder(const char_t* outputFile)
{
// Create AudioStreamInfo, MediaPin, and MediaSocket describing the LPCM input
// AudioStreamInfo
p::ref<AudioStreamInfo> pcmAudio(Library::createAudioStreamInfo());
pcmAudio->setStreamType(StreamType::LPCM);
pcmAudio->setSampleRate(48000);
pcmAudio->setChannels(2);
pcmAudio->setBitsPerSample(16);
pcmAudio->setBytesPerFrame((pcmAudio->bitsPerSample() / 8) * pcmAudio->channels());
// MediaPin
p::ref<MediaPin> inputPin(Library::createMediaPin());
inputPin->setStreamInfo(pcmAudio.get());
// MediaSocket
p::ref<MediaSocket> inputSocket(Library::createMediaSocket());
inputSocket->setStreamType(StreamType::LPCM);
inputSocket->pins()->add(inputPin.get());
// Create AudioStreamInfo, MediaPin, and MediaSocket describing the AAC / M4A output
// AudioStreamInfo
p::ref<AudioStreamInfo> aacAdtsAudio(Library::createAudioStreamInfo());
aacAdtsAudio->setStreamType(StreamType::AAC);
aacAdtsAudio->setStreamSubType(StreamSubType::AAC_ADTS);
aacAdtsAudio->setChannels(2);
aacAdtsAudio->setSampleRate(48000);
aacAdtsAudio->setBitsPerSample(16);
// MediaPin
p::ref<MediaPin> outputPin(Library::createMediaPin());
outputPin->setStreamInfo(aacAdtsAudio.get());
// MediaSocket
p::ref<MediaSocket> outputSocket(Library::createMediaSocket());
outputSocket->setStreamType(StreamType::AAC);
outputSocket->setStreamSubType(StreamSubType::AAC_ADTS);
outputSocket->setFile(outputFile);
outputSocket->pins()->add(outputPin.get());
// Transcoder
p::ref<Transcoder> aacEncoder(Library::createTranscoder());
aacEncoder->inputs()->add(inputSocket.get());
aacEncoder->outputs()->add(outputSocket.get());
return aacEncoder;
}
void encode_aac_adts_stream(Transcoder* lpcmReader, Transcoder* aacEncoder)
{
int32_t inputIndex = 0;
p::ref<MediaSample> audioFrame(Library::createMediaSample());
while (true)
{
// Simulate a raw frame
// Each call to Transcoder::pull returns one audio frame.
if (!lpcmReader->pull(inputIndex, audioFrame.get()))
break;
// Pass the raw audio frame to Transcoder::push to encode it as AAC / ADTS
if (!aacEncoder->push(0, audioFrame.get()))
break;
}
aacEncoder->flush();
}
void encode_aac_adts_stream(const char_t* inputFile, const char_t* outputFile)
{
// Create a reader to simulate raw audio frames. In reality you will likely
// have a different raw audio source like for example some kind of audio capture device.
p::ref<Transcoder> lpcmReader = create_lpcm_source(inputFile);
// Create an AAC encoder. We will pass the raw audio frames to it to encode them as AAC / ADTS stream
p::ref<Transcoder> aacEncoder = create_aac_encoder(outputFile);
if (lpcmReader->open())
{
if (aacEncoder->open())
{
encode_aac_adts_stream(lpcmReader.get(), aacEncoder.get());
aacEncoder->close();
}
lpcmReader->close();
}
}
void encode_aac_adts_stream()
{
Library::initialize();
auto inputFile = primo::ustring("equinox-48KHz.wav");
auto outputFile = primo::ustring("equinox-48KHz.aac");
encode_aac_adts_stream(inputFile, outputFile);
Library::shutdown();
}
int main(int argc, const char* argv[]) {
encode_aac_adts_stream();
return 0;
}
How to run¶
Create a C++ console application in Visual Studio and use the code from this article.
Copy the equinox-48KHz.wav file from the assets archive to project directory.
Run the application in Visual Studio.