如何用 OBS 录制延时(倍速)视频

发布时间:

信息

本教程所提供的脚本依赖 FFmpeg 命令行,请先自行安装,需要你会跑 Windows 命令行。

延时摄影(Time-lapse photography)是一种通过延长摄影时间间隔来观察缓慢变化的摄影技术。 与 Adobe 官方网站 《How to create time-lapse videos》 讲解延时摄影的文章略有不同,本篇文章更多地是讲解如何使用类似 OBS Studio 这样的软件来录制延时视频。 具体案例是:我是一个长期在电脑前学习和工作的人,因此大部分有关活动可以用电脑屏幕来展示。 希望用每 1 分钟的最终视频长度来对应现实生活中 1 小时实际流逝时间,也就是 60 倍速的延时视频。 这样我只需要 10 分钟左右的时间就能够快速回顾我在镜头下的一天。

其它类似场景:板绘、3D 建模、动画、特效等制作过程的记录。

虽然 60 倍速的画面看起来很鬼畜,但是效果还是很不错的,是不错的信息压缩手段。

原始方法

前期录制一切照常,后期用如 FFMPEG 对视频进行倍速处理,但这样会导致:

  • 前期录制需要更高的机器性能,有较高的 CPU 占用率
  • 前期录制的视频文件体积过大,直接导致后期处理的时间过长
  • 如果你设置不正确或做了多余的改动,甚至会导致视频重新渲染编码

问题在于,很多人并不知道 OBS 上面更正确、也是更轻松的延时视频的录制方法。

更轻松的方法

在录制时就降低视频的帧率,大幅减少视频文件的体积、后期处理的时间、机器资源的占用。

以 OBS 为例子,跑到 “设置-视频” 面板,将帧率调整为 1 FPS(每秒只录制 1 张图片)。 后期想得到 60 倍速的延时视频,只需要将视频加速 60 倍,帧率调整为 60 FPS 即可。 这样最终输出的视频每秒钟有 60 张图片,代表着在对应一分钟内的 60 次采样。

视频后期如何进行加速

以 FFMPEG 为例子,使用如下命令:

powershell
ffmpeg -i input.mkv -filter:v "setpts=PTS/60" -an -r 60 output.mkv

参数解释:

  • -i input.mkv:输入文件
  • -filter:v "setpts=PTS/60":将视频的时间戳除以 60,即加速 60 倍
  • -an:去掉音频
  • -r 60:设置输出视频的帧率为 60 FPS
  • output.mkv:输出文件

为了方便,我直接在 Windows 下写了个批处理文件,把源文件拖上去即可:

powershell
@echo off

set "input_file=%~1"
if "%input_file%"=="" goto NoFile

set "output_file=%~dpn1_60fps.mkv"

ffmpeg -i "%input_file%" -filter:v "setpts=PTS/60" -an -r 60 "%output_file%"

echo Output saved to "%output_file%"
goto End

:NoFile
echo No file provided.
echo Please drag and drop a file onto the script.
goto End

:End
pause

新建一个 convert-to-60fps.bat 文件,将上面的代码复制进去,保存即可。

可能有用的脚本

如果你录制中途突然中断,可以使用下面的代码来快速合并多个文件:
powershell
@echo off
setlocal enabledelayedexpansion

:: Check if at least two files are provided
if "%~1"=="" (
    echo Please drag at least two video files onto this script.
    pause
    exit /b
)

:: Create filelist.txt file
set "listFile=filelist.txt"
if exist "%listFile%" del "%listFile%"

:: Create a temporary file for sorting
set "tempFile=sorted_files.txt"
if exist "%tempFile%" del "%tempFile%"

:: List files and sort by name
for %%f in (%*) do (
    echo %%~f >> "%tempFile%"
)

:: Sort the files and write to filelist.txt
for /f "delims=" %%f in ('sort "%tempFile%"') do (
    echo file '%%f' >> "%listFile%"
)

:: Delete temporary file
del "%tempFile%"

:: Concatenate videos
ffmpeg -f concat -safe 0 -i "%listFile%" -c copy output.mkv

:: Delete the temporary filelist.txt file
del "%listFile%"

:: Completion message
echo Concatenation completed. Output file: output.mkv
pause
python
import os
import subprocess
from collections import defaultdict

def get_video_files(directory):
    video_extensions = ('.mp4', '.mkv', '.avi', '.mov', '.flv')
    return [f for f in os.listdir(directory) if f.endswith(video_extensions)]

def create_file_list(video_files, list_filename='filelist.txt'):
    with open(list_filename, 'w') as f:
        for video_file in video_files:
            f.write(f"file '{video_file}'\n")

def merge_videos(list_filename='filelist.txt', output_filename):
    command = [
        'ffmpeg', '-f', 'concat', '-safe', '0', 
        '-i', list_filename, '-c', 'copy', output_filename
    ]
    subprocess.run(command)

def check_video_extensions(video_files):
    extensions = defaultdict(int)
    for video_file in video_files:
        ext = os.path.splitext(video_file)[1]
        extensions[ext] += 1
    return extensions

if __name__ == "__main__":
    current_directory = os.getcwd()
    video_files = get_video_files(current_directory)
    
    if not video_files:
        print("No video files found in the current directory.")
    else:
        extensions = check_video_extensions(video_files)
        if len(extensions) > 1:
            print("Error: Multiple video file extensions found.")
            print("Please ensure all videos have the same extension.")
            print("Found extensions and their counts:", dict(extensions))
        else:
            print("Files to be merged in order:")
            for video_file in video_files:
                print(video_file)

            unique_extension = next(iter(extensions))
            output_filename = f"merged{unique_extension}"
            
            create_file_list(video_files)
            merge_videos(output_filename=output_filename)
            print("Videos have been merged into {output_filename}")
如果你想要裁剪视频,可以使用下面的 Python 代码:
python
import os
import subprocess
import argparse

def get_time_input(prompt):
    while True:
        time_input = input(prompt)
        try:
            h, m, s = map(int, time_input.split(':'))
            if 0 <= h < 24 and 0 <= m < 60 and 0 <= s < 60:
                return f"{h:02}:{m:02}:{s:02}"
            else:
                print("Invalid time format. Please enter in HH:MM:SS format.")
        except ValueError:
            print("Invalid time format. Please enter in HH:MM:SS format.")

def trim_video(input_path, start_time, end_time, output_path):
    command = [
        'ffmpeg', '-ss', start_time, '-to', end_time, '-accurate_seek', 
        '-i', input_path, '-c', 'copy', '-avoid_negative_ts', '1', output_path
    ]
    subprocess.run(command)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Trim a video file using ffmpeg.")
    parser.add_argument('-i', '--input', type=str, help="Path to the input video file")
    parser.add_argument('-s', '--start', type=str, help="Start time in HH:MM:SS format")
    parser.add_argument('-e', '--end', type=str, help="End time in HH:MM:SS format")
    parser.add_argument('-o', '--output', type=str, help="Output file name (e.g., output.mp4)")

    args = parser.parse_args()

    input_path = args.input or input("Enter the path to the video file: ").strip()
    
    if not os.path.isfile(input_path):
        print("The specified file does not exist.")
    else:
        start_time = args.start or get_time_input("Enter the start time (HH:MM:SS): ")
        end_time = args.end or get_time_input("Enter the end time (HH:MM:SS): ")
        
        output_path = args.output or input("Enter the output file name (e.g., output.mp4): ").strip()
        
        trim_video(input_path, start_time, end_time, output_path)
        print(f"Video has been trimmed and saved to {output_path}")

假设代码文件名为 trim_video.py,在命令行中执行:

bash
python trim_video.py -i input.mkv -s 00:00:10 -e 00:01:00 -o output.mkv

使用 FFMpeg 裁剪视频最麻烦的是关键帧的定位,这个脚本会进行精确的定位以及避免负时间戳,避免上传后音画不同步。 你也可以使用更加专业的 Bandicut 等软件来无损地裁剪视频,这里只是为了方便。

关于 OBS 其它地方的设置,视频封装格式采用了默认的 MKV 格式,可按需修改。 录像设置,我使用的是 x264 编码器,VBR 1000 Kbps 速率,1080p 分辨率,CRF 23. 差不多平均每天录制的视频文件大小在 300 MB 左右,加速后的视频文件大小在 150 MB 左右。 而且最终视频的每一帧都是清晰的,没有丢帧的情况。