DV转HDR10.ps1
|
<#PSScriptInfo
.VERSION 1.0.0 .GUID 5e8883a1-e074-4138-8def-d8c0a4607e41 .AUTHOR 埃博拉酱 .COMPANYNAME 一致行动党 .COPYRIGHT (c) 2026 埃博拉酱. All rights reserved. .TAGS ffmpeg DolbyVision HDR10 HEVC HEVCConvert VideoTranscode NvidiaEncode .LICENSEURI https://opensource.org/licenses/MIT .RELEASENOTES 1.0.0 - 初始发布。支持 Dolby Vision Profile 5 → HDR10 转换, 优先 hevc_nvenc GPU 编码,回退到 libx265 CPU 编码, 缺少 ffmpeg 时通过 winget 自动安装。 #> <# .SYNOPSIS 将 Dolby Vision Profile 5 HEVC 视频转换为标准 HDR10 HEVC。 .DESCRIPTION 使用 ffmpeg 的 libplacebo 过滤器(需 Vulkan)正确处理 Dolby Vision Profile 5 的 ICtCp 色彩空间,输出兼容所有 HDR10 播放器的标准 HEVC 文件。 编码器优先级:hevc_nvenc(NVIDIA GPU)→ libx265(CPU)。 缺少支持 libplacebo 的 ffmpeg 时,通过 winget 自动安装 Gyan.FFmpeg。 .PARAMETER 输入文件 源视频文件路径(MKV、MP4 等,须含 Dolby Vision Profile 5 视频轨)。 .PARAMETER 输出文件 输出文件路径。省略时自动在源文件同目录生成,后缀为 _HDR10.mkv。 .PARAMETER 质量 编码质量(1-51,默认 19)。hevc_nvenc 对应 CQ 值,libx265 对应 CRF 值。 数值越小质量越高、文件越大。 .PARAMETER 测试秒数 仅转换前 N 秒(默认 0 = 转换完整文件)。用于快速验证结果。 .EXAMPLE .\DV转HDR10.ps1 -输入文件 "movie.DV.mkv" 转换完整文件,输出 movie.DV_HDR10.mkv。 .EXAMPLE .\DV转HDR10.ps1 -输入文件 "movie.DV.mkv" -测试秒数 10 仅转换前 10 秒,输出 movie.DV_HDR10_test10s.mkv,用于验证颜色。 .EXAMPLE .\DV转HDR10.ps1 -输入文件 "movie.DV.mkv" -输出文件 "movie.HDR10.mkv" -质量 22 自定义输出路径和质量。 .NOTES 依赖:ffmpeg 8.1+(Gyan 完整版,含 libplacebo + Vulkan)。 缺少时脚本会通过 winget 自动安装。 Dolby Vision Profile 5 使用 ICtCp 色彩空间,普通播放器若不识别 DV 元数据 则会将像素误判为 BT.2020nc,导致画面严重偏绿。本脚本通过 libplacebo 的 apply_dolbyvision 参数在 GPU 上完成正确的颜色空间转换,输出标准 HDR10。 #> # 将 Dolby Vision Profile 5 HEVC 转换为标准 HDR10 HEVC # 优先使用 hevc_nvenc(GPU),不支持时回退到 libx265(CPU) # 依赖:ffmpeg 8.1+(含 libplacebo + Vulkan) param( [Parameter(Mandatory, HelpMessage = "输入 MKV/MP4 文件路径")] [string]$输入文件, [string]$输出文件 = "", [ValidateRange(1, 51)] [int]$质量 = 19, # nvenc CQ 或 libx265 CRF,数值越小质量越高 [int]$测试秒数 = 0 # 大于 0 时只转换前 N 秒(用于测试) ) # ── 自动生成输出文件名 ───────────────────────────── if (-not $输出文件) { $绝对路径 = (Resolve-Path $输入文件).Path $基础名 = [System.IO.Path]::GetFileNameWithoutExtension($绝对路径) $目录 = [System.IO.Path]::GetDirectoryName($绝对路径) $后缀 = if ($测试秒数 -gt 0) { "_HDR10_test${测试秒数}s" } else { "_HDR10" } $输出文件 = Join-Path $目录 "${基础名}${后缀}.mkv" } # ── 查找支持 libplacebo 的 ffmpeg ─────────────────── function 查找ffmpeg { # 优先查 winget 安装的 Gyan.FFmpeg $winget包路径 = Get-ChildItem "$env:LOCALAPPDATA\Microsoft\WinGet\Packages\Gyan.FFmpeg*" ` -Recurse -Filter "ffmpeg.exe" -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty FullName $候选列表 = @($winget包路径, (Get-Command ffmpeg -ErrorAction SilentlyContinue).Source) | Where-Object { $_ -and (Test-Path $_) } foreach ($路径 in $候选列表) { $临时文件 = "$env:TEMP\ff_filters_$PID.txt" Start-Process -FilePath $路径 -ArgumentList "-hide_banner", "-filters" ` -Wait -NoNewWindow -RedirectStandardOutput $临时文件 | Out-Null if (Select-String -Path $临时文件 -Pattern "libplacebo" -Quiet) { Remove-Item $临时文件 -ErrorAction SilentlyContinue return $路径 } Remove-Item $临时文件 -ErrorAction SilentlyContinue } return $null } $ffmpeg = 查找ffmpeg if (-not $ffmpeg) { Write-Host "未找到支持 libplacebo 的 ffmpeg,尝试通过 winget 自动安装 Gyan.FFmpeg ..." if (-not (Get-Command winget -ErrorAction SilentlyContinue)) { Write-Error "winget 不可用,请手动安装 ffmpeg 8.1+(Gyan 完整版)后重试。" exit 1 } $安装结果 = winget install Gyan.FFmpeg --silent 2>&1 if ($LASTEXITCODE -ne 0) { Write-Error "winget 安装失败:$安装结果" exit 1 } # 刷新当前进程的 PATH $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User") $ffmpeg = 查找ffmpeg if (-not $ffmpeg) { Write-Error "安装后仍未找到支持 libplacebo 的 ffmpeg,请重新打开终端后再试。" exit 1 } Write-Host "安装成功。" } $版本信息 = (& $ffmpeg -version 2>&1 | Select-Object -First 1) -replace "ffmpeg version ", "" Write-Host "ffmpeg : $版本信息" Write-Host "路径 : $ffmpeg" # ── 检测 hevc_nvenc ──────────────────────────────── $编码器临时 = "$env:TEMP\ff_encoders_$PID.txt" Start-Process -FilePath $ffmpeg -ArgumentList "-hide_banner", "-encoders" ` -Wait -NoNewWindow -RedirectStandardOutput $编码器临时 | Out-Null $支持nvenc = Select-String -Path $编码器临时 -Pattern "hevc_nvenc" -Quiet Remove-Item $编码器临时 -ErrorAction SilentlyContinue # ── 构建过滤器和编码参数 ────────────────────────── $视频过滤器基础 = "libplacebo=colorspace=bt2020nc:color_trc=smpte2084:color_primaries=bt2020:range=tv:apply_dolbyvision=true" if ($支持nvenc) { Write-Host "编码器 : hevc_nvenc(GPU)" $视频过滤器 = "$视频过滤器基础,format=p010le" $视频编码参数 = @( "-c:v", "hevc_nvenc", "-preset", "p4", "-rc", "vbr", "-cq", "$质量" ) } else { Write-Host "编码器 : libx265(CPU,hvenc 不可用)" $x265参数 = "colorprim=bt2020:transfer=smpte2084:colormatrix=bt2020nc:hdr10=1:crf=$质量" $视频过滤器 = "$视频过滤器基础,format=yuv420p10le" $视频编码参数 = @( "-c:v", "libx265", "-preset", "medium", "-x265-params", $x265参数 ) } $色彩元数据 = @( "-color_primaries", "bt2020", "-color_trc", "smpte2084", "-colorspace", "bt2020nc", "-color_range", "tv" ) # ── 组装完整参数列表 ───────────────────────────── $时长参数 = if ($测试秒数 -gt 0) { @("-t", "$测试秒数") } else { @() } $参数列表 = @("-y") + $时长参数 + @("-i", $输入文件, "-vf", $视频过滤器) + $视频编码参数 + $色彩元数据 + @("-map", "0:v:0", "-map", "0:a", "-c:a", "copy", $输出文件) # ── 执行转换 ────────────────────────────────────── Write-Host "" Write-Host "输入 : $输入文件" Write-Host "输出 : $输出文件" Write-Host "质量 : $质量" if ($测试秒数 -gt 0) { Write-Host "测试模式: 仅转换前 ${测试秒数} 秒" } Write-Host "" Write-Host "── 开始转换 ──────────────────────────────────" & $ffmpeg @参数列表 if ($LASTEXITCODE -eq 0) { $大小MB = [math]::Round((Get-Item $输出文件).Length / 1MB, 1) Write-Host "" Write-Host "── 转换完成 ──────────────────────────────────" Write-Host "输出文件:$输出文件(${大小MB} MB)" } else { Write-Error "转换失败(退出码:$LASTEXITCODE)" exit $LASTEXITCODE } |