Simple configurable bulk ffmpeg-based video converter.
It uses YAML configuration for generating several output files
from one (or more) input video.
Each output file is created according to profile
subsection
in YAML configuration. See examples below.
Main purpose of this tool is transcoding single source to several videos of different qualities and sizes. It is very helpful for handling raw videos from amateur camera.
I use this tool to convert a great amout of *.MTS
files to
web-compatible formats.
bash >= 4.3.11
ffmpeg >= 2.8.6
The latest version of standard FFmpeg pack is preferable. This is my ffmpeg configuration. Check it if something's gone wrong.
ffmpeg version 2.8.6 Copyright (c) 2000-2016 the FFmpeg developers
built with gcc 5.1.1 (GCC) 20150618 (Red Hat 5.1.1-4)
configuration:
...
--arch=x86_64
--enable-bzlib
--disable-crystalhd
--enable-frei0r
--enable-gnutls
--enable-ladspa
--enable-libass
--enable-libcdio
--enable-libdc1394
--disable-indev=jack
--enable-libfreetype
--enable-libgsm
--enable-libmp3lame
--enable-openal
--enable-libopencv
--enable-libopenjpeg
--enable-libopus
--enable-libpulse
--enable-libschroedinger
--enable-libsoxr
--enable-libspeex
--enable-libtheora
--enable-libvorbis
--enable-libv4l2
--enable-libvpx
--enable-libx264
--enable-libx265
--enable-libxvid
--enable-x11grab
--enable-avfilter
--enable-avresample
--enable-postproc
--enable-pthreads
--disable-static
--enable-shared
--enable-gpl
--enable-runtime-cpudetect
...
If you want to get one transcoded file from one source, you can only run this command.
bulk_video_converter.bash -c config.yaml /path/to/video/file/00001.MTS
Where:
00001.MTS
— is you source video file.config.yaml
— is configuration that describes you transcoded file.
ffmpeg:
bin: /usr/bin/ffmpeg # If you have several ffmpeg installations
threads: 0 # 0 — use CPU autodetection
profile:
base:
abstract: 1 # This profile will be used only for inheritance.
start: 00:00:10 # Optional parametr.
stop: 00:00:30 # Optional parametr.
output_dir_name: ./out # Optional parametr.
pass_log_dir_name: ./pass_log # Optional parametr.
my_profile:
extends: base # Other options are inherited from `base`.
passes: 2 # Two passes of ffmpeg.
video:
preset: veryslow
width: 1280
height: 720
bitrate: 2000k
codec:
name: h264
weightp: 2
bframes: 3
opts: "keyint=96:min-keyint=96:no-scenecut"
profile: high
level: 3.1
audio:
codec:
name: aac
channels: 5.1
bitrate: 320k
With such configuration the converter will generate output log and perform two sequential ffmpeg passes.
For one profile output will be YAML near like this:
bulk_video_converter.bash:
version: 0.1462751725
/home/user/Video/00001.MTS :
profile my_profile:
global input: -ss '00:00:10' -threads '0'
video: -preset 'veryslow' -b:v '2000k' -vf 'scale=1280:720' -codec:v 'libx264' -profile:v 'high' -level:v '3.1' -weightp '2' -bf '3' -x264opts 'keyint=96:min-keyint=96:no-scenecut'
audio: -b:a '320k' -ac '6' -strict 'experimental' -codec:a 'aac'
global output: -ss '00:00:10' -to '00:00:30'
passes:
pass 1: /usr/bin/ffmpeg -ss '00:00:10' -threads '0' -i '/home/user/Video/00001.MTS' -preset 'veryslow' -b:v '2000k' -vf 'scale=1280:720' -codec:v 'libx264' -profile:v 'high' -level:v '3.1' -weightp '2' -bf '3' -x264opts 'keyint=96:min-keyint=96:no-scenecut' -pass 1 -passlogfile ./pass_log/00001-my_profile.mts -b:a '320k' -ac '6' -strict 'experimental' -codec:a 'aac' -ss '00:00:10' -to '00:00:30' -f 'mp4' -y '/dev/null' 2>&1 | tee /var/log/bulk_video_converter.bash/2016-05-02_04-09-08-036081777/00001-my_profile-1-mp4.ffmpeg.log 1>&2;
pass 2: /usr/bin/ffmpeg -ss '00:00:10' -threads '0' -i '/home/user/Video/00001.MTS' -preset 'veryslow' -b:v '2000k' -vf 'scale=1280:720' -codec:v 'libx264' -profile:v 'high' -level:v '3.1' -weightp '2' -bf '3' -x264opts 'keyint=96:min-keyint=96:no-scenecut' -pass 2 -passlogfile ./pass_log/00001-my_profile.mts -b:a '320k' -ac '6' -strict 'experimental' -codec:a 'aac' -ss '00:00:10' -to '00:00:30' -f 'mp4' -y './out/00001-my_profile.mp4' 2>&1 | tee /var/log/bulk_video_converter.bash/2016-05-02_04-09-08-036081777/00001-my_profile-2-mp4.ffmpeg.log 1>&2;
# passes done
# profile my_profile done
# /home/user/Video/00001.MTS done
# bulk_video_converter.bash done
/usr/bin/ffmpeg \
-ss '00:00:10' \
-threads '0' \
-i '/path/to/video/file/00001.MTS' \
-preset 'veryslow' \
-b:v '2000k' \
-vf 'scale=1280:720' \
-codec:v 'libx264' \
-profile:v 'high' \
-level:v '3.1' \
-weightp '2' \
-bf '3' \
-x264opts 'keyint=96:min-keyint=96:no-scenecut' \
-pass '1' \
-passlogfile './pass_log/00001-my_profile.mts' \
-b:a '320k' \
-ac '6' \
-strict 'experimental' \
-codec:a 'aac' \
-ss '00:00:10' \
-to '00:00:30' \
-f 'mp4' \
-y '/dev/null';
/usr/bin/ffmpeg \
-ss '00:00:10' \
-threads '0' \
-i '/path/to/video/file/00001.MTS' \
-preset 'veryslow' \
-b:v '2000k' \
-vf 'scale=1280:720' \
-codec:v 'libx264' \
-profile:v 'high' \
-level:v '3.1' \
-weightp '2' \
-bf '3' \
-x264opts 'keyint=96:min-keyint=96:no-scenecut' \
-pass '2' \
-passlogfile './pass_log/00001-my_profile.mts' \
-b:a '320k' \
-ac '6' \
-strict 'experimental' \
-codec:a 'aac' \
-ss '00:00:10' \
-to '00:00:30' \
-f 'mp4' \
-y './out/00001-my_profile.mp4';
If you want to get several videos from one source you should describe several profiles in the config. For example, let we use profiles from H.264 web video encoding tutorial with FFmpeg.
ffmpeg:
bin: /usr/bin/ffmpeg
threads: 0
profile:
base:
is_abstract: 1 # It will be used only for inheritance.
source: /home/user/Video/*.MTS # also you can set file names here.
start: 00:00:10
stop: 00:00:30
output_dir_name: ./out
pass_log_dir_name: ./pass_log
virag_h264x1_pal_sd: # PAL SD video.
extends: base
passes: 1
video:
width: 0 # any
height: 576 # SD
preset: slower
codec:
profile: main
bitrate: 1000k
audio:
codec:
name: aac
channels: 5.1
bitrate: 196k
virag_h264x1_480p_web: # Standard web video.
extends: virag_h264x1_pal_sd
video:
height: 480
preset: slow
bitrate: 500k
maxrate: 500k
bufsize: 1000k
audio:
bitrate: 128k
channels: stereo
virag_h264x1_480p_tablet: # Video for iPads and tablets.
extends: virag_h264x1_480p_web
video:
codec:
profile: main
bitrate: 400k
maxrate: 400k
bufsize: 800k
virag_h264x1_360p_mobile: # 360p video for old phones.
extends: virag_h264x1_480p_tablet
video:
height: 360
codec:
profile: baseline
bitrate: 250k
maxrate: 250k
bufsize: 500k
audio:
bitrate: 96k
channels: mono
You can check what you will get with dry-run option (-d
).
So for virag's profiles you will have output with four ffmpeg-commands.
All profiles will be handled in parallel way.
It is same if you run ffmpeg four times at the same moment.
[user@host ~]$ ./bulk_video_converter.bash -c config.yaml -d
Vbulk_video_converter.bash:
version:
0.1462751725
# version done
# NOTICE 45: bulk_video_converter.bash creates directory '/tmp/bulk_video_converter.bash/HfxHObOW' at start up.
# NOTICE 48: bulk_video_converter.bash creates directory '/var/log/bulk_video_converter.bash/2016-05-09_03-44-52-038099657' at start up.
# NOTICE 53: bulk_video_converter.bash handles file async with PID 16873-2-16923
# NOTICE 56: bulk_video_converter.bash gets file names from config
input/files/are/from/config:
# DEBUG 66: bulk_video_converter.bash list of profiles = BASE VIRAG_H264X1_PAL_SD VIRAG_H264X1_480P_WEB VIRAG_H264X1_480P_TABLET VIRAG_H264X1_360P_MOBILE
# NOTICE 70: bulk_video_converter.bash handles profile async with PID 16873-6-16948
# NOTICE 70: bulk_video_converter.bash handles profile async with PID 16873-6-16951
# NOTICE 72: bulk_video_converter.bash handles profile async with PID 16873-6-16956
# NOTICE 72: bulk_video_converter.bash handles profile async with PID 16873-6-16957
# NOTICE 73: bulk_video_converter.bash handles profile async with PID 16873-6-16954
# NOTICE 127: bulk_video_converter.bash handles file async with PID 16873-10-17233
virag_h264x1_pal_sd for /home/user/Video/00001.MTS:
virag_h264x1_pal_sd:
global input:
-threads '0' -ss '00:00:10'
# global input done
video:
-codec:v 'libx264' -profile:v 'main' -b:v '1000k' -vf 'scale=0:576'
# video done
audio:
-strict 'experimental' -codec:a 'aac' -b:a '196k' -ac '6'
# audio done
global output:
-ss '00:00:10' -to '00:00:30'
# global output done
format:
-movflags '+faststart' -f 'mp4'
# format done
passes:
pass 1:
/usr/bin/ffmpeg -threads '0' -ss '00:00:10' -i '/home/user/Video/00001.MTS' -codec:v 'libx264' -profile:v 'main' -b:v '1000k' -vf 'scale=0:576' -strict 'experimental' -codec:a 'aac' -b:a '196k' -ac '6' -ss '00:00:10' -to '00:00:30' -movflags '+faststart' -f 'mp4' -y './out/00001-virag_h264x1_pal_sd.mp4' 2>&1 | tee /var/log/bulk_video_converter.bash/2016-05-09_03-44-52-038099657/00001-virag_h264x1_pal_sd-1-mp4.ffmpeg.log 1>&2;
# pass 1 done
# passes done
# virag_h264x1_pal_sd done
# virag_h264x1_pal_sd for /home/user/Video/00001.MTS done
# NOTICE 144: bulk_video_converter.bash handles file async with PID 16873-10-17331
virag_h264x1_480p_web for /home/user/Video/00001.MTS:
virag_h264x1_480p_web:
global input:
-threads '0' -ss '00:00:10'
# global input done
video:
-codec:v 'libx264' -profile:v 'main' -b:v '500k' -maxrate '500k' -bufsize '1000k' -vf 'scale=0:480'
# video done
audio:
-strict 'experimental' -codec:a 'aac' -b:a '128k' -ac '2'
# audio done
global output:
-ss '00:00:10' -to '00:00:30'
# global output done
format:
-movflags '+faststart' -f 'mp4'
# format done
passes:
pass 1:
/usr/bin/ffmpeg -threads '0' -ss '00:00:10' -i '/home/user/Video/00001.MTS' -codec:v 'libx264' -profile:v 'main' -b:v '500k' -maxrate '500k' -bufsize '1000k' -vf 'scale=0:480' -strict 'experimental' -codec:a 'aac' -b:a '128k' -ac '2' -ss '00:00:10' -to '00:00:30' -movflags '+faststart' -f 'mp4' -y './out/00001-virag_h264x1_480p_web.mp4' 2>&1 | tee /var/log/bulk_video_converter.bash/2016-05-09_03-44-52-038099657/00001-virag_h264x1_480p_web-1-mp4.ffmpeg.log 1>&2;
# pass 1 done
# passes done
# virag_h264x1_480p_web done
# virag_h264x1_480p_web for /home/user/Video/00001.MTS done
# NOTICE 166: bulk_video_converter.bash handles file async with PID 16873-10-17462
virag_h264x1_480p_tablet for /home/user/Video/00001.MTS:
virag_h264x1_480p_tablet:
global input:
-threads '0' -ss '00:00:10'
# global input done
video:
-codec:v 'libx264' -profile:v 'main' -b:v '400k' -maxrate '400k' -bufsize '800k' -vf 'scale=0:480'
# video done
audio:
-strict 'experimental' -codec:a 'aac' -b:a '128k' -ac '2'
# audio done
global output:
-ss '00:00:10' -to '00:00:30'
# global output done
format:
-movflags '+faststart' -f 'mp4'
# format done
passes:
pass 1:
/usr/bin/ffmpeg -threads '0' -ss '00:00:10' -i '/home/user/Video/00001.MTS' -codec:v 'libx264' -profile:v 'main' -b:v '400k' -maxrate '400k' -bufsize '800k' -vf 'scale=0:480' -strict 'experimental' -codec:a 'aac' -b:a '128k' -ac '2' -ss '00:00:10' -to '00:00:30' -movflags '+faststart' -f 'mp4' -y './out/00001-virag_h264x1_480p_tablet.mp4' 2>&1 | tee /var/log/bulk_video_converter.bash/2016-05-09_03-44-52-038099657/00001-virag_h264x1_480p_tablet-1-mp4.ffmpeg.log 1>&2;
# pass 1 done
# passes done
# virag_h264x1_480p_tablet done
# virag_h264x1_480p_tablet for /home/user/Video/00001.MTS done
# NOTICE 183: bulk_video_converter.bash handles file async with PID 16873-10-17560
virag_h264x1_360p_mobile for /home/user/Video/00001.MTS:
virag_h264x1_360p_mobile:
global input:
-threads '0' -ss '00:00:10'
# global input done
video:
-codec:v 'libx264' -profile:v 'baseline' -b:v '250k' -maxrate '250k' -bufsize '500k' -vf 'scale=0:360'
# video done
audio:
-strict 'experimental' -codec:a 'aac' -b:a '96k' -ac '1'
# audio done
global output:
-ss '00:00:10' -to '00:00:30'
# global output done
format:
-movflags '+faststart' -f 'mp4'
# format done
passes:
pass 1:
/usr/bin/ffmpeg -threads '0' -ss '00:00:10' -i '/home/user/Video/00001.MTS' -codec:v 'libx264' -profile:v 'baseline' -b:v '250k' -maxrate '250k' -bufsize '500k' -vf 'scale=0:360' -strict 'experimental' -codec:a 'aac' -b:a '96k' -ac '1' -ss '00:00:10' -to '00:00:30' -movflags '+faststart' -f 'mp4' -y './out/00001-virag_h264x1_360p_mobile.mp4' 2>&1 | tee /var/log/bulk_video_converter.bash/2016-05-09_03-44-52-038099657/00001-virag_h264x1_360p_mobile-1-mp4.ffmpeg.log 1>&2;
# pass 1 done
# passes done
# virag_h264x1_360p_mobile done
# virag_h264x1_360p_mobile for /home/user/Video/00001.MTS done
# input/files/are/from/config done
# bulk_video_converter.bash done
[user@host ~]$
This is fine formed YAML and can be represented with JSON.
[user@host ~]$ ./bulk_video_converter.bash -c config.yaml -d 2>&1 yaml2json -p -
The result will be:
{
"bulk_video_converter.bash": {
"version": 0.1462751725,
"input/files/are/from/config": {
"virag_h264x1_pal_sd for /home/user/Video/00001.MTS": {
"virag_h264x1_pal_sd": {
"global input": "-threads '0' -ss '00:00:10'",
"video": "-codec:v 'libx264' -profile:v 'main' -b:v '1000k' -vf 'scale=0:576'",
"audio": "-strict 'experimental' -codec:a 'aac' -b:a '196k' -ac '6'",
"global output": "-ss '00:00:10' -to '00:00:30'",
"format": "-movflags '+faststart' -f 'mp4'",
"passes": {
"pass 1": "/usr/bin/ffmpeg -threads '0' -ss '00:00:10' -i '/home/user/Video/00001.MTS' -codec:v 'libx264' -profile:v 'main' -b:v '1000k' -vf 'scale=0:576' -strict 'experimental' -codec:a 'aac' -b:a '196k' -ac '6' -ss '00:00:10' -to '00:00:30' -movflags '+faststart' -f 'mp4' -y './out/00001-virag_h264x1_pal_sd.mp4' 2>&1 | tee /var/log/bulk_video_converter.bash/2016-05-09_03-44-52-038099657/00001-virag_h264x1_pal_sd-1-mp4.ffmpeg.log 1>&2;"
}
}
},
"virag_h264x1_480p_web for /home/user/Video/00001.MTS": {
"virag_h264x1_480p_web": {
"global input": "-threads '0' -ss '00:00:10'",
"video": "-codec:v 'libx264' -profile:v 'main' -b:v '500k' -maxrate '500k' -bufsize '1000k' -vf 'scale=0:480'",
"audio": "-strict 'experimental' -codec:a 'aac' -b:a '128k' -ac '2'",
"global output": "-ss '00:00:10' -to '00:00:30'",
"format": "-movflags '+faststart' -f 'mp4'",
"passes": {
"pass 1": "/usr/bin/ffmpeg -threads '0' -ss '00:00:10' -i '/home/user/Video/00001.MTS' -codec:v 'libx264' -profile:v 'main' -b:v '500k' -maxrate '500k' -bufsize '1000k' -vf 'scale=0:480' -strict 'experimental' -codec:a 'aac' -b:a '128k' -ac '2' -ss '00:00:10' -to '00:00:30' -movflags '+faststart' -f 'mp4' -y './out/00001-virag_h264x1_480p_web.mp4' 2>&1 | tee /var/log/bulk_video_converter.bash/2016-05-09_03-44-52-038099657/00001-virag_h264x1_480p_web-1-mp4.ffmpeg.log 1>&2;"
}
}
},
"virag_h264x1_480p_tablet for /home/user/Video/00001.MTS": {
"virag_h264x1_480p_tablet": {
"global input": "-threads '0' -ss '00:00:10'",
"video": "-codec:v 'libx264' -profile:v 'main' -b:v '400k' -maxrate '400k' -bufsize '800k' -vf 'scale=0:480'",
"audio": "-strict 'experimental' -codec:a 'aac' -b:a '128k' -ac '2'",
"global output": "-ss '00:00:10' -to '00:00:30'",
"format": "-movflags '+faststart' -f 'mp4'",
"passes": {
"pass 1": "/usr/bin/ffmpeg -threads '0' -ss '00:00:10' -i '/home/user/Video/00001.MTS' -codec:v 'libx264' -profile:v 'main' -b:v '400k' -maxrate '400k' -bufsize '800k' -vf 'scale=0:480' -strict 'experimental' -codec:a 'aac' -b:a '128k' -ac '2' -ss '00:00:10' -to '00:00:30' -movflags '+faststart' -f 'mp4' -y './out/00001-virag_h264x1_480p_tablet.mp4' 2>&1 | tee /var/log/bulk_video_converter.bash/2016-05-09_03-44-52-038099657/00001-virag_h264x1_480p_tablet-1-mp4.ffmpeg.log 1>&2;"
}
}
},
"virag_h264x1_360p_mobile for /home/user/Video/00001.MTS": {
"virag_h264x1_360p_mobile": {
"global input": "-threads '0' -ss '00:00:10'",
"video": "-codec:v 'libx264' -profile:v 'baseline' -b:v '250k' -maxrate '250k' -bufsize '500k' -vf 'scale=0:360'",
"audio": "-strict 'experimental' -codec:a 'aac' -b:a '96k' -ac '1'",
"global output": "-ss '00:00:10' -to '00:00:30'",
"format": "-movflags '+faststart' -f 'mp4'",
"passes": {
"pass 1": "/usr/bin/ffmpeg -threads '0' -ss '00:00:10' -i '/home/user/Video/00001.MTS' -codec:v 'libx264' -profile:v 'baseline' -b:v '250k' -maxrate '250k' -bufsize '500k' -vf 'scale=0:360' -strict 'experimental' -codec:a 'aac' -b:a '96k' -ac '1' -ss '00:00:10' -to '00:00:30' -movflags '+faststart' -f 'mp4' -y './out/00001-virag_h264x1_360p_mobile.mp4' 2>&1 | tee /var/log/bulk_video_converter.bash/2016-05-09_03-44-52-038099657/00001-virag_h264x1_360p_mobile-1-mp4.ffmpeg.log 1>&2;"
}
}
}
}
}
}
We prefer to use this script in place without any installation.
But if you want you can just copy bulk_video_converter.bash
to directory under you $PATH
:
- to
/home/${USER}/bin
- or to
/usr/local/bin
(this is the best place) - or to
/usr/bin
Just run
bash ./test.bash
It calls bulk_video_converter.bash
with YAML-configurations from examples-folder.
It uses all files with suffix .test-config.yaml
to generate script output
and compare actual output with the expected result.
Tests work without running real video handling.
Only generated FFmpeg options are tested.
Check source code of test.bash for more details.
It has some special features.
- Dry-run. You can run script without doing anything to check generated FFmpeg options.
- Extended profiles. Any profile can inherit and extend options from another profile. Nested option structures are overridden too.
- Dependent profiles that are handles consequently when the result
of one processing is used as an input to another one
(system
wait
is applied). See dependent encoding config and dependent encoding log as a visual representation of processing sequence. - Configurable asynchronous handing for files and profiles.
- Device capturing based on FFmpeg's device handling. See screen capturing test config and screen capturing test output.
- Simple scene detection based on FFmpeg's filter
select='gt(scene,0.4)'
. See scene detection test config and scene detection test output.
There are some complex examples. The most notable are:
- «Virag H264» based on Virag's article H.264 web video encoding tutorial with FFmpeg.
- «TVZavr H264» based on way of encoding web video for online-cinema tvzavr.ru.
- «YouTube H264» based on Recommended Upload Encoding Settings from YouTube documentation.
- «Rate Control H264» — different encoding variants of encoding from article Understanding Rate Control Modes (x264, x265).
- «GOP Control H264» — different variants of assigning constant GOP.
- «Codec Control» — different variants of assigning different codecs and codec options.
See CHANGELOG to track changes.