[批处理]视频进度条异常修复工具

1. 背景

使用迅雷影音的“截取视频” 功能得到的视频片段在某些播放器下无法拖拽进度条,甚至无法播放,撰写此批处理实现视频无损修复(调用 ffmpeg )。

2. 功能

  1. 列出批处理所在文件夹内的所有视频
  2. 检查视频存在的问题
  3. 依次修复(需要用户确认,“y” 执行“n”跳过)
  4. 使用快速修复模式
  5. 快速修复失败会使用重编码方式修复

3. 使用方法

  1. 下载 ffmpeg.exe 和 ffprobe.exe 并放到视频所在的文件夹
  2. 将本文下面提供的批处理源码拷贝并存到视频所在的文件夹
  3. 修改文本名称为:“修复视频.bat”
  4. 双击运行
  5. 根据控制台提示操作,你只需要3个动作:敲回车,敲"y" 或者 “n”

4. 批处理源码

@echo off
setlocal enabledelayedexpansion
chcp 65001 >nul
title MP4视频智能扫描修复工具
mode con: cols=130 lines=40

set "LIST_ALL=%temp%\mp4_all_list.tmp"
set "LIST_BAD=%temp%\mp4_bad_list.tmp"
set "LIST_BAD_WORK=%temp%\mp4_bad_valid_list.tmp"
set "FFP_LOG=%temp%\ffp.log"
set "FFP_ERR=%temp%\ffp.err"
set "PS_OUT=%temp%\mp4_check_result.tmp"
set "FIX_LOG=%temp%\mp4_fix.log"
del /q "%LIST_ALL%" 2>nul
del /q "%LIST_BAD%" 2>nul
del /q "%LIST_BAD_WORK%" 2>nul
del /q "%FFP_LOG%" 2>nul
del /q "%FFP_ERR%" 2>nul
del /q "%PS_OUT%" 2>nul
del /q "%FIX_LOG%" 2>nul

echo.
echo ==============================================================================================================================
echo                                   MP4 智能扫描修复工具
echo        状态:正常 / 异常 / 已处理    检测:ffprobe 高速    修复:IBP帧/时间戳/进度条
echo ==============================================================================================================================
echo.

if not exist "%~dp0ffprobe.exe" (echo 缺少 ffprobe.exe & pause>nul & exit)
if not exist "%~dp0ffmpeg.exe" (echo 缺少 ffmpeg.exe & pause>nul & exit)

:: ==============================================
:: 步骤1:采集所有MP4(相对路径 + 大小)
:: ==============================================
echo 【步骤1/4】采集所有 MP4 文件(递归子目录)
echo ------------------------------------------------------------------------------------------------------------------------------
set "TOTAL=0"
for /r %%f in (*.mp4) do (
    set "RELATIVE=%%~f"
    set "RELATIVE=!RELATIVE:%cd%\=!"
    call :GET_MB "%%~f" MB
    echo !RELATIVE!          [!MB! MB]
    >> "%LIST_ALL%" echo %%~f
    set /a TOTAL+=1
)
echo ------------------------------------------------------------------------------------------------------------------------------
echo 采集完成!共找到 MP4:!TOTAL! 个
echo.
pause

:: ==============================================
:: 步骤2:状态分析
:: ==============================================
echo.
echo 【步骤2/4】视频状态分析
echo ------------------------------------------------------------------------------------------------------------------------------
set "CNT_NORMAL=0"
set "CNT_FIXED=0"
set "CNT_ERROR=0"

for /f "usebackq delims=" %%f in ("%LIST_ALL%") do (
    call :ANALYZE_FILE "%%f"
)
call :PREPARE_BAD_LIST

echo ------------------------------------------------------------------------------------------------------------------------------
echo 统计:正常=!CNT_NORMAL!   已处理=!CNT_FIXED!   异常=!CNT_ERROR!
echo ------------------------------------------------------------------------------------------------------------------------------
echo.
if !CNT_ERROR! equ 0 (
    echo ✅ 无异常视频,程序结束
    pause>nul
    exit /b
)
pause

:: ==============================================
:: 步骤3:逐条确认
:: ==============================================
echo.
echo 【步骤3/4】异常视频逐条确认(Y=修复 N=跳过)
echo ------------------------------------------------------------------------------------------------------------------------------
set "CUR=0"
for /f "usebackq delims=" %%f in ("%LIST_BAD_WORK%") do (
    set /a CUR+=1
    call :CONFIRM_FIX "%%f"
)

:: ==============================================
:: 步骤4:完成
:: ==============================================
echo.
echo ==============================================================================================================================
echo                                             ✅ 全部处理完成
echo ==============================================================================================================================
echo.
pause>nul
exit /b

:: ==============================================
:: 分析单个文件
:: ==============================================
:ANALYZE_FILE
set "FILE=%~1"
set "NAME=%~n1"
set "REASON="
set "STATE="
set "RELATIVE=%~1"
set "RELATIVE=!RELATIVE:%cd%\=!"
call :GET_MB "%~1" MB

:: 跳过修复文件
if /i "!NAME:~-6!"=="_fixed" (
    set "STATE=已处理(修复文件)"
    set /a CNT_FIXED+=1
    goto ANALYZE_SHOW
)

:: 已存在修复版
if exist "%~dp1%~n1_fixed.mp4" (
    set "STATE=已处理(已修复)"
    set /a CNT_FIXED+=1
    goto ANALYZE_SHOW
)

:: 基于 flat 输出做结构化检测
"%~dp0ffprobe.exe" -v error -select_streams v:0 -show_entries format=start_time,duration:stream=start_pts,time_base,start_time,avg_frame_rate -of flat "%~1" 1> "%FFP_LOG%" 2> "%FFP_ERR%"
set "code=!errorlevel!"

if !code! neq 0 (
    set "STATE=异常"
    set "REASON=文件无法读取"
    goto ANALYZE_BAD
)

call :CHECK_FFPROBE_RESULT
if /i "!CHECK_RESULT!"=="BAD" (
    set "STATE=异常"
    set "REASON=!CHECK_REASON!"
    goto ANALYZE_BAD
)

if exist "%FFP_ERR%" (
    findstr /i /c:"non monotonically increasing dts" /c:"non monotonically increasing pts" /c:"invalid timestamps" /c:"application provided invalid" "%FFP_ERR%" >nul && (
        set "STATE=异常"
        set "REASON=时间戳错乱"
        goto ANALYZE_BAD
    )
)

set "STATE=正常"
set /a CNT_NORMAL+=1
goto ANALYZE_SHOW

:ANALYZE_BAD
set /a CNT_ERROR+=1
>> "%LIST_BAD%" echo %~1

:ANALYZE_SHOW
echo !STATE!  ^| !RELATIVE!  [!MB! MB]
if not "!REASON!"=="" echo          原因:!REASON!
echo.
goto :eof

:: ==============================================
:: 检查 ffprobe 结果
:: ==============================================
:CHECK_FFPROBE_RESULT
set "CHECK_RESULT=OK"
set "CHECK_REASON="
set "FORMAT_START="
set "VIDEO_START="
set "CHECK_START="
set "DURATION="
set "START_PTS="
set "TIME_BASE="
set "TB_NUM="
set "TB_DEN="

for /f "usebackq tokens=1,* delims==" %%a in ("%FFP_LOG%") do (
    set "KEY=%%a"
    set "VAL=%%b"
    set "VAL=!VAL:\"=!"
    if /i "!KEY!"=="format.start_time" set "FORMAT_START=!VAL!"
    if /i "!KEY!"=="format.duration" set "DURATION=!VAL!"
    if /i "!KEY!"=="streams.stream.0.start_time" set "VIDEO_START=!VAL!"
    if /i "!KEY!"=="streams.stream.0.start_pts" set "START_PTS=!VAL!"
    if /i "!KEY!"=="streams.stream.0.time_base" set "TIME_BASE=!VAL!"
)

set "CHECK_START=!FORMAT_START!"
if not defined CHECK_START set "CHECK_START=!VIDEO_START!"
if /i "!CHECK_START!"=="N/A" set "CHECK_START=!VIDEO_START!"
if not defined CHECK_START set "CHECK_START=0"

call :PS_COMPARE_START "!DURATION!" "!CHECK_START!"
if /i "!CHECK_RESULT!"=="BAD" goto :eof

if defined TIME_BASE (
    for /f "tokens=1,2 delims=/" %%i in ("!TIME_BASE!") do (
        set "TB_NUM=%%i"
        set "TB_DEN=%%j"
    )
)

if defined START_PTS if defined TB_NUM if defined TB_DEN (
    call :PS_COMPARE_PTS "!DURATION!" "!START_PTS!" "!TB_NUM!" "!TB_DEN!"
)
goto :eof

:: ==============================================
:: PowerShell 比较:start_time / duration
:: ==============================================
:PS_COMPARE_START
set "CHECK_RESULT=OK"
set "CHECK_REASON="
del /q "%PS_OUT%" 2>nul
powershell -NoProfile -Command "$du=0.0; $st=0.0; $hasDu=[double]::TryParse('%~1',[System.Globalization.NumberStyles]::Float,[System.Globalization.CultureInfo]::InvariantCulture,[ref]$du); $hasSt=[double]::TryParse('%~2',[System.Globalization.NumberStyles]::Float,[System.Globalization.CultureInfo]::InvariantCulture,[ref]$st); if((-not $hasDu) -or $du -le 0){'BAD|时长异常'} elseif($hasSt -and $st -lt 0){'BAD|起始时间为负'} elseif($hasSt -and $st -gt 30 -and $st -gt ($du * 2)){'BAD|起始时间远大于时长'} else {'OK|'}" > "%PS_OUT%"
for /f "usebackq tokens=1,* delims=|" %%a in ("%PS_OUT%") do (
    set "CHECK_RESULT=%%a"
    set "CHECK_REASON=%%b"
)
goto :eof

:: ==============================================
:: PowerShell 比较:start_pts / time_base
:: ==============================================
:PS_COMPARE_PTS
set "CHECK_RESULT=OK"
set "CHECK_REASON="
del /q "%PS_OUT%" 2>nul
powershell -NoProfile -Command "$du=0.0; $pts=0.0; $num=0.0; $den=0.0; $hasDu=[double]::TryParse('%~1',[System.Globalization.NumberStyles]::Float,[System.Globalization.CultureInfo]::InvariantCulture,[ref]$du); $hasPts=[double]::TryParse('%~2',[System.Globalization.NumberStyles]::Float,[System.Globalization.CultureInfo]::InvariantCulture,[ref]$pts); $hasNum=[double]::TryParse('%~3',[System.Globalization.NumberStyles]::Float,[System.Globalization.CultureInfo]::InvariantCulture,[ref]$num); $hasDen=[double]::TryParse('%~4',[System.Globalization.NumberStyles]::Float,[System.Globalization.CultureInfo]::InvariantCulture,[ref]$den); if($hasDu -and $hasPts -and $hasNum -and $hasDen -and $den -gt 0){ $s=$pts*($num/$den); if($s -gt 30 -and $s -gt ($du * 2)){'BAD|首帧时间戳过大'} else {'OK|'} } else {'OK|'}" > "%PS_OUT%"
for /f "usebackq tokens=1,* delims=|" %%a in ("%PS_OUT%") do (
    set "CHECK_RESULT=%%a"
    set "CHECK_REASON=%%b"
)
goto :eof

:: ==============================================
:: 重建有效异常列表
:: ==============================================
:PREPARE_BAD_LIST
set "CNT_ERROR=0"
del /q "%LIST_BAD_WORK%" 2>nul
if not exist "%LIST_BAD%" goto :eof
for /f "usebackq delims=" %%f in ("%LIST_BAD%") do (
    if exist "%%f" (
        >> "%LIST_BAD_WORK%" echo %%f
        set /a CNT_ERROR+=1
    )
)
goto :eof

:: ==============================================
:: 逐条确认修复
:: ==============================================
:CONFIRM_FIX
set "RELATIVE=%~1"
set "RELATIVE=!RELATIVE:%cd%\=!"
echo 第 !CUR!/!CNT_ERROR! 个:!RELATIVE!
:CHOOSE_LOOP
choice /c YN /n /m "  是否修复?[Y/N]:"
if errorlevel 2 (
    echo  已跳过
) else if errorlevel 1 (
    call :FIX "%~1"
) else (
    goto CHOOSE_LOOP
)
echo.
goto :eof

:: ==============================================
:: 获取文件大小(MB)
:: ==============================================
:GET_MB
set "%~2="
for /f "usebackq delims=" %%a in (`powershell -NoProfile -Command "([double](%~z1) / 1MB).ToString('0')"`) do set "%~2=%%a"
if not defined %~2 set "%~2=0"
goto :eof

:: ==============================================
:: 修复函数
:: ==============================================
:FIX
set "OUT=%~dpn1_fixed.mp4"
del /q "!OUT!" 2>nul

echo 正在修复...
echo 修复模式:无损重封装(时间戳重建 + 起始时间归零 + faststart)
"%~dp0ffmpeg.exe" -y -hide_banner -stats -fflags +genpts -i "%~1" -map 0 -c copy -avoid_negative_ts make_zero -movflags +faststart "!OUT!"
if !errorlevel! equ 0 (
    echo ✅ 修复成功(模式:无损重封装)
    goto :eof
)

del /q "!OUT!" 2>nul
echo 无损重封装失败,切换到:重编码修复
echo 修复模式:重编码修复(H.264 + AAC,兼容性优先)
"%~dp0ffmpeg.exe" -y -hide_banner -stats -i "%~1" ^
-fflags +genpts ^
-reset_timestamps 1 ^
-vsync cfr ^
-c:v libx264 -preset fast -crf 22 ^
-c:a aac -b:a 192k ^
"!OUT!" 1> "%FIX_LOG%" 2>&1
if !errorlevel! equ 0 (
    echo ✅ 修复成功(模式:重编码修复)
) else (
    del /q "!OUT!" 2>nul
    echo ❌ 修复失败
    echo    可查看日志:%FIX_LOG%
)
goto :eof
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容