VP9 Decoder (Run)

This article explains how you can use {transcoder-run-net} to decode VP9 video in an IVF container to a raw YUV video file.

The code snippets in this article are from the dec_vp9_file .NET sample.

Source Video

For source we use the foreman_qcif_vp9.ivf file from the AVBlocks Assets repository. After downloading and unzipping you will find foreman_qcif_vp9.ivf in the vid subdirectory.

Code

This code takes a compressed VP9 video file in an IVF container and decodes it to raw YUV 4:2:0 video format.

Initialize AVBlocks

The first step in any AVBlocks application is to initialize the library. This must be done before using any other AVBlocks functionality. Always call Library.Shutdown() at the end of the program to clean up resources.

static int Main(string[] args)
{
    var opt = new Options();

    if (!opt.Prepare(args))
        return opt.Error ? (int)ExitCodes.OptionsError : (int)ExitCodes.Success;

    Library.Initialize();

    bool decodeResult = DecodeVp9File(opt);

    Library.Shutdown();

    return decodeResult ? (int)ExitCodes.Success : (int)ExitCodes.DecodingError;
}

Configure Input Socket

The input socket is created from MediaInfo, which reads the VP9/IVF input file and detects the encoded video stream parameters.

static bool DecodeVp9File(Options opt)
{
    // transcoder will fail if output exists (by design)
    DeleteFile(opt.OutputFile);

    // Create output directory if needed
    string outputDir = System.IO.Path.GetDirectoryName(opt.OutputFile);
    if (!string.IsNullOrEmpty(outputDir))
        System.IO.Directory.CreateDirectory(outputDir);

    using (var mediaInfo = new MediaInfo())
    {
        mediaInfo.Inputs[0].File = opt.InputFile;

        if (!mediaInfo.Open())
        {
            PrintError("MediaInfo open", mediaInfo.Error);
            return false;
        }

        // create input socket
        MediaSocket inputSocket = MediaSocket.FromMediaInfo(mediaInfo);
        mediaInfo.Close();

        // create output socket
        MediaSocket outputSocket = CreateOutputSocket(opt);

        // create Transcoder
        using (Transcoder transcoder = new Transcoder())
        {
            transcoder.AllowDemoMode = true;
            transcoder.Inputs.Add(inputSocket);
            transcoder.Outputs.Add(outputSocket);

            bool res = transcoder.Open();
            PrintError("Transcoder open", transcoder.Error);
            if (!res)
                return false;

            res = transcoder.Run();
            PrintError("Transcoder run", transcoder.Error);
            if (!res)
                return false;

            transcoder.Close();
        }
    }

    Console.WriteLine("Output: " + opt.OutputFile);
    return true;
}

Configure Output Socket

The output socket writes uncompressed video to the requested YUV file. The sample configures the output video stream as UncompressedVideo with ColorFormat.YUV420.

static MediaSocket CreateOutputSocket(Options opt)
{
    VideoStreamInfo vsi = new VideoStreamInfo();
    vsi.StreamType = StreamType.UncompressedVideo;
    vsi.ColorFormat = ColorFormat.YUV420;

    MediaPin pin = new MediaPin();
    pin.StreamInfo = vsi;

    MediaSocket socket = new MediaSocket();
    socket.File = opt.OutputFile;
    socket.StreamType = StreamType.UncompressedVideo;
    socket.Pins.Add(pin);

    return socket;
}

Complete Code

Here’s the complete working example that demonstrates VP9 decoding using AVBlocks for .NET.

using System;
using PrimoSoftware.AVBlocks;

namespace CliSample
{
    class Program
    {
        static int Main(string[] args)
        {
            var opt = new Options();

            if (!opt.Prepare(args))
                return opt.Error ? (int)ExitCodes.OptionsError : (int)ExitCodes.Success;

            Library.Initialize();

            // Set license information. Without this AVBlocks runs in Demo mode.
            // Library.SetLicense("<license-string>");

            bool decodeResult = DecodeVp9File(opt);

            Library.Shutdown();

            return decodeResult ? (int)ExitCodes.Success : (int)ExitCodes.DecodingError;
        }

        static bool DecodeVp9File(Options opt)
        {
            // transcoder will fail if output exists (by design)
            DeleteFile(opt.OutputFile);

            // Create output directory if needed
            string outputDir = System.IO.Path.GetDirectoryName(opt.OutputFile);
            if (!string.IsNullOrEmpty(outputDir))
                System.IO.Directory.CreateDirectory(outputDir);

            using (var mediaInfo = new MediaInfo())
            {
                mediaInfo.Inputs[0].File = opt.InputFile;

                if (!mediaInfo.Open())
                {
                    PrintError("MediaInfo open", mediaInfo.Error);
                    return false;
                }

                // create input socket
                MediaSocket inputSocket = MediaSocket.FromMediaInfo(mediaInfo);
                mediaInfo.Close();

                // create output socket
                MediaSocket outputSocket = CreateOutputSocket(opt);

                // create Transcoder
                using (Transcoder transcoder = new Transcoder())
                {
                    transcoder.AllowDemoMode = true;
                    transcoder.Inputs.Add(inputSocket);
                    transcoder.Outputs.Add(outputSocket);

                    bool res = transcoder.Open();
                    PrintError("Transcoder open", transcoder.Error);
                    if (!res)
                        return false;

                    res = transcoder.Run();
                    PrintError("Transcoder run", transcoder.Error);
                    if (!res)
                        return false;

                    transcoder.Close();
                }
            }

            Console.WriteLine("Output: " + opt.OutputFile);
            return true;
        }

        static void DeleteFile(string filename)
        {
            try
            {
                if (System.IO.File.Exists(filename))
                    System.IO.File.Delete(filename);
            }
            catch { }
        }

        static MediaSocket CreateOutputSocket(Options opt)
        {
            VideoStreamInfo vsi = new VideoStreamInfo();
            vsi.StreamType = StreamType.UncompressedVideo;
            vsi.ColorFormat = ColorFormat.YUV420;

            MediaPin pin = new MediaPin();
            pin.StreamInfo = vsi;

            MediaSocket socket = new MediaSocket();
            socket.File = opt.OutputFile;
            socket.StreamType = StreamType.UncompressedVideo;
            socket.Pins.Add(pin);

            return socket;
        }

        static void PrintError(string action, ErrorInfo e)
        {
            if (action != null)
            {
                Console.Write("{0}: ", action);
            }

            if (ErrorFacility.Success == e.Facility)
            {
                Console.WriteLine("Success");
                return;
            }
            else
            {
                Console.WriteLine("{0}, facility:{1} code:{2} hint:{3}", e.Message ?? "", e.Facility, e.Code, e.Hint ?? "");
            }
        }

        enum ExitCodes : int
        {
            Success = 0,
            OptionsError = 1,
            DecodingError = 2,
        }
    }
}

How to Run

See the dec_vp9_file .NET sample for details.

Command Line

dec_vp9_file --input <ivf file> --output <yuv file>

Examples

List options:

./bin/net10.0/dec_vp9_file --help

Decode the input file ./assets/vid/foreman_qcif_vp9.ivf into output file ./output/dec_vp9_file/foreman_qcif.yuv:

# Linux and macOS
mkdir -p ./output/dec_vp9_file

./bin/net10.0/dec_vp9_file \
  --input ./assets/vid/foreman_qcif_vp9.ivf \
  --output ./output/dec_vp9_file/foreman_qcif.yuv
# Windows
mkdir -Force -Path ./output/dec_vp9_file

./bin/net10.0/dec_vp9_file `
  --input ./assets/vid/foreman_qcif_vp9.ivf `
  --output ./output/dec_vp9_file/foreman_qcif.yuv