I needed a fast way to turn raw video into structured metadata without provisioning GPUs. In this tutorial, I will walk through a lightweight pipeline that extracts frames, captions them in parallel through Oxlo.ai, and synthesizes a JSON report. Because Oxlo.ai uses flat per-request pricing, the cost stays predictable even when you send large base64 image payloads.
What you'll need
- Python 3.10 or newer
pip install openai opencv-python- An Oxlo.ai API key from https://portal.oxlo.ai
- A sample MP4 file
Step 1: Extract key frames
I sample one frame every two seconds to keep the total number of API calls low. OpenCV reads the video, encodes the chosen frames as JPEG, and returns base64 strings paired with their timestamps.
import cv2
import base64
def extract_frames(video_path, interval_sec=2):
cap = cv2.VideoCapture(video_path)
fps = cap.get(cv2.CAP_PROP_FPS)
interval_frames = int(fps * interval_sec)
frames = []
frame_idx = 0
while True:
ret, frame = cap.read()
if not ret:
break
if frame_idx % interval_frames == 0:
_, buffer = cv2.imencode(".jpg", frame)
b64 = base64.b64encode(buffer).decode("utf-8")
timestamp = round(frame_idx / fps, 1)
frames.append((timestamp, b64))
frame_idx += 1
cap.release()
return frames
if __name__ == "__main__":
frames = extract_frames("sample.mp4")
print(f"Extracted {len(frames)} frames")
Step 2: Caption frames in parallel
Wall-clock time is the bottleneck, so I send frames concurrently. I use a ThreadPoolExecutor to call Oxlo.ai's vision endpoint with Kimi K2.6. Each request carries a flat cost, so parallelizing the frames does not introduce unpredictable per-token charges.
from concurrent.futures import ThreadPoolExecutor
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
def caption_frame(timestamp, b64_image):
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{
"role": "system",
"content": "Describe this video frame in one concise sentence. Focus on visible actions, objects, and setting."
},
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {"url": f"data:image/jpeg;base64,{b64_image}"}
}
]
}
],
max_tokens=64
)
return timestamp, response.choices[0].message.content
def batch_caption(frames):
captions = []
with ThreadPoolExecutor(max_workers=8) as exe:
futures = [exe.submit(caption_frame, ts, img) for ts, img in frames]
for future in futures:
captions.append(future.result())
captions.sort(key=lambda x: x[0])
return captions
if __name__ == "__main__":
frames = extract_frames("sample.mp4")
captions = batch_caption(frames)
for t, c in captions:
print(f"[{t}s] {c}")
Step 3: Synthesize the report
With timestamped captions in hand, I feed them into Llama 3.3 70B with a strict system prompt that demands structured JSON. I use JSON mode to avoid markdown fences and keep parsing reliable.
Here is the system prompt I use for the synthesis agent:
SYSTEM_PROMPT = """You are a video analysis engine. Your input is a list of timestamped frame captions from a video.
Generate a structured JSON report with exactly these keys:
- summary: a one-paragraph overview of the video
- key_events: an array of up to 5 objects, each with timestamp and description
- mood: the overall tone or atmosphere
- objects: an array of prominent objects or people mentioned
Respond with valid JSON only. Do not wrap the output in markdown fences."""
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
def generate_report(captions):
caption_text = "\n".join([f"[{t}s] {c}" for t, c in captions])
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": caption_text},
],
response_format={"type": "json_object"}
)
return response.choices[0].message.content
if __name__ == "__main__":
report = generate_report(captions)
print(report)
Run it
I tie the stages together in a single script and run it against a 10-second test clip. The parallel captioning keeps the end-to-end latency low, and the final synthesis call turns the scattered descriptions into a coherent report.
if __name__ == "__main__":
frames = extract_frames("demo.mp4", interval_sec=2)
captions = batch_caption(frames)
report = generate_report(captions)
print(report)
Example output:
{
"summary": "A person walks into a workshop, picks up a power drill, and begins assembling a wooden shelf.",
"key_events": [
{"timestamp": "0.0", "description": "Person enters the frame from the left"},
{"timestamp": "4.0", "description": "Picks up a yellow power drill"},
{"timestamp": "8.0", "description": "Starts driving screws into the shelf frame"}
],
"mood": "Focused and industrious",
"objects": ["power drill", "wooden shelf", "screws", "workbench"]
}
Wrap-up and next steps
This pipeline gives you a working foundation for low-latency video analysis on Oxlo.ai. Two concrete ways to push it further:
- Replace fixed-interval sampling with OpenCV scene-change detection to cut redundant API calls.
- Enable streaming responses from Oxlo.ai to emit partial reports as soon as individual frames finish processing.
Top comments (0)