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.
1 Answer 1
I've generally found that encoding lossy video with a lossless codec results in a larger file size. FFmpeg has to decompress your input before it can encode to ffv1, so it is working from the uncompressed version of your jpegs. The process is outlined here: https://ffmpeg.org/ffmpeg.html#Detailed-description
-
The link provided the information I needed. Because the jpeg is decompressed then re-compressed by ffv1 its not as small. Unfortunately this does not explain why individual jpeg images compress are better then multi frame compression by ffv1 but its still a fact.user1373984– user13739842016年03月20日 15:30:52 +00:00Commented Mar 20, 2016 at 15:30
-
1If I understand you correctly, you're wondering why -vcodec copy produced a smaller file than -vcodec ffv1? This is because it isn't re-encoding your jpegs,it's just copying the streama,. That's why it's so fast,the bottleneck is how fast your hard drive can copy/paste.kieran– kieran2016年03月20日 16:08:25 +00:00Commented Mar 20, 2016 at 16:08
-framerateinput option (and additionally the-routput option if you want to vary the input vs output frame rate). You can preprocess the JPG images withjpegtranto perform lossless optimization (see example using a bash "for loop") before muxing withffmpeg. Your results may vary depending on the JPG files themselves.