video-tools
September 24, 2023 · View on GitHub
The video-tools repo contains a collection of tools used to capture and record video footage from a VCR. This is a local workflow that involves running a VCR into a video capture device, then using OBS to capture, deinterlace, and upscale the footage before writing it to disk.
This workflow takes many cues from this YouTube video by The Oldskool PC:
Since we want to be able to stream the video footage at twitch.tv/GoldenVCR while recording, we need to run two instances of OBS:
-
A portable instance of OBS that's dedicated solely to capturing and recording VHS footage. This copy of OBS is installed to
obs/, and it's largely managed by the tools in this repo. -
A main instance of OBS that's configured for streaming: its scenes be configured to capture video and audio from our portable, VHS-only instance of OBS. This installation of OBS is not managed by this repository.
Note that Windows is the only supported platform for video-tools: we install the
Windows version of OBS, and the scripts that help us operate OBS use the Win32 API.
However, the install-obs.sh script requires a Windows-compatible
Bash shell: I use Git Bash.
Required hardware
This workflow requires a VCR and a video capture device. I happen to be using:
- A Sony SLV-N71 VCR
- An I-O Data GV-USB2 USB capture device
I'm using the Windows drivers recommended in the description of the video linked above,
installed from gvusb2_111.exe (8958272 bytes in size; md5 checksum
6675e635280c6938218241fee582c6fa).
It's also important to ensure that you have sufficient USB bandwidth to run the capture device alongside any other USB peripherals (cameras etc.) needed for streaming. It may be necessary to connect devices to multiple different buses, or to reduce USB bandwidth by lowering camera capture resolutions, configuring cameras to capture MJPEG instead of uncompressed footage, etc.
Initial setup
The obs/ directory contains a portable installation of OBS. This copy of
OBS is used solely for capturing and recording VHS footage locally.
We also have a handful of scripts used to make the process of capturing and editing video easier. To ensure that you're ready to run these scripts:
- Install the latest version of Python 3.
- Install dependencies with
pip install -r requirements.txt. - Use Git Bash (or another bash-compatible shell that has access to
cmd.exe) to invoke./install-obs.sh. This will install and configure a local, portable installation of OBS. - Install DaVinci Resolve 18.
- Install ffmpeg and ensure that the
ffmpegandffprobebinaries are in your PATH.
We use obs/configs to store machine-specific configuration details,
including the OBS Profile and Scene Collection used for capture. If there's an existing
config directory at obs/configs/your-hostname, the OBS install script will symlink it
into obs/config/obs-studio.
If you're setting up this repo on a new machine, you'll need to configure OBS manually as described below.
OBS Configuration
To set up OBS for VCR capture on a new machine:
- Once installed, start OBS from
obs/bin/64bit/obs64.exe - In the auto-configuration wizard, select Optimize for recording, and choose a Canvas Resolution of 1920x1080 and an FPS value of 60.
- Apply the suggested settings, then click the Settings button in the main UI.
- Under Output → Recording, if using the
Simpleoutput mode:- Set Recording Path to
..\..\..\capture - Set Recording Format to
.mkv
- Set Recording Path to
- Under Output, change Output Mode to
Advancedand configure the following:- Streaming tab → Streaming Settings:
- Audio Encoder:
FFmpeg AAC - Video Encoder:
NVIDIA NVENC H.264
- Audio Encoder:
- Streaming tab → Encoder Settings:
- Rate Control:
CQP - CQ Level:
18 - Keyframe Interval:
1 s - Preset:
P5: Slow (Good Quality) - Tuning:
High Quality - Multipass Mode:
Two Passes (Quarter Resolution) - Profile:
high - Look-ahead:
off - Psycho Visual Tuning:
on - GPU:
0 - Max B-frames:
2
- Rate Control:
- Recording tab:
- Type:
Standard - Recording Path:
..\..\..\capture - Recording Format:
Matroska Video (.mkv) - Video Encoder:
(Use stream encoder) - Audio Encoder:
(Use stream encoder)
- Type:
- Audio tab, Track 1:
- Audio Bitrate:
192
- Audio Bitrate:
- Streaming tab → Streaming Settings:
- Under Video:
- Set both Resolution values to
1440x900 - Set the FPS value to
59.94
- Set both Resolution values to
- Confirm your settings changes and return to the main UI.
- From the menu bar, choose Profile → Rename and enter
VHS. - Choose Scene Collection → Rename and enter
VHS. - In the Scenes panel, rename the default Scene to
VHS. - In the Sources panel, add a new Video Capture Source named
GV-USB2, and:- For Device, choose
GV-USB2, Analog Capture - Click Configure Video, and ensure that VID DEINTERLACE METHOD is set to
WEAVE
- For Device, choose
- Select the new GV-USB2 source and press Ctrl-E to edit its transforms:
- Set Size to
1440pxx1080px
- Set Size to
- Right-click the GV-USB2 soure and choose Deinterlacing → Yadif 2x
- Right-click the GV-USB2 source and choose Scale Filtering → Lanczos
- Arrange the UI as desired, ensuring that a point in the exact center of the
window is still positioned over the video preview. My preferences:
- Hide the Scenes panel via Docks → Scenes
- Hide the Scene Transitions via Docks → Scene Transitions
- Orient the Audio Mixer panel vertically, by clicking the ... button in the panel and choosing Vertical Layout
- Open the Stats panel via Docks → Stats
- Dock the Audio Mixer panel to the left of the video preview
- Dock the Sources panel to the left of the Audio Mixer panel
- Dock the Controls panel beneath the Sources panel
- Dock the Stats panel beneath the Controls panel
- In the Audio Mixer, mute both the Desktop Audio and Mic/Aux inputs so that the only active input is GV-USB2.
- Click the Gear button in the Audio Mixer to open the
Advanced Audio Properties dialog, and:
- For the GV-USB2 input, set Audio Monitoring to
Monitor and Output.
- For the GV-USB2 input, set Audio Monitoring to
- Play a tape and make a brief test recording. When you check the
capture/directory, you should have a new.mkvfile that looks and sounds identical to what you saw in the OBS preview while recording.
If your new OBS configuration is working as expected, then you may wish to start
versioning it in obs/configs. To do so, close OBS and then run
./bootstrap-obs-config.sh.
Capture workflow
Recording from OBS
When you're ready to start capturing video:
- Turn on the VCR.
- Run
python open.pyto ensure that our portable copy of OBS is running, with projector window open. - When ready to start writing video files to disk, click Start Recording.
- When finished recording the current tape or segment, click Stop Recording.
OBS should be configured to write timestamped .mkv files into the capture/
directory. Once you've finished a tape, you can cut a new recording from your captured
footage.
Cutting a new recording
To "cut" a recording, we simply grab all the clips recorded from OBS for a single tape,
and move them into a capture/<tape-id>/ subdirectory, following a specific naming
convention. You can use the cut.py script to handle this process:
- Verify that
capture/contains only the clips for your desired tape - Run
python cut.py <tape-id>to move those clips tocapture/<tape-id>
For example, if we've finished recording two clips from tape 54, then we'll have a couple of files that look something like this:
capture/2023-09-16 14-40-33.mkvcapture/2023-09-16 15-02-20.mkv
Running python cut.py 54 will move those two files to:
capture/54/54_raw.001.mkvcapture/54/54_raw.002.mkv
Editing the footage for a tape
Once you've got all the files for a tape organized in a capture/<tape-id>
subdirectory, you can use the edit.py script to automatically generate
a DaVinci Resolve project for that tape, with the proper settings
(1440x1080, 59.94 FPS) and a timeline prepopulated with all your clips. To create and
open a project in Resolve:
- Run
python edit.py <tape-id> --detect-cuts
For example, running python edit.py 54 will open Resolve (if it's not already open)
create a new project called "54", open that project, create a single timeline, and add
both 54_raw.001.mkv and 54_raw.002.mkv to that timeline, in sequence.
The --detect-cuts/-c flag will cause the script to analyze the footage up-front to
detect full-frame scene changes in the image, and then place clip markers in Resolve at
the location of those cuts. You can make cut detection more or less sensitive to image
changes by lower or raising the --cut-detection-threshold/-t value. If the tape is
going to be exported as a single video, rather than being split into multiple segments,
you're free to omit the --detect-cuts flag.
Once the script has finished, you should have a timeline open in Resolve, with "Ready for edit." displayed in the script console. You can close the console window, then begin editing. A few tips:
- The Up and Down arrows will navigate to the start and end of each timeline item, Left and Right will navigate by a single frame, and Ctrl+B will split at the current playback position.
- If you used
--detect-cuts, the clip will have a marker at the location of each detected cut. You can quickly jump between markers using Shift+Up and Shift+Down. If the footage for a tape needs to be split into multiple segments (e.g. home movies, or multiple programs/commercials in a TV recording), you can quickly seek around and break the timeline into multiple clips. - To name a clip: right-click on the clip in the timeline and choose
Clip Attributes..., then in the Name tab, enter a new Clip Name value.
Naming clips allows the automated export process to identify the export filename.
e.g. if tape 56 has two segments, and you name them
foobarandhello_worldin Resolve, they'll be exported as:56_01_foobar.mp456_02_hello_world.mp4
- If a phantom Resolve window starts cluttering your screen, open an Administrator
command prompt and run
taskkill /f /im dwm.exe.
Trimming final clips based on the edit
Once you've got the footage in Resolve, you're free to continue working in Resolve
right up through the export process. However, for the basic VHS capture workflow that
involves minimal non-linear editing (i.e. simply cutting off heads and tails and
splitting a video into multiple clips), the trim.py script can help
automate the process of exporting final videos.
Exported clips are canonically written to storage/<tape-id>/, with the filename
matching either <tape_id>.mp4 or <tape_id>_##_<underscore-delimited-name>.mp4.
If you're exporting clips with trim.py (instead of directly in Resolve), you'll want
to choose between one of two export modes:
-
Stream Copy: Video will be copied directly from the source
.mkv, without reencoding. This is very fast and does not add any compression overhead, but the video must begin on a keyframe, so the start point of your exported clip will be up to one second (or whatever keyframe interval you recorded with) ahead of your desired start point. This is a great choice for home movies, where seeing a bit of the previous scene isn't a problem, or for single-segment tapes where you simply need to cut the head and tail off the recording. -
Reencode: Video will be re-encoded using
libx264. This takes much longer than a simple stream copy, and it introduces a bit of additional compression, but it ensures that the video begins and ends exactly where you placed your cuts in Resolve. Quality loss from recompression is largely imperceptible at the default CRF value (10), but you can use higher CRF values to reduce the final file size. This is the best choice for anything requiring precise cuts, such as trimming lots of 30-second commercials out of a TV broadcast.
When you're ready to export clips using the automated process:
- Ensure that you have the desired timeline loaded in DaVinci Resolve
- Decide whether to copy or reencode:
- If using stream copy, run
python trim.py --copy - If reencoding, run
python trim.py [--crf 10]
- If using stream copy, run
- Wait for the script to finish - it could take a while. As each clip is processed, you'll see the output of each ffmpeg command.
When finished, your exported files can be found in storage/<tape-id>. From there, you
can upload them to YouTube, offload them to external storage, etc.
Configuring another OBS instance to stream while capturing
The open.py script automatically opens a
Windowed Projector (Preview) window and sets it to the full resolution of the
captured footage, i.e. 1440x1080, making it a suitable target window for capturing with
another instance of OBS.
If you want to stream this footage from another instance of OBS while running a
capture, run open.py to open the VHS-recording-only copy of OBS, the open your main
OBS installation and set up a Video Capture Device source with these settings:
- Window:
[obs64.exe]: Windowed Projector (Preview) - Capture Method:
Windows 10 (1903 and up) - Window Match Priority:
Window title must match - Capture Cursor:
off - Client Area:
on
Then add an Application Audio Capture source with these settings:
- Window:
[obs64.exe]: OBS 29.1.3 - Portable Mode - Profile: VHS - Scenes: VHS - Window Match Priority:
Window title must match
Note that open.py is idempotent: you can run it as many times as you like, and it
will simply ensure that OBS is running with an appropriately-sized projector window. It
will have no extra effect if run repeatedly, so you can safely bind it to a hotkey or
run it periodically in a loop while streaming.