Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/core/src/use-media-in-timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const useBasicMediaInTimeline = ({
const [initialVolume] = useState<VolumeProp | undefined>(() => volume);

const mediaDuration = calculateMediaDuration({
mediaDurationInFrames: videoConfig.durationInFrames,
mediaDurationInFrames: videoConfig.durationInFrames + (trimBefore ?? 0),
playbackRate,
trimBefore,
trimAfter,
Expand Down
21 changes: 21 additions & 0 deletions packages/docs/docs/media/audio.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,27 @@ export const MyComposition = () => {
};
```

### `credentials?`<AvailableFrom v="4.0.437" />

Controls the [`credentials`](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) option of the `fetch()` requests made to retrieve the audio data.

Accepts `"omit"`, `"same-origin"` (default behavior of `fetch()`) or `"include"`.
Set to `"include"` if you need to send cookies or authentication headers to a cross-origin audio URL.

```tsx twoslash
import {AbsoluteFill} from 'remotion';
import {Audio} from '@remotion/media';

// ---cut---
export const MyComposition = () => {
return (
<AbsoluteFill>
<Audio credentials="include" src="https://example.com/protected-audio.mp3" />
</AbsoluteFill>
);
};
```

### `toneFrequency?`

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.
Expand Down
21 changes: 21 additions & 0 deletions packages/docs/docs/media/video.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,27 @@ export const MyComposition = () => {
};
```

### `credentials?`<AvailableFrom v="4.0.437" />

Controls the [`credentials`](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) option of the `fetch()` requests made to retrieve the video data.

Accepts `"omit"`, `"same-origin"` (default behavior of `fetch()`) or `"include"`.
Set to `"include"` if you need to send cookies or authentication headers to a cross-origin video URL.

```tsx twoslash
import {AbsoluteFill} from 'remotion';
import {Video} from '@remotion/media';

// ---cut---
export const MyComposition = () => {
return (
<AbsoluteFill>
<Video credentials="include" src="https://example.com/protected-video.mp4" />
</AbsoluteFill>
);
};
```

### `toneFrequency?`

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.
Expand Down
4 changes: 3 additions & 1 deletion packages/media/src/audio-extraction/extract-audio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type ExtractAudioParams = {
trimAfter: number | undefined;
fps: number;
maxCacheSize: number;
credentials: RequestCredentials | undefined;
};

const extractAudioInternal = async ({
Expand All @@ -45,6 +46,7 @@ const extractAudioInternal = async ({
trimAfter,
fps,
maxCacheSize,
credentials,
}: ExtractAudioParams): Promise<
| {
data: PcmS16AudioData | null;
Expand All @@ -55,7 +57,7 @@ const extractAudioInternal = async ({
| 'network-error'
> => {
const {getAudio, actualMatroskaTimestamps, isMatroska, getDuration} =
await getSink(src, logLevel);
await getSink(src, logLevel, credentials);

let mediaDurationInSeconds: number | null = null;
if (loop) {
Expand Down
7 changes: 7 additions & 0 deletions packages/media/src/audio/audio-for-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type NewAudioForPreviewProps = {
readonly fallbackHtml5AudioProps: FallbackHtml5AudioProps | undefined;
readonly debugAudioScheduling: boolean;
readonly onError: MediaOnError | undefined;
readonly credentials: RequestCredentials | undefined;
};

type AudioForPreviewAssertedShowingProps = NewAudioForPreviewProps & {
Expand Down Expand Up @@ -79,6 +80,7 @@ const AudioForPreviewAssertedShowing: React.FC<
fallbackHtml5AudioProps,
debugAudioScheduling,
onError,
credentials,
controls,
}) => {
const videoConfig = useUnsafeVideoConfig();
Expand Down Expand Up @@ -237,6 +239,7 @@ const AudioForPreviewAssertedShowing: React.FC<
onVideoFrameCallback: null,
playing: initialPlaying.current,
sequenceOffset: initialSequenceOffset.current,
credentials,
});

mediaPlayerRef.current = player;
Expand Down Expand Up @@ -373,6 +376,7 @@ const AudioForPreviewAssertedShowing: React.FC<
buffer,
onError,
videoConfig.durationInFrames,
credentials,
]);

if (shouldFallbackToNativeAudio && !disallowFallbackToHtml5Audio) {
Expand Down Expand Up @@ -427,6 +431,7 @@ type InnerAudioProps = {
readonly fallbackHtml5AudioProps?: FallbackHtml5AudioProps;
readonly debugAudioScheduling?: boolean;
readonly onError?: MediaOnError;
readonly credentials?: RequestCredentials;
};

export const AudioForPreview: React.FC<
Expand All @@ -452,6 +457,7 @@ export const AudioForPreview: React.FC<
fallbackHtml5AudioProps,
debugAudioScheduling,
onError,
credentials,
controls,
}) => {
const preloadedSrc = usePreload(src);
Expand Down Expand Up @@ -508,6 +514,7 @@ export const AudioForPreview: React.FC<
toneFrequency={toneFrequency}
debugAudioScheduling={debugAudioScheduling ?? false}
onError={onError}
credentials={credentials}
fallbackHtml5AudioProps={fallbackHtml5AudioProps}
controls={controls}
/>
Expand Down
3 changes: 3 additions & 0 deletions packages/media/src/audio/audio-for-rendering.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const AudioForRendering: React.FC<AudioProps> = ({
trimAfter,
trimBefore,
onError,
credentials,
}) => {
const defaultLogLevel = Internals.useLogLevel();
const logLevel = overriddenLogLevel ?? defaultLogLevel;
Expand Down Expand Up @@ -129,6 +130,7 @@ export const AudioForRendering: React.FC<AudioProps> = ({
trimBefore,
fps,
maxCacheSize,
credentials,
})
.then((result) => {
const handleError = (
Expand Down Expand Up @@ -266,6 +268,7 @@ export const AudioForRendering: React.FC<AudioProps> = ({
maxCacheSize,
audioEnabled,
onError,
credentials,
]);

if (replaceWithHtml5Audio) {
Expand Down
1 change: 1 addition & 0 deletions packages/media/src/audio/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ export type AudioProps = {
delayRenderTimeoutInMilliseconds?: number;
debugAudioScheduling?: boolean;
onError?: MediaOnError;
credentials?: RequestCredentials;
};
4 changes: 4 additions & 0 deletions packages/media/src/extract-frame-and-audio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const extractFrameAndAudio = async ({
trimBefore,
fps,
maxCacheSize,
credentials,
}: {
src: string;
timeInSeconds: number;
Expand All @@ -33,6 +34,7 @@ export const extractFrameAndAudio = async ({
trimBefore: number | undefined;
fps: number;
maxCacheSize: number;
credentials: RequestCredentials | undefined;
}): Promise<ExtractFrameViaBroadcastChannelResult> => {
try {
const [video, audio] = await Promise.all([
Expand All @@ -47,6 +49,7 @@ export const extractFrameAndAudio = async ({
trimBefore,
fps,
maxCacheSize,
credentials,
})
: null,
includeAudio
Expand All @@ -62,6 +65,7 @@ export const extractFrameAndAudio = async ({
fps,
trimBefore,
maxCacheSize,
credentials,
})
: null,
]);
Expand Down
13 changes: 9 additions & 4 deletions packages/media/src/get-sink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ import {getSinks} from './video-extraction/get-frames-since-keyframe';

export const sinkPromises: Record<string, Promise<GetSink>> = {};

export const getSink = (src: string, logLevel: LogLevel) => {
let promise = sinkPromises[src];
export const getSink = (
src: string,
logLevel: LogLevel,
credentials: RequestCredentials | undefined,
) => {
const cacheKey = credentials ? `${src}::${credentials}` : src;
let promise = sinkPromises[cacheKey];
if (!promise) {
Internals.Log.verbose(
{
Expand All @@ -15,8 +20,8 @@ export const getSink = (src: string, logLevel: LogLevel) => {
},
`Sink for ${src} was not found, creating new sink`,
);
promise = getSinks(src);
sinkPromises[src] = promise;
promise = getSinks(src, credentials);
sinkPromises[cacheKey] = promise;
}

return promise;
Expand Down
12 changes: 10 additions & 2 deletions packages/media/src/media-player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export class MediaPlayer {
onVideoFrameCallback,
playing,
sequenceOffset,
credentials,
}: {
canvas: HTMLCanvasElement | OffscreenCanvas | null;
src: string;
Expand All @@ -111,6 +112,7 @@ export class MediaPlayer {
onVideoFrameCallback: null | ((frame: CanvasImageSource) => void);
playing: boolean;
sequenceOffset: number;
credentials: RequestCredentials | undefined;
}) {
this.canvas = canvas ?? null;
this.src = src;
Expand All @@ -133,9 +135,15 @@ export class MediaPlayer {
this.onVideoFrameCallback = onVideoFrameCallback;
this.playing = playing;
this.sequenceOffset = sequenceOffset;

this.input = new Input({
source: new UrlSource(this.src),
source: new UrlSource(
this.src,
credentials
? {
requestInit: {credentials},
}
: undefined,
),
formats: ALL_FORMATS,
});

Expand Down
1 change: 1 addition & 0 deletions packages/media/src/test/audio-encoding.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
];

test('Audio samples from MP3 should produce identical s16 output on Chrome and Firefox', async (t) => {
// TODO: Why is it different on webkit?

Check warning on line 20 in packages/media/src/test/audio-encoding.test.ts

View workflow job for this annotation

GitHub Actions / Linting + Formatting

Unexpected 'todo' comment: 'TODO: Why is it different on webkit?'

Check warning on line 20 in packages/media/src/test/audio-encoding.test.ts

View workflow job for this annotation

GitHub Actions / Linting + Formatting

Unexpected 'todo' comment: 'TODO: Why is it different on webkit?'
if (t.task.file.projectName === 'webkit') {
t.skip();
return;
Expand All @@ -35,6 +35,7 @@
maxCacheSize: getMaxVideoCacheSize('info'),
durationInSeconds: 1 / 30,
audioStreamIndex: 0,
credentials: undefined,
});

if (a === 'cannot-decode') {
Expand Down
5 changes: 5 additions & 0 deletions packages/media/src/test/browser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ test('Should be able to extract a frame', async () => {
trimBefore: undefined,
fps: 30,
maxCacheSize: getMaxVideoCacheSize('info'),
credentials: undefined,
});

if (result.type === 'cannot-decode') {
Expand Down Expand Up @@ -71,6 +72,7 @@ test('Should be able to extract the last frame', async () => {
trimBefore: undefined,
fps: 30,
maxCacheSize: getMaxVideoCacheSize('info'),
credentials: undefined,
});

if (result.type === 'cannot-decode') {
Expand Down Expand Up @@ -121,6 +123,7 @@ test('Should manage the cache', async (t) => {
trimBefore: undefined,
fps: 30,
maxCacheSize: getMaxVideoCacheSize('info'),
credentials: undefined,
});
}

Expand Down Expand Up @@ -150,6 +153,7 @@ test('Should be apply volume correctly', async () => {
trimBefore: undefined,
fps: 30,
maxCacheSize: getMaxVideoCacheSize('info'),
credentials: undefined,
});

if (result.type === 'cannot-decode') {
Expand Down Expand Up @@ -204,6 +208,7 @@ test('Should be able to loop', async () => {
trimBefore: undefined,
fps: 30,
maxCacheSize: getMaxVideoCacheSize('info'),
credentials: undefined,
});

if (result.type === 'cannot-decode') {
Expand Down
3 changes: 3 additions & 0 deletions packages/media/src/test/editlist-offset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ test('Audio extraction should be correct if there is edit list offset', async ()
trimBefore: undefined,
trimAfter: undefined,
maxCacheSize: getMaxVideoCacheSize('info'),
credentials: undefined,
});
assert(audio1 !== 'cannot-decode');
assert(audio1 !== 'unknown-container-format');
Expand All @@ -36,6 +37,7 @@ test('Audio extraction should be correct if there is edit list offset', async ()
trimBefore: undefined,
trimAfter: undefined,
maxCacheSize: getMaxVideoCacheSize('info'),
credentials: undefined,
});
assert(audio2 !== 'cannot-decode');
assert(audio2 !== 'unknown-container-format');
Expand All @@ -59,6 +61,7 @@ test('Audio extraction should be correct if there is edit list offset', async ()
trimBefore: undefined,
trimAfter: undefined,
maxCacheSize: getMaxVideoCacheSize('info'),
credentials: undefined,
});
assert(audio3 !== 'cannot-decode');
assert(audio3 !== 'unknown-container-format');
Expand Down
1 change: 1 addition & 0 deletions packages/media/src/test/extract-accuracy-uneven.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ test('Extract accuracy over 100 frames with playback rate 1.75', async () => {
trimBefore: undefined,
trimAfter: undefined,
maxCacheSize: getMaxVideoCacheSize('info'),
credentials: undefined,
});
if (audio === 'cannot-decode') {
throw new Error(`Cannot decode at frame ${i}`);
Expand Down
1 change: 1 addition & 0 deletions packages/media/src/test/extract-accuracy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ test('Extract accuracy over 100 frames with playback rate 2', async () => {
trimBefore: undefined,
trimAfter: undefined,
maxCacheSize: getMaxVideoCacheSize('info'),
credentials: undefined,
});
if (audio === 'cannot-decode') {
throw new Error(`Cannot decode at frame ${i}`);
Expand Down
1 change: 1 addition & 0 deletions packages/media/src/test/looping.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ test(
playbackRate,
fps,
maxCacheSize: getMaxVideoCacheSize('info'),
credentials: undefined,
});
expect(result.type).toBe('success');
assert(result.type === 'success');
Expand Down
1 change: 1 addition & 0 deletions packages/media/src/test/media-player.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ test('dispose should immediately unblock playback delays', async () => {
onVideoFrameCallback: null,
playing: false,
sequenceOffset: 0,
credentials: undefined,
});

await player.initialize(0, false);
Expand Down
1 change: 1 addition & 0 deletions packages/media/src/test/trim-change-seek.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ test('setTrimBefore and setTrimAfter should update frame when paused', async ()
onVideoFrameCallback: null,
playing: false,
sequenceOffset: 0,
credentials: undefined,
});

await player.initialize(0, false);
Expand Down
1 change: 1 addition & 0 deletions packages/media/src/test/video-after-end.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ test('Should render last frame for timestamps after video end', async () => {
playbackRate: 1,
fps: 24,
maxCacheSize: getMaxVideoCacheSize('info'),
credentials: undefined,
});

assert(result.type === 'success');
Expand Down
1 change: 1 addition & 0 deletions packages/media/src/test/video-non-zero-start.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ test('Should render first frame for videos starting after timestamp 0', async ()
playbackRate: 1,
fps: 30,
maxCacheSize: getMaxVideoCacheSize('info'),
credentials: undefined,
});

// Should successfully extract (no error thrown)
Expand Down
Loading
Loading