---
title: 비디오 필터
description: Sume 호스트 클립 하나에 dim / crop 또는 허용 목록 픽셀 filtergraph를 적용해 새 MP4를 만듭니다. 타임라인 배치가 아닌 소재 준비입니다.
---

> **현재 SoT.** Media L2 픽셀 패스(`sume/video-filter-1.0`, #5820 /
> #5839). dest와 prod. **구간 컷이 아닙니다** — 그건
> [비디오 트림](/models/video-trim)입니다. **조립이 아닙니다** —
> 시퀀스, 전환, 오디오 스파인은 [Timeline 1.0](/models/timeline)에
> 남습니다.

Video filter 1.0은 **하나의** 워크스페이스 `media.sume.com` 클립과
검증된 프로그램(`ops[]` dim/crop 및/또는 필터만 있는 `filtergraph`)을
받아 **새** MP4를 반환합니다. 소스는 그대로입니다. 서버가 워커 미디어
런타임에서 ffmpeg를 컴파일합니다(`apps/api/src/routes.ts`
`createVideoFilterV1` / `submitSumeVideoFilterJob`;
`packages/timeline-compiler/src/filter.ts` `compileVideoFilterProgram`).

```text
POST /v1/video-filter
POST /v1/video-filter/check              # 무료 계약 검사
POST /v1/models/sume/video-filter-1.0/runs   # 같은 본문, 추가 `model` 필드 없음
```

**`GET /v1/video-filter/:id`는 없습니다.** Job 봉투로 폴링하세요:

```text
GET /v1/jobs/:id/status
GET /v1/jobs/:id/result
```

호스트 MCP: `video_filter`(`packages/mcp-server/src/mcp.ts`). 쓰기는
`idempotency_key`(OAuth에서는 `mcp:write`)가 필요합니다. 흐름:
`check_only: true`인 `video_filter`(무료) → `video_filter` →
`jobs_wait` → `jobs_result`.

## 프로그램 검사(무료)

`POST /v1/video-filter/check`는 인코드와 같은 스키마, op 화이트리스트,
filtergraph 허용 목록, Sume 호스트 / HEAD 소스 프리플라이트를 돌리고
`400` 대신 diagnostics를 반환합니다. Job을 만들지 않고, 크레딧을
예약하지 않고, 박스를 켜지 않고, 인코더를 건드리지 않습니다. 여기를
통과한 프로그램도 박스에서 실패할 수 있습니다(나쁜 식, 메모리, 시간)
— 그건 구조화된 job 오류입니다.

체크에는 **`Idempotency-Key`가 필요 없습니다.** 유효한 응답은
`object: video_filter_check`이며 `valid`, `encode: "not_run"`,
`diagnostics[]`, 컴파일된 `program.filters`(이름만, argv 없음), 유효할
때의 `estimate`, `next_action`
`submit_video_filter` | `fix_program_and_recheck`를 담습니다.

```bash
curl -X POST https://api.sume.com/v1/video-filter/check \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "video_url": "https://media.sume.com/artifacts/artf_demo/talk.mp4",
    "ops": [{ "op": "dim", "amount": 0.45 }]
  }'
```

## 인코드

필수: `video_url`(이 워크스페이스의 `media.sume.com` 아티팩트 또는
에셋) **그리고** 프로그램 — `ops[]` 또는 비어 있지 않은 `filtergraph`
중 최소 하나. 오픈 인터넷 fetch는 없습니다. 먼저 가져오세요
(`POST /v1/media-imports`). `Idempotency-Key`가 필요합니다.

선택: `ops`, `filtergraph`, `metadata`, 그리고 일반적인 `mode` /
`webhook_url` / `wait_timeout_seconds` 통신 필드.

기본 `mode`는 **`async`**입니다. `mode: "sync"`를 보내면 최대 **30초**
기다린 뒤 끝난 job은 `200`, 아니면 `202`로 폴링합니다.

```bash
curl -X POST https://api.sume.com/v1/video-filter \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: video-filter-001" \
  -d '{
    "video_url": "https://media.sume.com/artifacts/artf_demo/talk.mp4",
    "ops": [{ "op": "dim", "amount": 0.45 }]
  }'
```

성공한 제출은 job을 반환합니다(`request_id`가 job id). `result_ready`일
때 `GET /v1/jobs/:id/result`는 `kind: video_filter`이며 `video_url`(새
`artf_`, 소스가 아님), `duration_seconds`, `ops_applied`,
`filtergraph`, 컴파일된 `filters[]`, 선택적 `warnings[]`를 담습니다. 그
MP4를 `timeline_create` `video[]`에 넣으세요.

공개 요금: **인코드 job당 `$0.02`**(`VIDEO_FILTER_PUBLIC_PRICING`; 라이브는
`GET /v1/catalog`). 체크는 무료입니다. 프로바이더 추론 없음 — 워커
ffmpeg만.

## 프로그램

| 필드 | 효과 |
|---|---|
| `ops[]` | 순서 있는 프로그램. `filtergraph` **앞**에 적용. 최대 **8**. op 하나 **또는** filtergraph가 필요합니다. |
| `ops[].op: "dim"` | 클립 전체 루마 곱. `amount`는 **(0, 1]** — `0.45`는 더 어둡고, `1`은 그대로. `0`과 `>1`은 거절. 검은색은 검은색, 채도는 안 바뀝니다. |
| `ops[].op: "crop"` | 소스 프레임의 **비율**: `x`, `y`는 `[0, 1]`; `width`, `height`는 `[0.05, 1]`; `x+width ≤ 1`, `y+height ≤ 1`. 컴파일러가 yuv420p용으로 짝수 반올림합니다. |
| `filtergraph` | `ops[]` 뒤에 적용되는 필터만 있는 ffmpeg 그래프. 최대 **2048**자, 이름 있는 필터 **32**. 입력/출력/경로 없음 — 서버가 `[0:v]…[vout]`로 감쌉니다. 내부 라벨(`split[a][b]`)은 되고 스트림 지정자(`[0:v]`)는 안 됩니다. |

상한(`packages/api-contract/src/index.ts`): 소스 ≤ **300**초(compose
밴드 클립 천장). 프로그램이 바꾸지 않으면 출력은 소스의 기하, 프레임
레이트, 오디오를 상속합니다.

허용 필터 이름은
`packages/timeline-compiler/src/filter.ts`
`VIDEO_FILTER_GRAPH_ALLOWED_FILTERS`(톤, 블러, 기하, fade, 내부
합성)입니다. **목록에 없는 것:** `trim` / `setpts`([비디오
트림](/models/video-trim)을 쓰세요), `drawtext` / `subtitles` /
`movie` / `lut3d`, 파일이나 소켓을 읽는 모든 필터.

## 거절(안정 코드)

| 코드 | 언제 |
|---|---|
| `video_filter_ops_empty` | `ops[]`도 비어 있지 않은 `filtergraph`도 없음. |
| `video_filter_too_many_ops` | op가 8개 초과. |
| `unsupported_filter_op` | `ops[].op`가 `dim` / `crop`이 아님. 다른 픽셀 작업은 `filtergraph`. |
| `unsupported_filter_op_field` | op에 여분 키(dim은 `{op, amount}`만, crop은 `{op, x, y, width, height}`). |
| `video_filter_amount_out_of_range` | dim `amount`가 `(0, 1]`이 아님. |
| `video_filter_crop_out_of_bounds` | crop 비율이 소스 프레임을 벗어나거나 한 변이 `0.05` 미만. |
| `invalid_filtergraph` | 비어 있음, 너무 김, 알 수 없는 필터, 잘못된 알파벳, 또는 스트림 지정자. `unknown_filter`는 토큰과 허용 목록을 이름합니다. |
| `ffmpeg_fields_rejected` | 클라이언트가 `vf` / `filter` / `ffmpeg` / `cmd` / `codec` / `crf` / `-i` 등을 보냄. 서버가 ffmpeg를 컴파일합니다. |
| `unsupported_media_source` | `video_url`이 Sume 미디어 호스트가 아님. |
| `source_not_found` | 죽은 또는 다른 워크스페이스의 `media.sume.com` URL. |
| `unsupported_media_type` | HEAD가 비디오가 아님. |
| `source_too_large` | 소스가 타임라인 다운로드 예산을 초과. |
| `output_duration_exceeded` | 소스가 300초보다 김(워커). |

호스트 밖 URL(`https://example.com/…`)은 접수에서 거절됩니다. 먼저
가져오세요.

## 이 표면이 아닌 것

| 필요한 일 | 쓸 것 |
|---|---|
| 프로브 / 스틸 / 선택적 STT | [비디오 검사](/models/video-inspect) |
| 새 MP4 컷 | [비디오 트림](/models/video-trim) |
| 오디오 트랙을 내구성 wav / mp3로 | [오디오 분리](/models/audio-detach) |
| 여러 클립 시퀀스 | [Timeline 1.0](/models/timeline) |
| 캡션 플레이트 | HyperFrames compose / 캡션 어셈블러 |
| `t`의 소스 크기 프레임 | [비디오 프레임](/models/video-frames) |

## 관련 문서

- [비디오 검사](/models/video-inspect)
- [비디오 프레임](/models/video-frames)
- [비디오 트림](/models/video-trim)
- [오디오 분리](/models/audio-detach)
- [Timeline 1.0](/models/timeline)
- [미디어 입력](/workflows/asset-library)
- [Job과 결과](/workflows/jobs-and-results)
- [MCP 도구와 게이트](/mcp/tools-and-gates)
