Upscale Video

This article explains how to scale a 480p (854x480) video up to Full HD 1080p (1920x1080) video.

Source Video

For a source video we use the MP4 file from the TED talk video What’s the next window into our universe? by Andrew Connolly. The original video format is Wide 480p or 16:9, 854 x 480.

Sample Code

This code takes an MP4 file with H.264 video and AAC audio, and scales the video stream up to Full HD 1920x1080 (1080p) using bicubic method for interpolation. The audio stream is copied from the source as is. The input and output file paths are hardcoded in the source code, no command line parsing is used.

The snippets in this section are from the simple_video_upscale macOS sample.

Linux and Windows samples are also available:

Initialize AVBlocks

The first step in any AVBlocks application is to initialize the library. This must be done before using any other AVBlocks functionality. The Library::initialize() method sets up the internal state and loads necessary codecs. Always remember to call Library::shutdown() at the end of your program to properly clean up resources and release any allocated memory.

int main(int argc, const char *argv[]) {
    Library::initialize();

    auto inputFile = primo::ustring(L"AndrewConnolly_2014.mp4");
    auto outputFile = primo::ustring(L"AndrewConnolly_2014_1080p.mp4");

    // ... upscaling code ...

    Library::shutdown();
    return 0;
}

Configure Transcoder and Upscale

We probe the input file with a MediaInfo object, and create an input socket from it. The output socket is created by cloning the input socket, so it starts out with an identical configuration, and then we set the output file path on it. The transcoder is the core component that performs the actual upscaling - it takes the 480p video from the input and resizes it to 1920x1080 using bicubic interpolation for the best upscaling quality.

The setAllowDemoMode(true) call allows the transcoder to work even without a valid license (useful for testing, but not recommended for production). We then add our input and output sockets to the transcoder, open it to prepare for processing, run the actual upscaling operation, and finally close it to clean up.

auto inputInfo = primo::make_ref(Library::createMediaInfo());
inputInfo->inputs()->at(0)->setFile(inputFile);

if (inputInfo->open()) {
    auto inputSocket = primo::make_ref(
        Library::createMediaSocket(inputInfo.get())
    );

    // Start with the same output as the input
    auto outputSocket = primo::make_ref(inputSocket->clone());
    outputSocket->setFile(outputFile);

    // Get the output video pin
    auto outVideoPin = outputSocket->pins()->at(0);

    // Set the new frame width and height to Full HD 1920x1080
    auto outVideoStream = (VideoStreamInfo*)outVideoPin->streamInfo();
    outVideoStream->setFrameWidth(1920);
    outVideoStream->setFrameHeight(1080);

    // Set the resize interpolation method:
    // InterpolationMethod::Cubic is best for upscaling (however, it is slow)
    auto outVideoPinParams = primo::make_ref(Library::createParameterList());

    auto interpolationMethod = primo::make_ref(Library::createIntParameter());
    interpolationMethod->setName(Param::Video::Resize::InterpolationMethod);
    interpolationMethod->setValue(InterpolationMethod::Cubic);
    outVideoPinParams->add(interpolationMethod.get());

    outVideoPin->setParams(outVideoPinParams.get());

    // Create Transcoder and configure it with
    // the input and output sockets
    auto transcoder = primo::make_ref(Library::createTranscoder());
    transcoder->inputs()->add(inputSocket.get());
    transcoder->outputs()->add(outputSocket.get());

    // Allow demo mode for the transcoder when
    // using the demo version of the library
    transcoder->setAllowDemoMode(true);

    // Run the transcoder
    if (transcoder->open()) {
        transcoder->run();
        transcoder->close();
    } else {
        std::cerr << "transcoder->open() failed: "
                  << primo::ustring(transcoder->error()->message())
                  << std::endl;
    }
} else {
    std::cerr << "inputInfo->open() failed: "
               << primo::ustring(inputInfo->error()->message())
               << std::endl;
}

Complete C++ Code

Here’s the complete working example that demonstrates video upscaling using AVBlocks. The input and output file paths are hardcoded, so no command line parsing is required.

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

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

int main(int argc, const char *argv[]) {
    Library::initialize();

    auto inputFile = primo::ustring(L"AndrewConnolly_2014.mp4");
    auto outputFile = primo::ustring(L"AndrewConnolly_2014_1080p.mp4");

    auto inputInfo = primo::make_ref(Library::createMediaInfo());
    inputInfo->inputs()->at(0)->setFile(inputFile);

    if (inputInfo->open()) {
        auto inputSocket = primo::make_ref(
            Library::createMediaSocket(inputInfo.get())
        );

        // Start with the same output as the input
        auto outputSocket = primo::make_ref(inputSocket->clone());
        outputSocket->setFile(outputFile);

        // Get the output video pin
        auto outVideoPin = outputSocket->pins()->at(0);

        // Set the new frame width and height to Full HD 1920x1080
        auto outVideoStream = (VideoStreamInfo*)outVideoPin->streamInfo();
        outVideoStream->setFrameWidth(1920);
        outVideoStream->setFrameHeight(1080);

        // Set the resize interpolation method:
        // InterpolationMethod::Cubic is best for upscaling (however, it is slow)
        auto outVideoPinParams = primo::make_ref(Library::createParameterList());

        auto interpolationMethod = primo::make_ref(Library::createIntParameter());
        interpolationMethod->setName(Param::Video::Resize::InterpolationMethod);
        interpolationMethod->setValue(InterpolationMethod::Cubic);
        outVideoPinParams->add(interpolationMethod.get());

        outVideoPin->setParams(outVideoPinParams.get());

        // Create Transcoder and configure it with
        // the input and output sockets
        auto transcoder = primo::make_ref(Library::createTranscoder());
        transcoder->inputs()->add(inputSocket.get());
        transcoder->outputs()->add(outputSocket.get());

        // Allow demo mode for the transcoder when
        // using the demo version of the library
        transcoder->setAllowDemoMode(true);

        // Run the transcoder
        if (transcoder->open()) {
            transcoder->run();
            transcoder->close();
        } else {
            std::cerr << "transcoder->open() failed: "
                      << primo::ustring(transcoder->error()->message())
                      << std::endl;
        }
    } else {
        std::cerr << "inputInfo->open() failed: "
                   << primo::ustring(inputInfo->error()->message())
                   << std::endl;
    }

    Library::shutdown();
    return 0;
}

How to run

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

Download the sample video, the AndrewConnolly_2014.mp4 file from the Internet Archive:

curl -L -o AndrewConnolly_2014.mp4 \
    https://archive.org/download/AndrewConnolly_2014/AndrewConnolly_2014.mp4

Run the sample from the sample directory - the input and output file paths are relative to the working directory:

cd samples/darwin/simple_video_upscale
../../../bin/x64/simple_video_upscale

The upscaled output file AndrewConnolly_2014_1080p.mp4 will be created in the samples/darwin/simple_video_upscale directory.