Invitez et gagnez

Fonctionnement des récompenses

Partagez votre lien. Lorsqu’un ami s’inscrit avec ce lien et recharge son solde, vous recevez la récompense affichée sur ses recharges ultérieures.

Indexer des vidéos longues avec l'API Gemini : horodatage, visuels et audit des lacunes

Un flux d'ingénierie pour indexer de longs enregistrements vidéo avec l'API Gemini : téléversement via l'API Files, génération de brouillons agentiques dans l'API Interactions, validation programmatique des lignes structurées et inspection ciblée des lacunes de couverture.

Sommaire
Indexer des vidéos longues avec l'API Gemini : horodatage, visuels et audit des lacunes

Les enregistrements vidéo de longue durée — présentations techniques, ateliers, revues d’architecture et captures d’écran vidéo — concentrent une grande densité d’informations réparties entre la voix et les éléments visuels à l’écran. Les résumés généraux classiques ne capturent que les grandes lignes, contraignant les ingénieurs à parcourir manuellement la vidéo dès qu’ils recherchent une commande de terminal précise ou un paramètre de configuration spécifique.

Les modèles multimodaux Gemini peuvent analyser simultanément les flux audio et vidéo afin de générer des index d’événements structurés et horodatés. Toutefois, la sortie brute issue d’une première passe d’inférence ne constitue qu’un ensemble de candidats préliminaires, et non un plan de référence prêt pour la production. L’obtention d’un index fiable exige un pipeline rigoureux : téléversement unique via la Files API et réutilisation de son URI, extraction d’intervalles préliminaires, validation stricte du schéma de sortie, audit des lacunes de couverture suspectes et calibration ciblée.


Architecture : méthodes de transmission et modes de traitement

Dans la documentation actuelle de Google Video Understanding, les implémentations principales s’articulent autour de l’Interactions API et de la bibliothèque google-genai. Bien que la méthode traditionnelle generate_content demeure prise en charge à des fins de rétrocompatibilité, l’Interactions API offre un contrôle plus précis sur les paramètres de traitement multimodal.

1. Méthodes de transmission vidéo

  • Files API (recommandée pour les vidéos longues) : idéale pour les enregistrements allant de quelques minutes à plusieurs heures. Le fichier est téléversé une seule fois, indexé sur le serveur, puis référencé par son URI au fil des requêtes répétées sans devoir réémettre les octets bruts.
  • Google Cloud Storage (GCS) : parfait pour les archives vidéo déjà hébergées sur l’infrastructure Google Cloud.
  • Données intégrées (Inline Data) : transmission des octets bruts directement dans le corps de la requête. La documentation décrit diverses contraintes de charge utile selon les environnements ; pour un traitement stable d’enregistrements d’une heure, il convient de s’appuyer sur la Files API ou Cloud Storage, en évitant d’injecter la vidéo brute en ligne.

2. Modes de traitement : statique vs agentique

  • Traitement statique (Static Processing) :
    Par défaut, le modèle échantillonne les images à une fréquence discrète de 1 image par seconde (1 FPS). Chaque seconde est convertie en jetons (tokens), ce qui engendre une charge de contexte substantielle sur une durée de 60 minutes. Gardez à l’esprit qu’un échantillonnage à 1 FPS peut manquer des événements visuels brefs (tels que des changements de fenêtre d’une fraction de seconde ou des infobulles furtives) et ne garantit pas la capture de chaque micro-interaction.
  • Compréhension vidéo agentique (Agentic Video Understanding) :
    Le modèle parcourt la vidéo de manière dynamique, en extrayant des images précises et des segments audio à la demande. Cela réduit drastiquement le volume de jetons de contexte traités.
    Limite : L’heuristique agentique s’appuie sur des indices sonores et des déclencheurs sémantiques. Si un présentateur effectue des actions silencieuses à l’écran (comme taper une commande ou examiner un schéma) sans parler, l’heuristique peut considérer cet intervalle comme un arrière-plan inactif et ne pas demander d’images visuelles détaillées.

Étape 1. Téléversement de la vidéo et interrogation du statut

Les fichiers envoyés à la Files API ne sont pas immédiatement exploitables pour l’inférence ; le serveur doit d’abord décompresser le conteneur et indexer les pistes audiovisuelles. Votre application doit interroger la ressource en boucle (polling) jusqu’à ce que son état devienne ACTIVE.

import time
from google import genai

client = genai.Client()

video_path = "tech_workshop_60min.mp4"
video_file = client.files.upload(file=video_path)

while not video_file.state or video_file.state.name != "ACTIVE":
    if video_file.state and video_file.state.name == "FAILED":
        error_info = getattr(video_file, "error", None)
        raise RuntimeError(
            f"Файл перешел в статус FAILED. Детали: {error_info}. "
            "Проверьте кодеки, целостность контейнера или повторите попытку."
        )
    time.sleep(5)
    video_file = client.files.get(name=video_file.name)

print("Видео готово к обработке.")

Étape 2. Demande d’un index préliminaire via l’Interactions API

Pour les enregistrements longs, appelez client.interactions.create avec "processing": "agentic". Le prompt impose une sortie tabulaire stricte : plage temporelle MM:SS - MM:SS, type d’événement (visual, speech, hybrid), affirmation vocale concise (CLAIM) et action affichée à l’écran (ACTION).

index_prompt = """
Ты — инструмент технической индексации видеозаписей. 
Сформируй хронологический индекс событий для всей 60-минутной записи от 00:00 до 60:00.

Требования к структуре ответа:
1. Выведи результат построчно в формате TSV с разделителем |.
2. Каждая строка должна содержать ровно 5 полей:
   START_TIME (MM:SS) | END_TIME (MM:SS) | TYPE (visual/speech/hybrid) | CLAIM | ACTION
3. Не объединяй слишком длинные интервалы в одну запись; фиксируй смену слайдов, терминал, ошибки и выводы спикера.
4. Если речи не было, в поле CLAIM укажи NONE. Если на экране не было динамики, в ACTION укажи STATIC.
5. Выводи исключительно строки данных без вводных слов и пояснений.
"""

interaction = client.interactions.create(
    model="gemini-3.8-flash",
    input=[
        {
            "type": "video",
            "uri": video_file.uri,
            "mime_type": video_file.mime_type,
            "processing": "agentic"
        },
        {
            "type": "text",
            "text": index_prompt
        }
    ]
)

raw_index_output = interaction.output_text

Sur les étapes de navigation agentique :
La présence d’entrées processing_call au sein de interaction.steps confirme que le modèle a navigué dynamiquement le long de la chronologie vidéo au lieu d’ingérer un flux continu. Néanmoins, l’observation de ces appels atteste uniquement de l’activité de navigation — cela ne garantit pas l’exhaustivité de la sortie (output completeness) pour l’ensemble des événements pertinents de la chronologie.


Étape 3. Exemple de structure de sortie (brouillon hypothétique)

Voici un extrait hypothétique illustrant la sortie du modèle pour démontrer le schéma de données attendu :

00:00 | 02:15 | hybrid | Вводное слово, обзор повестки миграции на шардированный кластер | Титульный слайд доклада, окно спикера
02:16 | 05:40 | visual | NONE | Переключение на схему архитектуры сервиса заказов в Miro
05:41 | 09:12 | speech | Пояснение причин отказа от распределенных транзакций в пользу Saga | Статичная схема Miro, курсор неподвижен
27:30 | 29:10 | visual | NONE | Открытие консоли, запуск сценария развертывания реплик БД
29:11 | 32:45 | hybrid | Разбор сценария split-brain при потере сетевой связности | Вывод логов etcd в терминале, подсветка таймаутов
54:10 | 57:25 | speech | Ответ на вопрос о допустимой задержке репликации данных | Финальный слайд с контактами спикера
57:26 | 60:00 | hybrid | Подведение итогов и демонстрация ссылки на репозиторий | Показ QR-кода на экране, завершение созвона

Étape 4. Validation du balisage et recherche locale

Les réponses brutes des LLM ne peuvent pas être considérées comme des jeux de données structurés fiables sans vérification préalable. Un parseur résilient ne doit pas ignorer silencieusement les enregistrements corrompus ; il doit plutôt les isoler en vue d’une correction manuelle. Le validateur contrôle : la présence d’exactement cinq champs, des types d’événements valides (visual, speech, hybrid) et des horodatages MM:SS bien formés respectant la durée totale de la vidéo.

import csv
import re
from typing import List, Dict, Tuple

TIME_PATTERN = re.compile(r"^(\d{2}):([0-5]\d)$")
ALLOWED_TYPES = {"visual", "speech", "hybrid"}

def time_to_seconds(t_str: str) -> int:
    match = TIME_PATTERN.match(t_str)
    if not match:
        raise ValueError(f"Некорректный формат времени: {t_str}")
    m, s = map(int, match.groups())
    return m * 60 + s

def parse_and_validate_tsv(
    raw_text: str, 
    max_duration_sec: int = 3600
) -> Tuple[List[Dict[str, str]], List[Dict[str, str]]]:
    valid_rows = []
    malformed_rows = []
    
    lines = [line.strip() for line in raw_text.strip().splitlines() if line.strip()]
    
    for idx, line in enumerate(lines, start=1):
        cols = [c.strip() for c in line.split("|")]
        
        if len(cols) != 5:
            malformed_rows.append({
                "line": idx,
                "content": line,
                "error": f"Ожидалось 5 полей, получено {len(cols)}"
            })
            continue
            
        start_str, end_str, event_type, claim, action = cols
        
        if event_type.lower() not in ALLOWED_TYPES:
            malformed_rows.append({
                "line": idx,
                "content": line,
                "error": f"Недопустимый тип события: {event_type}"
            })
            continue
            
        try:
            s_sec = time_to_seconds(start_str)
            e_sec = time_to_seconds(end_str)
        except ValueError as err:
            malformed_rows.append({
                "line": idx,
                "content": line,
                "error": str(err)
            })
            continue
            
        if s_sec > e_sec:
            malformed_rows.append({
                "line": idx,
                "content": line,
                "error": f"Время начала ({start_str}) больше времени окончания ({end_str})"
            })
            continue
            
        if e_sec > max_duration_sec:
            malformed_rows.append({
                "line": idx,
                "content": line,
                "error": f"Таймкод {end_str} выходит за хронометраж ({max_duration_sec} сек)"
            })
            continue
            
        valid_rows.append({
            "start": start_str,
            "end": end_str,
            "start_sec": s_sec,
            "end_sec": e_sec,
            "type": event_type.lower(),
            "claim": claim,
            "action": action
        })
        
    return valid_rows, malformed_rows

def search_index(
    rows: List[Dict[str, str]], 
    keyword: str = None, 
    event_type: str = None
) -> List[Dict[str, str]]:
    results = []
    for r in rows:
        if event_type and r["type"] != event_type.lower():
            continue
        if keyword:
            kw = keyword.lower()
            if kw not in r["claim"].lower() and kw not in r["action"].lower():
                continue
        results.append(r)
    return results

valid_index, errors = parse_and_validate_tsv(raw_index_output, max_duration_sec=3600)

if errors:
    print(f"Обнаружено некорректных строк: {len(errors)}. Требуется ручная правка:")
    for err in errors:
        print(f"Строка {err['line']}: {err['error']} -> {err['content']}")
else:
    print(f"Все строки валидны. Записей в индексе: {len(valid_index)}")

Étape 5. Audit de couverture et identification des lacunes

Avant de publier l’index, auditez la chronologie à la recherche de zones d’ombre :

  1. Vérification ponctuelle des zones repères :
    Contrôlez manuellement le début de la vidéo (introduction et diapositives de titre), le point médian (où culminent généralement les démonstrations en direct ou les débats d’architecture) et la conclusion (questions-réponses et remarques finales).
  2. Analyse des intervalles temporels :
    Il n’existe pas de seuil universel pour l’espacement admissible entre les entrées de l’index ; la tolérance dépend du cas d’usage. Dans un screencast dense, un écart de 90 secondes peut masquer une étape de configuration manquante. Dans une conférence de synthèse, une même thèse développée pendant 5 minutes peut s’avérer tout à fait normale. Si un intervalle semble incohérent par rapport au rythme de la présentation, marquez-le comme suspect.
  3. Inspection des événements visuels silencieux :
    Si un intervenant a manipulé l’écran sans explication vocale, le mode agentique a pu survoler ce passage. Redirigez ces créneaux vers un examen statique ciblé.

Étape 6. Inspection ciblée des zones suspectes via des extraits statiques

Le réexamen d’un segment suspect ne nécessite pas de réanalyser l’intégralité de la vidéo de 60 minutes. Le découpage en clips restreint l’analyse en mode statique à des bornes précises en secondes (start_offset et end_offset).

Limite de la méthode :
Le mode statique à 1 FPS offre une grille d’échantillonnage régulière, ce qui aide à repérer les actions omises lors de la passe agentique globale. Toutefois, il ne garantit pas un rappel absolu : les modifications visuelles survenant en moins d’une seconde peuvent toujours échapper aux images échantillonnées.

start_sec = 1200
end_sec = 1380

targeted_inspection = client.interactions.create(
    model="gemini-3.8-flash",
    input=[
        {
            "type": "video",
            "uri": video_file.uri,
            "mime_type": video_file.mime_type,
            "processing": {
                "type": "static",
                "start_offset": start_sec,
                "end_offset": end_sec,
                "fps": 1.0
            }
        },
        {
            "type": "text",
            "text": (
                "Хронологически опиши изменения на экране. "
                "Зафиксируй команды в терминале, смену окон и системные сообщения."
            )
        }
    ]
)

print("Результат точечного досмотра отрезка:")
print(targeted_inspection.output_text)

Intégrez les nouvelles entrées extraites à l’index validé, soit manuellement, soit au moyen d’une étape de révision semi-automatisée.

Une fois que vous avez résolu toutes les entrées présentes dans errors et vérifié les horodatages par rapport à l’enregistrement source, exportez l’index final. L’extrait de code ci-dessous s’interrompt délibérément si le parseur signale des erreurs résiduelles ; assurez-vous que valid_index contient vos corrections vérifiées avant de l’exécuter.

if errors:
    raise ValueError("Исправьте строки из errors и повторите проверку перед экспортом")

with open("video_index.csv", "w", newline="", encoding="utf-8") as output_file:
    writer = csv.DictWriter(
        output_file,
        fieldnames=["start", "end", "type", "claim", "action"],
        extrasaction="ignore",
    )
    writer.writeheader()
    writer.writerows(valid_index)

Le fichier CSV obtenu permet aux utilisateurs de rechercher des propos spécifiques ou des actions à l’écran, puis de naviguer directement vers les segments pertinents de l’enregistrement source. Il conserve le statut de brouillon tant qu’un éditeur humain n’a pas confirmé à la fois les événements sélectionnés et leurs limites d’événements, et non pas simplement les limites d’échantillonnage, par rapport à la vidéo originale.


Dépannage et cas limites

  • Statut FAILED lors du téléversement vers la Files API :
    Évitez de diagnostiquer le problème sans les retours de l’API. Les échecs peuvent provenir de formats de conteneurs non pris en charge, d’en-têtes de fichiers corrompus ou d’erreurs d’infrastructure temporaires. Examinez l’attribut file.error via le SDK, vérifiez la lecture locale avec ffprobe, standardisez les flux via ffmpeg (-c:v libx264 -c:a aac) si nécessaire, puis réessayez.
  • Erreur 401 Unauthorized ou coupures réseau :
    Une erreur 401 indique explicitement un échec d’authentification (clé invalide ou manquante, ou variable d’environnement GEMINI_API_KEY non configurée), et non une session de traitement expirée. Pour éviter l’expiration du délai d’attente de connexion HTTP côté client lors d’appels prolongés, activez le streaming avec stream=True.
  • Horodatages dépassant la durée totale :
    Cette dérive peut survenir avec la complexification du contexte. Compensez-la par des instructions strictes dans le prompt et une validation programmatique (e_sec > max_duration_sec) dans votre parseur.
  • Dérive de structure du format TSV :
    Lorsque le formatage se dégrade, dirigez les lignes malformées vers malformed_rows et fournissez 1 à 2 lignes de référence d’exemple (few-shot) dans le prompt système.

Flux de vérification avant publication

  1. Vérifier la disponibilité de la ressource : confirmez que le fichier a bien atteint le statut ACTIVE dans la Files API.
  2. Générer le brouillon initial : créez l’index de base en utilisant le mode de traitement agentic via l’Interactions API.
  3. Exécuter la validation programmatique : assurez-vous que toutes les lignes contiennent les cinq champs requis, des types valides et le format MM:SS. Isolez les lignes invalides pour correction.
  4. Auditer la couverture : inspectez les zones repères (début, milieu, fin) et évaluez la densité des horodatages par rapport au rythme de la présentation.
  5. Mener une inspection ciblée : réexaminez les lacunes suspectes ou les passages visuels silencieux à l’aide d’extraits statiques (start_offset/end_offset).
  6. Effectuer une calibration manuelle ponctuelle : vérifiez l’heure de début réelle de chaque événement par rapport à la source audio/vidéo pertinente selon les exigences du cas d’usage ; n’exigez pas de changement visuel correspondant, puisqu’un événement uniquement audio peut se produire.

Pour connaître les schémas de requête, les modes de traitement et les contraintes d’échantillonnage, consultez la documentation officielle de Gemini Video Understanding.

Prêt à optimiser votre workflow LLM ?

Connectez vos modèles via une API unique, gérez les clés et maîtrisez vos dépenses d’IA.

Commencer gratuitement