Skip to content

Instantly share code, notes, and snippets.

@cocoabox
Created December 29, 2014 11:03
Show Gist options
  • Save cocoabox/72e7ad26ff55887e13c8 to your computer and use it in GitHub Desktop.
Save cocoabox/72e7ad26ff55887e13c8 to your computer and use it in GitHub Desktop.
remux .mkv into .mp4 (transcode audio to AAC) using ffmpeg, with progress indicator and time remaining
#!/bin/bash
PURPLE=$(tput setaf 13)
RED=$(tput setaf 1)
CYAN=$(tput setaf 14)
YELLOW=$(tput setaf 11)
GREEN=$(tput setaf 2)
NONE=$(tput sgr0)
ERASE="`tput cuu1``tput el`"
if [ -z "$1" ]; then
echo "Usage: $(basename $0) [source movie file]"
exit 1
fi
if [ ! -f "$1" ]; then
echo "${RED}ERROR: $1 : not found$NONE"
exit 1
fi
FFMPEG=$(which ffmpeg 2>/dev/null)
if [ -z "$FFMPEG" ];
# for my thecus N5550
FFMPEG="/raid0/data/module/ffmpeg/sys/bin/ffmpeg"
if [ ! -f "$FFMPEG" ]; then
echo "$(RED) ffmpeg not found"
exit 666;
fi
fi
AUD=$("$FFMPEG" -i "$1" 2>&1 | sed -ne 's/^[ ]*Stream #0:\([0-9]*\).*: Audio: \([^\, ]*\), \(.*\)$/track="\1"; codec="\2"; etc="\3"/p')
VID=$("$FFMPEG" -i "$1" 2>&1 | sed -ne 's/^[ ]*Stream #0:\([0-9]*\).*: Video: \([^ (]*\)\(.*\)$/track="\1"; codec="\2"; etc="\3"/p')
TRANSCODE_AUDIO=0
if [ "$2" == "force_aac" ]; then
TRANSCODE_AUDIO=1
fi
if [ $(echo "$VID" | wc -l) -gt 1 ]; then
while read -r line; do
eval "$line"
echo "video ${PURPLE}$track ${YELLOW}$codec ${NONE}$etc"ERASE
done <<< "$AUD"
echo -n "--- Which ${PURPLE}video track${NONE} to use ? "; read VID_TRACK
VID_CODEC=""
while read -r line; do
eval "$line"
if [ "$track" == "$VID_TRACK" ]; then VID_CODEC="$codec"; break; fi
done <<< "$VID"
else
eval "$VID"
VID_TRACK="$track"
VID_CODEC="$codec"
fi
if [ -z "$VID_TRACK" -o -z "$VID_CODEC" ] ; then
echo "ERROR : video track or codec not specfied, or wasn't parsed properly in ffmpeg output"
exit 1
fi
if [ "$VID_CODEC" != "h264" ]; then
echo "WARNING : non H.264 video stream; might not be playable on PS3"
fi
echo "--> will use video track ${CYAN}$VID_TRACK${NONE} encoded in ${CYAN}$VID_CODEC${NONE}"
if [ $(echo "$AUD" | wc -l) -gt 1 ]; then
while read -r line; do
eval "$line"
echo "audio ${PURPLE}$track ${YELLOW}$codec ${NONE}$etc"
done <<< "$AUD"
echo -n "--- Which ${PURPLE}audio track${NONE} to use ? "; read AUD_TRACK
AUD_CODEC=""
while read -r line; do
eval "$line"
if [ "$track" == "$AUD_TRACK" ]; then AUD_CODEC="$codec"; break; fi
done <<< "$AUD"
else
eval "$AUD"
AUD_TRACK="$track"
AUD_CODEC="$codec"
fi
PARAM_ACODEC="-c:a copy"
PARAM_AQUALITY=""
if [ -z "$AUD_TRACK" -o -z "$AUD_CODEC" ] ; then
echo "ERROR : audio track or codec not specfied, or wasn't parsed properly in ffmpeg output"
exit 1
fi
if [ "$AUD_CODEC" == "aac" -a $TRANSCODE_AUDIO -eq 0 ]; then
echo "--> will copy audio track ${CYAN}$AUD_TRACK${NONE}"
else
echo "--> will transcode audio track ${CYAN}$AUD_TRACK${NONE} using libfaac"
PARAM_ACODEC="-c:a libfaac"
PARAM_AQUALITY="-q:a 100"
fi
OUT="$1.output.mp4"
if [ -f "$OUT" ]; then
echo -n "--> ${YELLOW}$OUT${NONE} already exists. Enter another name (Enter=delete existing) ? "
read FN
[[ -z "$FN" ]] && FN="$OUT"
[[ -f "$FN" ]] && rm -f "$FN"
OUT="$FN"
else
echo "--> output file is : ${CYAN}$OUT${NONE}"
fi
DURA=$("$FFMPEG" -i "$1" 2>&1 | sed -ne 's/^.*Duration: \([0-9]*\):\([0-9]*\):\([0-9]*\)\.\([0-9]*\).*$/hr=\1; min=\2; sec=3; msec=4/gp')
if [ -z "$DURA" ] ; then
"$FFMPEG" -i "$1" -c:v copy \
$PARAM_ACODEC $PARAM_AQUALITY \
-map 0:$VID_TRACK -map 0:$AUD_TRACK \
"$OUT"
exit 1
else
eval "$DURA"
hr=$(expr $hr + 0); min=$(expr $min + 0); sec=$(expr $sec + 0); msec=$(expr $msec + 0)
((TOTAL_SEC = hr * 3600 + min * 60 + sec))
echo "TOTAL $TOTAL_SEC sec"
"$FFMPEG" -i "$1" -c:v copy \
$PARAM_ACODEC $PARAM_AQUALITY \
-map 0:$VID_TRACK -map 0:$AUD_TRACK \
-loglevel debug \
"$OUT" 2>/tmp/ffmpeg.out 1>/dev/null &
PID=$!
trap "kill $PID 2>/dev/null" EXIT
TIME_START=$(date +%s)
while kill -0 $PID 2> /dev/null; do
LAST_LINE=$(cat /tmp/ffmpeg.out | tail -n1)
NOW=$(echo "$LAST_LINE" | sed -ne 's/.*time=\([0-9]*\):\([0-9]*\):\([0-9]*\)\.\([0-9]*\).*/hr=\1; min=\2; sec=\3; msec=\4/p')
if [ ! -z "$NOW" ]; then
eval "$NOW"
hr=$(expr $hr + 0); min=$(expr $min + 0); sec=$(expr $sec + 0); msec=$(expr $msec + 0)
((PROGRESS = hr * 3600 + min * 60 + sec))
TIME_NOW=$(date +%s)
((TIME_ELAPSED = TIME_NOW - TIME_START))
((SPEED = PROGRESS / TIME_ELAPSED))
((SEC_LEFT = (TOTAL_SEC - PROGRESS) / SPEED))
MIN_LEFT=0
if [ $SEC_LEFT -ge 60 ]; then
((MIN_LEFT = SEC_LEFT / 60))
((SEC_LEFT = SEC_LEFT % 60))
fi
TIME_LEFT=$(printf "%02d:%02d" $MIN_LEFT $SEC_LEFT)
echo "$ERASE progress: ${YELLOW}$PROGRESS${NONE}/$TOTAL_SEC, ETA ${YELLOW}$TIME_LEFT ${NONE}"
else
echo "$ERASE progress: --"
fi
sleep 1
done
echo "${ERASE}${GREEN}Done${NONE}"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment