1. スクリプト概要
スクリプト名: ffmpegReEncode.ps1
このスクリプトは、対象の動画ファイルを再エンコードします。
2. 処理と目的
処理の流れ
1. 再エンコード対象のファイルのフルパスを指定します
2. エンコードに使用するコーデックを選択します
3. ffprobe
を使用して、指定された動画ファイルの情報を取得します
4. ffmpeg
を使用して、動画の再エンコードを行います
5. 手順3~4を手順1で指定したファイルの数だけ繰り返します
目的
動画が正常に再生できない場合の対策の一つとして、コマンド一つで再エンコードを実行できると便利であるため、このスクリプトを作成しました。
3. 動作環境と要件
PowerShellのバージョン
7.0以上
OS
Windows10
必要なモジュール
ffprobe
ffmpeg
必要な権限
特になし
その他の設定
特になし
4. 使用方法
基本的な実行方法
スクリプトコードを拡張子ps1
で保存してPowershellで実行してください。
ファイルを保存する際は、文字コードをUTF8 BOM付
にしてください。
ffprobe
およびffmpeg
をインストールし、環境パスを通してください。
パラメータ
なし
使用例
- コマンドラインで
pwsh ffmpegReEncode.ps1
を実行します - エンコード対象のパスの入力を求められるので、ファイルのフルパスを入力します
- 再エンコードに使用するコーデックを選択します
5. スクリプトコード
# ffmpeg実行時の文字化けを防ぐため、コンソールの文字コードをUTF-8に設定
chcp 65001
$host.UI.RawUI.WindowTitle = ([IO.Path]::GetFilenameWithoutExtension($PSCommandPath))
$ErrorActionPreference = 'Stop'
$commonTitle = "ffmpeg(再エンコード)"
# 初期ウィンドウタイトルを設定
$Host.UI.RawUI.WindowTitle = $commonTitle
# エンコード対象のファイルパスを格納するためのArrayListを初期化
$pathList = New-Object System.Collections.ArrayList
# ファイルパスの入力ループ
do {
$inputText = Read-Host "エンコード対象のパス"
$inputText = $inputText.Trim('"')
# パスが入力されており、かつ、現在の入力が空の場合、ループを終了
if( $pathList.Count -gt 0 -and $inputText -eq ''){
break
}
# 入力されたパスが存在するかどうかをチェック
if(Test-Path -literal $inputText){
$null = $pathList.Add($inputText)
} else {
Write-Warning "無効なパスです"
}
} while($true) # 無限ループ(空の入力で終了)
# 利用可能なビデオエンコーダーの一覧の例(コメントアウト)
# ffmpeg -encoders | Where-Object { $_ -match '^ V' }
# 特定のエンコーダー(例: h264_nvenc)の詳細情報を表示する例(コメントアウト)
# ffmpeg -hide_banner -h encoder=h264_nvenc
# 利用可能なオーディオエンコーダーの一覧の例(コメントアウト)
# ffmpeg -encoders | Where-Object { $_ -match '^ A' }
# エンコードコーデックの選択肢を定義
$codecList = @()
$codecList += New-Object PsCustomObject -Property @{'codec'='libx264'; 'appendname'='h264'; 'name'='H264'}
$codecList += New-Object PsCustomObject -Property @{'codec'='h264_nvenc'; 'appendname'='h264'; 'name'='H264【GPU(GeForce)エンコード】'}
$codecList += New-Object PsCustomObject -Property @{'codec'='h264_amf'; 'appendname'='h264'; 'name'='H264【GPU(Radeon)エンコード】'}
$codecList += New-Object PsCustomObject -Property @{'codec'='libx265'; 'appendname'='h265'; 'name'='H265'}
$codecList += New-Object PsCustomObject -Property @{'codec'='hevc_nvenc'; 'appendname'='h265'; 'name'='H265【GPU(GeForce)エンコード】'}
$codecList += New-Object PsCustomObject -Property @{'codec'='hevc_amf'; 'appendname'='h265'; 'name'='H265【GPU(Radeon)エンコード】'}
do {
# Out-GridViewを使用してユーザーにコーデックを選択させる
$selectCodec = $codecList | Out-GridView -title 'エンコードするコーデックを選択してください' -PassThru
# コーデックが選択された場合、関連する情報を変数に格納
if($selectCodec){
$ffmpegCodec = $selectCodec.codec
$codecName = $selectCodec.name
$fileappendname = $selectCodec.appendname
}
} while([String]::IsNUllOrEmpty($ffmpegCodec)) # コーデックが選択されるまでループを継続
# 出力ディレクトリを定義
$outputDir = "D:\"
echo "`n------------------------------------------------------------"
echo "-- ffmpeg再エンコード対象ファイル"
foreach( $filepath in $pathList ){
echo "-- $($filepath)"
}
echo "-- コーデック = ${ffmpegCodec}(${codecName})"
echo "------------------------------------------------------------`n"
pause
foreach( $filepath in $pathList ){
# 現在処理中のファイル名を含むウィンドウタイトルを設定
$Host.UI.RawUI.WindowTitle = "${commonTitle}:""${filepath}""処理中..."
# 出力ファイルのパスを生成 元のファイル名に選択されたコーデック名(appendname)を付加し、拡張子を.mp4とする
$outputPath = Join-Path ([IO.Path]::GetDirectoryName($filepath)) ([IO.Path]::GetFileNameWithoutExtension($filepath) + "(${fileappendname}).mp4")
$bitrate = 0
# 入力ファイルの情報をffprobeで取得
# -loglevel quiet: 出力ログを抑制
# -show_streams: ストリーム情報を表示
# -print_format json: 出力形式をJSONに設定
$jsontext = & ffprobe -i $filepath -loglevel quiet -show_streams -print_format json
# JSONテキストをPowerShellオブジェクトに変換
$movieProp = ConvertFrom-Json -InputObject ($jsontext -join "`n")
# 動画ストリーム情報を抽出
$videostream = $movieProp.streams | Where-Object {$_.codec_type -eq 'video'}
# 動画ストリームが存在する場合
if( $videostream -ne $null){
echo $videostream
# ビットレートが直接取得できる場合
if ($videostream.bit_rate -ne $null){
$bitrate = $videostream.bit_rate
}
# ビットレートが直接取得できないが、デュレーションがある場合、ファイルサイズから計算
elseif( $videostream.duration -ne $null) {
$filesize = (Get-Item $filepath).Length # ファイルサイズをバイト単位で取得
$bitrate = [int](($filesize * 8) / $videostream.duration) # ビットレートをビット/秒で計算
}
# 動画情報が取得できない場合、警告を表示
else {
Write-Warning "ファイル""${filepath}""の動画情報が取得できませんでした"
}
}
# ビットレートが0より大きい場合のみエンコードを実行
if ( $bitrate -gt 0 ){
Write-Host """${filepath}""のエンコード開始" -BackgroundColor White -ForegroundColor Black
# ffmpegコマンドを実行
# -hide_banner: ffmpegの著作権情報などを非表示にする
# -i: 入力ファイルを指定
# -c:v: ビデオコーデックを指定
# -b:v: ビデオビットレートを指定
# -pix_fmt: ピクセルフォーマットをyuv420pに設定(幅広い互換性のため)
# -movflags +faststart: Web再生に最適化する(メタデータをファイルの先頭に移動)
# 2>&1 | %{Write-Host $_ -ForegroundColor Cyan -NoNewline}: ffmpegの標準出力と標準エラー出力をコンソールにリアルタイムで表示し、色をシアンにする
& ffmpeg "-hide_banner" "-i" """$($filepath)""" "-c:v" "${ffmpegCodec}" "-b:v" "${bitrate}" "-pix_fmt" "yuv420p" "-movflags" "+faststart" $outputPath 2>&1 | ForEach-Object {Write-Host $_ -ForegroundColor Cyan -NoNewline}
# 他のffmpegエンコードオプションの例(コメントアウト)
# & ffmpeg "-hide_banner" "-i" """$($filepath)""" "-c:v" "${ffmpegCodec}" "-preset" "slow" "-pix_fmt" "yuv420p" "-movflags" "+faststart" $outputPath 2>&1 | ForEach-Object {Write-Host $_ -ForegroundColor Cyan -NoNewline}
# & ffmpeg "-hide_banner" "-i" """$($filepath)""" "-c:v" "${ffmpegCodec}" "-g" "150" "-qcomp" "0.7" "-qmin" "10" "-qmax" "51" "-qdiff" "4" "-subq" "6" "-me_range" "16" "-i_qfactor" "0.714286" $outputPath 2>&1 | ForEach-Object {Write-Host $_ -ForegroundColor Cyan -NoNewline}
# & ffmpeg "-hide_banner" "-i" """$($filepath)""" "-c:v" "${ffmpegCodec}" "-qcomp" "0.7" "-qmin" "10" "-qmax" "51" "-qdiff" "4" $outputPath 2>&1 | ForEach-Object {Write-Host $_ -ForegroundColor Cyan -NoNewline}
# ffmpegの終了コードが0以外(エラー)の場合
if($LASTEXITCODE -ne 0){
Write-Warning "ffmpeg error '${LASTEXITCODE}'"
pause
}
}
}
# 全ての処理が完了した後、ウィンドウタイトルを「FIN:スクリプト名」に変更
$Host.UI.RawUI.WindowTitle = "FIN:${commonTitle}"
# スクリプトがコンソールで実行されている場合、終了時に一時停止してユーザー入力を待つ
if($host.Name.IndexOf('Console') -ge 0) {pause}
6. 注意事項と既知の問題
制約事項
大量のファイルやサイズの大きいファイルを処理する場合、完了までに時間がかかる可能性があります。
既知のバグ
もしバグを発見された場合は、コメントでご報告ください。
トラブルシューティング
・ps1ファイルのエンコーディングには注意してください。
・ffprobe・ffmpegがインストールされて環境パスが正しく設定されているか注意してください。
7. 免責事項
本スクリプトにはいかなる保証もありません。使用は自己責任で行ってください。