forked from pituz/webm-thread
-
Notifications
You must be signed in to change notification settings - Fork 0
/
fit-audio-to-limit
executable file
·90 lines (86 loc) · 3.19 KB
/
fit-audio-to-limit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/usr/bin/env zsh
set -e
zmodload zsh/mathfunc
programname=$0:t
die() {
echo $@ >&2
exit 1
}
time_to_seconds() {
[[ $1 =~ ^((([0-9]+):)?([0-9]+):)?([0-9.]+)$ ]]
local result=$((match[3]*3600 + match[4]*60 + match[5]))
[[ -n $2 ]] && (($2=result)) || echo $result
}
help() {
cat<<EOF
Usage: $programname [options] source.mkv fragment.webm
Options:
-o - force libopus audio codec (currently default, autodetection planned)
-v - force libvorbis audio codec (not implemented yet)
-s time - start of needle fragment in source
-F size - result size limit, KiB (default: 6144)
-f size - acceptable size skew, KiB (default: 128)
-A value - audio filters
EOF
exit 1
}
audio_start=0
((size_limit=6144*1024))
((max_size_skew=128*1024))
((max_audio_bitrate=160*1024))
max_tries=10
min_bitrate_diff=100 # bps
while getopts "ovs:F:f:A:" o
do
case $o in
s) audio_start=$OPTARG;;
F) size_limit=$((OPTARG * 1024));;
f) max_size_skew=$((OPTARG * 1024));;
A) audio_filters=$OPTARG;;
*) help;;
esac
done
shift $((OPTIND - 1))
audio_src=$1; video_src=$2
[[ -f $audio_src ]] && [[ -f $video_src ]] || help
dst_name="${video_src:t:r}-v2"
audio_file="${dst_name}-a.webm"
dst_file="$dst_name.webm"
time_to_seconds "$(ffprobe $video_src 2>&1|sed -n 's/.*Duration: \([^,]\+\),.*/\1/p')" duration
video_size=$(( $(ffmpeg -hide_banner -v error -i $video_src -an -c copy -f matroska - | wc -c) ))
result_size=0
((audio_bitrate = (size_limit - video_size) * 8 / duration))
try=1;
printf "Fitting %s to %d KiB limit by compressing audio..\n" $video_src $((size_limit/1024))
printf "video size: %d KiB; space for audio: %d KiB; duration: %d seconds; bitrate: %.2f kbps\n" \
$((video_size/1024)) $(((size_limit - video_size) / 1024)) $duration $((audio_bitrate / 1024))
((audio_bitrate < 0)) && die "Impossiburu: no space for audio"
while ((result_size > size_limit)) || ((result_size + max_size_skew < size_limit))
do
((try > max_tries)) && die "Too many failures, aborting."
if ((result_size))
then
((bitrate_diff = size_skew * 8 / duration ))
((abs(bitrate_diff) < min_bitrate_diff)) &&
((bitrate_diff = size_skew / abs(size_skew) * min_bitrate_diff))
((audio_bitrate += bitrate_diff))
fi
((audio_bitrate > max_audio_bitrate)) && audio_bitrate=$max_audio_bitrate
printf "Try %d of %d: bitrate %.2f kbps\n" \
$((try++)) $max_tries $((audio_bitrate/1024.0))
ffmpeg -hide_banner -v error -stats -ss $audio_start -i $audio_src -map 0:a:0 -t $duration \
${audio_filters:+-af} $audio_filters \
-c:a libopus -application audio -vbr on -b:a $audio_bitrate -y $audio_file
if ((MUX_WITH_FFMPEG))
then
ffmpeg -hide_banner -v error -i $video_src -i $audio_file -map 0:v -map 1:a -c copy -y $dst_file
else
mkvmerge -q -A $video_src $audio_file -o $dst_file
fi
result_size=$(( $(stat -c%s $dst_file) ))
((size_skew = size_limit - result_size ))
printf "Result size: %.3f KiB (%.3f KiB to limit)\n" \
$((result_size / 1024.0)) $((size_skew / 1024.0))
((audio_bitrate == max_audio_bitrate)) && break
done || :
rm $audio_file