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.