AAC Encoder (Push)

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/net10.0 under the project’s directory.

Run the application in Visual Studio.