Writing Videos
Note: Writing of audio streams is not yet implemented
Single-step Encoding
Videos can be encoded directly from image stack using encodevideo(filename::String,imgstack::Array)
where imgstack
is an array of image arrays with identical type and size.
For instance, say an image stack has been constructed from reading a series of image files 1.png
, 2.png
,3.png
etc. :
using FileIO
imgnames = filter(x->occursin(".png",x),readdir()) # Populate list of all .pngs
intstrings = map(x->split(x,".")[1],imgnames) # Extract index from filenames
p = sortperm(parse.(Int,intstrings)) #sort files numerically
imgstack = []
for imgname in imgnames[p]
push!(imgstack,load(imgname))
end
The entire image stack can be encoded in a single step:
using VideoIO
props = [:priv_data => ("crf"=>"22","preset"=>"medium")]
encodevideo("video.mp4",imgstack,framerate=30,AVCodecContextProperties=props)
[ Info: Video file saved: /Users/username/Documents/video.mp4
[ Info: frame= 100 fps=0.0 q=-1.0 Lsize= 129867kB time=00:00:03.23 bitrate=329035.1kbits/s speed=8.17x
[ Info: video:129865kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.001692%
VideoIO.encodevideo
— Functionencodevideo(filename::String,imgstack::Array;
AVCodecContextProperties = AVCodecContextPropertiesDefault,
codec_name = "libx264",
framerate = 24)
Encode image stack to video file and return filepath.
Iterative Encoding
Alternatively, videos can be encoded iteratively within custom loops. The encoding steps follow:
- Initialize encoder with
prepareencoder
- Iteratively add frames with
appendencode
- End the encoding with
finishencode
- Multiplex the stream into a video container with
mux
For instance:
using VideoIO
framestack = map(x->rand(UInt8, 100, 100), 1:100) #vector of 2D arrays
props = [:priv_data => ("crf"=>"22","preset"=>"medium")]
framerate=24
encoder = prepareencoder(framestack[1], framerate=framerate, AVCodecContextProperties=props)
open("temp.stream", "w") do io
for i in 1:length(framestack)
appendencode!(encoder, io, framestack[i], i)
end
finishencode!(encoder, io)
end
mux("temp.stream", "video.mp4", framerate) #Multiplexes the stream into a video container
A working example to save a series of png files as a video:
using VideoIO, ProgressMeter
dir = "" #path to directory holding images
imgnames = filter(x->occursin(".png",x),readdir(dir)) # Populate list of all .pngs
intstrings = map(x->split(x,".")[1],imgnames) # Extract index from filenames
p = sortperm(parse.(Int,intstrings)) #sort files numerically
imgnames = imgnames[p]
filename = "video.mp4"
framerate = 24
props = [:priv_data => ("crf"=>"22","preset"=>"medium")]
firstimg = load(joinpath(dir,imgnames[1]))
encoder = prepareencoder(firstimg, framerate=framerate, AVCodecContextProperties=props)
io = Base.open("temp.stream","w")
@showprogress "Encoding video frames.." for i in 1:length(imgnames)
img = load(joinpath(dir,imgnames[i]))
appendencode!(encoder, io, img, i)
end
finishencode!(encoder, io)
close(io)
mux("temp.stream",filename,framerate) #Multiplexes the stream into a video container
VideoIO.prepareencoder
— Functionprepareencoder(firstimg;framerate=30,AVCodecContextProperties=[:priv_data => ("crf"=>"22","preset"=>"medium")],codec_name::String="libx264")
Prepare encoder and return AV objects.
VideoIO.appendencode!
— Functionappendencode(encoder::VideoEncoder, io::IO, img, index::Integer)
Send image object to ffmpeg encoder and encode
VideoIO.finishencode!
— Functionfinishencode(encoder::VideoEncoder, io::IO)
End encoding by sending endencode package to ffmpeg, and close objects.
VideoIO.mux
— Functionmux(srcfilename,destfilename,framerate;silent=false,deletestream=true)
Multiplex stream file into video container. Deletes stream file by default.
Supported Colortypes
Encoding of the following image element color types currently supported:
UInt8
Gray{N0f8}
RGB{N0f8}
Encoder Settings
The AVCodecContextProperties
object allows control of the majority of settings required. Optional fields can be found here.
A few helpful presets for h264:
Goal | AVCodecContextProperties value |
---|---|
Perceptual compression, h264 default. Best for most cases | [:priv_data => ("crf"=>"23","preset"=>"medium") |
Lossless compression. Fastest, largest file size | [:priv_data => ("crf"=>"0","preset"=>"ultrafast")] |
Lossless compression. Slowest, smallest file size | [:priv_data => ("crf"=>"0","preset"=>"ultraslow")] |
Direct control of bitrate and frequency of intra frames (every 10) | [:bit_rate => 400000,:gop_size = 10,:max_b_frames=1] |
Lossless Encoding
Lossless RGB
If lossless encoding of RGB{N0f8}
is required, true lossless requires using codec_name = "libx264rgb"
, to avoid the lossy RGB->YUV420 conversion, and "crf" => "0"
.
Lossless Grayscale
If lossless encoding of Gray{N0f8}
or UInt8
is required, "crf" => "0"
should be set, as well as :color_range=>2
to ensure full 8-bit pixel color representation. i.e. [:color_range=>2, :priv_data => ("crf"=>"0","preset"=>"medium")]
Encoding Performance
See examples/lossless_video_encoding_testing.jl
for testing of losslessness, speed, and compression as a function of h264 encoding preset, for 3 example videos.