You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Python bindings around FFmpeg's libav* — open / inspect / decode / encode / mux audio + video + subtitle containers. Cross-compiled with a custom FFmpeg build for iOS arm64. Used by pydub, moviepy, image-processing pipelines, and any code that needs to touch a media container at the frame level.
importav# Inspect a media containercontainer=av.open('/path/Documents/video.mp4')
forsincontainer.streams:
print(f"{s.type}: {s.codec_context.name}{s.codec_context.width}x{s.codec_context.height}"ifs.type=="video"elsef"{s.type}: {s.codec_context.name}{s.codec_context.sample_rate}Hz")
# Decode video frames to PIL Imagesforframeincontainer.decode(video=0):
img=frame.to_image() # PIL.Imagearr=frame.to_ndarray(format="rgb24") # numpy HxWx3 uint8break# Decode audio to numpyforframeincontainer.decode(audio=0):
samples=frame.to_ndarray() # shape: (channels, samples)breakcontainer.close()
# Encode a sequence of numpy frames to MP4 via VideoToolboximportav, numpyasnpout=av.open('/path/Documents/out.mp4', mode='w')
stream=out.add_stream('h264_videotoolbox', rate=30) # iOS HW encoderstream.width=640stream.height=480stream.pix_fmt='yuv420p'foriinrange(60):
img=np.random.randint(0, 256, (480, 640, 3), dtype=np.uint8)
frame=av.VideoFrame.from_ndarray(img, format='rgb24')
forpacketinstream.encode(frame):
out.mux(packet)
forpacketinstream.encode(): # flushout.mux(packet)
out.close()
iOS notes
FFmpeg build
The bundled libav* is a custom GPL-free FFmpeg build cross-compiled
for arm64-apple-ios17.0. Native libraries are linked statically into
the SPM PyAV target. The 17 .so files in av/ are thin Cython
wrappers (~2-12 MB each).
av._core.library_versions returns the linked FFmpeg version tuple
at runtime; av._core.ffmpeg_version_info is the human-readable
string ("FFmpeg 7.x (offlinai-ios)").
Codec availability
Codec
Decode
Encode
Notes
H.264
yes (h264)
yes (h264_videotoolbox)
HW encoder via Apple VideoToolbox; libx264 NOT bundled (GPL)
H.265 / HEVC
yes (hevc)
yes (hevc_videotoolbox)
HW encoder
AV1
yes (av1, libdav1d)
no
Decode-only
VP8 / VP9
yes (vp8, vp9)
software
libvpx not bundled — encode falls back to internal
MPEG-4
yes
yes (mpeg4 software)
Always-available software fallback for encode
AAC
yes
yes
Both directions
Opus
yes
yes (libopus bundled)
MP3
yes
no
libmp3lame not bundled (license)
FLAC, ALAC, PCM
yes
yes
av.codecs_available returns the live list at runtime — call it on
device for the authoritative set.
Hardware acceleration
iOS HW encoders (h264_videotoolbox, hevc_videotoolbox) work for
encoding only. HW decoders aren't exposed through PyAV's hwaccel
API on iOS — decode is done in software. For most decode workloads
(< 1080p30) the CPU keeps up; 4K decode is slow.
manim uses h264_videotoolbox automatically on iOS — see
manim.md. Override with
OFFLINAI_MANIM_SOFTWARE_ENCODER=1 to force mpeg4 software encoding
(more deterministic memory profile for long scenes).
Devices
av.enumerate_input_devices() / enumerate_output_devices() return
empty on iOS — PyAV's device backends (avfoundation, v4l2, dshow)
aren't compiled in. For live camera capture, use Swift's AVFoundation
bridge frames into Python via numpy.
Pickling / threading
av.VideoFrame and av.Packet are not pickleable across processes
— but iOS has no multiprocessing anyway.
Encode/decode releases the GIL during the FFmpeg call. Safe to use
from a Python thread; the host app's UI stays responsive.
Limitations
No libx264, libx265, libmp3lame — GPL/license issues with iOS
App Store distribution. Use *_videotoolbox for H.264/HEVC encode;
use AAC/Opus for audio encode.
No streaming protocols beyond file:// and http(s):// —
RTMP, RTSP, SRT not enabled in the iOS FFmpeg build.
av.open(file_like) works via the pyio bridge but has higher
overhead than a real path. Prefer disk paths.
No subprocess fallback for codecs — the ffmpeg CLI isn't on iOS.
Everything goes through libav* directly.