GEMINI LABJP
SUNSET — gemini-robotics-er-1.6-preview shuts down on August 31. Twelve days out, there is not much room left to push the migration backMIGRATION — The replacement is Gemini Robotics ER 2, in public preview since July 30, with spatial reasoning, agentic code execution, and multi-step tool orchestrationPRICING — Gemini 3.7 Flash went GA on August 13 at $0.75 per million input tokens and $3.75 output, roughly half what Flash models have cost, holding through December 31, 2026PRICING — List prices double to $1.50 and $7.50 on January 1, 2027. Anything running across the new year deserves a two-stage estimate now rather than a surprise in JanuaryAVAILABILITY — Beyond the Gemini API and Google AI Studio, 3.7 Flash is live in Android Studio, Google Antigravity, and the Gemini Enterprise Agent PlatformDEPRECATION — The temperature, top_p, and top_k sampling parameters remain deprecated. If your code sets them explicitly, measure the output difference without them while there is still timeSUNSET — gemini-robotics-er-1.6-preview shuts down on August 31. Twelve days out, there is not much room left to push the migration backMIGRATION — The replacement is Gemini Robotics ER 2, in public preview since July 30, with spatial reasoning, agentic code execution, and multi-step tool orchestrationPRICING — Gemini 3.7 Flash went GA on August 13 at $0.75 per million input tokens and $3.75 output, roughly half what Flash models have cost, holding through December 31, 2026PRICING — List prices double to $1.50 and $7.50 on January 1, 2027. Anything running across the new year deserves a two-stage estimate now rather than a surprise in JanuaryAVAILABILITY — Beyond the Gemini API and Google AI Studio, 3.7 Flash is live in Android Studio, Google Antigravity, and the Gemini Enterprise Agent PlatformDEPRECATION — The temperature, top_p, and top_k sampling parameters remain deprecated. If your code sets them explicitly, measure the output difference without them while there is still time
Articles/API / SDK
API / SDK/2026-07-05Advanced

Collapsing Video Understanding into One Native Call with Omni Flash

How I replaced an ffmpeg frame-extraction pipeline (7-9 calls per clip) with a single native Omni Flash call, the measured differences, and the boundaries where keeping frame sampling still wins.

Gemini Omni Flash3video understanding2multimodal44Files API4cost design7

Premium Article

Once I started handling a handful of short promo clips as an indie developer, "making the model understand the video" became the heaviest step in the whole flow. I would cut one frame per second with ffmpeg, send each image to the model for a description, then summarize everything in one more call. It worked, but a single clip took 7-9 API calls to process, and it threw away the audio track entirely.

With Omni Flash entering public preview, passing a video in as-is and letting the model understand it natively became practical. This article walks through the minimal code I used when I moved from a frame-extraction setup to a single pass, the relative measurements, and the boundary where keeping frame sampling is still the better call.

The debt in a three-stage frame-extraction pipeline

The setup I had been running was three stages: extract, describe, summarize. Written out as code, the debt shows up clearly.

import subprocess, os
from google import genai
 
client = genai.Client()
 
def extract_frames(video_path: str, fps: float = 1.0, out_dir: str = "frames") -> list[str]:
    os.makedirs(out_dir, exist_ok=True)
    subprocess.run([
        "ffmpeg", "-i", video_path,
        "-vf", f"fps={fps}", f"{out_dir}/f_%04d.jpg",
    ], check=True)
    return sorted(os.path.join(out_dir, f) for f in os.listdir(out_dir))
 
def describe_video(video_path: str) -> str:
    frames = extract_frames(video_path, fps=1.0)
    notes = []
    for i, path in enumerate(frames):            # calls grow with frame count
        img = client.files.upload(file=path)
        r = client.models.generate_content(
            model="gemini-3.5-flash",
            contents=[img, f"Frame near second {i}. Describe what's shown in one line."],
        )
        notes.append(f"[{i}s] {r.text.strip()}")
    summary = client.models.generate_content(     # one more call to summarize
        model="gemini-3.5-flash",
        contents=["Below are per-second frame notes. Summarize the whole video in 3 lines.\n" + "\n".join(notes)],
    )
    return summary.text

There are three problems. First, call count grows in proportion to video length. Second, since the audio track is never looked at, anything that depends on narration or sound effects gets dropped. Third, frames are only lined up by a time index, so the model cannot reason about motion and treats the clip as a stack of stills. In my use case, that third point was the ceiling on accuracy.

The minimal setup: hand the video straight to Omni Flash

Because Omni Flash handles video natively, you upload the clip through the Files API and pass it to a single generate_content call. Combined with structured output, the downstream parsing disappears too.

import time
from google import genai
from pydantic import BaseModel
 
client = genai.Client()
 
class VideoReport(BaseModel):
    summary: str
    spoken_language: str
    has_music: bool
    safe_for_all_ages: bool
    key_moments: list[str]
 
def understand_video(video_path: str) -> VideoReport:
    f = client.files.upload(file=video_path)
    # Right after upload the file is PROCESSING. Using it before ACTIVE returns 400.
    while f.state.name == "PROCESSING":
        time.sleep(2)
        f = client.files.get(name=f.name)
    if f.state.name != "ACTIVE":
        raise RuntimeError(f"upload failed: {f.state.name}")
 
    r = client.models.generate_content(
        model="gemini-omni-flash-preview",   # public preview; match the exact ID in the changelog
        contents=[f, "Understand this video end to end and return it in the given schema."],
        config={
            "response_mime_type": "application/json",
            "response_schema": VideoReport,
        },
    )
    return r.parsed

It is one call. Because the model sees visuals, audio, and the passage of time in a single context, axes like has_music and spoken_language come back at once, which the frame-extraction version could never recover. I first mirrored the old output and only took a text summary, but passing response_schema and naming the axes I actually needed made the downstream branching trivial to write.

The Before/After gap is less about lines of code and more about what you're discarding. Before pays call count to throw away audio and motion; After picks both up in a single call.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A minimal setup that folds ffmpeg frame extraction plus per-frame calls (7-9 per clip) into one native Omni Flash call
The three boundaries where single-pass breaks down (long videos, frame-precise detection, cheap yes/no checks) and when to keep frame sampling
A hybrid design that runs a coarse single pass first and only escalates to frame inspection when needed, plus the upload-state and token-budget traps I hit
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

API / SDK2026-07-18
How Well Does Omni Flash Hear 'Rotate the Camera 30 Degrees Right'? Measuring Where Conversational Edits Land
Public-preview Gemini Omni Flash lets you re-edit a generated video in plain language. 'Make the lighting evening' lands; 'rotate the camera 30 degrees' often misses. Here is a running log of where instructions land, sorted mechanically by comparing before/after frames.
API / SDK2026-06-28
Read Video with Timestamps in the Gemini API: Pull Just the Scene You Need
Hunting for 'where was that step?' in a screen recording or app demo is a chore. Here is how to use Gemini API video understanding to pull just the right scene with timestamps, plus a design that keeps tokens down with FPS and resolution.
API / SDK2026-07-19
Still image or short clip? Deciding feature placement from the cost gap between Nano Banana 2 Lite and Omni Flash
When I froze over whether a wallpaper app's hero asset should be a still image or a short moving loop, the deciding factor was not taste but the order of magnitude of the cost. Here is how to normalize Nano Banana 2 Lite and Omni Flash onto the same footing, down to a working decision function.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →