VP8 Decoder (Run)

This article explains how to use Transcoder::run to decode VP8 video in an IVF container to a raw YUV video file.

The code snippets in this article are from the dec_vp8_file macOS sample.

Linux and Windows samples are also available:

Source Video

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

Code

This code takes a compressed VP8 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.

int main(int argc, char* argv[])
{
    Options opt;

    switch(prepareOptions(opt, argc, argv))
    {
        case Command: return 0;
        case Error:	return 1;
        case Parsed: break;
    }

    Library::initialize();

    bool decodeResult = decodeVp8File(opt);

    Library::shutdown();

    return decodeResult ? 0 : 1;
}

Configure Input Socket

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

bool decodeVp8File(Options& opt)
{
    // transcoder will fail if output exists (by design)
    deleteFile(primo::ustring(opt.outputFile));

    auto mediaInfo = primo::make_ref(Library::createMediaInfo());
    mediaInfo->inputs()->at(0)->setFile(primo::ustring(opt.inputFile));

    if(!mediaInfo->open())
    {
        printError("MediaInfo open:", mediaInfo->error());
        return false;
    }

    // create input socket
    auto inputSocket = primo::make_ref(Library::createMediaSocket(mediaInfo.get()));

    // ...existing code...
}

Configure Output Socket

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

primo::ref<MediaSocket> createOutputSocket(Options& opt)
{
    auto vsi = primo::make_ref(Library::createVideoStreamInfo());
    vsi->setStreamType(StreamType::UncompressedVideo);
    vsi->setColorFormat(ColorFormat::YUV420);

    auto pin = primo::make_ref(Library::createMediaPin());
    pin->setStreamInfo(vsi.get());

    auto socket = primo::make_ref(Library::createMediaSocket());
    socket->setFile(primo::ustring(opt.outputFile));
    socket->setStreamType(StreamType::UncompressedVideo);
    socket->pins()->add(pin.get());

    return socket;
}

Configure Transcoder and Decode

After creating the input socket from MediaInfo and the YUV output socket, the sample creates a transcoder, enables demo mode, adds the sockets, opens the transcoder, runs the decode, and closes it.

bool decodeVp8File(Options& opt)
{
    // transcoder will fail if output exists (by design)
    deleteFile(primo::ustring(opt.outputFile));

    auto mediaInfo = primo::make_ref(Library::createMediaInfo());
    mediaInfo->inputs()->at(0)->setFile(primo::ustring(opt.inputFile));

    if(!mediaInfo->open())
    {
        printError("MediaInfo open:", mediaInfo->error());
        return false;
    }

    // create input socket
    auto inputSocket = primo::make_ref(Library::createMediaSocket(mediaInfo.get()));

    // create output socket
    auto outputSocket = createOutputSocket(opt);

    // create transcoder
    auto transcoder = primo::make_ref(Library::createTranscoder());
    transcoder->setAllowDemoMode(true);
    transcoder->inputs()->add(inputSocket.get());
    transcoder->outputs()->add(outputSocket.get());

    if(!transcoder->open())
    {
        printError("Transcoder open", transcoder->error());
        return false;
    }

    if(!transcoder->run())
    {
        printError("Transcoder run", transcoder->error());
        return false;
    }

    transcoder->close();

    cout << "Output: " << opt.outputFile << endl;

    return true;
}

Complete C++ Code

Here’s the complete working example from dec_vp8_file.cpp:

#include <cstdio>
#include <memory.h>

#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>

#include <primo/avblocks/avb.h>

#include <primo/platform/reference++.h>
#include <primo/platform/error_facility.h>
#include <primo/platform/ustring.h>

#include "util.h"
#include "options.h"

using namespace primo::codecs;
using namespace primo::avblocks;
using namespace std;

primo::ref<MediaSocket> createOutputSocket(Options& opt)
{
    auto vsi = primo::make_ref(Library::createVideoStreamInfo());
    vsi->setStreamType(StreamType::UncompressedVideo);
    vsi->setColorFormat(ColorFormat::YUV420);

    auto pin = primo::make_ref(Library::createMediaPin());
    pin->setStreamInfo(vsi.get());

    auto socket = primo::make_ref(Library::createMediaSocket());
    socket->setFile(primo::ustring(opt.outputFile));
    socket->setStreamType(StreamType::UncompressedVideo);
    socket->pins()->add(pin.get());

    return socket;
}

bool decodeVp8File(Options& opt)
{
    // transcoder will fail if output exists (by design)
    deleteFile(primo::ustring(opt.outputFile));

    auto mediaInfo = primo::make_ref(Library::createMediaInfo());
    mediaInfo->inputs()->at(0)->setFile(primo::ustring(opt.inputFile));

    if(!mediaInfo->open())
    {
        printError("MediaInfo open:", mediaInfo->error());
        return false;
    }

    // create input socket
    auto inputSocket = primo::make_ref(Library::createMediaSocket(mediaInfo.get()));

    // create output socket
    auto outputSocket = createOutputSocket(opt);

    // create transcoder
    auto transcoder = primo::make_ref(Library::createTranscoder());
    transcoder->setAllowDemoMode(true);
    transcoder->inputs()->add(inputSocket.get());
    transcoder->outputs()->add(outputSocket.get());

    if(!transcoder->open())
    {
        printError("Transcoder open", transcoder->error());
        return false;
    }

    if(!transcoder->run())
    {
        printError("Transcoder run", transcoder->error());
        return false;
    }

    transcoder->close();

    cout << "Output: " << opt.outputFile << endl;

    return true;
}

int main(int argc, char* argv[])
{
    Options opt;

    switch(prepareOptions(opt, argc, argv))
    {
        case Command: return 0;
        case Error:	return 1;
        case Parsed: break;
    }

    Library::initialize();

    bool decodeResult = decodeVp8File(opt);

    Library::shutdown();

    return decodeResult ? 0 : 1;
}

How to Run

See the build instructions for macOS and the dec_vp8_file example for details.

Command Line

./dec_vp8_file --input <file> --output <yuv_file>

Examples

List options:

./bin/x64/dec_vp8_file --help
Usage: dec_vp8_file -i <ivf file> -o <yuv file>
  -h,    --help
         --input    VP8/IVF input file.
         --output   YUV output file.

The following command decodes a VP8 video from assets/vid/foreman_qcif_vp8.ivf to output/dec_vp8_file/foreman_qcif.yuv:

mkdir -p ./output/dec_vp8_file

./bin/x64/dec_vp8_file \
    --input ./assets/vid/foreman_qcif_vp8.ivf \
    --output ./output/dec_vp8_file/foreman_qcif.yuv