AVC / H.264 Annex B Decoder (Pull)¶
This article explains how you can use Transcoder.pull to decode an AVC / H.264 elementary stream.
The code snippets in this article are from the dec_avc_pull Python sample.
Video Input¶
As video input we use the foreman_qcif.h264 file from the AVBlocks Assets archive. After downloading and unzipping you will find foreman_qcif.h264 in the vid subdirectory.
Code¶
This code takes an H.264 stream, and decodes it to raw uncompressed YUV frames.
Initialize AVBlocks¶
The first step in any AVBlocks application is to initialize the library. This must be done before using any other AVBlocks functionality. Call Library.shutdown() at the end of the program to clean up resources.
Library.initialize()
# Set license information. Without this AVBlocks runs in Demo mode.
# Library.set_license("<license-string>")
result = decode(input_file, output_file)
Library.shutdown()
sys.exit(0 if result else 2)
Configure Transcoder¶
The input socket points to the H.264 file. The output socket has one raw YUV 4:2:0 video pin and no file, because we pull the decoded frames from the Transcoder.
# Create input socket from file
in_socket = MediaSocket()
in_socket.file = input_file
# Create output socket with one YUV 4:2:0 video pin
out_stream_info = VideoStreamInfo()
out_stream_info.stream_type = StreamType.UncompressedVideo
out_stream_info.color_format = ColorFormat.YUV420
out_stream_info.scan_type = ScanType.Progressive
out_pin = MediaPin()
out_pin.stream_info = out_stream_info
out_socket = MediaSocket()
out_socket.stream_type = StreamType.UncompressedVideo
out_socket.pins.add(out_pin)
# Create transcoder
with Transcoder() as transcoder:
transcoder.allow_demo_mode = True
transcoder.inputs.add(in_socket)
transcoder.outputs.add(out_socket)
if not transcoder.open():
print_error("Transcoder open", transcoder.error)
return False
Call Transcoder.pull¶
Each call to Transcoder.pull returns one raw YUV 4:2:0 frame. We write the frames to the output file until pull returns False, then check the Transcoder error.
yuv_frame = MediaSample()
frame_counter = 0
with open(output_file, 'wb') as outfile:
while True:
res, _ = transcoder.pull(yuv_frame)
if res:
# Each call to transcoder.pull returns a raw YUV 4:2:0 frame
outfile.write(bytes(yuv_frame.buffer.data))
frame_counter += 1
else:
break
print_error("Transcoder pull", transcoder.error)
transcoder.close()
print(f"Frames decoded: {frame_counter}")
print(f"Output file: {output_file}")
Complete Program¶
"""
Decode H.264/AVC file to YUV using pull mode.
This sample decodes an H.264/AVC video file to raw YUV frames using the pull mode
(manually pulling decoded frames and writing to output file).
"""
import os
import sys
import click
from avblocks import (
Library, Transcoder, MediaSocket, MediaPin,
VideoStreamInfo, MediaSample, StreamType,
ColorFormat, ScanType, 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 decode(input_file: str, output_file: str) -> bool:
"""Decode H.264/AVC file to YUV using pull mode."""
# Delete output file if 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)
# Create input socket from file
in_socket = MediaSocket()
in_socket.file = input_file
# Create output socket with one YUV 4:2:0 video pin
out_stream_info = VideoStreamInfo()
out_stream_info.stream_type = StreamType.UncompressedVideo
out_stream_info.color_format = ColorFormat.YUV420
out_stream_info.scan_type = ScanType.Progressive
out_pin = MediaPin()
out_pin.stream_info = out_stream_info
out_socket = MediaSocket()
out_socket.stream_type = StreamType.UncompressedVideo
out_socket.pins.add(out_pin)
# Create transcoder
with Transcoder() as transcoder:
transcoder.allow_demo_mode = True
transcoder.inputs.add(in_socket)
transcoder.outputs.add(out_socket)
if not transcoder.open():
print_error("Transcoder open", transcoder.error)
return False
yuv_frame = MediaSample()
frame_counter = 0
with open(output_file, 'wb') as outfile:
while True:
res, _ = transcoder.pull(yuv_frame)
if res:
# Each call to transcoder.pull returns a raw YUV 4:2:0 frame
outfile.write(bytes(yuv_frame.buffer.data))
frame_counter += 1
else:
break
print_error("Transcoder pull", transcoder.error)
transcoder.close()
print(f"Frames decoded: {frame_counter}")
print(f"Output file: {output_file}")
return True
@click.command()
@click.option('-i', '--input', 'input_file',
help='Input H.264/AVC file',
type=click.Path(exists=True))
@click.option('-o', '--output', 'output_file',
help='Output YUV file',
type=click.Path())
def main(input_file: str, output_file: str):
"""Decode H.264/AVC file to YUV using pull mode."""
# Set default options if not provided
if not input_file or not output_file:
script_dir = os.path.dirname(os.path.abspath(__file__))
if not input_file:
input_file = os.path.join(script_dir, "../../assets/vid/foreman_qcif.h264")
if not output_file:
output_file = os.path.join(script_dir, "../../output/dec_avc_pull/foreman_qcif.yuv")
print("Using default options:")
print(f" --input {input_file}")
print(f" --output {output_file}")
print()
# Validate options
print(f"--input: {input_file}")
print(f"--output: {output_file}")
Library.initialize()
# Set license information. Without this AVBlocks runs in Demo mode.
# Library.set_license("<license-string>")
result = decode(input_file, 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¶
dec-avc-pull --input <file.h264> --output <file.yuv>
Examples¶
List options:
dec-avc-pull --help
Usage: dec-avc-pull [OPTIONS]
Decode H.264/AVC file to YUV using pull mode.
Options:
-i, --input PATH Input H.264/AVC file
-o, --output PATH Output YUV file
--help Show this message and exit.
Decode the input file ./assets/vid/foreman_qcif.h264 into output file ./output/dec_avc_pull/foreman_qcif.yuv:
# Linux and macOS
mkdir -p ./output/dec_avc_pull
dec-avc-pull \
--input ./assets/vid/foreman_qcif.h264 \
--output ./output/dec_avc_pull/foreman_qcif.yuv
# Windows
New-Item -ItemType Directory -Force -Path ./output/dec_avc_pull
dec-avc-pull `
--input ./assets/vid/foreman_qcif.h264 `
--output ./output/dec_avc_pull/foreman_qcif.yuv