Skip to main content

Video

React Native Skia provides a way to load video frames as images, enabling rich multimedia experiences within your applications. A video frame can be used anywhere a Skia image is accepted: Image, ImageShader, and Atlas. Videos are also supported on Web.

Requirements

  • Reanimated version 3 or higher.
  • Android: API level 26 or higher.

Example

Here is an example of how to use the video support in React Native Skia. This example demonstrates how to load and display video frames within a canvas, applying a color matrix for visual effects. Tapping the screen will pause and play the video.

The video can be a remote (http://...) or local URL (file://), as well as a video from the bundle.

tsx
import React from "react";
import {
Canvas,
ColorMatrix,
Fill,
ImageShader,
useVideo
} from "@shopify/react-native-skia";
import { Pressable, useWindowDimensions } from "react-native";
import { useSharedValue } from "react-native-reanimated";
 
export const VideoExample = () => {
const paused = useSharedValue(false);
const { width, height } = useWindowDimensions();
const { currentFrame } = useVideo(
"https://bit.ly/skia-video",
{
paused,
}
);
return (
<Pressable
style={{ flex: 1 }}
onPress={() => (paused.value = !paused.value)}
>
<Canvas style={{ flex: 1 }}>
<Fill>
<ImageShader
image={currentFrame}
x={0}
y={0}
width={width}
height={height}
fit="cover"
/>
<ColorMatrix
matrix={[
0.95, 0, 0, 0, 0.05, 0.65, 0, 0, 0, 0.15, 0.15, 0, 0, 0, 0.5, 0,
0, 0, 1, 0,
]}
/>
</Fill>
</Canvas>
</Pressable>
);
};
tsx
import React from "react";
import {
Canvas,
ColorMatrix,
Fill,
ImageShader,
useVideo
} from "@shopify/react-native-skia";
import { Pressable, useWindowDimensions } from "react-native";
import { useSharedValue } from "react-native-reanimated";
 
export const VideoExample = () => {
const paused = useSharedValue(false);
const { width, height } = useWindowDimensions();
const { currentFrame } = useVideo(
"https://bit.ly/skia-video",
{
paused,
}
);
return (
<Pressable
style={{ flex: 1 }}
onPress={() => (paused.value = !paused.value)}
>
<Canvas style={{ flex: 1 }}>
<Fill>
<ImageShader
image={currentFrame}
x={0}
y={0}
width={width}
height={height}
fit="cover"
/>
<ColorMatrix
matrix={[
0.95, 0, 0, 0, 0.05, 0.65, 0, 0, 0, 0.15, 0.15, 0, 0, 0, 0.5, 0,
0, 0, 1, 0,
]}
/>
</Fill>
</Canvas>
</Pressable>
);
};

Returned Values

The useVideo hook returns currentFrame, which contains the current video frame, as well as currentTime, rotation, and size.

Playback Options

The following table describes the playback options available for the useVideo hook:

OptionDescription
seekAllows seeking to a specific point in the video in milliseconds. Default is null.
pausedIndicates whether the video is paused.
loopingIndicates whether the video should loop.
volumeA value from 0 to 1 representing the volume level (0 is muted, 1 is the maximum volume).

In the example below, every time we tap on the video, we set the video seek at 2 seconds.

tsx
import React from "react";
import {
Canvas,
Fill,
Image,
useVideo
} from "@shopify/react-native-skia";
import { Pressable, useWindowDimensions } from "react-native";
import { useSharedValue } from "react-native-reanimated";
 
export const VideoExample = () => {
const seek = useSharedValue<null | number>(null);
// Set this value to true to pause the video
const paused = useSharedValue(false);
const { width, height } = useWindowDimensions();
const {currentFrame, currentTime} = useVideo(
"https://bit.ly/skia-video",
{
seek,
paused,
looping: true
}
);
return (
<Pressable
style={{ flex: 1 }}
onPress={() => (seek.value = 2000)}
>
<Canvas style={{ flex: 1 }}>
<Image
image={currentFrame}
x={0}
y={0}
width={width}
height={height}
fit="cover"
/>
</Canvas>
</Pressable>
);
};
tsx
import React from "react";
import {
Canvas,
Fill,
Image,
useVideo
} from "@shopify/react-native-skia";
import { Pressable, useWindowDimensions } from "react-native";
import { useSharedValue } from "react-native-reanimated";
 
export const VideoExample = () => {
const seek = useSharedValue<null | number>(null);
// Set this value to true to pause the video
const paused = useSharedValue(false);
const { width, height } = useWindowDimensions();
const {currentFrame, currentTime} = useVideo(
"https://bit.ly/skia-video",
{
seek,
paused,
looping: true
}
);
return (
<Pressable
style={{ flex: 1 }}
onPress={() => (seek.value = 2000)}
>
<Canvas style={{ flex: 1 }}>
<Image
image={currentFrame}
x={0}
y={0}
width={width}
height={height}
fit="cover"
/>
</Canvas>
</Pressable>
);
};

Rotated Video

The rotation property can be 0, 90, 180, or 270. We provide a fitbox function that can help with rotating and scaling the video.

tsx
import React from "react";
import {
Canvas,
Image,
useVideo,
fitbox,
rect
} from "@shopify/react-native-skia";
import { Pressable, useWindowDimensions } from "react-native";
import { useSharedValue } from "react-native-reanimated";
 
export const VideoExample = () => {
const paused = useSharedValue(false);
const { width, height } = useWindowDimensions();
const { currentFrame, rotation, size } = useVideo("https://bit.ly/skia-video");
const src = rect(0, 0, size.width, size.height);
const dst = rect(0, 0, width, height)
const transform = fitbox("cover", src, dst, rotation);
return (
<Canvas style={{ flex: 1 }}>
<Image
image={currentFrame}
x={0}
y={0}
width={width}
height={height}
fit="none"
transform={transform}
/>
</Canvas>
);
};
tsx
import React from "react";
import {
Canvas,
Image,
useVideo,
fitbox,
rect
} from "@shopify/react-native-skia";
import { Pressable, useWindowDimensions } from "react-native";
import { useSharedValue } from "react-native-reanimated";
 
export const VideoExample = () => {
const paused = useSharedValue(false);
const { width, height } = useWindowDimensions();
const { currentFrame, rotation, size } = useVideo("https://bit.ly/skia-video");
const src = rect(0, 0, size.width, size.height);
const dst = rect(0, 0, width, height)
const transform = fitbox("cover", src, dst, rotation);
return (
<Canvas style={{ flex: 1 }}>
<Image
image={currentFrame}
x={0}
y={0}
width={width}
height={height}
fit="none"
transform={transform}
/>
</Canvas>
);
};

Using Assets

Below is an example where we use expo-asset to load a video file from the bundle.

tsx
import { useVideo } from "@shopify/react-native-skia";
import { useAssets } from "expo-asset";
 
// Example usage:
// const video = useVideoFromAsset(require("./BigBuckBunny.mp4"));
export const useVideoFromAsset = (
mod: number,
options?: Parameters<typeof useVideo>[1]
) => {
const [assets, error] = useAssets([mod]);
if (error) {
throw error;
}
return useVideo(assets ? assets[0].localUri : null, options);
};
tsx
import { useVideo } from "@shopify/react-native-skia";
import { useAssets } from "expo-asset";
 
// Example usage:
// const video = useVideoFromAsset(require("./BigBuckBunny.mp4"));
export const useVideoFromAsset = (
mod: number,
options?: Parameters<typeof useVideo>[1]
) => {
const [assets, error] = useAssets([mod]);
if (error) {
throw error;
}
return useVideo(assets ? assets[0].localUri : null, options);
};

Video Encoding

To encode videos from Skia images, you can use ffmpeg or also look into react-native-skia-video.