Create Video From JPEG Images¶
How to use the Transcoder to make a video from a sequence of JPEG images.
The code snippets in this article are from the slideshow sample app.
Create a Transcoder object¶
transcoder = Transcoder()
# In order to use the OEM release for testing (without a valid license),
# the transcoder demo mode must be enabled.
transcoder.allow_demo_mode = True
Configure inputs and outputs¶
Use the MediaSocket and MediaPin classes to configure the Transcoder inputs and outputs.
Configure inputs¶
Detect the input stream¶
If the input is a media file, you can use MediaInfo to read the stream information from the file. For images MediaInfo will return a VideoStreamInfo object readily configured with the image attributes. AVBlocks supports BMP, JPEG, PNG, and TIFF images.
# Configure Input
media_info = MediaInfo()
media_info.inputs[0].file = get_image_path(input_dir, 0)
result = media_info.open()
print_error("Open MediaInfo", media_info.error)
if not result:
return False
vid_info = media_info.outputs[0].pins[0].stream_info.clone()
vid_info.frame_rate = input_frame_rate
Configure the input socket¶
This requires the following steps:
Get a VideoStreamInfo object from MediaInfo
Create a MediaPin object and set the
MediaPin.stream_infoproperty.Create a MediaSocket object and add the pin to
MediaSocket.pins.Add the socket to
Transcoder.inputs.
pin = MediaPin()
pin.stream_info = vid_info
socket = MediaSocket()
socket.pins.add(pin)
transcoder.inputs.add(socket)
media_info.close()
Configure outputs¶
This requires the following steps:
Call the
MediaSocket.from_presetstatic method to create a MediaSocket object from a predefined presetSet the
MediaSocket.fileproperty to the output filename.Add the socket to
Transcoder.outputs.
# Configure Output
socket = MediaSocket.from_preset(preset_id)
socket.file = out_filename
transcoder.outputs.add(socket)
Encode images¶
This requires the following steps:
Call
Transcoder.openCall
Transcoder.pushfor each imageCall
Transcoder.flush(always needed after pushing)Call
Transcoder.close
# Encode Images
result = transcoder.open()
print_error("Open Transcoder", transcoder.error)
if not result:
return False
for i in range(image_count):
image_path = get_image_path(input_dir, i)
with open(image_path, 'rb') as f:
image_data = f.read()
media_buffer = MediaBuffer(image_data)
media_sample = MediaSample()
media_sample.start_time = i / input_frame_rate
media_sample.buffer = media_buffer
if not transcoder.push(0, media_sample):
print_error("Push Transcoder", transcoder.error)
return False
result = transcoder.flush()
print_error("Flush Transcoder", transcoder.error)
if not result:
return False
transcoder.close()
Pushing image data¶
The code that pushes the image data is fairly simple.
For each image:
Create a MediaBuffer object and load it with the image data.
Create a MediaSample, set the sample start time, and set the buffer with the image data.
Call
Transcoder.pushand provide the media sample object.
for i in range(image_count):
image_path = get_image_path(input_dir, i)
with open(image_path, 'rb') as f:
image_data = f.read()
media_buffer = MediaBuffer(image_data)
media_sample = MediaSample()
media_sample.start_time = i / input_frame_rate
media_sample.buffer = media_buffer
if not transcoder.push(0, media_sample):
print_error("Push Transcoder", transcoder.error)
return False
When you are done pushing, call Transcoder.flush to make sure all buffered data is processed.
result = transcoder.flush()
print_error("Flush Transcoder", transcoder.error)
if not result:
return False
Complete Program¶
Here is the complete sample:
"""
Create a video slideshow from a series of images.
Shows how to make a video clip from a sequence of images.
The input is a series of JPEG images. The output is configured with an AVBlocks preset.
"""
import os
import sys
import click
from avblocks import (
Library, Transcoder, MediaInfo, MediaSocket, MediaPin,
MediaBuffer, MediaSample, Preset, ErrorFacility
)
# Preset descriptors (name, file extension)
PRESETS = [
(Preset.Video.DVD.NTSC_16x9_MP2, "mpg"),
(Preset.Video.DVD.NTSC_16x9_PCM, "mpg"),
(Preset.Video.DVD.NTSC_4x3_MP2, "mpg"),
(Preset.Video.DVD.NTSC_4x3_PCM, "mpg"),
(Preset.Video.DVD.PAL_16x9_MP2, "mpg"),
(Preset.Video.DVD.PAL_4x3_MP2, "mpg"),
(Preset.Video.iPad.H264_576p, "mp4"),
(Preset.Video.iPad.H264_720p, "mp4"),
(Preset.Video.iPad.MPEG4_480p, "mp4"),
(Preset.Video.iPhone.H264_480p, "mp4"),
(Preset.Video.iPhone.MPEG4_480p, "mp4"),
(Preset.Video.iPod.H264_240p, "mp4"),
(Preset.Video.iPod.MPEG4_240p, "mp4"),
(Preset.Video.Generic.MP4.Base_H264_AAC, "mp4"),
(Preset.Video.AppleLiveStreaming.H264_480p, "ts"),
(Preset.Video.AppleLiveStreaming.H264_720p, "ts"),
(Preset.Video.AndroidPhone.H264_360p, "mp4"),
(Preset.Video.AndroidPhone.H264_720p, "mp4"),
(Preset.Video.AndroidTablet.H264_720p, "mpg"),
(Preset.Video.AndroidTablet.WebM_VP8_720p, "webm"),
(Preset.Video.VCD.NTSC, "mpg"),
(Preset.Video.VCD.PAL, "mpg"),
(Preset.Video.Generic.WebM.Base_VP8_Vorbis, "webm"),
]
def get_preset_by_name(preset_name: str):
"""Get preset descriptor by name."""
for name, ext in PRESETS:
if name.lower() == preset_name.lower():
return (name, ext)
return None
def print_presets():
"""Print supported presets."""
print()
print("PRESETS")
print("-----------")
for name, ext in PRESETS:
print(f"{name:<30} .{ext}")
print()
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 get_image_path(input_dir: str, image_number: int) -> str:
"""Get image file path for a given image number."""
return os.path.join(input_dir, f"cube{image_number:04d}.jpeg")
def encode(input_dir: str, output_file: str, preset_id: str, file_extension: str) -> bool:
"""Encode images into a video slideshow."""
out_filename = f"{output_file}.{file_extension}"
image_count = 250
input_frame_rate = 25.0
transcoder = Transcoder()
# In order to use the OEM release for testing (without a valid license),
# the transcoder demo mode must be enabled.
transcoder.allow_demo_mode = True
try:
delete_file(out_filename)
# Create output directory if it doesn't exist
output_dir = os.path.dirname(out_filename)
if output_dir and not os.path.exists(output_dir):
os.makedirs(output_dir)
# Configure Input
media_info = MediaInfo()
media_info.inputs[0].file = get_image_path(input_dir, 0)
result = media_info.open()
print_error("Open MediaInfo", media_info.error)
if not result:
return False
vid_info = media_info.outputs[0].pins[0].stream_info.clone()
vid_info.frame_rate = input_frame_rate
pin = MediaPin()
pin.stream_info = vid_info
socket = MediaSocket()
socket.pins.add(pin)
transcoder.inputs.add(socket)
media_info.close()
# Configure Output
socket = MediaSocket.from_preset(preset_id)
socket.file = out_filename
transcoder.outputs.add(socket)
# Encode Images
result = transcoder.open()
print_error("Open Transcoder", transcoder.error)
if not result:
return False
for i in range(image_count):
image_path = get_image_path(input_dir, i)
with open(image_path, 'rb') as f:
image_data = f.read()
media_buffer = MediaBuffer(image_data)
media_sample = MediaSample()
media_sample.start_time = i / input_frame_rate
media_sample.buffer = media_buffer
if not transcoder.push(0, media_sample):
print_error("Push Transcoder", transcoder.error)
return False
result = transcoder.flush()
print_error("Flush Transcoder", transcoder.error)
if not result:
return False
transcoder.close()
print(f'Output video: "{out_filename}"')
except Exception as ex:
print(str(ex))
return False
return True
@click.command()
@click.option('-i', '--input', 'input_dir',
help='Input directory containing images for the slideshow.')
@click.option('-o', '--output', 'output_file',
help='Output filename (without extension). The extension is added based on the preset.')
@click.option('-p', '--preset', 'preset_id',
help='output preset id.')
@click.option('--presets', 'list_presets', is_flag=True,
help='list supported input presets')
def main(input_dir: str, output_file: str, preset_id: str, list_presets: bool):
"""Create a video slideshow from a series of JPEG images."""
if list_presets:
print_presets()
sys.exit(0)
# Set default options if not provided
if not input_dir:
script_dir = os.path.dirname(os.path.abspath(__file__))
input_dir = os.path.join(script_dir, "../../assets/img")
output_file = os.path.join(script_dir, "../../output/slideshow/cube")
preset_id = Preset.Video.Generic.MP4.Base_H264_AAC # "mp4.h264.aac"
print("Using default options: ")
print(f" --input {input_dir} --output {output_file} --preset {preset_id}")
# Validate options
error = False
print("Input dir: ", end="")
if not input_dir:
print("[not set]")
error = True
else:
print(input_dir)
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)
# Get filename extension from preset descriptor
preset = get_preset_by_name(preset_id)
if preset is None:
print("\nPreset not found!")
sys.exit(1)
file_extension = preset[1]
Library.initialize()
# Set license information. To run AVBlocks in demo mode, comment the next line out
# Library.set_license("<license-string>")
result = encode(input_dir, output_file, preset_id, file_extension)
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¶
slideshow --input <directory> --output <filename_without_extension> --preset <preset_id>
Examples¶
List options:
slideshow --help
Usage: slideshow [OPTIONS]
Create a video slideshow from a series of JPEG images.
Options:
-i, --input TEXT Input directory containing images for the slideshow.
-o, --output TEXT Output filename (without extension). The extension is
added based on the preset.
-p, --preset TEXT output preset id.
--presets list supported input presets
--help Show this message and exit.
List supported presets:
slideshow --presets
Create an MP4 / H.264 clip from a sequence of images in the ./assets/img folder using the mp4.h264.aac preset:
# Linux and macOS
mkdir -p ./output/slideshow
slideshow \
--input ./assets/img \
--output ./output/slideshow/cube \
--preset mp4.h264.aac
# Windows
New-Item -ItemType Directory -Force -Path ./output/slideshow
slideshow `
--input ./assets/img `
--output ./output/slideshow/cube `
--preset mp4.h264.aac