AVC / H.264 Encoder#

How to use Transcoder.Push to encode an AVC / H.264 elementary stream from raw YUV frames.

Video Input#

As video input we use the foreman_qcif.yuv file from the AVBlocks Assets archive. After downloading and unzipping you will find foreman_qcif.yuv in the vid subdirectory.

Code#

This code shows how you can encode raw uncompressed YUV frames into an H.264 stream. Two Transcoder objects are used, one to read the raw YUV frames from a file, and another to encode the raw frames to AVC / H.264 Annex B stream. The encoding is done via the Transcoder.Push method.

Initialize AVBlocks#

static void EncodeH264Stream()
{
    Library.Initialize();

    EncodeH264Stream("foreman_qcif.yuv", "foreman_qcif.h264");

    Library.Shutdown();
}

Configure YUV Reader#

static Transcoder CreateYUVReader(string inputFile)
{
    // Create VideoStreamInfo, MediaPin, and MediaSocket describing the YUV input

    // MediaPin and VideoStreamInfo
    var yuvInPin = new MediaPin() {
        StreamInfo = new VideoStreamInfo() {
            StreamType = StreamType.UncompressedVideo,
            FrameRate = 30.0,
            FrameWidth = 176,
            FrameHeight = 144,
            ColorFormat = ColorFormat.YUV420,
            ScanType = ScanType.Progressive
        }
    };

    // MediaSocket
    var yuvInSocket = new MediaSocket() {
        StreamType = StreamType.UncompressedVideo,
        File = inputFile
    };

    yuvInSocket.Pins.Add(yuvInPin);

    // Create VideoStreamInfo, MediaPin, and MediaSocket describing the YUV 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 VideoStreamInfo
    var yuvOutPin = new MediaPin() {
        StreamInfo = new VideoStreamInfo() {
            StreamType = StreamType.UncompressedVideo,
            FrameRate = 30.0,
            FrameWidth = 176,
            FrameHeight = 144,
            ColorFormat = ColorFormat.YUV420,
            ScanType = ScanType.Progressive
        }
    };

    // MediaSocket
    var yuvOutSocket = new MediaSocket() {
        StreamType = StreamType.UncompressedVideo,
    };

    yuvOutSocket.Pins.Add(yuvOutPin);

    // Create Transcoder
    var yuvReader = new Transcoder();
    yuvReader.Inputs.Add(yuvInSocket);
    yuvReader.Outputs.Add(yuvOutSocket);

    return yuvReader;
}

Configure AVC / H.264 Encoder#

static Transcoder CreateH264Encoder(string outputFile, HardwareEncoder hardware)
{
    // Create VideoStreamInfo, MediaPin, and MediaSocket describing the YUV input

    // MediaPin and VideoStreamInfo
    var yuvInPin = new MediaPin() {
        StreamInfo = new VideoStreamInfo() {
            StreamType = StreamType.UncompressedVideo,
            FrameRate = 30.0,
            FrameWidth = 176,
            FrameHeight = 144,
            ColorFormat = ColorFormat.YUV420,
            ScanType = ScanType.Progressive
        }
    };

    // MediaSocket
    var yuvInSocket = new MediaSocket() {
        StreamType = StreamType.UncompressedVideo
    };

    yuvInSocket.Pins.Add(yuvInPin);

    // Create VideoStreamInfo, MediaPin, and MediaSocket describing the H.264 output

    // MediaPin and VideoStreamInfo
    var h264OutPin = new MediaPin() {
        StreamInfo = new VideoStreamInfo() {
            StreamType  = StreamType.H264,
            FrameRate = 30.0,
            FrameWidth = 176,
            FrameHeight = 144,
            ColorFormat = ColorFormat.YUV420,
            ScanType = ScanType.Progressive
        }
    };

    // Enable \ disable hardware acceleration
    h264OutPin.Params.Add(Param.HardwareEncoder, hardware);

    // MediaSocket
    var h264OutSocket = new MediaSocket() {
        StreamType = StreamType.H264,
        File = outputFile
    };

    h264OutSocket.Pins.Add(h264OutPin);

    // Transcoder
    var h264Encoder = new Transcoder();
    h264Encoder.Inputs.Add(yuvInSocket);
    h264Encoder.Outputs.Add(h264OutSocket);

    return h264Encoder;
}

Open Transcoders#

static void EncodeH264Stream(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 yuvReader = CreateYUVReader(inputFile);

    // Create a H.264 encoder. We will pass the raw video frames to it 
    // to encode them as AVC / H.264
    Transcoder h264Encoder = CreateH264Encoder(outputFile, HardwareEncoder.Auto);

    if (yuvReader.Open())
    {
        if (h264Encoder.Open())
        {
            EncodeH264Stream(yuvReader, h264Encoder);

            h264Encoder.Close();
        }

        yuvReader.Close();
    }
}

Call Transcoder.Pull and Transcoder.Push#

static void EncodeH264Stream(Transcoder yuvReader, Transcoder h264Encoder)
{
    int inputIndex = 0;
    MediaSample yuvFrame = new MediaSample();

    while (true)
    {
        // Simulate raw frames
        // Each call to Transcoder.Pull returns one video frame.
        if (!yuvReader.Pull(out inputIndex, yuvFrame))
            break;

        // Pass the raw video frame to Transcoder.Push to encode it as AVC / H.264
        if (!h264Encoder.Push(0, yuvFrame))
            break;
    }

    h264Encoder.Flush();
}

Complete Program#

using System;
using System.Linq;

using PrimoSoftware.AVBlocks;

namespace H264Encoder
{
    class Program
    {
        static Transcoder CreateYUVReader(string inputFile)
        {
            // Create VideoStreamInfo, MediaPin, and MediaSocket describing the YUV input

            // MediaPin and VideoStreamInfo
            var yuvInPin = new MediaPin() {
                StreamInfo = new VideoStreamInfo() {
                    StreamType = StreamType.UncompressedVideo,
                    FrameRate = 30.0,
                    FrameWidth = 176,
                    FrameHeight = 144,
                    ColorFormat = ColorFormat.YUV420,
                    ScanType = ScanType.Progressive
                }
            };

            // MediaSocket
            var yuvInSocket = new MediaSocket() {
                StreamType = StreamType.UncompressedVideo,
                File = inputFile
            };

            yuvInSocket.Pins.Add(yuvInPin);

            // Create VideoStreamInfo, MediaPin, and MediaSocket describing the YUV 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 VideoStreamInfo
            var yuvOutPin = new MediaPin() {
                StreamInfo = new VideoStreamInfo() {
                    StreamType = StreamType.UncompressedVideo,
                    FrameRate = 30.0,
                    FrameWidth = 176,
                    FrameHeight = 144,
                    ColorFormat = ColorFormat.YUV420,
                    ScanType = ScanType.Progressive
                }
            };

            // MediaSocket
            var yuvOutSocket = new MediaSocket() {
                StreamType = StreamType.UncompressedVideo,
            };

            yuvOutSocket.Pins.Add(yuvOutPin);

            // Create Transcoder
            var yuvReader = new Transcoder();
            yuvReader.Inputs.Add(yuvInSocket);
            yuvReader.Outputs.Add(yuvOutSocket);

            return yuvReader;
        }

        static Transcoder CreateH264Encoder(string outputFile, HardwareEncoder hardware)
        {
            // Create VideoStreamInfo, MediaPin, and MediaSocket describing the YUV input

            // MediaPin and VideoStreamInfo
            var yuvInPin = new MediaPin() {
                StreamInfo = new VideoStreamInfo() {
                    StreamType = StreamType.UncompressedVideo,
                    FrameRate = 30.0,
                    FrameWidth = 176,
                    FrameHeight = 144,
                    ColorFormat = ColorFormat.YUV420,
                    ScanType = ScanType.Progressive
                }
            };

            // MediaSocket
            var yuvInSocket = new MediaSocket() {
                StreamType = StreamType.UncompressedVideo
            };

            yuvInSocket.Pins.Add(yuvInPin);

            // Create VideoStreamInfo, MediaPin, and MediaSocket describing the H.264 output
    
            // MediaPin and VideoStreamInfo
            var h264OutPin = new MediaPin() {
                StreamInfo = new VideoStreamInfo() {
                    StreamType  = StreamType.H264,
                    FrameRate = 30.0,
                    FrameWidth = 176,
                    FrameHeight = 144,
                    ColorFormat = ColorFormat.YUV420,
                    ScanType = ScanType.Progressive
                }
            };

            // Enable \ disable hardware acceleration
            h264OutPin.Params.Add(Param.HardwareEncoder, hardware);
    
            // MediaSocket
            var h264OutSocket = new MediaSocket() {
                StreamType = StreamType.H264,
                File = outputFile
            };

            h264OutSocket.Pins.Add(h264OutPin);

            // Transcoder
            var h264Encoder = new Transcoder();
            h264Encoder.Inputs.Add(yuvInSocket);
            h264Encoder.Outputs.Add(h264OutSocket);

            return h264Encoder;
        }

        static void EncodeH264Stream(Transcoder yuvReader, Transcoder h264Encoder)
        {
            int inputIndex = 0;
            MediaSample yuvFrame = new MediaSample();

            while (true)
            {
                // Simulate raw frames
                // Each call to Transcoder.Pull returns one video frame.
                if (!yuvReader.Pull(out inputIndex, yuvFrame))
                    break;

                // Pass the raw video frame to Transcoder.Push to encode it as AVC / H.264
                if (!h264Encoder.Push(0, yuvFrame))
                    break;
            }

            h264Encoder.Flush();
        }

        static void EncodeH264Stream(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 yuvReader = CreateYUVReader(inputFile);

            // Create a H.264 encoder. We will pass the raw video frames to it 
            // to encode them as AVC / H.264
            Transcoder h264Encoder = CreateH264Encoder(outputFile, HardwareEncoder.Auto);

            if (yuvReader.Open())
            {
                if (h264Encoder.Open())
                {
                    EncodeH264Stream(yuvReader, h264Encoder);

                    h264Encoder.Close();
                }

                yuvReader.Close();
            }
        }

        static void EncodeH264Stream()
        {
            Library.Initialize();

            EncodeH264Stream("foreman_qcif.yuv", "foreman_qcif.h264");

            Library.Shutdown();
        }

        static void Main(string[] args)
        {
            EncodeH264Stream();
        }
    }
}

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 foreman_qcif.yuv file from the assets archive to bin/x64/Debug/net6.0 under the project’s directory.

Run the application in Visual Studio.