Demux Audio and Video From WebM

This topic describes how to use the Transcoder.Run method to extract the first audio and video streams from a WebM container and save each stream into a separate WebM file.

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

Source File

For source we use the big-buck-bunny_trailer_vp8_vorbis.webm file from the AVBlocks Assets repository. After downloading and unzipping you will find it in the mov subdirectory.

Code

This code extracts the first audio and video elementary streams from a WebM container and writes them to separate WebM files.

Initialize AVBlocks

Initialize the AVBlocks library before creating the transcoder, and shut it down after the demuxing operation is complete.

var opt = new Options();

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

Library.Initialize();

// Set license information. To run AVBlocks in demo mode, comment the next line out
// Library.SetLicense("<license-string>");

bool demuxResult = DemuxWebM(opt);

Library.Shutdown();

Read the Input Container

The sample uses the MediaInfo class to inspect the WebM input file and create the input socket from the discovered stream information.

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

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

    MediaSocket inSocket = MediaSocket.FromMediaInfo(info);

    info.Close();
    // ...
}

Select Audio and Video Streams

The sample keeps the first audio stream and the first video stream. Any additional pins are disabled.

Transcoder transcoder = new Transcoder();
transcoder.AllowDemoMode = true;
transcoder.Inputs.Add(inSocket);

bool audio = false;
bool video = false;

for (int i = 0; i < inSocket.Pins.Count; ++i)
{
    string fileName;
    if (inSocket.Pins[i].StreamInfo.MediaType == MediaType.Audio && !audio)
    {
        audio = true;
        fileName = opt.OutputFile + ".aud.webm";
    }
    else if (inSocket.Pins[i].StreamInfo.MediaType == MediaType.Video && !video)
    {
        video = true;
        fileName = opt.OutputFile + ".vid.webm";
    }
    else
    {
        inSocket.Pins[i].Connection = PinConnection.Disabled;
        continue;
    }

    MediaSocket outSocket = new MediaSocket();
    outSocket.Pins.Add(inSocket.Pins[i]);
    DeleteFile(fileName);
    outSocket.File = fileName;

    transcoder.Outputs.Add(outSocket);

    Console.WriteLine("Output file: {0}", fileName);
}

Run the Transcoder

After configuring the input and output sockets, the sample opens the transcoder, runs the demuxing operation, closes it, and returns the result.

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

    using (var transcoder = GenerateOutputFileName(opt))
    {
        if (transcoder == null)
            return false;

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

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

        transcoder.Close();
        return true;
    }
}

Complete Code

Here’s the complete working example that demonstrates WebM demuxing 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. To run AVBlocks in demo mode, comment the next line out
            // Library.SetLicense("<license-string>");

            bool demuxResult = DemuxWebM(opt);

            Library.Shutdown();

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

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

            using (var transcoder = GenerateOutputFileName(opt))
            {
                if (transcoder == null)
                    return false;

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

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

                transcoder.Close();
                return true;
            }
        }

        static Transcoder GenerateOutputFileName(Options opt)
        {
            using (var info = new MediaInfo())
            {
                info.Inputs[0].File = opt.InputFile;

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

                MediaSocket inSocket = MediaSocket.FromMediaInfo(info);

                info.Close();

                Transcoder transcoder = new Transcoder();
                transcoder.AllowDemoMode = true;
                transcoder.Inputs.Add(inSocket);

                bool audio = false;
                bool video = false;

                for (int i = 0; i < inSocket.Pins.Count; ++i)
                {
                    string fileName;
                    if (inSocket.Pins[i].StreamInfo.MediaType == MediaType.Audio && !audio)
                    {
                        audio = true;
                        fileName = opt.OutputFile + ".aud.webm";
                    }
                    else if (inSocket.Pins[i].StreamInfo.MediaType == MediaType.Video && !video)
                    {
                        video = true;
                        fileName = opt.OutputFile + ".vid.webm";
                    }
                    else
                    {
                        inSocket.Pins[i].Connection = PinConnection.Disabled;
                        continue;
                    }

                    Console.WriteLine("Output file: {0}", fileName);
                    DeleteFile(fileName);

                    MediaSocket outSocket = new MediaSocket();
                    outSocket.Pins.Add(inSocket.Pins[i]);
                    outSocket.File = fileName;

                    transcoder.Outputs.Add(outSocket);
                }

                return transcoder;
            }
        }

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

        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 build instructions for macOS and the demux_webm_file .NET sample for details.

Command Line

demux_webm_file --input <webm file> --output <output file name without extension>

Examples

List options:

./bin/net10.0/demux_webm_file --help
Usage: demux_webm_file --input <webm file> --output <output file name without extension>
  -i,    --input    Input webm file
  -o,    --output   Output webm filename (without extension)
  --help            Display this help screen.

Demux the input file ./assets/mov/big-buck-bunny_trailer_vp8_vorbis.webm into separate audio and video files:

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

./bin/net10.0/demux_webm_file \
  --input ./assets/mov/big-buck-bunny_trailer_vp8_vorbis.webm \
  --output ./output/demux_webm_file/big-buck-bunny_trailer_vp8_vorbis
# Windows
mkdir -Force -Path ./output/demux_webm_file

./bin/net10.0/demux_webm_file `
  --input ./assets/mov/big-buck-bunny_trailer_vp8_vorbis.webm `
  --output ./output/demux_webm_file/big-buck-bunny_trailer_vp8_vorbis

This will produce:

  • big-buck-bunny_trailer_vp8_vorbis.aud.webm (audio only)

  • big-buck-bunny_trailer_vp8_vorbis.vid.webm (video only)