Skip to content

Instantly share code, notes, and snippets.

@nyanshiba
Last active August 19, 2018 22:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nyanshiba/47a5f0aae5a9e3ae2deab2f0ff411fb0 to your computer and use it in GitHub Desktop.
Save nyanshiba/47a5f0aae5a9e3ae2deab2f0ff411fb0 to your computer and use it in GitHub Desktop.
TSファイルをミリ秒単位でカットしてgif、mp4、avi、連番jpgにエンコードする汎用性の高いバッチ
@echo off
rem 180809
rem 最早編集ソフトと化したTSファイルをD&Dでミリ秒単位でカットしてgif、mp4、avi、連番jpgにエンコードするバッチ
rem D&Dした複数ファイルを表示
set cnt=0
setlocal enabledelayedexpansion
for %%a in ( %1 %2 %3 %4 %5 %6 %7 %8 %9 ) do (
if not %%a == "" (
set /a cnt+=1
echo !cnt!.%%a
)
)
setlocal disabledelayedexpansion
echo.
rem 複数ファイルD&Dでエンコ処理を呼ぶ
for %%a in ( %1 %2 %3 %4 %5 %6 %7 %8 %9 ) do (
if not %%a == "" (
title %~nx0:%%a
echo 処理対象:%%a
echo.
call :encode %%a
echo 処理終了:%%a
echo.
)
)
echo すべての処理終了
pause
exit
:encode
rem ==========出力フォルダを作成==========
rem D&DしたTSファイルのあるディレクトリにその名前のフォルダを作成
md "%~dpn1"
echo 出力ディレクトリ:"%~dpn1"
echo.
rem ==========カット時刻の計算方式==========
set /p hms_or_ffplay=カット時刻を入力する方式を選択[hms:hms表記、ff:ffplay等の秒数表記、Enter:カットしない]:
echo.
if "%hms_or_ffplay%" == "hms" (
call :cutcal_hms %1
) else if "%hms_or_ffplay%" == "ff" (
call :cutcal_ffplay %1
) else if "%hms_or_ffplay%" == "" (
rem カット変数クリア
set SS=
set T=
)
rem ==========ビデオフィルタ==========
rem vfプリセット
set "vf0= "
set "vf1=-vf bwdif=0:-1:1,pp=ac"
set "vf2=-vf bwdif=0:-1:1,hqdn3d"
set "vf3=-vf bwdif=0:-1:1,pp=ac,unsharp=3:3:2:3:3:2:0,scale=1280:720"
set "vf4=-vf bwdif=0:-1:1,hqdn3d,unsharp=3:3:2:3:3:2:0,scale=1280:720"
set "vf5=-vf bwdif=0:-1:1,pp=ac,unsharp=3:3:1:3:3:1:0,scale=1920:1080"
set "vf6=-vf bwdif=0:-1:1,pp=ac,unsharp=3:3:1:3:3:1:0"
set "vf7=-vf pp=ac"
set "vf8=-vf pp=ac,scale=1280:720"
set "vf9=-vf pp=ac,scale=960:540"
set "vf10=-vf pp=ac,scale=720:480"
rem set "vf8=-vf pp7=16:0,scale=1280:720:sws_dither=none"
rem set "vf9=-vf pp7=16:0,scale=960:540:sws_dither=none"
rem set "vf10=-vf pp7=16:0,scale=720:480:sws_dither=none"
set "vf11=-vf scale=1280:720"
set "vf12=-vf framestep=50"
set "vf13=-vf framestep=100"
rem set "vf13=-vf select=eq^(pict_type^\,I^),framestep=50"
rem 疑似foreachループで変数を展開して引数を表示
rem ループ用
set i=0
setlocal enabledelayedexpansion
:foreach
rem callで変数vfを遅延展開、!で巻き込まれないようにする
call set vfecho=%%vf!i!%%
rem 変数vfvarが定義されている場合のみ実行、gotoループでその間のみ実行
if defined vfecho (
echo vf!i!:!vfecho!
set /a i+=1
goto :foreach
)
rem 聞く
set /p i=ビデオフィルタ選択:vf
rem callで変数内の変数を遅延展開
call set vf=%%vf!i!%%
setlocal disabledelayedexpansion
echo vf:%vf%
echo.
rem ==========出力形式選択==========
echo ffplay:再生
echo gif:ImageMagick、FFmpeg
echo mp4:h264_qsv LA-ICQ。品質、GOP長選択機能。
echo avi:rawvideo、pcm_s16le出力。連番機能。
echo jpg:コマ数かキーフレーム選択。
echo png:コマ数かキーフレーム選択。
set /p gif_or_mp4=出力形式を選択[gif、mp4、avi、jpg、png]:
echo.
call :%gif_or_mp4% %1
exit /b
rem ==========カット時刻の計算サブルーチン==========
rem ----------hms表記のプレイヤ(秒単位)----------
:cutcal_hms
set /p SS=カット開始時刻を入力[例:0時間1分23秒=000123]:
set /p TT=カット終了時刻を入力:
rem 秒になおす計算
set /a SS=(%SS:~0,2%*3600+%SS:~2,2%*60+%SS:~4,2%)
set /a TT=(%TT:~0,2%*3600+%TT:~2,2%*60+%TT:~4,2%)
rem カット開始時刻と終了時刻の差を計算
set /a T=(%TT%-%SS%)
rem カット開始秒と終了までの秒数を表示し、再入力するか聞く
set ask_again=
set /p ask_again=カット開始秒は%SS%、カット終了までの秒数は%T%ですが、よろしいですか[Enter/n]:
echo.
if not "%ask_again%" == "" (
goto :cutcal_hms
)
rem ffmpegの引数を追加
set "SS=-ss %SS%"
set "T=-t %T%"
exit /b
rem ----------ffplay等の秒数表記のプレイヤ(ミリ秒単位)----------
rem tsファイルのptsズレに対応
:cutcal_ffplay
rem カット開始時刻と終了時刻をffplayに表示されているミリ秒数で入力
set /p SS=カット開始時刻を入力[例:0時間1分23秒4=000123.400s="000123400"ms]:
set /p TT=カット終了時刻を入力:
rem カット開始時刻と終了時刻の差を計算
set /a T=(%TT%-%SS%)
rem ffprobeの-show_entriesスイッチを使ってformat=start_timeを出力し環境変数start_timeに設定
for /f "delims=" %%a in ('ffprobe -v quiet -i %1 -show_entries format^=start_time -of default^=noprint_wrappers^=1:nokey^=1') do (
set "start_time=%%a"
)
rem ffplayの表示から入力したカット開始時間-ffprobeで取得したstart_time(tsファイル等は開始時間がズレている場合があるため)(小数点を抜きミリ秒単位で計算)
rem 39533.817722
set /a SS=(%SS%-%start_time:~0,-7%%start_time:~-6,-3%)
rem ミリ秒を秒に戻し桁揃え
set SS=%SS:~0,-3%.%SS:~-3%
set T=%T:~0,-3%.%T:~-3%
rem カット開始秒と終了までの秒数を表示し、再入力するか聞く
set ask_again=
set /p ask_again=カット開始秒は%SS%、カット終了までの秒数は%T%ですが、よろしいですか[Enter/n]:
echo.
if not "%ask_again%" == "" (
goto :cutcal_ffplay
)
rem ffmpegの引数を追加
set "SS=-ss %SS%"
set "T=-t %T%"
exit /b
rem ==========出力形式選択サブルーチン==========
rem ----------ffplay----------
:ffplay
ffplay %SS% -i %1 %T% %vf%
exit /b
rem ----------gif----------
:gif
rem フレームレートを入力
set /p framerate=フレームレートを入力[5、10、24、30等]:
rem ffmpegでカット、インタレ解除、ノイズ除去、リサイズ、avi出力
ffmpeg -y -hide_banner %SS% -i %1 %T% -an %vf% -c:v rawvideo "%~dpn1\temp.avi"
rem ffmpegでaviからpngを切り出し
ffmpeg -y -hide_banner -i "%~dpn1\temp.avi" -c:v png -r %framerate% "%~dpn1\%%05d.png"
rem imagemagickで複数のpngをgifに変換
magick convert "%~dpn1\*.png" -layers Optimize "%~dpn1\%~n1.gif"
exit /b
rem ----------mp4----------
:mp4
rem 品質を入力
set /p quality=crf又はglobal_qualityの値[LA-ICQ:27,x265:25]:
rem 入力された数値を元に音声オプションを選択し環境変数へ格納
set /p audio_option=音声オプションを選択[128,256,320,copy,an]:
if "%audio_option%" == "128" (
set audio_option=-c:a aac -b:a 128k
) else if "%audio_option%" == "256" (
set audio_option=-c:a aac -b:a 256k
) else if "%audio_option%" == "320" (
set audio_option=-c:a aac -b:a 320k
) else if "%audio_option%" == "copy" (
set audio_option=-c:a copy -bsf:a aac_adtstoasc
) else if "%audio_option%" == "an" (
set audio_option=-an
)
rem 入力された数値を元にGOPオプションを選択し環境変数へ格納
set /p gop_option=GOPオプションを選択[p:保存用、s:ストリーミング用(closed gop)]:
if "%gop_option%" == "p" (
set gop=30
set bf=16
) else if "%gop_option%" == "s" (
set gop=15
set bf=2
)
rem PID
ffmpeg -hide_banner -i %1
set /p pid_need=必要があればPIDを入力[-map i:0x140 -map i:0x141]:
rem ffmpegでカット、インタレ解除、ノイズ除去、リサイズ、mp4出力
set /p encoder_option=エンコーダを選択[qsv,x265]:
:loop
if "%encoder_option%" == "qsv" (
ffmpeg -y -hide_banner -analyzeduration 30M -probesize 100M -fflags +discardcorrupt %SS% -i %1 %T% %audio_option% %vf% -global_quality %quality% -c:v h264_qsv -preset:v veryslow -g %gop% -bf %bf% -refs 4 -b_strategy 1 -look_ahead 1 -look_ahead_depth 60 -pix_fmt nv12 -bsf:v h264_metadata=colour_primaries=1:transfer_characteristics=1:matrix_coefficients=1 %pid_need% -movflags +faststart "%~dpn1\%~n1.mp4"
) else if "%encoder_option%" == "x265" (
ffmpeg -y -hide_banner -analyzeduration 30M -probesize 100M -fflags +discardcorrupt %SS% -i %1 %T% %audio_option% %vf% -c:v libx265 -preset:v fast -x265-params crf=%quality%:rc-lookahead=40:psy-rd=0.3:keyint=%gop%:no-open-gop:bframes=%bf%:rect=1:amp=1:me=umh:subme=3:ref=3:rd=3 -pix_fmt yuv420p -bsf:v hevc_metadata=colour_primaries=1:transfer_characteristics=1:matrix_coefficients=1 %pid_need% -movflags +faststart "%~dpn1\%~n1.mp4"
)
rem 稀にQSVのトランスコードに失敗すると"Error during encoding: device failed (-17)"が返されエンコに失敗してしまうので、ファイルサイズが0バイトなら失敗とみなしループさせて復旧を試みる
rem 終了コードが1且つループカウントが50以下までの間、エンコードを試みる
if "%errorlevel%" equ "1" (
if "%cnt%" leq "50" (
set /a cnt+=1
timeout /t 10
goto :encode
) else (
goto :err
)
)
exit /b
rem ----------avi----------
:avi
rem 同じ名前のファイルがあれば連番付け(編集向け機能)
set cnt=1
if not exist "%~dpn1\%~n1_%cnt%.avi" (
call :avienc %1
) else if exist "%~dpn1\%~n1_%cnt%.avi" (
:loop
set /a cnt+=1
if not exist "%~dpn1\%~n1_%cnt%.avi" (
call :avienc %1
) else if exist "%~dpn1\%~n1_%cnt%.avi" (
goto :loop
)
)
exit /b
:avienc
ffmpeg -hide_banner %SS% -i %1 %T% %vf% -c:v rawvideo -c:a pcm_s16le "%~dpn1\%~n1_%cnt%.avi"
exit /b
rem ----------jpg----------
:jpg
rem キーフレームかコマ数出力か
set /p key_or_rate=キーフレームかコマ数出力か選択[k:キーフレーム、r:コマ数]:
if "%key_or_rate%" == "k" (
ffmpeg -hide_banner -an -skip_frame nokey %SS% -i %1 %T% %vf% -f image2 -q:v 0 -vsync 0 "%~dpn1\%%05d.jpg"
) else if "%key_or_rate%" == "r" (
ffmpeg -hide_banner -an %SS% -i %1 %T% %vf% -f image2 -q:v 0 -r 0.5 "%~dpn1\%%05d.jpg"
)
exit /b
rem ----------png----------
:png
rem キーフレームかコマ数出力か
set /p key_or_rate=キーフレームかコマ数出力か選択[k:キーフレーム、r:コマ数]:
if "%key_or_rate%" == "k" (
ffmpeg -hide_banner -an -skip_frame nokey %SS% -i %1 %T% %vf% -vsync 0 "%~dpn1\%%05d.png"
) else if "%key_or_rate%" == "r" (
ffmpeg -hide_banner -an %SS% -i %1 %T% %vf% -c:v png -r 0.5 "%~dpn1\%%05d.png"
)
exit /b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment