Is it possible for FFmpeg / ffv1.3 / mkv to create a lossless video is not larger than the sum of its parts (and if so, how) and if not, what is a good alternative to come close on size? The video must be lossless.
I am attempting to use FFmpeg to create a mkv with ffv1.3 on a set of similar jpeg images (images from a web cam). I am able to create the video however, it at least 2 times the size of the collection of jpeg images. I would have expected about the same level of compression or better.
I do understand that jpeg on its own is optimized for compression. However a large set of very similar images should be able to be further compressed, or in this case similar to the original images added up.
The commands I am using to create the file are:
ffmpeg -pattern_type glob -i '*.jpg' -vcodec ffv1 -level 3 -an -pass 1 -passlogfile passlog -f matroska /dev/null
ffmpeg -pattern_type glob -i '*.jpg' -vcodec ffv1 -level 3 -an -pass 2 -passlogfile passlog -f matroska ~/foo.mkv
The input files are 24M in total but the resulting mkv is 96M (per du -h).
The results from the second pass:
ffmpeg version N-79053-g7eedad9 Copyright (c) 2000-2016 the FFmpeg developers
built with gcc 4.6 (Debian 4.6.3-14+rpi1)
configuration: --enable-cross-compile --cross-prefix= --arch=armel --target-os=linux --prefix=/my/path/were/i/keep/built/arm/stuff
libavutil 55. 19.100 / 55. 19.100
libavcodec 57. 28.103 / 57. 28.103
libavformat 57. 28.101 / 57. 28.101
libavdevice 57. 0.101 / 57. 0.101
libavfilter 6. 39.102 / 6. 39.102
libswscale 4. 0.100 / 4. 0.100
libswresample 2. 0.101 / 2. 0.101
Input #0, image2, from '*.jpg':
Duration: 00:00:21.32, start: 0.000000, bitrate: N/A
Stream #0:0: Video: mjpeg, yuvj422p(pc, bt470bg/unknown/unknown), 640x480, 25 fps, 25 tbr, 25 tbn, 25 tbc
File '/home/pi/foo.mkv' already exists. Overwrite ? [y/N] y
[swscaler @ 0x3671f30] deprecated pixel format used, make sure you did set range correctly
Output #0, matroska, to '/home/pi/foo.mkv':
Metadata:
encoder : Lavf57.28.101
Stream #0:0: Video: ffv1 (FFV1 / 0x31564646), yuv422p, 640x480, q=2-31, pass 2, 200 kb/s, 25 fps, 1k tbn, 25 tbc
Metadata:
encoder : Lavc57.28.103 ffv1
Stream mapping:
Stream #0:0 -> #0:0 (mjpeg (native) -> ffv1 (native))
Press [q] to stop, [?] for help
frame= 533 fps=4.2 q=-0.0 Lsize= 97815kB time=00:00:21.32 bitrate=37584.4kbits/s speed=0.168x
video:97808kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.006607%
Each jpeg is approximately 43143 bytes (per ls -al) but running the following command the tool states each image is 614400 bytes (both in the video and the independent jpeg images). I would assume this is the uncompresses size of the image (640x480x2).
ffmpeg -pattern_type glob -i '*.jpg' -f framemd5 -
results below are for the images, same sizes are reported for the mkv.
ffmpeg version N-79053-g7eedad9 Copyright (c) 2000-2016 the FFmpeg developers
built with gcc 4.6 (Debian 4.6.3-14+rpi1)
configuration: --enable-cross-compile --cross-prefix= --arch=armel --target-os=linux --prefix=/my/path/were/i/keep/built/arm/stuff
libavutil 55. 19.100 / 55. 19.100
libavcodec 57. 28.103 / 57. 28.103
libavformat 57. 28.101 / 57. 28.101
libavdevice 57. 0.101 / 57. 0.101
libavfilter 6. 39.102 / 6. 39.102
libswscale 4. 0.100 / 4. 0.100
libswresample 2. 0.101 / 2. 0.101
Input #0, image2, from '*.jpg':
Duration: 00:00:21.32, start: 0.000000, bitrate: N/A
Stream #0:0: Video: mjpeg, yuvj422p(pc, bt470bg/unknown/unknown), 640x480, 25 fps, 25 tbr, 25 tbn, 25 tbc
#format: frame checksums
#version: 1
#hash: MD5
#software: Lavf57.28.101
#tb 0: 1/25
#stream#, dts, pts, duration, size, hash
Output #0, framemd5, to 'pipe:':
Metadata:
encoder : Lavf57.28.101
Stream #0:0: Video: rawvideo (Y42B / 0x42323459), yuvj422p, 640x480, q=2-31, 200 kb/s, 25 fps, 25 tbn, 25 tbc
Metadata:
encoder : Lavc57.28.103 rawvideo
Stream mapping:
Stream #0:0 -> #0:0 (mjpeg (native) -> rawvideo (native))
Press [q] to stop, [?] for help
0, 0, 0, 1, 614400, 2148ff8b95296f4bacc1099d13235063
0, 1, 1, 1, 614400, da330e2acd2f7d7a1e79fe79fccc90cf
0, 2, 2, 1, 614400, f55563c13a9f232ce32419fa26e37214
0, 3, 3, 1, 614400, c53d994fff0a9b26529fa8763deae520
EDIT: The best compression (and good speed) came from a container of the jpeg images themself:
ffmpeg -pattern_type glob -i '*.jpg' -vcodec copy -an -f matroska ~/foo.mkv
The resulting file was just over 24M (vs. the 96M ffv1). Also the decompressed jpeg images was 312M, in both cases they compressed fairly well. If I had started with anything other than a jpeg, ffv1 would have been ideal but in my case the lossy jpeg where also the lossless initial images.
-framerate
input option (and additionally the-r
output option if you want to vary the input vs output frame rate). You can preprocess the JPG images withjpegtran
to perform lossless optimization (see example using a bash "for loop") before muxing withffmpeg
. Your results may vary depending on the JPG files themselves.