Clips
There are two ways to get a time range out of a stream:
Stored clip (createClip) | Ad-hoc cut (URL parameters) | |
|---|---|---|
| What you get | A new asset in your account with its own playbackId | A one-off MP4, MKV, WebM, or TS response |
| Stored | Yes, until you delete it or retention expires it | No |
| Playable as | Every protocol (HLS, DASH, WebRTC, MP4, …) and the player | The single file you requested |
| Access policy | Snapshot of the source’s policy at creation | The source’s policy, checked on the request |
| Billed as | Storage, plus delivery when watched | Delivery, like any other playback |
| Use it for | Highlights you share, embed, or list later | Downloads, exports, and handing a range to another tool |
Stored clips
Section titled “Stored clips”A stored clip is processed on the media cluster and saved as its own asset. It gets a
playbackId, shows up in your storage listing, and plays through the same player and URLs as
any stream or VOD asset, without exposing the source stream’s playback ID.
Create a clip
Section titled “Create a clip”createClip takes the source stream’s global ID, a title, and a time range in one of four modes:
mode | Range | Fields |
|---|---|---|
ABSOLUTE (default) | Exact start and stop | startUnix + stopUnix |
DURATION | Start time plus a length | startUnix + duration |
CLIP_NOW | The last N seconds of the live stream | duration |
RELATIVE | Seconds from the stream start | startMedia + stopMedia |
startUnix and stopUnix are Unix timestamps in seconds; duration, startMedia, and
stopMedia are seconds. expiresAt (Unix seconds) optionally deletes the clip at that time.
mutation { createClip( input: { streamId: "<stream global id>" title: "Last 30 seconds" mode: CLIP_NOW duration: 30 } ) { __typename ... on Clip { id clipHash playbackId status } ... on ValidationError { field message } ... on NotFoundError { message } }}import { createClient, CreateClipDocument, expectResult } from "@livepeer-frameworks/api";
const client = createClient({ url: "https://bridge.example.com/graphql", token: process.env.FRAMEWORKS_API_TOKEN,});
const created = await client.request(CreateClipDocument, { input: { streamId, title: "Keynote intro", mode: "ABSOLUTE", startUnix: 1706000000, stopUnix: 1706000060, },});const clip = expectResult(created.createClip, "Clip");console.log(clip.playbackId, clip.status);mode := frameworks.ClipCreationModeAbsolutestart, stop := 1706000000, 1706000060resp, err := frameworks.CreateClip(ctx, client, frameworks.CreateClipInput{ StreamId: streamID, Title: "Keynote intro", Mode: &mode, StartUnix: &start, StopUnix: &stop,})if err != nil { log.Fatal(err)}clip, err := frameworks.ExpectResult[*frameworks.CreateClipCreateClip](resp.CreateClip)from livepeer_frameworks import FrameWorksClient, expect_resultfrom livepeer_frameworks.graphql import Clip, ClipCreationMode, CreateClipInput
with FrameWorksClient(token=os.environ["FRAMEWORKS_API_TOKEN"]) as fw: created = fw.create_clip( input=CreateClipInput( stream_id=stream_id, title="Keynote intro", mode=ClipCreationMode.ABSOLUTE, start_unix=1706000000, stop_unix=1706000060, ) ) clip = expect_result(created.create_clip, Clip)See SDKs for client setup, retries, and typed errors.
Where the bytes come from
Section titled “Where the bytes come from”Always pass the live stream’s ID, however far back the range goes. FrameWorks picks the source from where the range falls:
- The live buffer (about the last two minutes): read from the live stream.
- The rolling DVR window, when the stream is recording: read from the DVR segments.
- A finalized chapter (
FINALIZED,FROZEN, orRECLAIMED): read from the chapter file.
A range that spans two sources, such as the live buffer and the DVR window or two chapters, is
rejected with a ValidationError. Split it into two clips.
Processing and events
Section titled “Processing and events”The clip exists as soon as createClip returns, and status moves through queued,
processing, and ready, or ends at failed. Follow it with the clip events of
tenantEvents (pass streamId for one stream only) or the matching webhooks:
subscription { tenantEvents(types: ["clip.requested", "clip.ready", "clip.failed"]) { type time data { ... on ClipReady { artifact { artifactId } durationMs sizeBytes } ... on ClipFailed { artifact { artifactId } reason } } }}artifactId is the clip’s clipHash. Clip events leave artifact.playbackId empty; read the
clip’s playbackId with clip(id: "<clipHash>") or with storageArtifactsConnection and
artifactHash. See Events for every payload field.
Play a clip
Section titled “Play a clip”A ready clip plays at the same URLs as any other content, or through the
player with its playbackId:
https://foghorn.frameworks.network/play/{clipPlaybackId}/hls/index.m3u8https://foghorn.frameworks.network/play/{clipPlaybackId}.mp4For a custom player, resolveViewerEndpoint(contentId: "<clip playback id>") returns the edge
node and its output catalog.
The clip’s playback policy is a snapshot of the source stream’s policy when the clip was created. Changing the stream’s policy later does not change clips that already exist. See playback access control.
List and delete clips
Section titled “List and delete clips”Clips are listed with the other stored media through storageArtifactsConnection:
query { storageArtifactsConnection(input: { kinds: [CLIP], streamId: "<stream global id>", first: 20 }) { nodes { id hash title playbackId status durationSeconds sizeBytes createdAt expiresAt } totalCount hasNextPage }}clip(id: ID!) fetches one clip by global ID. Deleting removes the clip’s media and cannot be
undone:
mutation { deleteClip(id: "<clip global id>") { __typename ... on DeleteSuccess { success } ... on NotFoundError { message } }}Clips count toward storage and follow your retention settings; see
storage and retention. Agents create and delete clips with the
create_clip and delete_clip MCP tools.
Ad-hoc cuts from a playback URL
Section titled “Ad-hoc cuts from a playback URL”Any progressive playback URL (.mp4, .mkv, .webm, .ts) accepts time-range parameters. The
edge cuts the range on the fly and returns it as a single file. Nothing is processed in advance or
saved to your account.
# The last 60 seconds of a live stream, as a downloadcurl -L -o last-minute.mp4 "https://foghorn.frameworks.network/play/{playbackId}.mp4?startunix=-60&dl=1"
# A fixed wall-clock range from the DVR window, delivered as fast as possiblecurl -L -o keynote.mkv "https://foghorn.frameworks.network/play/{dvrPlaybackId}.mkv?startunix=1706000000&stopunix=1706000060&rate=0"/play validates these parameters and passes them on to the edge it redirects to. Other query
parameters are not forwarded.
| Parameter | Value | Effect |
|---|---|---|
startunix | Unix seconds | Start of the cut in wall-clock time. A value of 36000 or less is relative to now: -300 means five minutes ago. |
stopunix | Unix seconds | End of the cut, with the same relative rule. |
duration | Seconds | Length of the cut from its start. Takes precedence over stop and stopunix. |
start | Milliseconds | Start of the cut on the content’s own media timeline. |
stop | Milliseconds | End of the cut on the content’s own media timeline. |
dl | 1 or a file name | Sends the response as a download. A value with a dot, such as dl=keynote.mp4, is the file name; otherwise the URL’s file name is used. Also turns off realtime pacing. |
rate | Number | Delivery speed: 0 sends as fast as the network allows, 2 at twice realtime. |
audio, video, subtitle | Track selector | Which tracks to include, such as video=all or audio=none. |
Behavior to expect:
- The cut starts at the keyframe at or before the requested start.
- A start earlier than the available media is moved up to the oldest available point. A stop earlier than the available media returns an error.
- On a live stream, a stop in the future holds the response open until that moment arrives.
- Cuts are supported on progressive formats only. Segmented protocols (HLS, DASH) and WebRTC are not a reliable way to request a range.
How far back you can reach depends on the playback ID:
| Playback ID | Reach | Use |
|---|---|---|
Live playbackId | About the last two minutes | ”The last N seconds” with startunix |
dvrPlaybackId | The recording’s rolling DVR window | Longer ranges while the stream is live |
A chapter’s playbackId | That chapter | Ranges from finished recordings |
A VOD asset’s or clip’s playbackId | The whole asset, with start and stop | Trimming uploaded or stored media |
Related
Section titled “Related”- Recordings, clips, and VOD: DVR windows, chapters, and uploads
- DVR chapters: finalized chapter playback IDs and wall-clock times
- Playback:
/playURLs and output formats - SDKs: TypeScript, Go, and Python clients