試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

OpenGL/ALSA環境向けキャプチャツールglcでキャプチャした動画ファイルを無劣化の形式にエンコードする

glcではキャプチャした内容を劣化のない形式で保存するが、これは一般的な動画プレーヤでは再生できず、再生できるようにするにはglc-playによる変換が必要となるが、yuv4mpeg形式も連番PNG形式もディスク領域を大きく使用してしまう。
OpenGLアプリケーションが快適にキャプチャできるglcについて(キャプチャ、再生、エンコード)」の作業や「OpenGL/ALSA環境向けキャプチャツールglcの(H.264+AAC).mp4向けエンコード支援スクリプトを更新(2010/1/6版)」で扱ったスクリプトでは大きくファイルサイズを小さくできるが、可逆ではない(H.264 + AAC).mp4という形式を最終的に出力するようになっており、出力ファイルを動画編集ソフトで編集するのには向かない。
そこで、ロスレス(無劣化)指定のH.264コーデックを用いて中間ファイルとして使える「(H.264 ロスレス + WAVE).avi」形式のファイルを生成する用途のスクリプトを作成した。音声については現時点ではWAVEのまま(FLACにするとうまく再生できない)でファイルサイズは大きめになる(オプションで音声なしの出力にも対応した)が、KdenliveOpenShotといった動画編集ソフトでクリップとして取り込んで再生が正常に行えることを確認した。拡大/縮小にも対応したが、この処理は可逆ではないので、拡大/縮小する場合は元の.glcファイルを消すことは推奨しない(消す場合は別途サイズ変更なしで変換してそれを残す)。
動作にはglcの他、Zenity,libx264,libx264サポート付きのFFmpegが必要。スレッド数はCPUの認識数から自動的に指定するようにした。MEncoderは使用していない。
スクリプトはテスト不足につき、不具合が残っている可能性がある。
[任意]ファイル名: glc2losslessavi.sh ライセンス: GPL-3 (or lator)

#! /bin/bash

# glc -> (H.264 lossless + WAVE).avi convert/encode script
# version 20110121
# (C) 2011 kakurasan
# Licensed under GPLv3+

PATH=/bin:/usr/bin

STREAM_AUDIO=1
STREAM_VIDEO=1

FPS=30

TMP_AUDIO=audio.wav
OUTFILE=../glc2losslessavi-output.avi

print_usage()
{
  printf "Usage: %s ( [OPTIONS...] ) [INPUT]
  -a NUM           use audio stream NUM
  -c WIDTHxHEIGHT  scale to WIDTHxHEIGHT
  -d NUM           use video stream NUM
  -f FPS           set frame rate to FPS (default: %s)
  -h               print help
  -m TERM          display log using TERM (X terminal emulator)
  -o OUTFILE       set output location
  -p               use spline algorithm (with -c option, default: lanczos)
  -v               video only

Environment variables:
  GLCPLAY          glc-play command (default: glc-play)
  FFMPEG           ffmpeg command (default: ffmpeg)
  ICON             use ICON for notification icon
" ${0} ${BITRATE_AUDIO} ${FPS} ${BITRATE_VIDEO}
}

SCALE=0
SPLINE=0
VIDEOONLY=0
USE_XTERM=0
while getopts 'a:c:d:f:hm:o:pv' OPT
do
  case ${OPT} in
   a)
    STREAM_AUDIO=${OPTARG}
    ;;
   c)
    SCALE=1
    SIZE=${OPTARG}
    ;;
   d)
    STREAM_VIDEO=${OPTARG}
    ;;
   f)
    FPS=${OPTARG}
    ;;
   m)
    USE_XTERM=1
    TERMINAL=${OPTARG}
    ;;
   o)
    OUTFILE="${OPTARG}"
    OUTDIR="$(dirname ${OUTFILE})"
    if [ ! -d "${OUTDIR}" ]; then
      if ! mkdir -p "${OUTDIR}"; then
        echo "Error: Cannot create output directory \"${OUTDIR}\"" >&2
        exit 1
      fi
    fi
    OUTFILE=$(cd $(dirname "${OUTFILE}") && pwd)/$(basename "${OUTFILE}")
    ;;
   p)
    SPLINE=1
    ;;
   v)
    VIDEOONLY=1
    ;;
   ?)
    print_usage
    exit 1
    ;;
  esac
done
shift $((${OPTIND} - 1))
if [ ${#} -lt 1 ]; then
  print_usage
  exit 1
fi
INFILE=${1}
if [ ! -f ${INFILE} ]; then
  echo "Error: input file \"${INFILE}\" not found." >&2
  exit 1
fi

LOGFILE="$(printf ../glc2losslessavi-%s-%s.log "$(basename "${INFILE}")" $(date +%Y%m%d%H%M%S))"

TEMPDIR=$(mktemp -d glc2losslessavi.XXXXXXXX)
if [ ${?} -ne 0 ]; then
  echo "Error: cannot create tempdir" >&2
  exit 1
fi
INFILE=$(cd $(dirname ${INFILE}) && pwd)/$(basename ${INFILE})
cd ${TEMPDIR}

GLCPLAY=${GLCPLAY:-glc-play}
FFMPEG=${FFMPEG:-ffmpeg}
SCALEFLAGS=""
if [ ${SCALE} -eq 1 ]; then
  SCALEFLAGS="-s ${SIZE}"
  if [ ${SPLINE} -eq 1 ]; then
    SCALEFLAGS="${SCALEFLAGS} -sws_flags spline+print_info"
  else
    SCALEFLAGS="${SCALEFLAGS} -sws_flags lanczos+print_info"
  fi
fi
THREADSFLAGS="-threads $(egrep "^processor\s:\s[0-9]+$" /proc/cpuinfo | wc -l)"
CMD_VIDEO_CONV="${GLCPLAY} -g \"0;0;1;1;1\" ${INFILE} -o - -y ${STREAM_VIDEO}"
CMD_VIDEO="${FFMPEG} ${THREADSFLAGS} -i - ${SCALEFLAGS} -i ${TMP_AUDIO} -acodec copy -vcodec libx264 -vpre lossless_max -y -r ${FPS} ${OUTFILE}"
CMD_VIDEO_NOAUDIO="${FFMPEG} ${THREADSFLAGS} -i - ${SCALEFLAGS} -vcodec libx264 -vpre lossless_max -y -r ${FPS} ${OUTFILE}"
CMD_AUDIO_CONV="${GLCPLAY} -a ${STREAM_AUDIO} ${INFILE} -o ${TMP_AUDIO}"

exec 3> >(zenity --notification --listen ${ICON:+--window-icon }${ICON:-})
touch "${LOGFILE}"
if [ ${USE_XTERM} -eq 1 ]; then
  ${TERMINAL} -e tail -f "${LOGFILE}" &
  PID=${!}
fi

if [ ${VIDEOONLY} -eq 0 ]; then
  # extract audio (WAVE)
  echo "tooltip: ${CMD_AUDIO_CONV}" >&3
  echo "${CMD_AUDIO_CONV}" >> "${LOGFILE}"
  ${CMD_AUDIO_CONV} >> "${LOGFILE}" 2>&1 || exit 1
  if [ ! -f ${TMP_AUDIO} ]; then
    echo "audio not found"
    exit 1
  fi
  echo "message: audio: done" >&3
  # encode: (H.264 lossless + WAVE).avi
  echo "tooltip: ${CMD_VIDEO_CONV} | ${CMD_VIDEO}" >&3
  echo "${CMD_VIDEO_CONV} | ${CMD_VIDEO}" >> "${LOGFILE}"
  ${CMD_VIDEO_CONV} | ${CMD_VIDEO} >> "${LOGFILE}" 2>&1 || exit 1
  echo "message: video: done" >&3
else
  # encode: (H.264 lossless).avi
  echo "tooltip: ${CMD_VIDEO_CONV} | ${CMD_VIDEO_NOAUDIO}" >&3
  echo "${CMD_VIDEO_CONV} | ${CMD_VIDEO_NOAUDIO}" >> "${LOGFILE}"
  ${CMD_VIDEO_CONV} | ${CMD_VIDEO_NOAUDIO} >> "${LOGFILE}" 2>&1 || exit 1
  echo "message: video (no audio): done" >&3
fi
echo "message: all done" >&3

if [ ${USE_XTERM} -eq 1 ]; then
  kill ${PID}
fi
cd ..
rm ${TEMPDIR} -fr

関連記事:

使用したバージョン:

  • glc 0.5.8
  • FFmpeg 0.6.1
  • x264 0.110 (開発版)