There is no automatic way to do that. You have to look at the parameters of the original file and apply them to the output file.
In most cases, these will be the following:
- Container format (MP4, MKV, …)
- Video and audio codec (H.264, H.265, …)
- Audio-specific:
- Number of audio channels
- Audio sampling rate
- Audio bitrate
- Video-specific:
- Profile and Level (to ensure compatibility, see here)
- Maximum bitrate limitations (e.g. for H.264)
- Maximum video resolution, change via
-filter:v scale
or -s:v
- Framerate, change via
-filter:v fps
-r
- Chroma subsampling, change via
-pix_fmt
(e.g., -pix_fmt yuv420p
should give you the best compatibility)
- GOP size (distance between IDR-frames), set via
-g
- Other specific encoding settings
But even if you get that all right, some devices may require specific, proprietary information embedded in the bitstream.
As for the specific task of using x264
, this is not going to be trivial. I'm not aware of a single script that'd take care of these tasks, which are usually done manually. For the most info about the encoding settings, on Unix/Linux or OS X, you can use mediainfo
with some Bash tricks.
For example, for an x264-encoded video in an MP4 file:
mediainfo input.mp4 | grep "Encoding settings" | cut -d':' -f2- | tr '/' '\n' | sed 's/ //'
This will output a list of x264 options:
cabac=1
ref=3
deblock=1:-1:-1
analyse=0x3:0x113
me=hex
subme=7
psy=1
…
You could then manually pass these options to the x264 binary.
If you go through FFmpeg, that's a little more complicated though, as not all of x264's options can or should be mapped like this. Note that often a simple preset, tune and profile specification will do as well (as seen in x264 --fullhelp
and the x264 encoding guide), and specifying the CRF level is enough.
And this is not even considering audio, where luckily, there aren't that many options.
Adjust the h264 stream to be streamable:
Apparently the h264 codec has a special mode that is required for it to be efficiently streamable and you enable it with: -bsf:v h264_mp4toannexb
Script
The script I use to setup a H264+AAC matroska streaming pipe is this:
#!/bin/bash
# ----------------------------------------------------------------------------
# This script is a helper to transcode a video to H264+AAC with subtitles to a
# Matroska (.mkv) container that is suitable for live streaming to a mobile
# device. It will transcode media that is not H264 or that has too high
# resolution. It will not upsample content.
#
# Other suitable containers (and reasons for not using them) include:
# * ASF (Microsoft, proprietary)
# * MPEG2 Transport Stream (Standard, only supports bitmap subtitles)
# * WebM (Has no support for metadata)
# * DivX (Can't contain H264)
# * FLV (Proprietary Bad support on target device)
# * MP4 (Only bitmap subtitles, didn't work for streaming with FFMPEG)
# * OGG (No support for H264)
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Video options
# ----------------------------------------------------------------------------
LINES=720
# One of: ultrafast,superfast, veryfast, faster, fast, medium, slow, slower,
# veryslow or placebo
PRESET=ultrafast
# One of: baseline, main, high, high10, high422 or high444
PROFILE=high10
# One of: film animation grain stillimage psnr ssim fastdecode zerolatency
TUNE=zerolatency
# ----------------------------------------------------------------------------
# Audio options
# ----------------------------------------------------------------------------
AUDIO="-c:a libfaac -b:a 128k -ar 48000 -ac 2 -async 1"
SUBTITLES="-c:s copy"
# ----------------------------------------------------------------------------
# Read input video parameters
# ----------------------------------------------------------------------------
IN_RESOLUTION=`/usr/bin/ffmpeg -i "${1}" 2>&1 | grep Video | \
perl -lane 'print $1 if /(\d+x\d+)/'`
IN_CODEC=`/usr/bin/ffmpeg -i "${1}" 2>&1 | grep Video | \
perl -lane 'print $1 if /Video: (\S+)/'`
IN_DIMS=(${IN_RESOLUTION//x/ })
V_TRANSCODE="-c:v libx264 -bsf:v h264_mp4toannexb -preset ${PRESET} \
tune ${TUNE} -profile:v ${PROFILE}"
V_COPY="-c:v copy -bsf:v h264_mp4toannexb"
if [ "${IN_DIMS[1]}" > "${LINES}" ]; then
SCALE="-filter:v scale=-1:${LINES} ${OPT_TRANSCODE}"
else
if ["${IN_CODEC}" != "h264" ]; then
VIDEO=$OPT_TRANSCODE
else
VIDEO=$V_COPY
fi
fi
exec /usr/bin/ffmpeg -threads `nproc` -i "${1}" $VIDEO $AUDIO $SUBTITLES \
-f matroska -y "${2}" &> /store/tmp/log
TODO:
Make it read subtitles from external files if found and add them to the matroska stream. Make it not transcode the audio stream if it is already in a suitable format.
Best Answer
UPDATE ON 3 AUGUST 2017: According to a newer answer by user 林正浩, ffmpeg now has support for VP9 encoding through VAAPI. I still don't have the hardware required to test this though so my answer will be of limited help. I'll leave my original answer on how to encode VP9 in software below.
For some reason FFmpeg doesn't support VP9 encoding on Intel's QuickSync hardware encoder, even though they support H.264 and HEVC. A search through the FFmpeg source code repository shows it's not even a matter of it being disabled, the feature just hasn't been implemented yet. But if it does become available at some point in the future, it should be usable in a manner similar to the other QuickSync encoders: a switch like
-c:v vp9_qsv
instead of-c:v libvpx-vp9
should do the job.FFmpeg command line usage is the same on all platforms, with the one notable exception I know of being Windows users having to use
NUL
instead of/dev/null
for output during the first pass of a 2-pass encode. But since you're doing 1-pass and lossless this shouldn't affect you.If you want to speed up your encodes the most obvious thing you should try is setting an encoding speed value with the
-speed
switch. Recommended values are numbers from 0 to 4, with 0 being really, really slow (think-preset placebo
in x264 but worse) but high quality and 4 being fast while being lower quality. ffmpeg uses-speed 1
by default which is a good speed-for-quality tradeoff for lossy encoding. However, I just did a quick lossless encoding test with different speed values and noticed a 32% reduction in file size when going from-speed 1
to-speed 0
with lossless encoding. The encoding time tripled though, so whether using 0 is worth it is up to you. The file produced by-speed 4
was only 1.1% larger than the one produced by-speed 1
though, and it was encoded 43% faster. So I'd say that if you're doing lossless and-speed 0
is too slow you might as well use-speed 4
.Another important encoding performance increase is turning on multi-threading with the
-threads
switch; libvpx doesn't automatically use multiple threads so this must be set manually by the user. You should also set the number of tile columns with the-tile-columns
switch. This option makes libvpx divide the video into multiple tiles and encode these tiles in parallel for better multi-threading. You can find recommended numbers for the amount of tile columns and threads in the "Tiling and Threading Recommendations" section of Google's VP9 encoding guide. As you can see, the number of threads used goes up with the number of tiles, which means that depending on the number of CPU cores available your processor might not be fully saturated while encoding sub-HD-resolution video. If you mainly encode low-resolution videos you might want to consider encoding multiple files at the same time.However, there is yet another way to speed up VP9 encoding: multi-threading within a single column tile that can by turned on with
-row mt 1
. As of April 4 (2017, hello future people), it isn't part of a released version of libvpx but will most likely be in libvpx 1.6.2. If you want to try it out before the next release you need to compile recent git versions of libvpx and ffmpeg from source. Just follow FFmpeg's compilation guide for your distro of choice but instead of downloading and extracting a release tarball dogit pull https://chromium.googlesource.com/webm/libvpx
instead.As for the
veryslow
preset, that's only used in x264 and x265. libvpx uses the-speed
switch and additionally the-quality best
,-quality good
, or-quality realtime
options to define how much time the encoder is allowed to spend encoding a frame. The default is-quality good
because-quality best
is so slow it's unusable and-quality realtime
is meant to be used for time-critical applications like video calls and livestreaming.