Skip to content

Commit 3350cf7

Browse files
authored
Merge pull request #5643 from remotion-dev/stream-audio
2 parents 04a4d47 + a842da2 commit 3350cf7

29 files changed

+1081
-380
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"testwebcodecs": "turbo run testwebcodecs --no-update-notifier",
1414
"testlambda": "turbo run testlambda --concurrency=1 --no-update-notifier",
1515
"ci": "turbo run make test --concurrency=1 --no-update-notifier",
16-
"watch": "turbo watch make --concurrency=2 --experimental-write-cache --ui=tui",
16+
"watch": "turbo watch make --concurrency=2 --experimental-write-cache",
1717
"makewhisperweb": "turbo run make --filter='@remotion/whisper-web'",
1818
"watchwhisperweb": "turbo watch make --experimental-write-cache --filter='@remotion/whisper-web'",
1919
"makewebcodecs": "turbo run make --filter='@remotion/media-parser' --filter='@remotion/webcodecs'",

packages/core/src/CompositionManager.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,17 @@ export type AudioOrVideoAsset = {
136136
audioStreamIndex: number;
137137
};
138138

139+
export type InlineAudioAsset = {
140+
type: 'inline-audio';
141+
id: string;
142+
audio: number[];
143+
frame: number;
144+
sampleRate: number;
145+
numberOfChannels: number;
146+
timestamp: number;
147+
duration: number;
148+
};
149+
139150
type DiscriminatedArtifact =
140151
| {
141152
contentType: 'binary';
@@ -157,7 +168,7 @@ export type ArtifactAsset = {
157168
downloadBehavior: DownloadBehavior | null;
158169
} & DiscriminatedArtifact;
159170

160-
export type TRenderAsset = AudioOrVideoAsset | ArtifactAsset;
171+
export type TRenderAsset = AudioOrVideoAsset | ArtifactAsset | InlineAudioAsset;
161172

162173
export const compositionsRef = React.createRef<{
163174
getCompositions: () => AnyComposition[];

packages/core/src/no-react.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export type {
22
ArtifactAsset,
33
AudioOrVideoAsset,
4+
InlineAudioAsset,
45
TRenderAsset,
56
} from './CompositionManager';
67
export {DownloadBehavior} from './download-behavior';

packages/docs/docs/new-video/index.mdx

Lines changed: 23 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ crumb: 'API'
55
---
66

77
:::warning
8-
**Very experimental**: This component is in a very early stage and **slower** than OffthreadVideo.
8+
**Very experimental**: This component is in a very early stage and does not support some basic features such as volume or playback rate or CSS styling.
99
The current focus is on correctness, not on performance.
1010

1111
We recommend that you use [`<OffthreadVideo/>`](/docs/offthreadvideo) for now.
@@ -49,7 +49,7 @@ export const MyComposition = () => {
4949

5050
The URL of the video to be rendered. Can be a remote URL or a local file referenced with [`staticFile()`](/docs/staticfile).
5151

52-
### `trimBefore?`<AvailableFrom v="4.0.319"/>
52+
### `trimBefore?`
5353

5454
Will remove a portion of the video at the beginning (left side).
5555

@@ -76,107 +76,29 @@ export const MyComposition = () => {
7676
};
7777
```
7878

79-
### `trimAfter?`<AvailableFrom v="4.0.319"/>
79+
### `trimAfter?`
8080

8181
Removes a portion of the video at the end (right side). See [`trimBefore`](/docs/video#trimbefore) for an explanation.
8282

8383
### `volume?`
8484

85-
Allows you to control the volume for the whole track or change it on a per-frame basis. Refer to the [using audio](/docs/audio/volume) guide to learn how to use it.
85+
**Currently not supported!**
8686

87-
```tsx twoslash title="Example using static volume"
88-
import {AbsoluteFill, staticFile} from 'remotion';
89-
import {experimental_NewVideo as NewVideo} from '@remotion/video';
90-
91-
// ---cut---
92-
export const MyComposition = () => {
93-
return (
94-
<AbsoluteFill>
95-
<NewVideo volume={0.5} src={staticFile('video.webm')} />
96-
</AbsoluteFill>
97-
);
98-
};
99-
```
87+
### `loopVolumeCurveBehavior?`
10088

101-
```tsx twoslash title="Example of a ramp up over 100 frames"
102-
import {AbsoluteFill, interpolate, staticFile} from 'remotion';
103-
import {experimental_NewVideo as NewVideo} from '@remotion/video';
89+
**Currently not supported!**
10490

105-
// ---cut---
106-
export const MyComposition = () => {
107-
return (
108-
<AbsoluteFill>
109-
<NewVideo volume={(f) => interpolate(f, [0, 100], [0, 1], {extrapolateLeft: 'clamp'})} src={staticFile('video.webm')} />
110-
</AbsoluteFill>
111-
);
112-
};
113-
```
114-
115-
By default, volumes between 0 and 1 are supported, where in iOS Safari, the volume is always 1.
116-
See [Volume Limitations](/docs/audio/volume#limitations) for more information.
117-
118-
### `loopVolumeCurveBehavior?`<AvailableFrom v="4.0.142" />
119-
120-
Controls the `frame` which is returned when using the [`volume`](#volume) callback function and wrapping `OffthreadVideo` in a [`<Loop>`](/docs/loop).
121-
122-
Can be either `"repeat"` (default, start from 0 on each iteration) or `"extend"` (keep increasing frames).
123-
124-
### `style?`
125-
126-
You can pass any style you can pass to a native HTML element. Keep in mind that during rendering, `<NewVideo>` renders a `canvas` tag, but a `<video>` tag is used during preview.
127-
128-
```tsx twoslash
129-
import {AbsoluteFill, Img, staticFile} from 'remotion';
130-
131-
// ---cut---
132-
export const MyComposition = () => {
133-
return (
134-
<AbsoluteFill>
135-
<Img src={staticFile('video.webm')} style={{height: 720, width: 1280}} />
136-
</AbsoluteFill>
137-
);
138-
};
139-
```
140-
141-
### `name?`<AvailableFrom v="4.0.71"/>
91+
### `name?`
14292

14393
A name and that will be shown as the label of the sequence in the timeline of the Remotion Studio. This property is purely for helping you keep track of items in the timeline.
14494

145-
### `toneFrequency?`<AvailableFrom v="4.0.47"/>
146-
147-
Adjust the pitch of the audio - will only be applied during rendering.
148-
149-
Accepts a number between `0.01` and `2`, where `1` represents the original pitch. Values less than `1` will decrease the pitch, while values greater than `1` will increase it.
150-
151-
A `toneFrequency` of 0.5 would lower the pitch by half, and a `toneFrequency` of `1.5` would increase the pitch by 50%.
152-
15395
### `onError?`
15496

155-
Handle an error playing the video. From v3.3.89, if you pass an `onError` callback, then no exception will be thrown. Previously, the error could not be caught.
156-
157-
### `playbackRate?`<AvailableFrom v="2.2.0" />
158-
159-
Controls the speed of the video. `1` is the default and means regular speed, `0.5` slows down the video so it's twice as long and `2` speeds up the video so it's twice as fast.
97+
**Currently not supported!**
16098

161-
While Remotion doesn't limit the range of possible playback speeds, in development mode the [`HTMLMediaElement.playbackRate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/playbackRate) API is used which throws errors on extreme values. At the time of writing, Google Chrome throws an exception if the playback rate is below `0.0625` or above `16`.
162-
163-
```tsx twoslash title="Example of a video playing twice as fast"
164-
import {AbsoluteFill, staticFile} from 'remotion';
165-
import {experimental_NewVideo as NewVideo} from '@remotion/video';
166-
167-
// ---cut---
168-
export const MyComposition = () => {
169-
return (
170-
<AbsoluteFill>
171-
<NewVideo playbackRate={2} src={staticFile('video.webm')} />
172-
</AbsoluteFill>
173-
);
174-
};
175-
```
99+
### `playbackRate?`
176100

177-
:::note
178-
Playing a video in reverse is not supported.
179-
:::
101+
**Currently not supported!**
180102

181103
### `muted?`
182104

@@ -195,87 +117,46 @@ export const MyComposition = () => {
195117
};
196118
```
197119

198-
### `acceptableTimeShiftInSeconds?`<AvailableFrom v="3.2.42" />
199-
200-
In the [Studio](/docs/terminology/studio) or in the [Remotion Player](/docs/player), Remotion will seek the video if it gets too much out of sync with Remotion's internal time - be it due to the video loading or the page being too slow to keep up in real-time. By default, a seek is triggered if `0.45` seconds of time shift is encountered. Using this prop, you can customize the threshold.
201-
202-
### `toneFrequency?`<AvailableFrom v="4.0.47"/>
120+
### `acceptableTimeShiftInSeconds?`
203121

204-
Adjust the pitch of the audio - will only be applied during rendering.
205-
206-
Accepts a number between `0.01` and `2`, where `1` represents the original pitch. Values less than `1` will decrease the pitch, while values greater than `1` will increase it.
207-
208-
A `toneFrequency` of 0.5 would lower the pitch by half, and a `toneFrequency` of `1.5` would increase the pitch by 50%.
209-
210-
### `audioStreamIndex?`<AvailableFrom v="4.0.340" />
211-
212-
Select the audio stream to use. The default is `0`.
213-
214-
```tsx twoslash
215-
import {AbsoluteFill, Audio} from 'remotion';
216-
217-
// ---cut---
218-
export const MyComposition = () => {
219-
return (
220-
<AbsoluteFill>
221-
<Audio audioStreamIndex={1} src={'https://parser.media/multichannel-audio.mov'} />
222-
</AbsoluteFill>
223-
);
224-
};
225-
```
226-
227-
:::note
228-
This prop only works during rendering.
229-
Browsers do not support selecting the audio track without enabling experimental flags.
230-
231-
Not to be confused with audio channels. A video can have multiple audio streams, each stream can have multiple channels.
232-
Multiple audio streams can be used for example for adding multiple languages to a video.
233-
234-
Audio streams are zero-indexed.
235-
:::
122+
**Pending removal**: Future iterations will also have client-side playback and not support this prop which was designed for the `<video>` tag.
236123

237-
### `pauseWhenBuffering?`<AvailableFrom v="4.0.111"/>
124+
### `pauseWhenBuffering?`
238125

239126
If set to `true` and the video is loading, the Player will enter into the [native buffering state](/docs/player/buffer-state). The default is `false`, but will become `true` in Remotion 5.0.
240127

241-
### `showInTimeline?`<AvailableFrom v="4.0.122"/>
128+
### `showInTimeline?`
242129

243130
If set to `false`, no layer will be shown in the timeline of the Remotion Studio. The default is `true`.
244131

245-
### `delayRenderTimeoutInMilliseconds?`<AvailableFrom v="4.0.150" />
132+
### `delayRenderTimeoutInMilliseconds?`
246133

247134
Customize the [timeout](/docs/delay-render#modifying-the-timeout) of the [`delayRender()`](/docs/delay-render) call that this component makes.
248135

249-
### `delayRenderRetries?`<AvailableFrom v="4.0.178" />
136+
### `delayRenderRetries?`
250137

251138
Customize the [number of retries](/docs/delay-render#retrying) of the [`delayRender()`](/docs/delay-render) call that this component makes.
252139

253-
### `onAutoPlayError?`<AvailableFrom v="4.0.187" />
140+
### `onAutoPlayError?`
254141

255-
A callback function that gets called when the video fails to play due to autoplay restrictions.
256-
If you don't pass a callback, the video will be muted and be retried once.
257-
This prop is useful if you want to handle the error yourself, e.g. for pausing the Player.
258-
Read more here about [autoplay restrictions](/docs/player/autoplay).
142+
**Pending removal**: Future iterations will also have client-side playback and not support this prop which was designed for the `<video>` tag.
259143

260-
### `onVideoFrame?`<AvailableFrom v="4.0.190" />
144+
### `onVideoFrame?`
261145

262146
A callback function that gets called when a frame is extracted from the video.
263147
Useful for [video manipulation](/docs/video-manipulation).
264-
The callback is called with a [`CanvasImageSource`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasImageSource) object.
265-
During preview, this is a `HTMLVideoElement` object, during rendering, it is an `HTMLImageElement`.
148+
The callback is called with a [`CanvasImageSource`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasImageSource) object, more specifically, either an `ImageBitmap` or a `VideoFrame`.
266149

267150
### `crossOrigin?`<AvailableFrom v="4.0.190" />
268151

269-
Corresponds to the [`crossOrigin`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-crossorigin) attribute of the `<video>` element.
270-
One of `"anonymous"`, `"use-credentials"` or `undefined`.
271-
Default: `"anonymous"` if `onVideoFrame` is specified, `undefined`, otherwise.
152+
**Pending removal**: Future iterations will also have client-side playback and not support this prop which was designed for the `<video>` tag.
272153

273154
### `useWebAudioApi?`<AvailableFrom v="4.0.306" />
274155

275-
Enable the [Web Audio API](/docs/audio/volume#limitations) for the video tag.
156+
**Pending removal**: Future iterations will also have client-side playback and not support this prop which was designed for the `<video>` tag.
276157

277158
## See also
278159

279-
- [Source code for this component](https://github.com/remotion-dev/remotion/blob/main/packages/video/src/video.tsx)
160+
- [Source code for this component](https://github.com/remotion-dev/remotion/blob/main/packages/video/src/new-video.tsx)
280161
- [`<Video />`](/docs/video)
281162
- [`<OffthreadVideo>`](/docs/offthreadvideo)

packages/renderer/src/assets/download-map.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import {OffthreadVideoServerEmitter} from '../offthread-video-server';
66
import type {FrameAndAssets} from '../render-frames';
77
import {tmpDir} from '../tmp-dir';
88
import type {RenderMediaOnDownload} from './download-and-map-assets-to-file';
9+
import {
10+
makeInlineAudioMixing,
11+
type InlineAudioMixing,
12+
} from './inline-audio-mixing';
913

1014
export type AudioChannelsAndDurationResultCache = {
1115
channels: number;
@@ -44,6 +48,7 @@ export type DownloadMap = {
4448
preventCleanup: () => void;
4549
allowCleanup: () => void;
4650
isPreventedFromCleanup: () => boolean;
51+
inlineAudioMixing: InlineAudioMixing;
4752
};
4853

4954
export type RenderAssetInfo = {
@@ -57,7 +62,7 @@ export type RenderAssetInfo = {
5762
forSeamlessAacConcatenation: boolean;
5863
};
5964

60-
const makeAndReturn = (dir: string, name: string) => {
65+
export const makeAndReturn = (dir: string, name: string) => {
6166
const p = path.join(dir, name);
6267
mkdirSync(p);
6368
return p;
@@ -93,6 +98,7 @@ export const makeDownloadMap = (): DownloadMap => {
9398
isPreventedFromCleanup: () => {
9499
return prevented;
95100
},
101+
inlineAudioMixing: makeInlineAudioMixing(dir),
96102
};
97103
};
98104

@@ -104,6 +110,9 @@ export const cleanDownloadMap = (downloadMap: DownloadMap) => {
104110
deleteDirectory(downloadMap.downloadDir);
105111
deleteDirectory(downloadMap.complexFilter);
106112
deleteDirectory(downloadMap.compositingDir);
113+
114+
downloadMap.inlineAudioMixing.cleanup();
115+
107116
// Assets dir must be last since the others are contained
108117
deleteDirectory(downloadMap.assetDir);
109118
};

0 commit comments

Comments
 (0)