Optimize video filesize without quality loss
Is there a simple way (on the command line - I want to write a script which compresses all videos in a folder) to reduce the filesize of a video (almost) without quality loss? Is there a method which works equally well for different video format (mp4, flv, m4v, mpg, mov, avi)?
I should mention that most of the videos I would like to compress are downloaded web-videos (mp4, flv), so it's not clear if there is much room for further compression.
In the mystical land of the PNG, the most effective way of near-lossless compression is to compress the image with almost every possible combination of settings and compare the output. This is what applications like pngcrush
do.
There's no reason why a similar process wouldn't work, on paper. In practice, there are a few issues:
- Compressing video once is already a much longer process than encoding a PNG image.
- There are about 1000 times as many configuration combinations.
- Those two conspire to make the process exponentially longer by several powers.
- It would also use an unreasonable amount of disk space, memory and CPU time.
- Frames blend if there's a framerate change and obviously change if you change the frame size or crop, making comparison process even harder
To top it all off, you're dealing with content that's already poisoned with artefacts and encoding sludge. My simple answer for "Is there a simple way" has to be No.
But if you have a shed load of low-compression videos like FLV (a fairly rubbish compression in my experience with the format), it might be worth having a go with ffmpeg
or mencoder
for the FLVs. Something like this might work:
find -iname '*.flv' -exec \
mencoder {} -o {}.recomp.avi -ovc x264 -x264encopts threads=9:bitrate=400 -oac mp3lame -lameopts abr:br=52 \;
But there will be loss. You just have to judge how much is acceptable.
You could also accomplish this with avconv based onavconv -i <sourcefile> -c:v libx264 -crf 23 output.mp4
you can adjust the quality upwards by reducing the crf factor (23 in the example) and adjust downwards by increasing it.
You can set the values between 0 and 51, where lower values would result in better quality (at the expense of higher file sizes). Sane values are between 18 and 28. The default for x264 is 23, so you can use this as a starting point.
A script that should allow you to change to a different format with no loss of quality (but little or no reduction in size would be
#!/bin/bash
echo "This script will attempt to copy the video and audio streams of all non-mkv files in the current directory to a mkv video container of the same name as the sources without overwriting."
read -p "Press any key to continue or CTRL-C to cancel"
for f in *.*
do
name=$(echo "$f" | sed 's/\.[^\.]*$//')
ext=$(echo "$f" | sed 's/^.*\.//')
echo "$f is made up of the base $name and ends with $ext"
target="$name.mkv"
echo target = $target
if [ "$f" = "$target" ];
then
echo "$f=$target skipping overwrite"
else
avconv -i "$f" -c:a copy -c:v copy "$name.mkv"
fi
done
Note that the audio and video codecs in use in this script are copy
so no re-encoding should take place. Any savings in size will be minimal and will only be the result of a more efficient container than previously in use. To obtain a reduction in size, re-encoding is required and you'd need to adjust the script by replacing c:v copy
with -c:v libx264 -crf 23
(adjusting the -crf value to your needs as mentioned above). Feel free to comment out or remove any superfluous echo lines.
Note: The formats you mention "mp4, flv, m4v, mpg, mov, avi" are containers and have little or no bearing on the codecs used to encode the streams within them. A discussion regarding re-encoding for size reduction would require knowledge of the codecs contained in the containers and would have to be evaluated on a case by case basis.
Sources: Testing and http://slhck.info/articles/crf