Change Video Frame Rate

How to change the frame rate of a video from 24 fps to 30 fps.

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

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.

Code

This code takes an MP4 file encoded at 24 fps, and increases the video frame rate to 30 fps. The audio stream is copied from the source as is.

Initialize AVBlocks and Configure Transcoder

The sample uses hardcoded input and output file paths. It creates a MediaInfo object to probe the input file, then creates input and output sockets. The output socket is cloned from the input socket, then the frame rate is set to 30 fps.

static int Main(string[] args)
{
    string inputPath = "AndrewConnolly_2014.mp4";
    string outputPath = "AndrewConnolly_2014_30fps.mp4";

    Library.Initialize();

    bool result = ChangeFramerate(inputPath, outputPath);

    Library.Shutdown();

    return result ? (int)ExitCodes.Success : (int)ExitCodes.EncodeError;
}

static bool ChangeFramerate(string inputPath, string outputPath)
{
    string outputDir = Path.GetDirectoryName(outputPath);
    if (!string.IsNullOrEmpty(outputDir) && !Directory.Exists(outputDir))
    {
        Directory.CreateDirectory(outputDir);
    }

    if (File.Exists(outputPath))
    {
        File.Delete(outputPath);
    }

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

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

        var inputSocket = MediaSocket.FromMediaInfo(mediaInfo);
        var outputSocket = (MediaSocket)inputSocket.Clone();
        outputSocket.File = outputPath;

        var outVideoPin = outputSocket.Pins[0];
        var outVideoStream = outVideoPin.StreamInfo as VideoStreamInfo;

        outVideoStream.FrameRate = 30.0;

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

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

            if (!transcoder.Run())
            {
                PrintError("Transcoder run", transcoder.Error);
                transcoder.Close();
                return false;
            }

            transcoder.Close();
        }
    }

    return true;
}

PrintError Helper

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

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

Complete Code

Here’s the complete working example that demonstrates frame rate conversion using AVBlocks for .NET.

using System;
using System.IO;
using PrimoSoftware.AVBlocks;

namespace SimpleVideoFramerate
{
    class Program
    {
        static int Main(string[] args)
        {
            string inputPath = "AndrewConnolly_2014.mp4";
            string outputPath = "AndrewConnolly_2014_30fps.mp4";

            Library.Initialize();

            bool result = ChangeFramerate(inputPath, outputPath);

            Library.Shutdown();

            return result ? (int)ExitCodes.Success : (int)ExitCodes.EncodeError;
        }

        static bool ChangeFramerate(string inputPath, string outputPath)
        {
            string outputDir = Path.GetDirectoryName(outputPath);
            if (!string.IsNullOrEmpty(outputDir) && !Directory.Exists(outputDir))
            {
                Directory.CreateDirectory(outputDir);
            }

            if (File.Exists(outputPath))
            {
                File.Delete(outputPath);
            }

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

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

                var inputSocket = MediaSocket.FromMediaInfo(mediaInfo);
                var outputSocket = (MediaSocket)inputSocket.Clone();
                outputSocket.File = outputPath;

                var outVideoPin = outputSocket.Pins[0];
                var outVideoStream = outVideoPin.StreamInfo as VideoStreamInfo;

                outVideoStream.FrameRate = 30.0;

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

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

                    if (!transcoder.Run())
                    {
                        PrintError("Transcoder run", transcoder.Error);
                        transcoder.Close();
                        return false;
                    }

                    transcoder.Close();
                }
            }

            return true;
        }

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

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

        enum ExitCodes : int
        {
            Success = 0,
            EncodeError = 2,
        }
    }
}

How to Run

See the simple_video_framerate .NET sample for details.

Command Line

This sample uses hardcoded input and output file paths — no command line parsing.

bin/net10.0/simple_video_framerate

Examples

Build the sample from the repository root:

dotnet build samples.sln

Download the sample video:

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

Run the sample (the input/output file paths are relative to the working directory):

# Linux and macOS
../../bin/net10.0/simple_video_framerate
# Windows
..\..\bin\net10.0\simple_video_framerate.exe

The converted output file AndrewConnolly_2014_30fps.mp4 will be created in the samples/simple_video_framerate directory.