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 Python 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.
Library.initialize()
# Set license information. Without this AVBlocks runs in Demo mode.
# Library.set_license("<license-string>")
result = demux_webm(input_file, output_base)
Library.shutdown()
sys.exit(0 if result else 2)
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.
media_info = MediaInfo()
media_info.inputs[0].file = input_file
if not media_info.open():
print_error("MediaInfo open", media_info.error)
return None
in_socket = MediaSocket.from_media_info(media_info)
media_info.close()
transcoder = Transcoder()
transcoder.allow_demo_mode = True
transcoder.inputs.add(in_socket)
Select Audio and Video Streams¶
The sample keeps the first audio stream and the first video stream. Any additional pins are disabled.
audio = False
video = False
for pin in in_socket.pins:
stream_info = pin.stream_info
if stream_info.media_type == MediaType.Audio and not audio:
audio = True
filename = output_base + ".aud.webm"
elif stream_info.media_type == MediaType.Video and not video:
video = True
filename = output_base + ".vid.webm"
else:
# Disable this pin - we don't need other streams
pin.connection = PinConnection.Disabled
continue
out_socket = MediaSocket()
out_socket.pins.add(pin)
delete_file(filename)
out_socket.file = filename
transcoder.outputs.add(out_socket)
print(f"Output file: {filename}")
return transcoder
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.
def demux_webm(input_file: str, output_base: str) -> bool:
"""Demux WebM file into separate audio and video files."""
# Create output directory if needed
output_dir = os.path.dirname(output_base)
if output_dir and not os.path.exists(output_dir):
os.makedirs(output_dir)
transcoder = generate_output_transcoder(input_file, output_base)
if transcoder is None:
return False
with transcoder:
if not transcoder.open():
print_error("Transcoder open", transcoder.error)
return False
if not transcoder.run():
print_error("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 Python.
"""
Demux WebM file.
This sample demuxes a WebM file into separate audio and video WebM files.
"""
import os
import sys
import click
from avblocks import (
Library, Transcoder, MediaInfo, MediaSocket,
MediaType, ErrorFacility, PinConnection
)
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 generate_output_transcoder(input_file: str, output_base: str) -> Transcoder:
"""Generate transcoder with output filenames based on stream types."""
media_info = MediaInfo()
media_info.inputs[0].file = input_file
if not media_info.open():
print_error("MediaInfo open", media_info.error)
return None
in_socket = MediaSocket.from_media_info(media_info)
media_info.close()
transcoder = Transcoder()
transcoder.allow_demo_mode = True
transcoder.inputs.add(in_socket)
audio = False
video = False
for pin in in_socket.pins:
stream_info = pin.stream_info
if stream_info.media_type == MediaType.Audio and not audio:
audio = True
filename = output_base + ".aud.webm"
elif stream_info.media_type == MediaType.Video and not video:
video = True
filename = output_base + ".vid.webm"
else:
# Disable this pin - we don't need other streams
pin.connection = PinConnection.Disabled
continue
out_socket = MediaSocket()
out_socket.pins.add(pin)
delete_file(filename)
out_socket.file = filename
transcoder.outputs.add(out_socket)
print(f"Output file: {filename}")
return transcoder
def demux_webm(input_file: str, output_base: str) -> bool:
"""Demux WebM file into separate audio and video files."""
# Create output directory if needed
output_dir = os.path.dirname(output_base)
if output_dir and not os.path.exists(output_dir):
os.makedirs(output_dir)
transcoder = generate_output_transcoder(input_file, output_base)
if transcoder is None:
return False
with transcoder:
if not transcoder.open():
print_error("Transcoder open", transcoder.error)
return False
if not transcoder.run():
print_error("Transcoder run", transcoder.error)
return False
transcoder.close()
return True
@click.command()
@click.option('-i', '--input', 'input_file',
help='Input WebM file',
type=click.Path(exists=True))
@click.option('-o', '--output', 'output_base',
help='Output file base name (without extension)',
type=click.Path())
def main(input_file: str, output_base: str):
"""Demux WebM file into separate audio and video files."""
# Set default options if not provided
if not input_file or not output_base:
script_dir = os.path.dirname(os.path.abspath(__file__))
if not input_file:
input_file = os.path.join(script_dir, "../../assets/mov/big-buck-bunny_trailer_vp8_vorbis.webm")
if not output_base:
output_base = os.path.join(script_dir, "../../output/demux_webm_file/big-buck-bunny_trailer_vp8_vorbis")
print("Using default options:")
print(f" --input {input_file}")
print(f" --output {output_base}")
print()
# Validate options
print(f"Input file: {input_file}")
print(f"Output file: {output_base}")
Library.initialize()
# Set license information. Without this AVBlocks runs in Demo mode.
# Library.set_license("<license-string>")
result = demux_webm(input_file, output_base)
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¶
demux-webm-file --input <webm file> --output <output filename without extension>
Examples¶
List options:
demux-webm-file --help
Usage: demux-webm-file [OPTIONS]
Demux WebM file into separate audio and video files.
Options:
-i, --input PATH Input WebM file
-o, --output PATH Output file base name (without extension)
--help Show this message and exit.
Extract the audio and video streams from ./assets/mov/big-buck-bunny_trailer_vp8_vorbis.webm and save to ./output/demux_webm_file/big-buck-bunny_trailer_vp8_vorbis.aud.webm and ./output/demux_webm_file/big-buck-bunny_trailer_vp8_vorbis.vid.webm:
# Linux and macOS
mkdir -p ./output/demux_webm_file
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
New-Item -ItemType Directory -Force -Path ./output/demux_webm_file
demux-webm-file `
--input ./assets/mov/big-buck-bunny_trailer_vp8_vorbis.webm `
--output ./output/demux_webm_file/big-buck-bunny_trailer_vp8_vorbis