rokkonet

PC・Androidソフトウェア・アプリの開発・使い方に関するメモ

MPEG2動画ファイルをH.264に変換するbashシェルスクリプト

2021 Mar. 14.
2020 Apr. 19.
2020 Apr. 18.

コマンドラインに与えられたMPEG2ファイルを解像度を落としたH.264に変換

ts2h264ffmpeg.sh

#!/bin/bash

# 2020 Apr. 19.
# 2010 Jun. 11.
# Ryuichi Hashimoto.

# Convert mpeg2-ts-video to H.264 codec of 800x450 and AAC codec.
# Usage: COMMAND INFILE [...]
#  INFILE codec type : MPEG2-file."
#  Outfile-name : INFILE.mp4.
#  But INFILE(abc.mp4) -> OUTFILE(abc.mp4)
#
# This program uses ffmpeg, tssplitter_lite.
#
# Change "NewX=MAX-WIDTH-VALUE" in the script to change max width of a converted video.
#
# Filename of converted video is "original video basefilename+SID.mp4"
#
# Put libx264-ts-good.ffpreset in any directory and tell the directory in the script.
#
### libx264-ts-good.ffpreset           ###
### (remove "# " of head of each line) ###
# level=41
# crf=25
# coder=1
# flags=+loop
# cmp=+chroma
# partitions=+parti8x8+parti4x4+partp8x8+partb8x8
# me_method=umh
# subq=7
# me_range=16
# g=250
# keyint_min=25
# sc_threshold=40
# i_qfactor=0.71
# b_strategy=1
# qmin=10
# rc_eq=’blurCplx^(1-qComp)’
# bf=16
# bidir_refine=1
# refs=6
################################


Cmdname=`\basename $0`
NewX=870
CPU_CORES=$(/usr/bin/getconf _NPROCESSORS_ONLN)
FFMPEGCMD='ffmpeg'
# FFMPEGpresetfile='/YOUR/FFMPEG/PRESET/DIR/libx264-ts-good.ffpreset'
FFMPEGpresetfile='/usr/local/my/ffmpeg/libx264-ts-good.ffpreset'

TmpStr=`which ${FFMPEGCMD}`
if [ ${#TmpStr} -lt 1 ]; then 
   \echo "${FFMPEGCMD} is not installed." 1>&2
   \exit 1
fi

TmpStr=`\which tssplitter_lite`
if [ ${#TmpStr} -lt 1 ]; then 
   \echo "tssplitter_lite is not installed." 1>&2
   \exit 1
fi



# function
function usage() {
cat << EOP
Usage: $Cmdname INFILE [...]
  INFILE codec type : MPEG2-file.
  Outfile-name : INFILE.mp4.
  But INFILE(abc.mp4) -> OUTFILE(abc.mp4)
  STANDARD-OUT: successfully converted filenames

Convert mpeg2-ts-video to H.264 codec of 800x450 and AAC codec.
EOP
}


# function
# get resolution of movie.
getResolut(){
  Resolut=`${FFMPEGCMD} -i ${infileTmp} 2>&1 | \grep -oP '\d\d+x\d\d+'`
  ResolutX=`\echo $Resolut | \grep -oP '^\d\d+'`
  ResolutY=`\echo $Resolut | \grep -oP '\d\d+$'`

  # x-size of resolution is ResolutX
  # y-size of resolution is ResolutY
  # by the result of above routine.
}


# function
getNewResolution(){
  # get resolution for new video.
  getResolut

  # NewX : max length of x-width
  while [[ NewX -gt 100 ]]; do
    NewY=$(( NewX * ResolutY / ResolutX ))
    # NewYが小数点以下を有するかどうかチェック
    NewYReal=`\echo "scale=5; $NewY + 0.00000" | \bc`
    NewY2=`\echo "scale=5; $NewX * $ResolutY / $ResolutX" | \bc`
    if [[ $NewYReal = $NewY2 ]]; then
      # NewYが奇数ではffmpegでの動画変換が失敗する
      if [ `expr $NewY % 2` == 0 ]; then
        # 偶数
        \break
      fi
    fi
    NewX=$(( NewX - 10 ))
  done
  if [[ $NewX -lt 101 ]]; then
    \echo "Could not get the resolution for new video of ${infileTmp}." 2>&1
    \continue
  fi
  # x-size of resolution for new video is NewX
  # y-size of resolution for new video is NewY
  # by the result of above routine.
}


# function
convert2h264(){
  ${FFMPEGCMD} -y -i ${infileTmp} -f mp4 -vcodec libx264 -fpre ${FFMPEGpresetfile} -r 30000/1001 -aspect 16:9 -s ${NewX}x${NewY} -bufsize 20000k -maxrate 25000k -acodec aac -absf aac_adtstoasc -strict -2 -threads $CPU_CORES $outfileTmp
  Result=$?
  if [ $Result -eq 0 ]; then
    \echo "${outfileTmp}"
  else
    if [ -f $outfileTmp ]; then
      \rm -f $outfileTmp
    fi
    \echo "Failed H.264 converting." 1>&2
  fi
}


############
### main ###
############

# display usage when no args
if [ $# -lt 1 ]; then
  usage 1>&2
  \exit 1
fi

# get filenames in array
infiles=($@)

for fname in ${infiles[@]}
do
  if [ ! -f $fname ]; then
    \echo "$fname does not exist." 1>&2
    \continue
  fi

  # Ignore BS-campus-program(*BS11*) because converting this kind of files to mp4 would fail.
  \echo $fname | \grep -q BS11
  Result=$?
  if [[ 0 = $Result ]]; then
    \continue
  fi

  infile=$fname
  outfile=${infile}.mp4

  # check if video codec is mpeg2
  # ( ${FFMPEGCMD} -i $infile 1> /dev/null ) 2>&1 | \egrep ': Video: ' | \egrep -q 'mpeg2video'
  if [ $? -ne 0 ]; then
    \echo "$infile is not mpeg2video." 1>&2
    \continue
  fi

  # get SIDs and convert each SID-program to H.264.
  SIDs=`${FFMPEGCMD} -i ${infile} 2>&1 | \get_high-resolution-sids_by_ffmpeg.awk`
  CountSID=`\echo $SIDs | \wc -w`

  if [ $CountSID -eq 1 ]; then
    infileTmp=${infile}
    getResolut   
    if [ $ResolutX -lt 1000 ]; then
      # In case of small resolution movie, not change resolution
      NewX=$ResolutX
      NewY=$ResolutY
    else
      getNewResolution
    fi

    # convert to H.264
    outfileTmp="${infile}-${SIDs}.mp4"
    convert2h264

  elif [ $CountSID -gt 1 ]; then
    for SID in $SIDs
    do
      # ts-split
      \tssplitter_lite ${infile} "${infile}-${SID}.ts" ${SID}

      infileTmp="${infile}-${SID}.ts"
      getResolut   
      if [ $ResolutX -lt 1000 ]; then
        # In case of small resolution movie, not change resolution
        NewX=$ResolutX
        NewY=$ResolutY
      else
        getNewResolution
      fi

      # convert to H.264.
      outfileTmp="${infile}-${SID}.mp4"
      convert2h264
    done
  else
    echo "${infile} has no video." 1>&2
    \continue
  fi
done

\exit 0
ts2h264ffmpeg.shを使ってカレントディレクトリ内のMPEG2ファイルをH.264に変換

tss2h264s-ffmpeg.sh

#!/bin/bash

# 2020 Apr. 18.
# 2011 May  07.
# ryuichi Hashimoto.
#
# Convert EPGREC-type-filename-video to H.264-files in current directory.
#   If there are plural files of almost same filename, no process works.
# ts2h264${FFMPEGCMD}.sh is required.
# specifications of converted files depend on ts2h264${FFMPEGCMD}.sh.
# STNDARD-OUT: (depends on ts2h264${FFMPEGCMD}.sh)


CMDNAME=`basename $0`
FFMPEGCMD='ffmpeg'
FILE1='tmpls1.txt'
FILE2='tmpls2.txt'
N=0

umask 0000


function usage() {
cat << EOP
Usage: $CMDNAME
  Convert EPGREC-type-filename-video to H.264-files in current directory.
    If there are plural files of almost same filename, no process works.
  ts2h264${FFMPEGCMD}.sh is required.
  specifications of converted files depend on ts2h264${FFMPEGCMD}.sh.
  STNDARD-OUT: (depends on ts2h264${FFMPEGCMD}.sh)
EOP
}


************
*** main ***
************

if [ $# -gt 0 ]; then
  usage 1>&2
  \exit 1
fi


\ls > $FILE1
\ls > $FILE2

## カレントディレクトリ内のファイル名を総当たりしてmpeg2をH.264にする
# カレントディレクトリ内のファイル名を配列tmparyに格納する
\mapfile tmpary < $FILE1
for LINELF in "${tmpary[@]}"
do
  # ファイル名末尾の改行コードを取り除く
  LINE=${LINELF%?}

  # timestampが現在時刻のファイルは無視する
  \touch tmpnow
  if [ $LINE -ot tmpnow ]; then

    # 拡張子だけを得る
    KAKUCHOSI=${LINE##*.}

    if [ $KAKUCHOSI = 'ts' ] || [ $KAKUCHOSI = 'mp4' ] ; then

      # .mp4ファイルのコーデックがmpeg2の場合は処理対象とする
      if [ $KAKUCHOSI = 'mp4' ] ; then
        ( ${FFMPEGCMD} -i $LINE 1> /dev/null ) 2>&1 | \egrep ': Video: ' | \egrep 'mpeg2video' -q
        if [[ $? -ne 0 ]]; then
          # mpeg2でない。ループの先頭に戻り次のファイルを処理
          \continue
        fi
      fi

      # ファイル名の最初のピリオド以下を取り除く
      BASETMP=${LINE%%.*}

      # _tss がファイル名語尾にあれば取り除く
      BASESTR=${BASETMP%%_tss}

      # 拡張子を無視したファイル名が検索先ファイルに1つだけの場合 tsファイルをmp4ファイルに変換する
      CNT=`\grep -c $BASESTR ${FILE2}`
      if [ $CNT -eq 1 ]; then
        N=$(($N + 1))
        \ts2h264${FFMPEGCMD}.sh $LINE
      fi
    fi
  fi
done

if [ -e tmpnow ]; then
  \rm tmpnow
fi

\echo "Finished." 1>&2
\echo "$N file(s) done." 1>&2

exit 0