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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
|
#!/bin/sh
# settings
d_bitrate_audio=448 # kb/s - max for dvd is 448
d_disc_size=4700000 # KB - standard dvd is 4.7GB so 4.7*1000*1000
d_min_bitrate_video=2300 # kb/s - video bitrate is calculated to saturate the disc
d_max_bitrate_video=9000 # kb/s - max for dvd is 9800. mpeg2video is capped at 9000 however difference is negligible
# functions
notify() { [ -n "$V2D_NOTIFICATIONS" ] && notify-send -t 10 "$0" "$1" ; }
confirm_or_quit() {
notify "Confirmation needed:\n$1"
while true; do
printf "\n$1 [y/n] " >&2
read response
case "$response" in
y|Y) break ;;
n|N) echo "Quitting" && exit 1 ;;
esac
printf "\nPlease reply with [y/n]" >&2
done
}
echomsg() { echo "$@" 1>&2; }
get_duration() {
for video in "$@"; do
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$1"
done | paste -sd+ | bc | sed 's/\..*//'
}
create_ffconcat_file() {
# ARGUMENTS
concatfile="$1/ffconcat"
shift
for video in "$@"; do
echo "file '$(realpath "$video")'" >> "$concatfile"
done
echo "$concatfile"
}
calculate_max_video_bitrate() {
# ARGUMENTS
duration=$1
bitrate_audio=${V2D_BITRATE_AUDIO:-$d_bitrate_audio}
disc_size=${V2D_DISC_SIZE:-$d_disc_size}
min_bitrate_video=${V2D_MIN_BITRATE_VIDEO:-$d_min_bitrate_video}
max_bitrate_video=${V2D_MAX_BITRATE_VIDEO:-$d_max_bitrate_video}
audio_filesize=$(( (bitrate_audio / 8) * duration )) # convert kb/s to KB/s
bitrate_video=$(( ((disc_size - audio_filesize) / duration) * 8 )) # convert KB/s to kb/s
# check bitrate
if [ $bitrate_video -gt $max_bitrate_video ]; then
bitrate_video=$max_bitrate_video
saturated=$(( 100 * disc_size / (audio_filesize + (max_bitrate_video * duration)) ))
echomsg "Video bitrate is capped to ${max_bitrate_video}kb/s"
echomsg "Disc saturation: $(( saturated ))%"
echomsg "Even at maximum bitrate, you are wasting $(( 100 - saturated))% of the disc! Do you still want to proceed?"
# TODO
#echo "Keep in mind, you can add multiple video files to a single disc by calling $0 [vid1] [vid2] ..."
confirm_or_quit "Continue?"
elif [ $bitrate_video -lt $min_bitrate_video ]; then
echomsg "Warning low calculated bitrate: ${bitrate_video}kb/s"
echomsg "This bitrate is lower than the quality threshold will allow (${min_bitrate_video}kb/s)"
# TODO
##echo "At lower than ~2300kb/s, the dvd creation will likely fail since that is approximately the lowest bitrate achievable by mpeg2video"
# TODO
#echo
#echo "You can split the video into multiple discs by using one of the following command flags:"
#echo " --max-quality Split the video into multiple discs after it falls below the max dvd quality"
#echo " --split-bitrate [min video bitrate] Split the video into multiple discs after it falls below the minimum bitrate"
#echo " --split-time [time] Split the video into multiple discs after a max length"
#echo " --split-discs [discs] Split the video evenly into X discs"
confirm_or_quit "Would you still like to continue at a low bitrate?"
# TODO
##if [ $bitrate_video -lt 2300 ]; then
## echo "The bitrate is below 2300kb/s, so it is nearly certain to fail"
##fi
fi
echo $bitrate_video
}
vids_to_iso() {
# ARGUMENTS
iso="$1"
shift
bitrate_audio=${V2D_BITRATE_AUDIO:-$d_bitrate_audio}
# make and trap temporary files
# TODO
#tmpfs=${TMPDIR:-/tmp}
#tmpfs_size=$(df -k --output=avail /tmp | tail -1) # Size in kB. Should be size in KB (--block-size=KB over -k) but this is more convenient and allows for extra space
#[ $tmpfs_size -lt $(( disc_size * 2 )) ] && tmpfs=. # Just create it in local directory instead. Not ideal since it could persist between reboots if the trap doesnt function
tmpfs=.
tmpdir="$(mktemp -d "$tmpfs/vid2dvd.XXXXXX")"
trap "{ rm -rf '$tmpdir'; exit 1; }" EXIT INT
# create concat file
concat_file="$(create_ffconcat_file "$tmpdir" "$@")"
# get bitrate
echomsg "Finding optimal bitrate for video..."
videos_duration=$(get_duration "$@")
bitrate_video=$(calculate_max_video_bitrate $videos_duration)
echomsg "Audio bitrate: ${bitrate_audio}kb/s"
echomsg "Video bitrate: ${bitrate_video}kb/s (Calculated)"
echomsg
# transcode with ffmpeg, author with dvdauthor, convert to iso with genisoimage
echomsg "Transcoding videos to correct ntsc dvd-video codecs. This could take a while..."
mpegvid="$tmpdir/vid.mpg"
ffmpeg -hide_banner -f concat -safe 0 -i "$concat_file" -target ntsc-dvd -b:a ${bitrate_audio}k -b:v ${bitrate_video}k "$mpegvid"
mpegvid_size=$(du "$mpegvid" | grep -o "^[0-9]*")
#if [ $mpegvid_size -gt $disc_size ]; then
# echomsg "Conversion failed! Converted video ($mpegvid) is larger than the set disc size!"
# echomsg "Video size: $mpegvid_size"
# echomsg "Disc size: $disc_size"
# confirm_or_quit "Continue regardless? The final iso will be larger than a normal disc"
#fi
echomsg "Done\n"
echomsg "Authoring DVD. This will also take some time..."
dvddir="$tmpdir/dvd"
mkdir -p "$dvddir"
dvdauthor -o "$dvddir" --title "$mpegvid" 2>/dev/null # Generates the VIDEO_TS and AUDIO_TS folders with VOB files from the video stream
dvdauthor -o "$dvddir" --toc 2>/dev/null # Generate table of contents for disc
echomsg "Done\n"
echomsg "Creating ISO image. You thought the last two steps were long? Well lucky you, this will be quick... relatively speaking..."
genisoimage -dvd-video -o "$iso" "$dvddir" # Generate the disc image
echomsg "Done\n"
notify "Job completed iso created\n$@"
echo "$iso"
}
# TODO
# burn to blank dvd
help() {
echomsg "Usage: $0 [arguments] [video] ... [[-c append video] ...]"
echomsg
echomsg "Standard arguments:"
echomsg " -h, --help Output this menu"
echomsg " -n, --enable-notifications Enable sending notifications with notify-send"
echomsg " -o, --output-directory [directory] Put created isos into directory"
echomsg " -d, --output-iso-size [size in kb] Maximum size of iso to be created (Default: $d_disc_size)"
echomsg " -a, --bitrate-audio [bitrate in kb/s] Audio bitrate. Max for standard dvd is 448 (Default: $d_bitrate_audio)"
echomsg " -V, --bitrate-video-max [bitrate in kb/s] Maximum video bitrate. Max for standard dvd is 9800, but max for ffmpeg is 9000 (Default: $d_min_bitrate_video)"
echomsg " -v, --bitrate-video-min [bitrate in kb/s] Minimum video bitrate. Realistic minimum is ~2300 (Default: $d_min_bitrate_video)"
echomsg
echomsg "Video arguments:"
echomsg " [video1] [-c, --concat] [video2] ... Concatenate each video in the chain into a single dvd (still a single track)"
exit ${1:-1}
}
# process arguments
while [ -n "$1" ]; do
case "$1" in
-o|--output-directory) shift && export V2D_OUTDIR=$1 ;;
-d|--output-iso-size) shift && export V2D_DISC_SIZE=$1 ;;
-a|--bitrate-audio) shift && export V2D_BITRATE_AUDIO=$1 ;;
-V|--bitrate-video-max) shift && export V2D_BITRATE_VIDEO_MAX=$1 ;;
-v|--bitrate-video-min) shift && export V2D_BITRATE_VIDEO_MIN=$1 ;;
-n|--enable-notifications) export V2D_NOTIFICATIONS=1 ;;
-h|--help) help 0 ;;
-*) echo "Invalid argument '$1'\n" && help 1 ;;
*) break ;;
esac
shift
done
for video in "$@"; do
if [ -n "$V2D_OUTDIR" ]; then
mkdir -p "$V2D_OUTDIR"
mvideo="$V2D_OUTDIR/$(basename "$video")"
fi
out="$(echo "${mvideo:-$video}" | sed 's/\.[^.]*$//').iso"
if [ -f "$out" ]; then
echomsg "Output file already exists! => $out"
echomsg "Skipping"
continue
fi
vids_to_iso "$out" "$video"
done
|