Skip to content

Commit 4a69b34

Browse files
authored
Merge pull request #6259 from remotion-dev/feature/can-render-media-on-web
2 parents f59d898 + cd969d9 commit 4a69b34

17 files changed

+929
-110
lines changed

.claude/skills/writing-docs/SKILL.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ Documentation lives in `packages/docs/docs` as `.mdx` files.
1414
3. Write the content following guidelines below
1515
4. Run `bun render-cards.ts` in `packages/docs` to generate social preview cards
1616

17+
**One API per page**: Each function or API should have its own dedicated documentation page. Do not combine multiple APIs (e.g., `getEncodableVideoCodecs()` and `getEncodableAudioCodecs()`) on a single page. This is the established pattern throughout the codebase.
18+
19+
**Public API only**: Documentation is for public APIs only. Do not mention, reference, or compare against internal/private APIs or implementation details. Users should only see what is exported and intended for public use.
20+
21+
**Use headings for all fields**: When documenting API options or return values, each property should be its own heading. Use `###` for top-level properties and `####` for nested properties within an options object. Do not use bullet points for individual fields.
22+
1723
## Language guidelines
1824

1925
- **Keep it brief**: Developers don't like to read. Extra words cause information loss.

packages/docs/docs/web-renderer/TableOfContents.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ export const TableOfContents: React.FC = () => {
1414
<strong>{'renderStillOnWeb()'}</strong>
1515
<div>Render a still image in the browser</div>
1616
</TOCItem>
17+
<TOCItem link="/docs/web-renderer/can-render-media-on-web">
18+
<strong>{'canRenderMediaOnWeb()'}</strong>
19+
<div>Check if a render can be performed</div>
20+
</TOCItem>
21+
<TOCItem link="/docs/web-renderer/get-encodable-video-codecs">
22+
<strong>{'getEncodableVideoCodecs()'}</strong>
23+
<div>Get video codecs the browser can encode</div>
24+
</TOCItem>
25+
<TOCItem link="/docs/web-renderer/get-encodable-audio-codecs">
26+
<strong>{'getEncodableAudioCodecs()'}</strong>
27+
<div>Get audio codecs the browser can encode</div>
28+
</TOCItem>
1729
</Grid>
1830
</div>
1931
);
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
---
2+
image: /generated/articles-docs-web-renderer-can-render-media-on-web.png
3+
id: can-render-media-on-web
4+
title: canRenderMediaOnWeb()
5+
crumb: '@remotion/web-renderer'
6+
---
7+
8+
:::warning
9+
Very experimental feature - expect bugs and breaking changes at any time.
10+
[Track progress on GitHub](https://github.com/remotion-dev/remotion/issues/5913) and discuss in the [`#web-renderer`](https://remotion.dev/discord) channel on Discord.
11+
:::
12+
13+
_Part of the `@remotion/web-renderer` package._
14+
15+
Checks if a render can be performed with the given configuration before actually attempting it.
16+
This is useful for providing feedback to users about browser compatibility and configuration issues.
17+
18+
```tsx twoslash title="Example usage"
19+
import {canRenderMediaOnWeb} from '@remotion/web-renderer';
20+
21+
const result = await canRenderMediaOnWeb({
22+
container: 'mp4',
23+
videoCodec: 'h264',
24+
width: 1920,
25+
height: 1080,
26+
});
27+
28+
if (!result.canRender) {
29+
for (const issue of result.issues) {
30+
console.error(issue.message);
31+
}
32+
} else {
33+
console.log('Ready to render!');
34+
console.log('Video codec:', result.resolvedVideoCodec);
35+
console.log('Audio codec:', result.resolvedAudioCodec);
36+
}
37+
```
38+
39+
## Arguments
40+
41+
An object with the following properties:
42+
43+
### `width`
44+
45+
_number - required_
46+
47+
The width of the video in pixels.
48+
49+
### `height`
50+
51+
_number - required_
52+
53+
The height of the video in pixels.
54+
55+
### `container?`
56+
57+
_string_ <TsType type="WebRendererContainer" source="@remotion/web-renderer" />
58+
59+
The container format. Default is `"mp4"`.
60+
61+
### `videoCodec?`
62+
63+
_string_ <TsType type="WebRendererVideoCodec" source="@remotion/web-renderer" />
64+
65+
The video codec to use. Default depends on the container:
66+
67+
- For `mp4` container: `"h264"`
68+
- For `webm` container: `"vp8"`
69+
70+
### `audioCodec?`
71+
72+
_string | null_ <TsType type="WebRendererAudioCodec" source="@remotion/web-renderer" />
73+
74+
The audio codec to use. Default depends on the container:
75+
76+
- For `mp4` container: `"aac"`
77+
- For `webm` container: `"opus"`
78+
79+
### `transparent?`
80+
81+
_boolean_
82+
83+
Whether the video should have an alpha channel. Default is `false`.
84+
85+
Only `vp8` and `vp9` codecs support transparency.
86+
87+
### `muted?`
88+
89+
_boolean_
90+
91+
If `true`, audio codec checks are skipped. Default is `false`.
92+
93+
### `videoBitrate?`
94+
95+
_number | string_ <TsType type="WebRendererQuality" source="@remotion/web-renderer" />
96+
97+
The video bitrate. Can be a number (bits per second) or a quality preset: `"very-low"`, `"low"`, `"medium"`, `"high"`, `"very-high"`.
98+
99+
Default is `"medium"`.
100+
101+
### `audioBitrate?`
102+
103+
_number | string_ <TsType type="WebRendererQuality" source="@remotion/web-renderer" />
104+
105+
The audio bitrate. Can be a number (bits per second) or a quality preset: `"very-low"`, `"low"`, `"medium"`, `"high"`, `"very-high"`.
106+
107+
Default is `"medium"`.
108+
109+
### `outputTarget?`
110+
111+
_string | null_ <TsType type="WebRendererOutputTarget" source="@remotion/web-renderer" />
112+
113+
The output target to use for rendering. Can be:
114+
115+
- `"web-fs"`: Use the File System Access API for streaming writes to disk
116+
- `"arraybuffer"`: Buffer the entire video in memory
117+
- `null`: Auto-detect based on browser support (default)
118+
119+
If `"web-fs"` is requested but not supported by the browser, an `output-target-unsupported` error will be returned.
120+
121+
## Return value
122+
123+
Returns a `Promise` that resolves to an object with the following properties:
124+
125+
### `canRender`
126+
127+
_boolean_
128+
129+
`true` if the render can proceed, `false` if there are blocking issues.
130+
131+
### `issues`
132+
133+
<TsType type="CanRenderIssue[]" source="@remotion/web-renderer" />
134+
135+
An array of issues found during the check. Each issue has:
136+
137+
- `type`: The type of issue (see [Issue types](#issue-types) below)
138+
- `message`: A human-readable description of the issue
139+
- `severity`: Either `"error"` (blocking) or `"warning"` (non-blocking, e.g. fallback was used)
140+
141+
### `resolvedVideoCodec`
142+
143+
_string_ <TsType type="WebRendererVideoCodec" source="@remotion/web-renderer" />
144+
145+
The video codec that will be used for rendering.
146+
147+
### `resolvedAudioCodec`
148+
149+
_string | null_ <TsType type="WebRendererAudioCodec" source="@remotion/web-renderer" />
150+
151+
The audio codec that will be used for rendering. This may differ from the requested codec if a fallback was applied. `null` if `muted` is `true`.
152+
153+
### `resolvedOutputTarget`
154+
155+
<TsType type="WebRendererOutputTarget" source="@remotion/web-renderer" />
156+
157+
The output target that will be used. See [`renderMediaOnWeb()` outputTarget](/docs/web-renderer/render-media-on-web#outputtarget) for details.
158+
159+
## Issue types
160+
161+
The following issue types may be returned:
162+
163+
| Type | Description |
164+
| ------------------------------- | ------------------------------------------------------------- |
165+
| `webcodecs-unavailable` | WebCodecs API is not available in this browser |
166+
| `container-codec-mismatch` | The video codec is not supported for the selected container |
167+
| `invalid-dimensions` | H.264 and H.265 require width and height to be multiples of 2 |
168+
| `video-codec-unsupported` | The browser cannot encode the selected video codec |
169+
| `audio-codec-unsupported` | The browser cannot encode the selected audio codec |
170+
| `transparent-video-unsupported` | Transparency requires VP8 or VP9 codec |
171+
| `webgl-unsupported` | WebGL is required for 3D CSS transforms |
172+
| `output-target-unsupported` | The requested output target is not supported by this browser |
173+
174+
## See also
175+
176+
- [Source code for this function](https://github.com/remotion-dev/remotion/blob/main/packages/web-renderer/src/can-render-media-on-web.ts)
177+
- [`renderMediaOnWeb()`](/docs/web-renderer/render-media-on-web)
178+
- [Client-side rendering limitations](/docs/client-side-rendering/limitations)
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
image: /generated/articles-docs-web-renderer-get-encodable-audio-codecs.png
3+
id: get-encodable-audio-codecs
4+
title: getEncodableAudioCodecs()
5+
crumb: '@remotion/web-renderer'
6+
---
7+
8+
:::warning
9+
Very experimental feature - expect bugs and breaking changes at any time.
10+
[Track progress on GitHub](https://github.com/remotion-dev/remotion/issues/5913) and discuss in the [`#web-renderer`](https://remotion.dev/discord) channel on Discord.
11+
:::
12+
13+
_Part of the `@remotion/web-renderer` package._
14+
15+
Returns the audio codecs that the current browser can encode for a given container format.
16+
17+
Use this function to dynamically show users which audio codecs are available in their browser.
18+
19+
```tsx twoslash title="Example usage"
20+
import {getEncodableAudioCodecs} from '@remotion/web-renderer';
21+
22+
const codecs = await getEncodableAudioCodecs('mp4');
23+
console.log(codecs); // e.g. ['aac', 'opus'] or ['opus'] on Firefox
24+
```
25+
26+
## Arguments
27+
28+
### `container`
29+
30+
_string_ <TsType type="WebRendererContainer" source="@remotion/web-renderer" /> - required
31+
32+
The container format: `"mp4"` or `"webm"`.
33+
34+
### `options?`
35+
36+
_object_ <TsType type="GetEncodableAudioCodecsOptions" source="@remotion/web-renderer" />
37+
38+
Optional configuration object.
39+
40+
#### `audioBitrate?`
41+
42+
_number | string_ <TsType type="WebRendererQuality" source="@remotion/web-renderer" />
43+
44+
A number (bits per second) or quality preset (`"very-low"`, `"low"`, `"medium"`, `"high"`, `"very-high"`).
45+
46+
## Return value
47+
48+
Returns a `Promise<WebRendererAudioCodec[]>` - an array of audio codec identifiers that the browser can encode.
49+
50+
Possible values: `"aac"`, `"opus"`
51+
52+
:::note
53+
AAC encoding is not supported in Firefox. On Firefox, only `["opus"]` will be returned for both containers.
54+
:::
55+
56+
## See also
57+
58+
- [`getEncodableVideoCodecs()`](/docs/web-renderer/get-encodable-video-codecs)
59+
- [`canRenderMediaOnWeb()`](/docs/web-renderer/can-render-media-on-web)
60+
- [`renderMediaOnWeb()`](/docs/web-renderer/render-media-on-web)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
image: /generated/articles-docs-web-renderer-get-encodable-video-codecs.png
3+
id: get-encodable-video-codecs
4+
title: getEncodableVideoCodecs()
5+
crumb: '@remotion/web-renderer'
6+
---
7+
8+
:::warning
9+
Very experimental feature - expect bugs and breaking changes at any time.
10+
[Track progress on GitHub](https://github.com/remotion-dev/remotion/issues/5913) and discuss in the [`#web-renderer`](https://remotion.dev/discord) channel on Discord.
11+
:::
12+
13+
_Part of the `@remotion/web-renderer` package._
14+
15+
Returns the video codecs that the current browser can encode for a given container format.
16+
17+
Use this function to dynamically show users which video codecs are available in their browser.
18+
19+
```tsx twoslash title="Example usage"
20+
import {getEncodableVideoCodecs} from '@remotion/web-renderer';
21+
22+
const codecs = await getEncodableVideoCodecs('mp4');
23+
console.log(codecs); // e.g. ['h264', 'h265', 'av1']
24+
```
25+
26+
## Arguments
27+
28+
### `container`
29+
30+
_string_ <TsType type="WebRendererContainer" source="@remotion/web-renderer" /> - required
31+
32+
The container format: `"mp4"` or `"webm"`.
33+
34+
### `options?`
35+
36+
_object_ <TsType type="GetEncodableVideoCodecsOptions" source="@remotion/web-renderer" />
37+
38+
Optional configuration object.
39+
40+
#### `videoBitrate?`
41+
42+
_number | string_ <TsType type="WebRendererQuality" source="@remotion/web-renderer" />
43+
44+
A number (bits per second) or quality preset (`"very-low"`, `"low"`, `"medium"`, `"high"`, `"very-high"`).
45+
46+
## Return value
47+
48+
Returns a `Promise<WebRendererVideoCodec[]>` - an array of video codec identifiers that the browser can encode.
49+
50+
Possible values: `"h264"`, `"h265"`, `"vp8"`, `"vp9"`, `"av1"`
51+
52+
## See also
53+
54+
- [`getEncodableAudioCodecs()`](/docs/web-renderer/get-encodable-audio-codecs)
55+
- [`canRenderMediaOnWeb()`](/docs/web-renderer/can-render-media-on-web)
56+
- [`renderMediaOnWeb()`](/docs/web-renderer/render-media-on-web)

packages/docs/sidebars.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,9 @@ const sidebars: SidebarsConfig = {
785785
},
786786
'web-renderer/render-media-on-web',
787787
'web-renderer/render-still-on-web',
788+
'web-renderer/can-render-media-on-web',
789+
'web-renderer/get-encodable-video-codecs',
790+
'web-renderer/get-encodable-audio-codecs',
788791
],
789792
},
790793
{

packages/studio/src/components/RenderModal/use-encodable-audio-codecs.ts

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import type {
22
WebRendererAudioCodec,
33
WebRendererContainer,
44
} from '@remotion/web-renderer';
5-
import {getSupportedAudioCodecsForContainer} from '@remotion/web-renderer';
6-
import {getEncodableAudioCodecs} from 'mediabunny';
5+
import {
6+
getEncodableAudioCodecs,
7+
getSupportedAudioCodecsForContainer,
8+
} from '@remotion/web-renderer';
79
import {useEffect, useRef, useState} from 'react';
810

911
type CacheEntry = {
@@ -43,21 +45,25 @@ export const useEncodableAudioCodecs = (
4345
status: 'fetching',
4446
};
4547

46-
getEncodableAudioCodecs(supported, {}).then((encodable) => {
47-
const filtered = supported.filter((c) => encodable.includes(c));
48-
49-
// Update cache
50-
cacheRef.current[container] = {
51-
codecs: filtered,
52-
status: 'done',
53-
};
48+
getEncodableAudioCodecs(container)
49+
.then((encodable) => {
50+
cacheRef.current[container] = {
51+
codecs: encodable,
52+
status: 'done',
53+
};
5454

55-
// Update state - always safe because we're updating for a specific container key
56-
setCodecsByContainer((prev) => ({
57-
...prev,
58-
[container]: filtered,
59-
}));
60-
});
55+
setCodecsByContainer((prev) => ({
56+
...prev,
57+
[container]: encodable,
58+
}));
59+
})
60+
.catch(() => {
61+
// On error, keep using the supported codecs fallback
62+
cacheRef.current[container] = {
63+
codecs: supported,
64+
status: 'done',
65+
};
66+
});
6167
}, [container]);
6268

6369
// Return codecs for current container, or fall back to supported codecs

0 commit comments

Comments
 (0)