diff --git a/jobs/compose.mdx b/jobs/compose.mdx index 7a0ffe4..eb35326 100644 --- a/jobs/compose.mdx +++ b/jobs/compose.mdx @@ -145,13 +145,25 @@ curl -X POST https://api.rendobar.com/jobs \ **Assets:** video, image, text, audio, composition ยท **Output:** mp4 / webm / gif / mp3 (audio only) / jpg / png (still frame) -Without `trim`, a clip plays its source from the start for `length` seconds. To use a specific segment instead, add `trim: { from, to }` (in source seconds) to the asset. `length` is always the timeline duration. `trim` only chooses which part of the source fills it. +Without `trim`, a clip plays its source from the start for `length` seconds, or for the whole source when `length` is left off. To use a specific segment instead, add `trim: { from, to }` (in source seconds) to the asset. `length` is always the timeline duration. `trim` only chooses which part of the source fills it. That is the shape of every render. Everything below is one more clip, one more track, or one more field. ## Join two clips -Put a second clip on the same track and a transition between them. The clips overlap by the transition's `duration`, so the second clip starts a little before the first ends. +Put a second clip on the same track and a transition between them. + +```json +"clips": [ + { "asset": { "type": "video", "src": "https://cdn.rendobar.com/assets/examples/river.mp4" } }, + { "type": "transition", "transition": "crossfade", "duration": 1 }, + { "asset": { "type": "video", "src": "https://cdn.rendobar.com/assets/examples/forest.mp4" } } +] +``` + +No timing anywhere. Each clip plays its whole source, the second follows the first, and the transition overlaps them by its own `duration`. You do not need to know how long either file is. + +Writing the timing out by hand gives the same render. The second clip starts `duration` seconds before the first ends, which is what the engine works out for you above. ```json "clips": [ @@ -190,10 +202,63 @@ Put a second clip on the same track and a transition between them. The clips ove The model is two ideas: - **Tracks stack.** The first track is the background. Each later track composites on top of the ones below it: overlays, titles, picture-in-picture. -- **Clips sit in time.** Each clip has a `start` and a `length` in seconds. Two clips on the same track are joined by a transition. A gap between them plays nothing. +- **Clips sit in time.** A clip can carry a `start` and a `length` in seconds, and it can leave both out. Two clips on the same track are joined by a transition. A gap between them plays nothing. So a multi-track timeline is layers (tracks) of sequences (clips). That is the whole structure. +### Timing is optional + +Every clip sits in a **container**: the scene it belongs to, or the whole video. Two sentences cover every case. + +- **Omit `length`** and the clip runs as long as its **content** does, never past its container. +- **Write `length: "end"`** and it runs to the end of its **container**, whatever its content says. + +Content means the source's duration, or the trim span, or the source duration divided by `speed` (so `speed: 0.5` on a 4 second clip occupies 8 seconds), or a composition's own end. + +Text and images have no duration of their own, so for them the two sentences say the same thing. A title with no `length` stays up for the whole video, which is what a watermark wants. + +`start` follows the same shape: omit it and the clip begins after everything already placed on its track, pulled back by any transition immediately before it. Give it a number and that is seconds from the start of its container. + +Set either field wherever you want something else. Every timeline written with full timing keeps rendering exactly as it did. + +### Worked examples + +A music bed. The source is three minutes long, the video is not, and `"end"` is how you say so without totalling the clips above it. + +```json +"tracks": [ + { "clips": [ + { "asset": { "type": "video", "src": "https://cdn.rendobar.com/assets/examples/river.mp4" } }, + { "asset": { "type": "video", "src": "https://cdn.rendobar.com/assets/examples/forest.mp4" } } + ] }, + { "clips": [ + { "asset": { "type": "audio", "src": "https://cdn.rendobar.com/assets/showcase/music.mp3", "volume": 0.35, "fadeOut": 2 }, + "length": "end" } + ] } +] +``` + +A watermark. It has no duration of its own, so it needs nothing at all. + +```json +{ "clips": [ + { "asset": { "type": "image", "src": "https://cdn.rendobar.com/assets/brand/logo-mark.png" }, + "transform": { "scale": 0.15, "position": { "x": "88%", "y": "12%" } }, "opacity": 0.8 } +] } +``` + +Both stop on the last frame, whatever the clips elsewhere add up to. Add another clip later and they still do. + +### The rules that fall out of it + +A clip running to the end of the **video** is measured against every clip that is not, so two of them on different tracks both land on the real end rather than chasing each other. It runs to the last frame, so nothing may follow it on the same track, and a timeline where nothing has a length of its own has no end to measure and is rejected. + +A clip never runs past its container, so a 6 second video in a 3 second scene plays 3 seconds, and a 2 second video in a 5 second scene plays 2. That applies to a length you wrote as well as one the engine worked out. + +`"end"` is the opposite request and does not cap: it asks for the container's full extent whatever the source holds. On a source shorter than the container that is impossible, and the job is rejected for outrunning its source. Leave the length off instead, or trim less. + +Inside a `composition`, `start` and `length` both stay required. A nested timeline's length is set by the clip hosting it, so there is nothing to append to and no container to measure against. + ## Add a layer A second track renders over the first. This lays an animated title over the video. @@ -416,13 +481,13 @@ Tracks give you precise multi-track control. For a sequential edit, you can inst "clips": [{ "asset": { "type": "video", "src": "https://cdn.rendobar.com/assets/examples/forest.mp4" } }] } ], "overlays": [ - { "asset": { "type": "image", "src": "https://cdn.rendobar.com/assets/brand/logo-mark.png" }, "start": 0, "length": 7.4, + { "asset": { "type": "image", "src": "https://cdn.rendobar.com/assets/brand/logo-mark.png" }, "transform": { "scale": 0.2, "position": { "x": "88%", "y": "12%" } } } ] } ``` -A scene's clips can omit `start` and `length` to span the whole scene. Every clip field works the same as in tracks mode. `scenes` and `tracks` are mutually exclusive: a timeline uses one or the other. +A scene's clips can omit `start` and `length` to span the whole scene, and a scene can omit its own `duration` to take the length of the video inside it. Every clip field works the same as in tracks mode. `scenes` and `tracks` are mutually exclusive: a timeline uses one or the other. ## Edit like a pro @@ -505,7 +570,7 @@ A clip's `asset` is one of: ### Clip fields -Every clip has `asset`, `start`, `length`, plus any of: +Every clip has an `asset`. `start` and `length` are optional, and `length` also accepts `"end"` (see [Timing is optional](#timing-is-optional)). Plus any of: | Field | What it does | | --- | --- | @@ -540,10 +605,10 @@ The scene-first shape, when you use `scenes` instead of `tracks`: | Field | Notes | | --- | --- | -| `scenes[].duration` | Seconds. Defaults to the longest clip in the scene | +| `scenes[].duration` | Seconds. Defaults to the longest thing in the scene, measured from its clips' own lengths or the sources inside them | | `scenes[].transition` | The transition INTO this scene from the previous one: `{ transition, duration, direction? }`, same kinds as the table above | -| `scenes[].clips` | Clips as in tracks mode. `start` and `length` are optional and default to spanning the scene | -| `overlays` | Clips composited over the whole video (a logo, a watermark, a persistent title). `start` and `length` are required here | +| `scenes[].clips` | Clips as in tracks mode. The scene is their container: an omitted `length` runs the clip as long as its source but never past the scene, and `length: "end"` spans the scene | +| `overlays` | Clips composited over the whole video (a logo, a watermark, a persistent title). The whole video is their container, so an omitted `length` spans it. `start` defaults to 0 | ### Text style