I recently
posted a script that converts video to VCD format using mencoder and some other tools. I've since greatly modified the script so it can handle VCD, SVCD, and DVD output. It's still somewhat of a hack, since bash scripting is not my strong suit, but it may be useful to someone who needs to get video into the right format for burning to CD or DVD.
Requires the following:
- mplayer for video playback and denoising
- mjpegtools for encoding and adjusting frame rate
- perl for string manipulation (if someone could help me remove this dependency, I'd be gracious!)
- transcode for multiplexing
Here's the script. Save it as 'tovid' and be sure to chmod +x it so you can execute it.
Code:
#!/bin/bash
# Convert any video/audio stream that mplayer can play
# into VCD, SVCD, or DVD-compatible Mpeg output file.
# Arguments: $1 - format: Video CD, Super Video CD, DVD, or VCD-on-DVD [vcd|svcd|dvd]
# $2 - aspect: Widescreen or full-frame [wide|full]
# $3 - name of input file
# $4 - name of output prefix
USAGE="Usage: tovid [vcd|svcd|dvd|dvd-vcd] [wide|full] <input file> <output prefix>"
if [ $# -ne 4 ]; then
echo $USAGE
exit 1
elif [[ $1 == "vcd" || $1 == "dvd-vcd" ]]; then
TGTWIDTH="352"
TGTHEIGHT="240"
PALHEIGHT="288"
FORMAT="VCD"
VIDFMT="-f 1"
# For VCD, use VCD sound profile of mp2enc
if [[ $1 == "vcd" ]]; then
SNDOPTS="-V"
# For VCD-on-DVD, use DVD-format audio
else
SNDOPTS="-r 48000 -s"
fi
MUXOPTS="-m v"
SUF="m1v"
elif [[ $1 == "svcd" ]]; then
TGTWIDTH="480"
TGTHEIGHT="480"
PALHEIGHT="576"
FORMAT="SVCD"
# -d for dummy SVCD scan offsets
VIDFMT="-f 4 -d"
SNDOPTS="-V"
MUXOPTS="-m s"
SUF="m2v"
elif [[ $1 == "dvd" ]]; then
TGTWIDTH="720"
TGTHEIGHT="480"
PALHEIGHT="576"
FORMAT="DVD"
VIDFMT="-f 8"
SNDOPTS="-r 48000 -s"
MUXOPTS="-m d"
SUF="m2v"
else
echo $USAGE
exit 1
fi
if [[ $2 == "wide" ]]; then
ASPECT="WIDE"
elif [[ $2 == "full" ]]; then
ASPECT="FULL"
else
echo "$USAGE"
exit 1
fi
INFILE=$3
OUTPREFIX=$4
# Probe for width, height, and frame rate
tcprobe -i "$INFILE" > fileinfo
CURWIDTH=`grep 'import frame size' fileinfo | \
perl -e ' $line=<STDIN> ; $line =~ /import frame size: -g (\d+?)x\d+ / ; print $1' `
CURHEIGHT=`grep 'import frame size' fileinfo | \
perl -e ' $line=<STDIN> ; $line =~ /import frame size: -g \d+?x(\d+) / ; print $1' `
CURFPS=`grep 'frame rate' fileinfo | \
perl -e ' $line=<STDIN> ; $line =~ /frame rate: -f (.+?) \[/ ; print $1' `
echo "Input file is $CURWIDTH x $CURHEIGHT at $CURFPS fps."
# If FPS is already 29.97 (NTSC) or 23.976 (NTSC film), leave it alone
if [[ $CURFPS == "29.970" ]];
then
echo "Source is 29.970 fps (NTSC). Encoding as NTSC video."
ADJUSTFPS=""
VIDFPS="-F 4"
elif [[ $CURFPS == "23.976" ]];
then
echo "Source is 23.976 fps (NTSC film). Encoding as NTSC film."
ADJUSTFPS="yuvfps -s 24000:1001 -r 30000:1001 -v 0 |"
VIDFPS="-F 4"
else
echo "Source is not at an NTSC frame rate. Adjusting FPS."
ADJUSTFPS="yuvfps -r 30000:1001 -v 0 |"
VIDFPS="-F 4"
fi
# Set appropriate aspect ratio for output format
# Widescreen on DVD should be 16:9
[ $ASPECT == "WIDE" ] && [ $FORMAT == "DVD" ] && ASPECTFMT="-a 3"
# Widescreen on VCD/SVCD needs to be padded out to 4:3
[ $ASPECT == "WIDE" ] && [ $FORMAT != "DVD" ] && ASPECTFMT="-a 2"
# Standard (fullscreen) is always 4:3
[ $ASPECT == "FULL" ] && ASPECTFMT="-a 2"
# Estimate existing aspect ratio (integer math!)
ESTASPECT=$(($CURWIDTH * 100 / $CURHEIGHT))
# Tolerances for wide/full aspect ratio (+/- 10% of target)
if [[ $ASPECT == "WIDE" ]];
then
MINASPECT=160
MAXASPECT=195
else
MINASPECT=120
MAXASPECT=147
fi
# Determine whether any rescaling needs to be done
# If resolution is already the same as the target, do not rescale.
if [[ $CURWIDTH == $TGTWIDTH && $CURHEIGHT == $TGTHEIGHT ]];
then
echo "Source is already at target resolution ($TGTWIDTH x $TGTHEIGHT)."
echo "No rescaling will be applied."
ADJSIZE=""
# See if source is target resolution in PAL
# If so, just rescale; aspect ratio should be fine
elif [[ $CURWIDTH == $TGTWIDTH && $CURHEIGHT == $PALHEIGHT ]];
then
echo "Source appears to be PAL of target resolution ($TGTWIDTH x $PALHEIGHT)."
echo "Assuming correct aspect ratio and rescaling."
ADJSIZE="yuvscaler -O $FORMAT -v 0 -n n |"
elif [[ $ESTASPECT -ge $MINASPECT && $ESTASPECT -le $MAXASPECT ]];
then
echo "Source is within 10% of target aspect ratio."
echo "Assuming correct aspect ratio and rescaling."
ADJSIZE="yuvscaler -O $FORMAT -v 0 -n n |"
# Otherwise, scale and/or pad with black bars
else
echo "Scaling and/or padding with letterbox bars"
# For non-DVD formats, widescreen needs to be padded to make
# it fullscreen.
[ $FORMAT != "DVD" ] && [ $ASPECT == "WIDE" ] && \
ADJSIZE="yuvscaler -O $FORMAT -v 0 -n n -M WIDE2STD |"
# Non-DVD standard sizes can be scaled directly
[ $FORMAT != "DVD" ] && [ $ASPECT == "FULL" ] && \
ADJSIZE="yuvscaler -O $FORMAT -v 0 -n n |"
# DVD can be scaled directly
[ $FORMAT == "DVD" ] && \
ADJSIZE="yuvscaler -O $FORMAT -v 0 -n n |"
fi
echo "Creating and encoding video stream..."
mkfifo stream.yuv
mplayer -nosound -noframedrop -vo yuv4mpeg -vf pp=hb/vb/dr,hqdn3d "$INFILE" &
eval `echo "cat stream.yuv | $ADJUSTFPS $ADJSIZE nice -n 16 mpeg2enc $ASPECTFMT $VIDFMT $VIDFPS -v 0 -n n -H -o $OUTPREFIX.$SUF"`
echo "Creating WAV of audio stream..."
mplayer -vc dummy -vo null -ao pcm -aofile stream.wav "$INFILE"
# echo "Normalizing WAV audio..."
# normalize --amplitude=-14dBFS stream.wav
echo "Encoding WAV..."
cat stream.wav | mp2enc $SNDOPTS -o "$OUTPREFIX.mpa"
echo "Multiplexing audio and video together..."
tcmplex -i "$OUTPREFIX.$SUF" -p "$OUTPREFIX.mpa" -o "$OUTPREFIX.mpg" $MUXOPTS
echo "Cleaning up..."
rm stream.yuv
rm stream.wav
# rm "$OUTPREFIX.$SUF"
# rm "$OUTPREFIX.mpa"
rm fileinfo
echo "Done"
I had some problems with frame rate which I think are temporarily fixed, although messily. Encoding is fairly slow - for DVD encoding, about 4 frames per second on my Athlon XP 1600. VCD is faster, but still takes a good chunk of time. YMMV. I've included a 'nice' statement in the encoding command, so it should be possible to let it run in the background without much interference (though it'll still hog a lot of your disk activity).
There's an option for encoding VCD resolution in DVD format (the 'dvd-vcd' argument), which is supposed to allow something like 450 minutes of video on a DVD, but I have not tried using it yet.
This script is exclusively written to encode NTSC, so if you are going for PAL, you will need to make some modifications. Maybe I'll add PAL someday, but I have no use for it.
No guarantees of any kind are made regarding this script. Use at your own risk! Let me know if you find bugs or have suggestions for improvement. Also let me know if you try it and it works!
Update: Several improvements have been made. Some minor logic cleanup, and the addition of a fuzzy-logic bit that tries to guess whether the input is at the correct aspect ratio (to account for almost-correct aspect ratios or weird sizes). I've tested the script on a short clip for all output formats in full-frame mode. Seems to work OK.