AAC Encoder
Contents
AAC Encoder#
How to use Transcoder.Push to encode an AAC ADTS 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 WAV file, and another to encode the raw frames to AAC ADTS stream. The encoding is done via the Transcoder.Push
method.
Initialize AVBlocks#
static void EncodeAacAdtsStream()
{
Library.Initialize();
EncodeAacAdtsStream("equinox-48KHz.wav", "equinox-48KHz.aac");
Library.Shutdown();
}
Configure WAV Reader#
static Transcoder CreateWavReader(string inputFile)
{
// Create MediaSocket describing the WAV input using MediaInfo
MediaInfo mediaInfo = new MediaInfo();
mediaInfo.Inputs[0].File = inputFile;
if (!mediaInfo.Open())
throw new Exception($"Could not parse {inputFile}.");
MediaSocket inputSocket = MediaSocket.FromMediaInfo(mediaInfo);
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
// MediaPin and AudioStreamInfo
var outputPin = new MediaPin() {
StreamInfo = new AudioStreamInfo() {
StreamType = StreamType.LPCM,
Channels = 2,
SampleRate = 48000,
BitsPerSample = 16
}
};
// MediaSocket
var outputSocket = new MediaSocket();
outputSocket.StreamType = StreamType.LPCM;
outputSocket.Pins.Add(outputPin);
// Create Transcoder
var wavReader = new Transcoder();
wavReader.Inputs.Add(inputSocket);
wavReader.Outputs.Add(outputSocket);
return wavReader;
}
Configure AAC Encoder#
static Transcoder CreateAacEncoder(string outputFile)
{
// Create AudioStreamInfo, MediaPin, and MediaSocket describing the LPCM input
// MediaPin and AudioStreamInfo
var inputPin = new MediaPin() {
StreamInfo = new AudioStreamInfo() {
StreamType = StreamType.LPCM,
SampleRate = 48000,
Channels = 2,
BitsPerSample = 16,
BytesPerFrame = 4 // = (BitsPerSample / 8) * Channels
}
};
// MediaSocket
var inputSocket = new MediaSocket() {
StreamType = StreamType.LPCM
};
inputSocket.Pins.Add(inputPin);
// Create AudioStreamInfo, MediaPin, and MediaSocket describing the AAC / M4A output
// MediaPin and AudioStreamInfo
var outputPin = new MediaPin() {
StreamInfo = new AudioStreamInfo() {
StreamType = StreamType.Aac,
StreamSubType = StreamSubType.AacAdts,
Channels = 2,
SampleRate = 48000,
BitsPerSample = 16
}
};
// MediaSocket
var outputSocket = new MediaSocket() {
StreamType = StreamType.Aac,
StreamSubType = StreamSubType.AacAdts,
File = outputFile
};
outputSocket.Pins.Add(outputPin);
// Transcoder
var aacEncoder = new Transcoder();
aacEncoder.Inputs.Add(inputSocket);
aacEncoder.Outputs.Add(outputSocket);
return aacEncoder;
}
Open Transcoders#
static void EncodeAacAdtsStream(string inputFile, string outputFile)
{
// Create a reader to simulate raw video frames. In reality you will likely
// have a different raw video source, for example some kind of video capture device.
Transcoder wavReader = CreateWavReader(inputFile);
// Create a H.264 encoder. We will pass the raw video frames to it
// to encode them as AVC / H.264
Transcoder aacEncoder = CreateAacEncoder(outputFile);
if (wavReader.Open())
{
if (aacEncoder.Open())
{
EncodeAacAdtsStream(wavReader, aacEncoder);
aacEncoder.Close();
}
wavReader.Close();
}
}
Call Transcoder.Pull and Transcoder.Push#
static void EncodeAacAdtsStream(Transcoder wavReader, Transcoder aacEncoder)
{
int inputIndex = 0;
MediaSample audioFrame = new MediaSample();
while (true)
{
// Simulate raw frames
// Each call to Transcoder.Pull returns one video frame.
if (!wavReader.Pull(out inputIndex, audioFrame))
break;
// Pass the raw video frame to Transcoder.Push to encode it as AVC / H.264
if (!aacEncoder.Push(0, audioFrame))
break;
}
aacEncoder.Flush();
}
Complete Program#
using System;
using System.Linq;
using PrimoSoftware.AVBlocks;
namespace AacEncoder
{
class Program
{
static Transcoder CreateWavReader(string inputFile)
{
// Create MediaSocket describing the WAV input using MediaInfo
MediaInfo mediaInfo = new MediaInfo();
mediaInfo.Inputs[0].File = inputFile;
if (!mediaInfo.Open())
throw new Exception($"Could not parse {inputFile}.");
MediaSocket inputSocket = MediaSocket.FromMediaInfo(mediaInfo);
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
// MediaPin and AudioStreamInfo
var outputPin = new MediaPin() {
StreamInfo = new AudioStreamInfo() {
StreamType = StreamType.LPCM,
Channels = 2,
SampleRate = 48000,
BitsPerSample = 16
}
};
// MediaSocket
var outputSocket = new MediaSocket();
outputSocket.StreamType = StreamType.LPCM;
outputSocket.Pins.Add(outputPin);
// Create Transcoder
var wavReader = new Transcoder();
wavReader.Inputs.Add(inputSocket);
wavReader.Outputs.Add(outputSocket);
return wavReader;
}
static Transcoder CreateAacEncoder(string outputFile)
{
// Create AudioStreamInfo, MediaPin, and MediaSocket describing the LPCM input
// MediaPin and AudioStreamInfo
var inputPin = new MediaPin() {
StreamInfo = new AudioStreamInfo() {
StreamType = StreamType.LPCM,
SampleRate = 48000,
Channels = 2,
BitsPerSample = 16,
BytesPerFrame = 4 // = (BitsPerSample / 8) * Channels
}
};
// MediaSocket
var inputSocket = new MediaSocket() {
StreamType = StreamType.LPCM
};
inputSocket.Pins.Add(inputPin);
// Create AudioStreamInfo, MediaPin, and MediaSocket describing the AAC / M4A output
// MediaPin and AudioStreamInfo
var outputPin = new MediaPin() {
StreamInfo = new AudioStreamInfo() {
StreamType = StreamType.Aac,
StreamSubType = StreamSubType.AacAdts,
Channels = 2,
SampleRate = 48000,
BitsPerSample = 16
}
};
// MediaSocket
var outputSocket = new MediaSocket() {
StreamType = StreamType.Aac,
StreamSubType = StreamSubType.AacAdts,
File = outputFile
};
outputSocket.Pins.Add(outputPin);
// Transcoder
var aacEncoder = new Transcoder();
aacEncoder.Inputs.Add(inputSocket);
aacEncoder.Outputs.Add(outputSocket);
return aacEncoder;
}
static void EncodeAacAdtsStream(Transcoder wavReader, Transcoder aacEncoder)
{
int inputIndex = 0;
MediaSample audioFrame = new MediaSample();
while (true)
{
// Simulate raw frames
// Each call to Transcoder.Pull returns one video frame.
if (!wavReader.Pull(out inputIndex, audioFrame))
break;
// Pass the raw video frame to Transcoder.Push to encode it as AVC / H.264
if (!aacEncoder.Push(0, audioFrame))
break;
}
aacEncoder.Flush();
}
static void EncodeAacAdtsStream(string inputFile, string outputFile)
{
// Create a reader to simulate raw video frames. In reality you will likely
// have a different raw video source, for example some kind of video capture device.
Transcoder wavReader = CreateWavReader(inputFile);
// Create a H.264 encoder. We will pass the raw video frames to it
// to encode them as AVC / H.264
Transcoder aacEncoder = CreateAacEncoder(outputFile);
if (wavReader.Open())
{
if (aacEncoder.Open())
{
EncodeAacAdtsStream(wavReader, aacEncoder);
aacEncoder.Close();
}
wavReader.Close();
}
}
static void EncodeAacAdtsStream()
{
Library.Initialize();
EncodeAacAdtsStream("equinox-48KHz.wav", "equinox-48KHz.aac");
Library.Shutdown();
}
static void Main(string[] args)
{
EncodeAacAdtsStream();
}
}
}
How to run#
Follow the steps to create a C# console application in Visual Studio but in Program.cs
use the code from this article.
Copy the equinox-48KHz.wav
file from the assets archive to bin/x64/Debug/net6.0
under the project’s directory.
Run the application in Visual Studio.