YouTube に高音質でアップロードできる動画ファイルを作るシェル・スクリプト (改良 FFmpeg 版)

2008-02-18 追記

外部に MP3 ファイルがなかった場合の処理がおかしかったので修正した。

FFmpeg 版も改良しておく

"YouTube に高音質でアップロードできる動画ファイルを作るシェル・スクリプト (改良 MEncoder 版) - だらりな。" のシェル・スクリプトでうまくエンコードできない動画があって、FFmpeg だとうまくエンコードできたので、FFmpeg を使ったほうも一応改良しておくことにした。ただし、こっちはあくまで非常時用。
たぶん、動画ファイル側のむちゃくちゃな自己申請が悪い。

$ mplayer -vo null -ao null -frames 0 -identify <ファイル名> 2> /dev/null | grep -e ^ID_
ID_VIDEO_ID=0
ID_AUDIO_ID=1
ID_FILENAME=<ファイル名>
ID_DEMUXER=lavfpref
ID_VIDEO_FORMAT=VP6F
ID_VIDEO_BITRATE=0
ID_VIDEO_WIDTH=512
ID_VIDEO_HEIGHT=384
ID_VIDEO_FPS=1000.000
ID_VIDEO_ASPECT=0.0000
ID_AUDIO_FORMAT=85
ID_AUDIO_BITRATE=112000
ID_AUDIO_RATE=44100
ID_AUDIO_NCH=2
ID_LENGTH=188.72
ID_VIDEO_CODEC=ffvp6f
ID_AUDIO_BITRATE=32000
ID_AUDIO_RATE=44100
ID_AUDIO_NCH=2
ID_AUDIO_CODEC=mp3

映像が 1000 fps とか音声が 32 kbps とかむちゃくちゃ。ID_VIDEO_BITRATE=0 ってのは、たぶん MPlayer のその部分が壊れてるか、まだ実装されてないかで、他の動画ファイルでもいっしょだけど。
それはともかく、MEncoder 版とほぼ同じ感じで改良した。

  • 画質改善オプションについては、そんなオプション知らないと怒られてしまったものをハズした。
  • FFmpeg で外部音声ファイルを使う方法もわかったのだけど、うまく mux できないと怒られるケースがあったので、結局その部分は MEncoder を使うことにした。

以下が問題の動画。MEncoderエンコードすると数秒の動画になってしまったのだけど、FFmpeg だとだいじょうぶだった。

どんくさいシェル・スクリプト

MEncoder 版とほぼ同じ感じ。

#! /bin/sh

source ${HOME}/bin/ffmpeg-youtube.conf

MSG_PREFIX="[FFmpeg for YouTube] "

#
# Probe
#

INPUT_FILE=`echo $1 | sed -e 's/"/\\"/g' -e 's/!/\\!/g'`
if [ ! -f "${INPUT_FILE}" ]; then
	echo "${MSG_PREFIX}Can't find \"${INPUT_FILE}\"" >&2 
	exit 1
fi
OUTPUT_FILE=`basename "${INPUT_FILE}" | sed "s/\w\+$/forYouTube.flv/"`
OUTPUT_FILE_NEW_AUDIO=`basename "${INPUT_FILE}" | sed "s/\w\+$/forYouTube.NewAudio.flv/"`

INPUT_AUDIO_FILE=`echo "${INPUT_FILE}" | sed "s/\w\+$/mp3/"`
if [ -f "${INPUT_AUDIO_FILE}" ]; then
	echo "${MSG_PREFIX}Found \"${INPUT_AUDIO_FILE}\""
else
	INPUT_AUDIO_FILE=${INPUT_FILE}
fi

probe () {
	PROBE_DATA=`mplayer -vo null -ao null -frames 0 -identify "$1" 2> /dev/null | grep -e "$2"`
	eval ${PROBE_DATA}
}

probe "${INPUT_AUDIO_FILE}" "^ID_AUDIO"

VIDEO_BITRATE=`expr ${TOTAL_BITRATE} - ${ID_AUDIO_BITRATE}`
echo "${MSG_PREFIX}AUDIO_BITRATE: ${ID_AUDIO_BITRATE} VIDEO_BITRATE: ${VIDEO_BITRATE}"
#exit 0	# Debug

probe "${INPUT_FILE}" "^ID_VIDEO"

[ ! ${WIDTH} ] && WIDTH=${ID_VIDEO_WIDTH}

ID_VIDEO_ASPECT_X10000=`echo ${ID_VIDEO_ASPECT} | sed "s/\.//"`
if [ ${ID_VIDEO_ASPECT_X10000} -eq 0 ]; then
	HEIGHT=`expr ${ID_VIDEO_HEIGHT} \* ${WIDTH} / ${ID_VIDEO_WIDTH}`
else
	HEIGHT=`expr ${WIDTH} \* ${ID_VIDEO_ASPECT_X10000} / 10000`
fi

PAD_SIZE=`expr ${WIDTH} \* 3 / 4 - ${HEIGHT}`
PADTOP_SIZE=`expr ${PAD_SIZE} / 2`
PADBOTTOM_SIZE=`expr ${PAD_SIZE} - ${PADTOP_SIZE}`

[ ! ${FPS} ] && FPS=${ID_VIDEO_FPS}

echo "${MSG_PREFIX}WIDTH: ${WIDTH}; HEIGHT: ${HEIGHT}; PAD_SIZE: ${PAD_SIZE}; FPS: ${FPS}"
#exit 0	# Debug

#
# Encode
#

VIDEO_OPTIONS="-mbd 2 -mv0 -trell -last_pred 3"
PASSLOGFILE="ffmpeg-youtube"

encode () {
	ffmpeg -y -i "${INPUT_FILE}" $1 -pass $2 -passlogfile ${PASSLOGFILE}\
		-s ${WIDTH}x${HEIGHT} -padtop ${PAD_SIZE} -padbottom ${PAD_SIZE} -r ${FPS}\
		-vcodec flv -b ${VIDEO_BITRATE} ${VIDEO_OPTIONS} "${OUTPUT_FILE}"
}

echo "${MSG_PREFIX}Encoding \"${INPUT_FILE}\" (pass 1/2)"
encode "-an" 1
echo "${MSG_PREFIX}Encoding \"${INPUT_FILE}\" (pass 2/2)"
if [ -f "`echo ${INPUT_FILE} | sed 's/\w\+$/mp3/'`" ]; then
	encode "-an" 2
	mencoder -oac copy -ovc copy -o "${OUTPUT_FILE_NEW_AUDIO}" -audiofile "${INPUT_AUDIO_FILE}" "${OUTPUT_FILE}" -of lavf -lavfopts format=flv
	rm "${OUTPUT_FILE}"; OUTPUT_FILE=${OUTPUT_FILE_NEW_AUDIO}
else
	encode "-acodec copy" 2
fi

OUTPUT_FILE_SIZE_BYTE=`ls -l "${OUTPUT_FILE}" | cut -d " " -f 5`
OUTPUT_FILE_SIZE_BIT_X100=`expr ${OUTPUT_FILE_SIZE_BYTE} \* 8 \* 100`
probe "${OUTPUT_FILE}" "^ID_LENGTH"
OUTPUT_FILE_LENGTH_X100=`echo ${ID_LENGTH} | sed "s/\.//"`
echo "${MSG_PREFIX}TOTAL_BITRATE: `expr ${OUTPUT_FILE_SIZE_BIT_X100} / ${OUTPUT_FILE_LENGTH_X100}` bps"

rm ${PASSLOGFILE}*.log

exit 0

設定用ファイル。

TOTAL_BITRATE=340000

WIDTH=320

#FPS=1
#FPS=7
#FPS=10
#FPS=12
FPS=15