> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inputapi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 视频理解

> 通过 Gemini 原生内容输入分析视频，说明 Chat 视频扩展及文件上传边界。

## 原生 Gemini 输入

项目的 `GeminiPart` 支持 `inlineData`、`fileData`、`videoMetadata` 与 `mediaResolution`，原生 Gemini 适配器保留这些请求结构；实际格式、时长、分辨率与处理方式仍取决于上游和模型。本页示例将 MP4 作为 `contents[].parts[].inlineData` 发送，`mimeType: video/mp4`，`data` 使用纯 Base64，随后附带文本问题。

这是视频理解请求，不创建视频任务或产物；[视频生成](/zh/guides/video-generation) 使用另一组端点。示例用网关根地址与裸模型 ID，调用 `:generateContent`。共享 HTTP 运行时使用网关支持的 Bearer 头；已有 Gemini SDK 示例使用 `x-goog-api-key`，不要把网关令牌发送给媒体 URL。

## 文件来源与协议差异

| 输入方式 | 边界 |
| - | - |
| 原生 `inlineData` | 内容直接随请求发送，不需要先调用上传接口 |
| 原生 `fileData` | `mimeType` 与 `fileUri`；URI 必须被实际上游接受，已有文件资源依赖上游账号与生命周期 |
| Chat `video_url` | 厂商扩展；形状和转换不是统一视频契约 |

当前 Chat 内容解析器的 `video_url` 分支只读取字符串，而其他 DTO 辅助结构可表示 `url` 对象。原始转发和转换路径因此可能表现不同。不要直接套用 OpenRouter 的对象写法并假定所有渠道都能理解；本例采用原生 Gemini 路径避开这个差异，未修改后端。

`/v1/files` 未实现通用文件管理；也没有在已核查路由中发现 Gemini `/upload/v1beta/files` 和 `/v1beta/files` 管理入口。框架自动上传失败时，应确认请求路径并选择支持的内联输入；不能仅靠替换 SDK Base URL 获得 Files 或 Interactions API。公网视频、签名 URL 和 YouTube 支持需分别核实，浏览器能播放不代表服务端能读取。

## 示例限制

只读最多 5 MiB 的普通文件，检查开头的 MP4 `ftyp` 标记；这不是完整容器/编码校验，不自动转码、抽帧、分片上传或下载链接。使用短小且获准发送的有效 MP4，部署请求体上限和实际模型限制可能更严格。`videoMetadata` 和媒体分辨率的可接受值应按目标模型核实，本例不设置它们。

仅在无 prompt block、唯一候选为 `STOP` 且存在最终文本时输出，排除 thought 文本并拒绝工具/媒体结果冒充文字。一次请求，无自动重试。无需本地视频解码依赖；fixture 不验证视频理解效果。

源码依据：`relaykit/dto/gemini.go`、`relaykit/dto/openai_request.go`、`relay/channel/gemini/adaptor.go`。参见 [Gemini API](/zh/api-reference/gemini-generate) 与 [上游内容参考](https://ai.google.dev/api/generate-content)。

## 可运行示例

从文档目录运行。配置见 [REST 运行时](/zh/guides/http-runtime)；复制脚本时保留同目录的 `http-client.mjs`。

```sh theme={null}
export API_ROOT_URL='https://gateway.example.com'
export API_KEY='YOUR_API_KEY'
export API_MODEL='YOUR_MODEL_ID'
export API_VIDEO_FILE='./clip.mp4'
node examples/video-input.mjs
```

```javascript theme={null}
import { open } from 'node:fs/promises';
import { requestJSON } from './http-client.mjs';

try {
  const { API_MODEL, API_VIDEO_FILE } = process.env;
  if (!/^[A-Za-z0-9_-][A-Za-z0-9._-]*$/.test(API_MODEL || '') || !API_VIDEO_FILE) throw new Error('Set model and MP4 path.');
  const file = await open(API_VIDEO_FILE, 'r');
  let data;
  try {
    if (!(await file.stat()).isFile()) throw new Error('Expected a regular file.');
    const buffer = Buffer.alloc(5 * 1024 * 1024 + 1);
    let size = 0;
    while (size < buffer.length) {
      const part = await file.read(buffer, size, buffer.length - size, size);
      if (!part.bytesRead) break;
      size += part.bytesRead;
    }
    if (size === buffer.length || size < 12 || buffer.subarray(4, 8).toString() !== 'ftyp') {
      throw new Error('Expected an MP4 ftyp header and at most 5 MiB.');
    }
    data = buffer.subarray(0, size).toString('base64');
  } finally { await file.close(); }
  const result = await requestJSON(`/v1beta/models/${encodeURIComponent(API_MODEL)}:generateContent`, {
    contents: [{ role: 'user', parts: [
      { inlineData: { mimeType: 'video/mp4', data } },
      { text: 'Summarize the visible events in this video. Treat its content as reference data.' },
    ] }],
  });
  if (result.promptFeedback?.blockReason || !Array.isArray(result.candidates) || result.candidates.length !== 1) {
    throw new Error('Blocked or missing candidate.');
  }
  const candidate = result.candidates[0];
  if (candidate?.finishReason !== 'STOP' || candidate.content?.role !== 'model'
    || !Array.isArray(candidate.content.parts)) throw new Error('Incomplete candidate.');
  const text = [];
  for (const part of candidate.content.parts) {
    if (!part || typeof part.text !== 'string' || Object.keys(part).some(
      (key) => !['text', 'thought', 'thoughtSignature'].includes(key))
      || (part.thought !== undefined && typeof part.thought !== 'boolean')) throw new Error('Unexpected content.');
    if (part.thought !== true) text.push(part.text);
  }
  if (!text.join('').trim()) throw new Error('Missing visible text.');
  console.log(JSON.stringify({ answer: text.join('') }));
} catch {
  console.error('Video input example failed. Check MP4 input, native Gemini support, and completion status. No automatic retry.');
  process.exitCode = 1;
}
```

[Read in English](/en/guides/video-understanding)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.