#!/bin/sh # Copyright © 2010-2014, 2016 Jonas Smedegaard # Description: Recode a video into web-optimized format(s) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Depends: ffmpeg melt mediainfo # Recommends: moreutils vainfo vpx-tools vorbis-tools opus-tools # # TODO: # * Offer to skip rendering again if an output file exist already. # * Support --width and --height, resolving the other part from input # or forced aspect ratio. # * Check and fail if all needed tools are not available. # * Test if beneficial to apply real_time=-2. # * Normalize each infile separately when xml fed as infile keeps sync. # Maybe as workaround re-feed audio separately from xml, as done at # . # * Resolve flash player to use. # * Make choice of encoders configurable. # * Figure out how to apply application option when using opusenc. # * Handle channels per-codec for low-bitrate joint stereo Opus speech. # * Double-check audio bandwidth algorithms: # http://trac.ffmpeg.org/wiki/Encode/HighQualityAudio # * Change WebM bandwidth algorithm: # Use https://developers.google.com/media/vp9/settings/vod/ # * Tune VP8 parameters: # http://www.webmproject.org/docs/encoder-parameters/ # * Tune VP9 parameters: # https://sites.google.com/a/webmproject.org/wiki/ffmpeg/vp9-encoding-guide # * Support DASH: # https://sites.google.com/a/webmproject.org/wiki/adaptive-streaming/instructions-to-playback-adaptive-webm-using-dash # * Support watermark # * Support live streaming - i.e. from (maybe faked) endless source # + Lipsync may require single stream of equally chunked tracks: # https://sites.google.com/a/webmproject.org/wiki/adaptive-streaming/instructions-to-do-webm-live-streaming-via-dash # * Support realtime streaming - i.e. with less than 200ms latency # * Compute (or hardcode) html5 codec types: # + Use mp4v2-utils # + http://stackoverflow.com/a/16365526 set -e PRG=$(basename "$0") showhelp() { cat < EOF } exit1() { response="${1:+Error: }${1:-Internal error!}" echo >&2 "$response" exit 1 } # defaults formats=webm,vp9,mp4 stillframe=0 samplestart=0 samplelength=150 compression=normal # establish/verify rendering settings for reference codec # # 1) pick reference frame:encoding format: 360p30:h.264 # 2) pick reference codec (incl. normal settings) # 3) render reference frame format with --compression=quality # * check that bpp is near "refbpp" of 0.12 # (otherwise adapt settings or do research and update refbpp) # 4) render misc. frame formats (esp. modulus 16) with --compression=quality # * check that bpp derives from refbpp by "rule of .75" # (otherwise adapt settings and/or do research and update rule) # 5) render misc. frame formats with --compression=normal # * check that bpp derives from refbpp by "rule of .75" # 6) optionally pick dirty/hq settings # * check that --compression=quality has filesize similar as normal # establish/verify rendering settings for non-reference codec # # 1) pick normal settings # 2) render reference frame format with --compression=quality # * check that this "codec-bpp" deviates reasonably from refbpp # 3) render misc. frame formats (esp. modulus 16) with --compression=quality # * check that bpp derives from codec-bpp by "rule of .75" # (otherwise adapt settings and/or extend rule) # 4) render misc. frame formats with --compression=normal # * check that bpp derives from codec-bpp by "rule of .75" # 5) optionally pick dirty/hq settings # * check that --compression=quality has similar filesize as normal # VP8 is rumored to compress ~20% worse than H.264 factor_vp8=120/100 # VP9 compresses ~30% better than H.264 for practical use-cases # factor_vp9=70/100 # Mimic ReplayGain level when loudness is enabled (EBU R128: -23LUFS) # lufs=-18 # parse cmdline options TEMP="`getopt -s sh -o hp:s:a:r:b:t: -l help,profile:,size:,aspect:,rate:,video:,refbpp:,formats:,audio:,audioprefilter:,loudness,loudness-data:,filter:,stem:,title:,still-frame:,sample,sample-start:,sample-length:,compression:,quality:,gpu:,gpudevice: -n "$PRG" -- "$@"`" || exit1 "Internal getopt error." eval set -- "$TEMP" while true ; do case "$1" in -h|--help) showhelp; exit;; -p|--profile) profile="$2"; shift 2;; -s|--size) size="$2"; shift 2;; -a|--aspect) aspect="$2"; shift 2;; -r|--rate) framerate="$2"; shift 2;; --video) video="$2"; shift 2;; --refbpp) refbpp="$2"; shift 2;; --formats) formats="$2"; shift 2;; --audio) audio="$2"; shift 2;; --audioprefilter) audioprefilters="${audioprefilters:+$audioprefilters }-filter $2"; shift 2;; --loudness) loudness=yes; shift;; --loudness-data) loudness=yes; loudness_data="$2"; shift 2;; --filter) filters="${filters:+$filters }-filter $2"; shift 2;; --stem) stem="$2"; shift 2;; -t|--title) title="$2"; shift 2;; --still-frame) stillframe="$2"; shift 2;; --sample) sample=yes; shift;; --sample-start) sample=yes; samplestart="$2"; shift 2;; --sample-length) sample=yes; samplelength="$2"; shift 2;; --compression) compression="$2"; shift 2;; --quality) quality="$2"; shift 2;; --gpu) gpu="$2"; shift 2;; --gpudevice) gpudevice="$2"; shift 2;; --) shift; break;; *) exit1 "Internal error resolving options.";; esac done [ -n "$sample" ] || notsample=yes # Resolve if system has many CPU cores processors=$(nproc) [ $processors -gt 2 ] || processors= # sanitize infiles infiles=$* infile_first=$(perl -e 'print shift @ARGV' $infiles) [ -e "$infile_first" ] || exit1 "Cannot read first input file \"$infile_first\"!" # resolve stem and title (if not explicitly set) stem=${stem:-$(basename "$infile_first" | perl -pe 's/\.[^.]*//')} title=${title:-$stem} #use_ffmpeg_vp8=yes #use_ffmpeg_vp9=yes #use_ffmpeg_h264=yes #use_vpxenc=yes # Avoid discrete audio encoders if possible: may cause sync problems #use_oggenc=yes #use_opusenc=yes # Avoid AAC: Lame arguably better than any free AAC encoder # Argument for AAC: Safari possibly lacks mp3 support in video container #use_mp3=yes [ -n "$use_mp3" ] || use_aac=yes # Avoid MP3 VBR but seems ABR is fine: Set to CBR for strict correctnes #use_lame_cbr=yes [ -n "$use_lame_cbr" ] || use_lame_abr=yes # resolve quality/speed hints qkey_vp8=crf qkey_vp9=crf qkey_h264=crf q_theora=5 q_vp8=33 cpu_vp8=2 q_vp9=33 cpu_vp9=2 q_h264=23 preset_h264=medium profile_vp8=0 profile_vp9=0 profile_h264=baseline case "$compression" in normal) :;; dirty) gpu=${gpu:-auto} q_theora=1 cpu_vp8=4 cpu_vp9=4 preset_h264=veryfast ;; hq) multipass=yes q_theora=6 cpu_vp8=0 cpu_vp9=1 preset_h264=veryslow ;; exact) multipass=yes ;; quality) no_bitrate=yes ;; *) exit1 "Unknown compression mode \"$compression\".";; esac case "$quality" in auto|'') :;; none) no_q=yes;; *) q=$quality;; esac gpudevice=${gpudevice:-/dev/dri/renderD128} if [ auto = "$gpu" ]; then vainfo=$(vainfo --display drm --device "$gpudevice" 2>/dev/null | grep -Po 'Driver version:\s*\K.*') case "$vainfo" in '') gpu=;; *) for vformat in vp8 vp9 h264; do eval "no_gpu_decoder_$vformat=yes" eval "no_gpu_encoder_$vformat=yes" done case $(vainfo --display drm --device "$gpudevice" 2>/dev/null | grep -Po 'VAProfile\S+\s*:\s*VAEntrypointVLD') in *VAProfileVP8Version0_3*) no_gpu_decoder_vp8=;; *VAProfileVP9Profile0*) no_gpu_decoder_vp9=;; *VAProfileH264ConstrainedBaseline*) no_gpu_decoder_h264=;; esac case $(vainfo --display drm --device "$gpudevice" 2>/dev/null | grep -Po 'VAProfile\S+\s*:\s*VAEntrypointEncSlice') in *VAProfileVP8Version0_3*) no_gpu_encoder_vp8=;; *VAProfileVP9Profile0*) no_gpu_encoder_vp9=;; *VAProfileH264ConstrainedBaseline*) no_gpu_encoder_h264=;; esac if [ -n "$no_gpu_decoder_vp8$no_gpu_decoder_vp9$no_gpu_decoder_h264" ]; then case "$vainfo" in # TODO *"Intel iHD driver"*) gpu=qsv;; *) gpu=vaapi;; esac fi esac fi case "$gpu" in none|'') gpu=;; qsv) multipass= preset_vp8=medium preset_vp9=medium preset_h264= profile_h264=578 qkey_h264=qp ;; vaapi) multipass= preset_h264= profile_h264=578 qkey_h264=qp case "$compression" in dirty) preset_h264= ;; esac ;; *) exit1 "Unknown GPU type \"$gpu\".";; esac [ -n "$multipass" ] || singlepass=yes [ -z "$gpu" ] && no_gpu=yes || eval "gpu_$gpu=yes" # parse/resolve size and framerate case "$profile" in '') :;; *@*) while read s r foo; do profilesize="${size:-$s}" framerate="${framerate:-$r}" done << EOF $(echo "$profile" | perl -F@ -anE 'say join " ", @F') EOF ;; *p*) while read s r foo; do profilesize="${size:-${s}p}" framerate="${framerate:-$r}" done << EOF $(echo "$profile" | perl -Fp -anE 'say join " ", @F') EOF ;; *) profilesize="$profile" ;; esac size=${size:-$profilesize} case "$size" in qvga) size=320x240;; hvga) size=480x360;; vga) size=640x480;; svga) size=800x600;; xga) size=1024x768;; 240p|wqvga) size=424x240;; 360p|nhd) size=640x360;; 480p|wvga) size=848x480;; 576p|wsvga) size=1024x576;; 720p|wxga|hd) size=1280x720;; esac if [ -n "$size" ]; then while read w h foo; do width="${width:-$w}" height="${height:-$h}" done << EOF $(echo "$size" | perl -Fx -anE 'say join " ", @F') EOF if [ -z "$width" ] || [ -z "$height" ]; then exit1 "Failed to parse size \"$size\"." fi fi case "$framerate" in */*) while read n d foo; do framerate_num="${framerate_num:-$n}" framerate_den="${framerate_den:-$d}" done << EOF $(echo "$framerate" | perl -F/ -anE 'say join " ", @F') EOF ;; ?*) framerate_num="$framerate" framerate_den=1 ;; esac # resolve input size and framerate (needed for computing bitrate) while read w h r s foo; do width_in="${width_in:-$w}" height_in="${height_in:-$h}" framerate_in="${framerate_in:-$r}" scantype_in="${scantype_in:-$s}" done << EOF $(mediainfo --Inform="Video;%Width% %Height% %FrameRate% %ScanType%" \ "$infile_first") EOF [ Progressive = "$scantype_in" ] || do_deinterlace=yes case "$video" in talkinghead) refbpp="${refbpp:-0.1}" x264tune=film ;; action) refbpp="${refbpp:-0.15}" x264tune=film ;; '') refbpp="${refbpp:-0.12}" ;; *) exit1 "Unknown video style \"$video\".";; esac for format in $(echo "$formats" | sed -e 's/,/ /g'); do case $format in theora|ogg) ogg=yes;; h264|mp4) mp4=yes;; vp8|webm) webm=yes;; vp9) vp9=yes;; *) exit1 "Unknown format \"$format\".";; esac done _width="${width:-$width_in}" _height="${height:-$height_in}" if [ -n "$_width" ] && [ -n "$_height" ]; then _pixels="$((_width*_height))" fi _frames="${framerate:-$framerate_in}" webm_qmin=0 webm_qmax=63 webm_lag=16 webm_tokenparts_log2=0 if [ -n "$_pixels" ] && [ $_pixels -ge $((1024*768)) ]; then webm_qmin=11 webm_qmax=51 webm_tokenparts_log2=2 if [ -n "$_frames" ] && [ $_frames -gt 40 ]; then webm_lag=25 profile_vp8=1 profile_vp9=1 fi fi # compute average bitrate from reference data and "power of .75" rule if [ -n "$_pixels" ] && [ -n "$_frames" ]; then bitrate_h264=$(perl -E '$refsize=640*360;' \ -E "say int( +(($_pixels/\$refsize)**0.75*\$refsize*$_frames*$refbpp) )") bitrate_theora=$bitrate_h264 # FIXME bitrate_vp8=$(perl -E "say int( +($bitrate_h264*$factor_vp8) )") #" bitrate_vp9=$(perl -E "say int( +($bitrate_h264*$factor_vp9) )") #" fi # default per-codec-channel bitrates quality_vorbis=3 bitrate_opus=48 quality_lame=6 bitrate_lame=64 bitrate_aac=64 case "$audio" in music) opusapp=audio ;; speech) mono=yes quality_vorbis=2 bitrate_opus=32 quality_lame=7 bitrate_lame=48 opusapp=voip ;; silence) silence=yes ;; '') : ;; *) exit1 "Unknown audio style \"$audio\".";; esac which ffprobe > /dev/null && ffprobe=ffprobe || ffprobe=avprobe channels=$($ffprobe -v warning -show_streams "$infile_first" \ | perl -ne 's/channels=// and print $_' || echo -1) # adapt channel count and flags to reflect actual downmix/silence need if [ -n "$stereo" ]; then if [ $channels -gt 2 ]; then channels=2 else stereo= fi elif [ -n "$mono" ]; then if [ $channels -gt 1 ]; then channels=1 else mono= fi elif [ -n "$silence" ]; then if [ $channels -gt 0 ]; then channels=0 else silence= fi fi [ -z "$silence" ] || channels= if [ -n "$stereo$mono" ]; then # melt cannot downmix with (stereo?) filters applied (bug#763911) if [ -n "$audioprefilters$compress$loudness$limit$filters" ]; then if [ -z "${webm:+$use_oggenc}${webm_vp9:+$use_opusenc}" ]; then downmix_ffmpeg=yes else downmix_oggenc=yes downmix_oggenc=yes fi else downmix_melt=yes fi fi encoder_vp8=libvpx encoder_vp9=libvpx-vp9 encoder_h264=libx264 for vformat in vp8 vp9 h264; do [ -z "$no_bitrate" ] || eval "no_bitrate_$vformat=yes" eval "[ -n \"$no_bitrate\$no_bitrate_$vformat\" ] || do_bitrate_$vformat=yes" eval "[ -z \"$no_q\$no_q_$vformat\" ] || q_$vformat=" [ -z "$q" ] || eval "q_$vformat=$q" eval "[ -n \"\$q_$vformat\" ] || no_q_$vformat=yes" [ -z "$gpu" ] || eval "[ -n \"\$no_gpu_encoder_$vformat\" ] || encoder_$vformat=${vformat}_$gpu" # melt supports dual-pass only for h.264 [ h264 = "$vformat" ] || [ -n "$use_vpxenc" ] || eval "[ -z \"$multipass\" ] || use_ffmpeg_$vformat=yes" # melt (before v6.26) does not support encode-only hardware acceleration [ h264 = "$vformat" ] || [ -n "$use_vpxenc$gpu_vaapi" ] || eval "[ -z \"$multipass\" ] || use_ffmpeg_$vformat=yes" done if [ -n "$use_ffmpeg_vp8$use_ffmpeg_vp9$use_vpxenc" ]; then [ -n "$use_oggenc" ] || use_ffmpeg_ogg=yes [ -n "$use_opusenc" ] || use_ffmpeg_opus=yes [ -z "$use_oggenc$use_opusenc" ] || use_wav=yes [ -z "$use_ffmpeg_ogg$use_ffmpeg_opus" ] || use_matroska=yes fi if [ -n "$use_ffmpeg_h264" ]; then use_matroska=yes else use_melt_h264=yes fi [ -z "$gpu" ] || echo >&2 "GPU platform enabled: $gpu" # gpu options _melt_gpu_init=${gpu:+ ${gpu}_device=$gpudevice} _ffmpeg_gpu_vaapi_init="-${gpu}_device $gpudevice" _ffmpeg_gpu_qsv_init="-init_hw_device qsv=hw:$gpudevice -filter_hw_device hw" _ffmpeg_gpu_init="${gpu_vaapi:+ ${_ffmpeg_gpu_vaapi_init}}${gpu_qsv:+ ${_ffmpeg_gpu_qsv_init}}" _ffmpeg_gpu_vaapi_filter="-vf 'format=nv12|$gpu,hwupload'" _ffmpeg_gpu_qsv_filter="-vf 'hwupload=extra_hw_frames=64,format=qsv'" _ffmpeg_gpu_filter="${gpu_vaapi:+ ${_ffmpeg_gpu_vaapi_filter}}${gpu_qsv:+ ${_ffmpeg_gpu_qsv_filter}}" # generic options melt="melt -progress$_melt_gpu_init" which ffmpeg > /dev/null && ffmpeg=ffmpeg || ffmpeg=avconv ffmpeg_alone="$ffmpeg -threads auto -y -hide_banner -v info -stats$_ffmpeg_gpu_init" ffmpeg_chained="$ffmpeg -threads auto -y -v warning$_ffmpeg_gpu_init" vpxenc_chained="vpxenc --quiet ${processors:+-t $((processors-1))}" oggenc="oggenc" opusenc="opusenc" _melt_infiles="${notsample:+$infiles}${sample:+$infile_first in=$((samplestart)) out=$((samplestart + samplelength))}" # filter options _melt_video="progressive=1${framerate:+ frame_rate_num="$framerate_num" frame_rate_den="$framerate_den"}${size:+ s=${width:+$width}x${height:+$height}}${aspect:+ aspect=$aspect}" _ffmpeg_video="${no_gpu:+${do_deinterlace:+-filter:v yadif}${framerate:+ -r $framerate_num/$framerate_den}${size:+ -s ${width:+$width}x${height:+$height}}${aspect:+ -aspect $aspect}}" _melt_downmix="${downmix_melt:+ac=$channels}" _ffmpeg_downmix="${downmix_ffmpeg:+-ac $channels}" # FIXME: how to downmix to stereo? _oggenc_downmix="${downmix_oggenc:+${mono:+--downmix}}" _opusenc_downmix="${downmix_opusenc:+${stereo:+--downmix-stereo}${mono:+--downmix-mono}}" # limit (i.e. avoid peaks "clipping") _melt_postfilters_audio="${limit:+-filter ladspa.1077}" # codec bitrate options _melt_bitrate_vp8="${do_bitrate_vp8:+ vb=$bitrate_vp8} minrate=$((bitrate_vp8/20)) maxrate=$((bitrate_vp8*12))" _ffmpeg_bitrate_vp8="${do_bitrate_vp8:+-b:v $bitrate_vp8} -minrate $((bitrate_vp8/20)) -maxrate $((bitrate_vp8*12))" _vpxenc_bitrate_vp8="--target-bitrate=$((bitrate_vp8/1000)) --minsection-pct=5 --maxsection-pct=1200" _melt_bitrate_vp9="${do_bitrate_vp9:+ vb=$bitrate_vp9} minrate=$((bitrate_vp9/20)) maxrate=$((bitrate_vp9*12))" _ffmpeg_bitrate_vp9="${do_bitrate_vp9:+-b:v $bitrate_vp9} -minrate $((bitrate_vp9/20)) -maxrate $((bitrate_vp9*12))" _vpxenc_bitrate_vp9="--target-bitrate=$((bitrate_vp9/1000)) --minsection-pct=5 --maxsection-pct=1200" # codec low-level options _melt_misc_vp8="qmin=$webm_qmin qmax=$webm_qmax g=120 deadline=good cpu-used=$cpu_vp8${profile_vp8:+ vprofile=$profile_vp8} auto-alt-ref=1 lag-in-frames=$webm_lag arnr-maxframes=7 arnr-strength=5 arnr-type=centered" _ffmpeg_misc_vp8="-qmin $webm_qmin -qmax $webm_qmax -g 120 -deadline good -cpu-used $cpu_vp8${profile_vp8:+ -profile:v $profile_vp8} -auto-alt-ref 1 -lag-in-frames $webm_lag -arnr-maxframes 7 -arnr-strength 5 -arnr-type centered" _vpxenc_misc_vp8="--min-q=$webm_qmin --max-q=$webm_qmax --kf-max-dist=120 --good --cpu-used=$cpu_vp8${profile_vp8:+ --profile=$profile_vp8} --token-parts=$webm_tokenparts_log2 --auto-alt-ref=1 --lag-in-frames=$webm_lag --arnr-maxframes=7 --arnr-strength=5 --arnr-type=3" _melt_misc_vp9="qmin=$webm_qmin qmax=$webm_qmax g=120 deadline=good cpu-used=$cpu_vp9${profile_vp9:+ vprofile=$profile_vp9} auto-alt-ref=1 lag-in-frames=$webm_lag arnr-maxframes=7 arnr-strength=5 arnr-type=centered" _ffmpeg_misc_vp9="-qmin $webm_qmin -qmax $webm_qmax -g 120 -deadline good -cpu-used $cpu_vp9${profile_vp9:+ -profile:v $profile_vp9} -auto-alt-ref 1 -lag-in-frames $webm_lag -arnr-maxframes 7 -arnr-strength 5 -arnr-type centered" _vpxenc_misc_vp9="--min-q=$webm_qmin --max-q=$webm_qmax --kf-max-dist=120 --good --cpu-used=$cpu_vp9${profile_vp9:+ --profile=$profile_vp9} --tile-columns=$webm_tokenparts_log2 --tile-rows=$webm_tokenparts_log2 --auto-alt-ref=1 --lag-in-frames=$webm_lag --arnr-maxframes=7 --arnr-strength=5 --arnr-type=3" # codec options # emulate VP8 Constant Quality using "loose" Constrained Quality _melt_theora="vcodec=libtheora${bitrate_theora:+ vb=$bitrate_theora} qscale=$q_theora" _melt_vp8="vcodec=$encoder_vp8${pre_vp8:+ vpreset=$pre_vp8}${preset_vp8:+ preset=$preset_vp8}${do_bitrate_vp8:+ $_melt_bitrate_vp8}${q_vp8:+ ${no_bitrate_vp8:+vb=$((bitrate_vp8*10)) }$qkey_vp8=$q_vp8} $_melt_misc_vp8" _ffmpeg_vp8="-c:v $encoder_vp8${pre_vp8:+ -vpre $pre_vp8}${preset_vp8:+ preset $preset_vp8}${do_bitrate_vp8:+ $_ffmpeg_bitrate_vp8}${q_vp8:+ ${no_bitrate_vp8:+-b:v $((bitrate_vp8*10)) }-$qkey_vp8 $q_vp8} $_ffmpeg_misc_vp8" _vpxenc_vp8="--codec=vp8${do_bitrate_vp8:+ $_vpxenc_bitrate_vp8} --end-usage=${no_q_vp8:+vbr}${q_vp8:+cq --cq-level=$q_vp8} $_vpxenc_misc_vp8" _melt_vp9="vcodec=$encoder_vp9${pre_vp9:+ vpreset=$pre_vp9}${preset_vp9:+ preset=$preset_vp9}${do_bitrate_vp9:+ $_melt_bitrate_vp9}${q_vp9:+ ${no_bitrate_vp9:+vb=0 }$qkey_vp9=$q_vp9} $_melt_misc_vp9" _ffmpeg_vp9="-c:v $encoder_vp9${pre_vp9:+ -vpre $pre_vp9}${preset_vp9:+ -preset $preset_vp9}${do_bitrate_vp9:+ $_ffmpeg_bitrate_vp9}${q_vp9:+ ${no_bitrate_vp9:+-b:v 0 }-$qkey_vp9 $q_vp9} $_ffmpeg_misc_vp9" _vpxenc_vp9="--codec=vp9${do_bitrate_vp9:+ $_vpxenc_bitrate_vp9} --end-usage=${no_q_vp9:+vbr}${q_vp9:+cq --cq-level=$q_vp9} $_vpxenc_misc_vp9" _melt_h264="vcodec=$encoder_h264${preset_h264:+ vpreset=$preset_h264}${profile_h264:+ vprofile=$profile_h264}${x264tune:+ tune=$x264tune}${do_bitrate_h264:+ maxrate=$bitrate_h264${no_gpu:+ bufsize=$((bitrate_h264*2))}}${q_h264:+ $qkey_h264=$q_h264} threads=0${no_gpu:+ movflags=+faststart}" _ffmpeg_h264="-c:v $encoder_h264${preset_h264:+ -preset $preset_h264}${profile_h264:+ -profile:v $profile_h264}${x264tune:+ -tune $x264tune}${do_bitrate_h264:+ -maxrate $bitrate_h264${no_gpu:+ -bufsize $((bitrate_h264*2))}}${q_h264:+ -$qkey_h264 $q_h264}${no_gpu:+ -movflags +faststart}" _melt_pcm="$_melt_downmix acodec=pcm_s16le" _melt_vorbis="$_melt_downmix acodec=libvorbis aq=$quality_vorbis" _ffmpeg_vorbis="$_ffmpeg_downmix -c:a libvorbis -aq $quality_vorbis" _oggenc_vorbis="$_oggenc_downmix -q $quality_vorbis" _melt_opus="$_melt_downmix acodec=libopus ab=$((channels*bitrate_opus))k${opusapp:+ application=$opusapp}" _ffmpeg_opus="$_ffmpeg_downmix -c:a libopus -b:a $((channels*bitrate_opus))k${opusapp:+ -application $opusapp}" _opusenc_opus="$_opusenc_downmix --bitrate $((channels*bitrate_opus))" _melt_mp3="$_melt_downmix acodec=libmp3lame${use_lame_abr:+ aq=$quality_lame}${use_lame_cbr:+ ab=$((channels*bitrate_lame))k}" _ffmpeg_mp3="$_ffmpeg_downmix -c:a libmp3lame${use_lame_abr:+ -q:a $quality_lame}${use_lame_cbr:+ -b:a $((channels*bitrate_lame))k}" _melt_aac="$_melt_downmix acodec=aac ab=$((channels*bitrate_aac))k" _ffmpeg_aac="$_ffmpeg_downmix -c:a aac -b:a $((channels*bitrate_aac))k" # container options _melt_stdout="-consumer avformat:pipe:1 f=yuv4mpegpipe $_melt_video pix_fmt=yuv420p an=1 audio_off=1" _ffmpeg_stdin="-f yuv4mpegpipe -i pipe:0$_ffmpeg_gpu_filter" _ffmpeg_rawvideo="-f rawvideo" _melt_wav="f=wav $_melt_pcm vn=1 video_off=1" _ffmpeg_wav_in="-f wav" _oggenc_wav_in= _opusenc_wav_in= _melt_matroska_pcm="f=matroska $_melt_downmix $_melt_pcm vn=1 video_off=1" _ffmpeg_matroska_pcm_in="-f matroska" _melt_ogg="f=ogg $_melt_video $_melt_theora ${silence:+an=1 audio_off=1}${channels:+$_melt_vorbis}" _ffmpeg_ogg_in="-f ogg" _ffmpeg_ogg_vorbis="-f ogg -vn $_ffmpeg_vorbis" _ffmpeg_ogg_opus="-f ogg -vn $_ffmpeg_opus" _melt_webm="f=webm $_melt_video $_melt_vp8 ${silence:+an=1 audio_off=1}${channels:+$_melt_vorbis}" _ffmpeg_webm="-f webm $_ffmpeg_video $_ffmpeg_vp8 ${silence:+-an}${channels:+ $_ffmpeg_vorbis}" _ffmpeg_webm_in="-f webm" _ffmpeg_webm_onlyvideo="-f webm $_ffmpeg_video $_ffmpeg_vp8 -an" _ffmpeg_webm_keepvideo="-f webm $_ffmpeg_video -c:v copy ${silence:+-an}${channels:+$_ffmpeg_vorbis}" _ffmpeg_webm_keepvideo_opus="-f webm $_ffmpeg_video -c:v copy ${silence:+-an}${channels:+$_ffmpeg_opus}" _melt_webm_vp9="f=webm $_melt_video $_melt_vp9 ${silence:+an=1 audio_off=1}${channels:+$_melt_opus}" _ffmpeg_webm_vp9="-f webm $_ffmpeg_video $_ffmpeg_vp9 ${silence:+-an}${channels:+$_ffmpeg_opus}" _ffmpeg_webm_onlyvideo_vp9="-f webm $_ffmpeg_video $_ffmpeg_vp9 -an" _melt_mp4="f=mp4 $_melt_video $_melt_h264 ${silence:+an=1 audio_off=1}${channels:+${use_mp3:+$_melt_mp3}${use_aac:+$_melt_aac}}" _ffmpeg_mp4="-f mp4 $_ffmpeg_video $_ffmpeg_h264 ${silence:+-an}${channels:+${use_mp3:+$_ffmpeg_mp3}${use_aac:+$_ffmpeg_aac}}" _ffmpeg_mp4_onlyvideo="-f mp4 $_ffmpeg_video $_ffmpeg_h264 -an" _ffmpeg_mp4_keepvideo="-f mp4 $_ffmpeg_video -c:v copy ${silence:+-an}${channels:+$_ffmpeg_mp3}" _ffmpeg_mp4_keepvideo_aac="-f mp4 $_ffmpeg_video -c:v copy ${silence:+-an}${channels:+$_ffmpeg_aac}" _melt_img="f=image2 $_melt_video" _melt_loudness="$loudness_data" if [ -n "$loudness" ] && [ -z "$silence$_melt_loudness" ]; then echo >&2 "Analyzing audio dynamics..." $melt $_melt_infiles \ $audioprefilters -filter loudness $filters \ -consumer xml:$stem.xml video_off=1 all=1 _melt_loudness="$(perl -n \ -e 'm!<(property) name="results">([^<]+)! and print $2' \ $stem.xml)" echo >&2 "Loudness data: $_melt_loudness" fi if [ -n "$multipass" ] && [ -n "${webm:+$use_vpxenc$use_ffmpeg_vp8$use_oggenc}${vp9:+$use_vpxenc$use_ffmpeg_vp9$use_opusenc}${mp4:+$use_ffmpeg_h264}" ]; then echo >&2 "Analyzing video complexity..." if [ -n "$use_vpxenc" ]; then $melt $_melt_infiles $filters $_melt_stdout \ | pee \ ${webm:+"$vpxenc_chained - $_vpxenc_vp8 \ -p 2 --pass=1 --fpf=${stem}_vp8.log -o /dev/null"} \ ${vp9:+"$vpxenc_chained - $_vpxenc_vp9 \ -p 2 --pass=1 --fpf=${stem}_vp9.log -o /dev/null"} \ ${mp4:+${use_ffmpeg_h264:+$_ffmpeg_rawvideo $_ffmpeg_h264 -an \ -pass 1 -passlogfile ${stem}_h264 /dev/null}} else $melt $_melt_infiles $filters $_melt_stdout \ | $ffmpeg_chained $_ffmpeg_stdin \ ${webm:+$_ffmpeg_rawvideo $_ffmpeg_vp8 -an \ -pass 1 -passlogfile ${stem}_vp8 /dev/null} \ ${vp9:+$_ffmpeg_rawvideo $_ffmpeg_vp9 -an \ -pass 1 -passlogfile ${stem}_vp9 /dev/null} \ ${mp4:+${use_ffmpeg_h264:+$_ffmpeg_rawvideo $_ffmpeg_h264 -an \ -pass 1 -passlogfile ${stem}_h264 /dev/null}} [ -z "$webm" ] || [ -n "$use_ffmpeg_vp8" ] || mv -f ${stem}_vp8-*.log ${stem}_vp8_2pass.log [ -z "$vp9" ] || [ -n "$use_ffmpeg_vp9" ] || mv -f ${stem}_vp9-*.log ${stem}_vp9_2pass.log fi fi if [ -n "${webm:+$use_vpxenc$use_ffmpeg_vp8$use_oggenc}${vp9:+$use_vpxenc$use_ffmpeg_vp9$use_opusenc}${mp4:+$use_ffmpeg_h264}" ]; then echo >&2 "Encoding video${channels:+ and extracting audio}, as discrete files..." $melt $_melt_infiles \ ${channels:+$audioprefilters \ ${_melt_loudness:+-filter loudness program=$lufs \ results="$_melt_loudness"}} \ $filters${channels:+ $_melt_postfilters_audio} \ ${ogg:+-consumer avformat:$stem.ogv $_melt_ogg} \ ${mp4:+${use_melt_h264:+-consumer avformat:$stem.mp4 $_melt_mp4}} \ ${channels:+${use_wav:+-consumer avformat:$stem.wav \ $_melt_wav}} \ ${channels:+${use_matroska:+-consumer avformat:$stem.mkv \ $_melt_matroska_pcm}} \ $_melt_stdout \ | pee \ ${webm:+${use_ffmpeg_vp8:+"$ffmpeg_chained \ $_ffmpeg_stdin $_ffmpeg_webm_onlyvideo \ ${multipass:+-pass 2 -passlogfile ${stem}_vp8} \ ${stem}${channels:+_silent}.webm"}} \ ${webm:+${use_vpxenc:+"$vpxenc_chained - $_vpxenc_vp8 \ ${singlepass:+-p 1} \ ${multipass:+-p 2 --pass=2 --fpf=${stem}_vp8.log} \ -o ${stem}${channels:+_silent}.webm"}} \ ${vp9:+${use_ffmpeg_vp9:+"$ffmpeg_chained \ $_ffmpeg_stdin $_ffmpeg_webm_onlyvideo_vp9 \ ${multipass:+-pass 2 -passlogfile ${stem}_vp9} \ ${stem}_vp9${channels:+_silent}.webm"}} \ ${vp9:+${use_vpxenc:+"$vpxenc_chained - $_vpxenc_vp9 \ ${singlepass:+-p 1} \ ${multipass:+-p 2 --pass=2 --fpf=${stem}_vp9.log} \ -o ${stem}_vp9${channels:+_silent}.webm"}} \ ${mp4:+${use_ffmpeg_h264:+"$ffmpeg_chained \ $_ffmpeg_stdin $_ffmpeg_mp4_onlyvideo \ ${multipass:+-pass 2 -passlogfile ${stem}_h264} \ ${stem}${channels:+_silent}.mp4"}} if [ -n "${webm:+$use_vpxenc$use_ffmpeg_vp8$use_oggenc}" ] && [ -n "$channels" ]; then echo >&2 "Encoding Vorbis audio and muxing with VP8 video..." if [ -n "$use_oggenc" ]; then $oggenc $_oggenc_wav_in $_oggenc_vorbis -o - $stem.wav \ | $ffmpeg_chained \ $_ffmpeg_webm_in -i ${stem}_silent.webm \ $_ffmpeg_ogg_in -i pipe:0 \ $_ffmpeg_webm_keepvideo $stem.webm else $ffmpeg_alone $_ffmpeg_webm_in -i ${stem}_silent.webm \ $_ffmpeg_matroska_wav_in -i $stem.mkv \ $_ffmpeg_webm_keepvideo ${stem}.webm fi fi if [ -n "${vp9:+$use_vpxenc$use_ffmpeg_vp9$use_opusenc}" ] && [ -n "$channels" ]; then echo >&2 "Encoding Opus audio and muxing with WebM/VP9 video..." if [ -n "$use_opusenc" ]; then $opusenc $_opusenc_wav_in $_opusenc_opus -o - $stem.wav \ | $ffmpeg_chained \ $_ffmpeg_webm_in -i ${stem}_vp9_silent.webm \ $_ffmpeg_ogg_in -i pipe:0 \ $_ffmpeg_webm_keepvideo_opus ${stem}_vp9.webm else $ffmpeg_alone $_ffmpeg_webm_in -i ${stem}_vp9_silent.webm \ $_ffmpeg_matroska_wav_in -i $stem.mkv \ $_ffmpeg_webm_keepvideo_opus ${stem}_vp9.webm fi fi if [ -n "${mp4:+$use_ffmpeg_h264}" ] && [ -n "$channels" ]; then if [ -n "$use_mp3" ]; then echo "Encoding MP3 audio and muxing with h.264 video..." $ffmpeg_alone $_ffmpeg_mp4_in -i ${stem}_silent.mp4 \ $_ffmpeg_matroska_wav_in -i $stem.mkv \ $_ffmpeg_mp4_keepvideo ${stem}.mp4 else echo "Encoding AAC audio and muxing with h.264 video..." $ffmpeg_alone $_ffmpeg_mp4_in -i ${stem}_silent.mp4 \ $_ffmpeg_matroska_wav_in -i $stem.mkv \ $_ffmpeg_mp4_keepvideo_aac ${stem}.mp4 fi fi else echo >&2 "Encoding video${channels:+ and audio}, at once..." $melt $_melt_infiles \ ${channels:+$audioprefilters \ ${_melt_loudness:+-filter loudness program=$lufs \ results="$_melt_loudness"}} \ $filters${channels:+ $_melt_postfilters_audio} \ ${ogg:+-consumer avformat:$stem.ogv $_melt_ogg} \ ${webm:+-consumer avformat:$stem.webm $_melt_webm \ ${multipass:+pass=2 passlogfile=${stem}_vp8}} \ ${vp9:+-consumer avformat:${stem}_vp9.webm $_melt_webm_vp9 \ ${multipass:+pass=2 passlogfile=${stem}_vp9}} \ ${mp4:+-consumer avformat:$stem.mp4 $_melt_mp4} fi # cleanup encoding cruft rm -f $stem.xml $stem.wav $stem.mkv ${stem}_*.log ${stem}_*silent.webm # JPEG preview $melt $infile_first in=$stillframe out=$stillframe \ -group $filters \ -consumer avformat:$stem.jpg $_melt_img __width="${_width:+ width=\"$_width\"}" __height="${_height:+ height=\"$_height\"}" # Flash object needs extra space for controllers __heightplus=${_height:+ height=\"$((_height+4))\"} _source_ogg="" _source_webm="" _source_vp9="" _source_mp4="" [ -z "$flashplayer" ] || flash=yes [ -n "$mp4" ] || [ -z "$flash" ] || error1 "Cannot enable flash when mp4 format is disabled." _object_flash="" _param_name="" _param_flashvars="" __oggfile=${ogg:+open format Ogg} __webmfile=${webm:+open format WebM (VP8)} __vp9file=${vp9:+open format WebM (VP9/Opus)} __mp4file=${mp4:+closed format MPEG-4} cat >"$stem.html" < ${mp4:+$_source_mp4 }${vp9:+$_source_vp9 }${webm:+$_source_webm }${ogg:+$_source_ogg }${flash:+$_object_flash $_param_name $_param_flashvars }$title ${flash:+ }

Download Video:

    ${vp9:+
  • $__vp9file }${webm:+
  • $__webmfile }${ogg:+
  • $__oggfile }${mp4:+
  • $__mp4file }

EOF