@azzapp/react-native-skia-video
Version:
video support for react-native-skia
95 lines (91 loc) • 2.74 kB
JavaScript
;
import { runOnJS } from 'react-native-reanimated';
import { Platform } from 'react-native';
import { Skia, BlendMode } from '@shopify/react-native-skia';
import RNSkiaVideoModule from './RNSkiaVideoModule';
import { runOnNewThread } from './utils/thread';
const Promise = global.Promise;
const OS = Platform.OS;
/**
* Exports a video composition to a video file.
*
* @returns A promise that resolves when the export is complete.
*/
export const exportVideoComposition = async ({
videoComposition,
drawFrame,
beforeDrawFrame,
afterDrawFrame,
onProgress,
...options
}) => new Promise((resolve, reject) => {
runOnNewThread(() => {
'worklet';
let surface = null;
let frameExtractor = null;
let encoder = null;
const {
width,
height
} = options;
try {
surface = Skia.Surface.MakeOffscreen(width, height);
if (!surface) {
throw new Error('Failed to create Skia surface');
}
encoder = RNSkiaVideoModule.createVideoEncoder(options);
encoder.prepare();
frameExtractor = RNSkiaVideoModule.createVideoCompositionFramesExtractorSync(videoComposition);
frameExtractor.start();
const nbFrames = videoComposition.duration * options.frameRate;
const canvas = surface.getCanvas();
const clearColor = Skia.Color('#00000000');
for (let i = 0; i < nbFrames; i++) {
const currentTime = i / options.frameRate;
const frames = frameExtractor.decodeCompositionFrames(currentTime);
canvas.drawColor(clearColor, BlendMode.Clear);
const context = beforeDrawFrame?.();
drawFrame({
context,
canvas,
videoComposition,
currentTime,
frames,
width: options.width,
height: options.height
});
surface.flush();
// On iOS and macOS, the first flush is not synchronous,
// so we need to wait for the next frame
if (i === 0 && (OS === 'ios' || OS === 'macos')) {
RNSkiaVideoModule.usleep?.(1000);
}
const texture = surface.getNativeTextureUnstable();
encoder.encodeFrame(texture, currentTime);
afterDrawFrame?.(context);
if (onProgress) {
runOnJS(onProgress)({
framesCompleted: i + 1,
nbFrames
});
}
}
} catch (e) {
runOnJS(reject)(e);
return;
} finally {
frameExtractor?.dispose();
surface?.dispose();
}
try {
encoder.finishWriting();
} catch (e) {
runOnJS(reject)(e);
return;
} finally {
encoder?.dispose();
}
runOnJS(resolve)();
});
});
//# sourceMappingURL=exportVideoComposition.js.map