Mux Audio and Video Into WebM

This topic describes how to use the Transcoder.run method to mux Vorbis audio and VP8 video into a WebM file.

The code snippets in this article are from the mux_webm_file Python sample.

Source Files

For source we use the big-buck-bunny_trailer_vp8_vorbis.aud.webm and big-buck-bunny_trailer_vp8_vorbis.vid.webm files from the AVBlocks Assets repository. After downloading and unzipping you will find them in the aud and vid subdirectories.

Code

This code multiplexes two single-stream WebM files, one containing Vorbis audio and one containing VP8 video, into a WebM container file.

Initialize AVBlocks

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

Library.initialize()

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

result = webm_mux(audio_files, video_files, output_file)

Library.shutdown()

sys.exit(0 if result else 2)

Create the WebM Output Socket

The output socket is configured as StreamType.WebM. The sample adds one output pin for each input audio or video stream.

# Create output socket
output_socket = MediaSocket()
output_socket.file = output_file
output_socket.stream_type = StreamType.WebM

# audio
for audio_file in audio_files:
    output_pin = MediaPin()
    asi = AudioStreamInfo()
    asi.stream_type = StreamType.Vorbis
    output_pin.stream_info = asi

    output_socket.pins.add(output_pin)

    input_socket = MediaSocket()
    input_socket.file = audio_file
    input_socket.stream_type = StreamType.WebM
    transcoder.inputs.add(input_socket)

    print(f"Muxing audio input: {audio_file}")

# video
for video_file in video_files:
    output_pin = MediaPin()
    vsi = VideoStreamInfo()
    vsi.stream_type = StreamType.VP8
    output_pin.stream_info = vsi

    output_socket.pins.add(output_pin)

    input_socket = MediaSocket()
    input_socket.file = video_file
    input_socket.stream_type = StreamType.WebM
    transcoder.inputs.add(input_socket)

    print(f"Muxing video input: {video_file}")

transcoder.outputs.add(output_socket)

Run the Transcoder

After all input sockets and output pins are configured, the sample adds the output socket to the transcoder, opens it, runs the muxing operation, closes it, and prints the output file path.

# Open transcoder
if not transcoder.open():
    print_error("Open Transcoder", transcoder.error)
    return False

# Run transcoder
if not transcoder.run():
    print_error("Run Transcoder", transcoder.error)
    return False

transcoder.close()

print(f"Output file: {output_file}")

return True

Complete Code

Here’s the complete working example that demonstrates WebM muxing using AVBlocks for Python.

"""
Mux audio and video streams from WebM files into a single WebM container.

This sample shows how to combine separate Vorbis audio and VP8 video streams
from WebM files into a single WebM file.
"""

import os
import sys
import click

from avblocks import (
    Library, Transcoder, MediaSocket, MediaPin,
    AudioStreamInfo, VideoStreamInfo,
    StreamType, ErrorFacility
)


def delete_file(filename: str):
    """Delete a file if it exists."""
    try:
        if os.path.exists(filename):
            os.remove(filename)
    except OSError:
        pass


def print_error(action: str, error) -> None:
    """Print error information."""
    if action:
        print(f"{action}: ", end="")

    if error.facility == ErrorFacility.Success:
        print("Success")
        return

    print(f"{error.message or ''}, facility:{error.facility} code:{error.code} hint:{error.hint or ''}")


def webm_mux(audio_files: tuple, video_files: tuple, output_file: str) -> bool:
    """Mux audio and video files into WebM."""
    # Delete output file if it exists
    delete_file(output_file)

    # Create output directory if it doesn't exist
    output_dir = os.path.dirname(output_file)
    if output_dir and not os.path.exists(output_dir):
        os.makedirs(output_dir)

    transcoder = Transcoder()

    # Transcoder demo mode must be enabled,
    # in order to use the production release for testing (without a valid license)
    transcoder.allow_demo_mode = True

    # Create output socket
    output_socket = MediaSocket()
    output_socket.file = output_file
    output_socket.stream_type = StreamType.WebM

    # audio
    for audio_file in audio_files:
        output_pin = MediaPin()
        asi = AudioStreamInfo()
        asi.stream_type = StreamType.Vorbis
        output_pin.stream_info = asi

        output_socket.pins.add(output_pin)

        input_socket = MediaSocket()
        input_socket.file = audio_file
        input_socket.stream_type = StreamType.WebM
        transcoder.inputs.add(input_socket)

        print(f"Muxing audio input: {audio_file}")

    # video
    for video_file in video_files:
        output_pin = MediaPin()
        vsi = VideoStreamInfo()
        vsi.stream_type = StreamType.VP8
        output_pin.stream_info = vsi

        output_socket.pins.add(output_pin)

        input_socket = MediaSocket()
        input_socket.file = video_file
        input_socket.stream_type = StreamType.WebM
        transcoder.inputs.add(input_socket)

        print(f"Muxing video input: {video_file}")

    transcoder.outputs.add(output_socket)

    # Open transcoder
    if not transcoder.open():
        print_error("Open Transcoder", transcoder.error)
        return False

    # Run transcoder
    if not transcoder.run():
        print_error("Run Transcoder", transcoder.error)
        return False

    transcoder.close()

    print(f"Output file: {output_file}")

    return True


@click.command()
@click.option('-a', '--audio', 'audio_files', multiple=True,
              help='Input Vorbis/WebM files. Could be a list of files.',
              type=click.Path(exists=True))
@click.option('-v', '--video', 'video_files', multiple=True,
              help='Input VP8/WebM files. Could be a list of files.',
              type=click.Path(exists=True))
@click.option('-o', '--output', 'output_file',
              help='Output WebM file',
              type=click.Path())
def main(audio_files: tuple, video_files: tuple, output_file: str):
    """Mux audio and video streams from WebM files into a single WebM container."""

    # Set default options if not provided
    if not audio_files and not video_files:
        script_dir = os.path.dirname(os.path.abspath(__file__))

        video_file = os.path.join(script_dir, "../../assets/vid/big-buck-bunny_trailer_vp8_vorbis.vid.webm")
        audio_file = os.path.join(script_dir, "../../assets/aud/big-buck-bunny_trailer_vp8_vorbis.aud.webm")
        output_file = os.path.join(script_dir, "../../output/mux_webm_file/big-buck-bunny_trailer.webm")

        audio_files = (audio_file,)
        video_files = (video_file,)

        print("Using default options: ")
        print(f"mux_webm_file --audio {audio_file} --video {video_file} --output {output_file}")
        print()

    # Validate options
    error = False

    print("Audio files: ")
    if audio_files:
        for f in audio_files:
            print(f"   {f}")
    else:
        print("[not set]")
        error = True

    print("Video files: ")
    if video_files:
        for f in video_files:
            print(f"   {f}")
    else:
        print("[not set]")
        error = True

    print("Output file: ", end="")
    if not output_file:
        print("[not set]")
        error = True
    else:
        print(output_file)

    if error:
        click.echo("\nUse --help for usage information.")
        sys.exit(1)

    Library.initialize()

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

    result = webm_mux(audio_files, video_files, output_file)

    Library.shutdown()

    sys.exit(0 if result else 2)


if __name__ == '__main__':
    main()

How to Run

See the Python sample for details. The samples are installed as command-line tools when you install the package from the repository root with uv pip install --editable . (see Installation).

Command Line

mux-webm-file --audio <Vorbis_file>.webm --video <VP8_file>.webm --output <output>.webm

Examples

List options:

mux-webm-file --help
Usage: mux-webm-file [OPTIONS]

  Mux audio and video streams from WebM files into a single WebM container.

Options:
  -a, --audio PATH   Input Vorbis/WebM files. Could be a list of files.
  -v, --video PATH   Input VP8/WebM files. Could be a list of files.
  -o, --output PATH  Output WebM file
  --help             Show this message and exit.

Multiplex the WebM files ./assets/aud/big-buck-bunny_trailer_vp8_vorbis.aud.webm and ./assets/vid/big-buck-bunny_trailer_vp8_vorbis.vid.webm into a WebM file big-buck-bunny_trailer.webm:

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

mux-webm-file \
    --audio ./assets/aud/big-buck-bunny_trailer_vp8_vorbis.aud.webm \
    --video ./assets/vid/big-buck-bunny_trailer_vp8_vorbis.vid.webm \
    --output ./output/mux_webm_file/big-buck-bunny_trailer.webm
# Windows
New-Item -ItemType Directory -Force -Path ./output/mux_webm_file

mux-webm-file `
    --audio ./assets/aud/big-buck-bunny_trailer_vp8_vorbis.aud.webm `
    --video ./assets/vid/big-buck-bunny_trailer_vp8_vorbis.vid.webm `
    --output ./output/mux_webm_file/big-buck-bunny_trailer.webm