Video Understanding#
A video is frames plus audio plus time. Split it into those three, and a problem that looked impossible becomes three you already know how to solve.
โฑ ~8 min read ยท ~12 min hands-on ๐ needs: Speech AI ยท Vision Models for Scraping
Never treat video as an opaque blob. Decompose it: audio โ transcript; frames โ vision models; time โ the index that ties them together.
Try it in 5 minutes โ ffmpeg is the whole toolkit#
# One frame per second, numbered
ffmpeg -i video.mp4 -vf fps=1 frame_%04d.jpg
# Audio only, for transcription
ffmpeg -i video.mp4 -vn -acodec libmp3lame audio.mp3
# A single frame at 01:23
ffmpeg -ss 00:01:23 -i video.mp4 -frames:v 1 shot.jpg
# Duration and stream info as JSON
ffprobe -v quiet -print_format json -show_format -show_streams video.mp4โ Extraction, sampling, and metadata โ four commands cover most of what a video pipeline needs.
Sampling every frame is almost always waste: at 30 fps, a 10-minute video is 18,000 near-identical images. 1 fps is a sane default; scene-change detection is better still:
# Keep only frames where the scene actually changes
ffmpeg -i video.mp4 -vf "select='gt(scene,0.3)',showinfo" -vsync vfr scene_%03d.jpgThe pipeline#
flowchart LR
V["video.mp4"] --> A["ffmpeg โ audio"]
V --> F["ffmpeg โ frames (1 fps / scenes)"]
A --> T["Transcript + timestamps"]
F --> C["Frame captions / OCR"]
T --> M["Merge on timestamp"]
C --> M
M --> Q["Searchable, citable index"]Merging on the timestamp is what makes it powerful: you can then answer “when was the pricing slide on screen, and what was being said?” โ a question neither audio nor frames could answer alone.
# /// script
# requires-python = ">=3.12"
# ///
"""Turn ffprobe output into a shot list โ no dependencies beyond ffmpeg.
Run: uv run shotlist.py video.mp4
"""
import json
import subprocess
import sys
path = sys.argv[1] if len(sys.argv) > 1 else "video.mp4"
meta = json.loads(subprocess.run(
["ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", path],
capture_output=True, text=True, check=True,
).stdout)
duration = float(meta["format"]["duration"])
video = next(s for s in meta["streams"] if s["codec_type"] == "video")
print(f"{duration:.0f}s {video['width']}x{video['height']} {video.get('avg_frame_rate')}")
print(f"Sampling at 1 fps โ about {int(duration)} frames")Native video models#
Some models now accept a video file directly and reason over time โ “what happened after the person sat down?” โ which frame-by-frame analysis handles poorly. Qwen3-VL handles hour-long video with timestamp-level localisation among open weights; hosted Gemini models take video natively too.
Use native video for temporal questions. Use frames + transcript when you need cheap, auditable, citable extraction at scale โ you keep the exact frame and timestamp behind every claim.
โ๏ธ Downloading video is governed by the platform’s Terms, and faces in frames are personal data โ Legal & Ethical Scraping. For lecture content, prefer official captions/transcripts where they exist.
When it fails#
| Symptom | Cause | Fix |
|---|---|---|
| Thousands of identical frames | Sampling every frame | fps=1 or scene detection |
ffmpeg: command not found | Not installed | apt install ffmpeg / brew install ffmpeg |
| Audio extraction fails | No audio stream | Check ffprobe streams first |
| Timestamps drift | Variable frame rate | Use -vsync vfr; trust showinfo times |
| Token costs explode | Sending every frame to a VLM | Scene-change frames only; caption once, cache |
Your turn (โ12 min)#
- Download a short Creative Commons video and run
shotlist.py. - Extract frames at 1 fps, then with scene detection โ compare the counts.
- Extract the audio and transcribe it with Speech AI.
- Merge: for a keyword in the transcript, print its timestamp and the nearest extracted frame.
Checklist#
- I decompose video into audio, frames, and time rather than treating it as a blob.
- I can extract frames, audio, a single timestamped shot, and metadata with ffmpeg/ffprobe.
- I sample at 1 fps or by scene change instead of every frame.
- I can merge transcript and frames on timestamps.
- I know when a native video model beats a frame pipeline.
Go deeper#
- ffmpeg documentation โ filters, seeking, encoding.
- ffprobe โ structured metadata about any media file.
- Qwen3-VL โ open-weight video-capable vision-language models.