In this article we are going to take a look at how to compress videos in React Native as well as general media, such as photos or audio content. It’s standard practice to compress the full size of raw images/videos before they are sent to be stored on the server in order to conserve storage cost, but most importantly to improve the user experience by an order of magnitude.
The library we are taking a look at today is react-native-ffmpeg. We are going to learn how to compress videos and media files in React Native apps by leveraging react-native-ffmpeg open-source framework.
Why Do You Need To Compress Videos in React Native?
Before we jump into implementation details, let’s check out several reasons why you should compress videos in React Native apps:
- Improve performance of mobile app, by saving data usage, CPU cycles, and data storage costs.
- Videos in their raw form are large and would take up too much space on your backend storage.
- Compressed videos are smaller in size and hence are easier to upload even in slower internet.
- By compressing videos you maintain a uniform encoding format of videos stored on your database.
Now that we have outlined the motivation behind this advanced performance technique, let’s see how we can code it in practice.
How To Compress Videos in React Native
The react-native-ffmpeg library is based on MobileFFmpeg and supports both Android (API level 16 or later) and iOS (SDK 9.3 or later). The framework also supports devices with the following hardware architectures: arm-v7a
, arm-v7a-neon
, arm64-v8a
, x86
and x86_64
on Android and armv7
, armv7s
, arm64
, arm64e
, i386
and x86_64
on iOS.
ffmpeg
includes built-in encoders for some popular formats. However, there are certain external libraries that needs to be enabled in order to encode specific formats/codecs
Installation
You only need to run: yarn add react-native-ffmpeg
Usage
Let’s take a look at how we embed this library in a cache manager that we use at Instamobile. In our company, we use this piece of infrastructure across all of our React Native Templates, to make our apps a lot more performant by compressing videos at upload time.
The ffmpeg library has a bunch of classes such as RNFFmpeg, RNFFProbe and RNFFmpegConfig.
1. The RNFFmpeg class exposes a static method called execute() that takes a string command with the format "-i sourceuri -c:v mpeg4 newSource"
as an argument and returns a promise.
const BASE_DIR = `${FileSystem.cacheDirectory}expo-cache/`; const compressVideo = (sourceUri, callback) => { console.log(' compressVideo ++++', sourceUri); const newSource = `${BASE_DIR}${uuidv4()}.mp4`; RNFFmpeg.execute(`-i ${sourceUri} -c:v mpeg4 ${newSource}`).then((result) => { console.log(' did compressVideo newSource++++', newSource); callback(newSource); }); };
Alternatively you can call the other static method executeAsync() which takes an array as an argument: executeAsync("-i", "sourceuri", "-c:v" "mpeg4", "newSource"])
RFFmpeg.listExecutions() lists all the currently running transactions:
RNFFmpeg.listExecutions().then(executionList => { executionList.forEach(execution => { console.log(`Execution id is ${execution.executionId}`); console.log(`Execution start time is ` + new Date(execution.startTime)); console.log(`Execution command is ${execution.command}`); }); });
Other static methods on the RNFFmpeg class include RNFFmpeg.cancel();
to stop all executons and RNFFmpeg.cancelExecution(executionId);
to stop a specific execution where the exectionId (the executionId gotten from RNFFmpeg.listExecutions()).
2. RNFFprobe just like the class we just looked at has an execute(command) method that takes only two commands ‘-i oldSource’:
import { RNFFprobe } from 'react-native-ffmpeg'; RNFFprobe.execute('-i file1.mp4').then(result => console.log(`FFprobe process exited with rc=${result}.`));
and executeWithArguments(array) that accepts an array containing the commands.
import { LogLevel, RNFFprobe } from 'react-native-ffmpeg'; RNFFprobe.executeWithArguments(["-i", "file1.mp4"]).then(result => console.log(`FFmpeg process exited with rc=${result}.`));
To get the information about a file requires only one method call from this class:
RNFFprobe.getMediaInformation('<file path or uri>').then(information => { if (information.getMediaProperties() !== undefined) { if (information.getMediaProperties().filename !== undefined) { console.log(`Path: ${information.getMediaProperties().filename}`); } if (information.getMediaProperties().format_name !== undefined) { console.log(`Format: ${information.getMediaProperties().format_name}`); } if (information.getMediaProperties().bit_rate !== undefined) { console.log(`Bitrate: ${information.getMediaProperties().bit_rate}`); } if (information.getMediaProperties().duration !== undefined) { console.log(`Duration: ${information.getMediaProperties().duration}`); } if (information.getMediaProperties().start_time !== undefined) { console.log(`Start time: ${information.getMediaProperties().start_time}`); } if (information.getMediaProperties().nb_streams !== undefined) { console.log(`Number of streams: ${information.getMediaProperties().nb_streams.toString()}`); } let tags = information.getMediaProperties().tags; if (tags !== undefined) { Object.keys(tags).forEach((key) => { console.log(`Tag: ${key}:${tags[key]}`); }); } let streams = information.getStreams(); if (streams !== undefined) { for (let i = 0; i < streams.length; ++i) { let stream = streams[i]; console.log("---"); if (stream.getAllProperties().index !== undefined) { console.log(`Stream index: ${stream.getAllProperties().index.toString()}`); } if (stream.getAllProperties().codec_type !== undefined) { console.log(`Stream type: ${stream.getAllProperties().codec_type}`); } if (stream.getAllProperties().codec_name !== undefined) { console.log(`Stream codec: ${stream.getAllProperties().codec_name}`); } } } } });
React Native Code Example to Compress Videos
A quick short app to test this library is written below:
/** * Sample React Native App * https://instamobile.io * * @format * @flow strict-local */ import React from 'react'; import {View, Button} from 'react-native'; import {launchImageLibrary} from 'react-native-image-picker'; const CompressionTest = () => { const openImageLibrary = () => { launchImageLibrary( { mediaType: 'photo', }, (response) => { compressVideo(response.uri); }, ); }; const compressVideo = (sourceUri) => { console.log(' compressVideo ++++', sourceUri); RNFFmpeg.execute(`-i ${sourceUri} -c:v mpeg4 resultimage.\mp4`).then( (result) => { console.log(result); }, ); }; return ( <View style={styles.container}> <Button onPress={openImageLibrary} title="Pick Image" color="#841584" /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, }, }); export default CompressionTest;
3. RNFFmpegConfig has several helpful static methods
disableLogs()
disableStatistics()
setFontconfigConfigurationPath('<fontconfig configuration directory>');
setLogLevel(LogLevel.AV_LOG_WARNING)
We’ll leave these methods as an exercise for the reader. Feel free to look up the official documentation and learn how to use these config methods to improve and debug your code.
That’s all. We have learnt the importance of compression and have explored the major React Native library to compress photos and videos.
We use video compression in our templates to upload media to app feeds in TikTok clone, Social Network App, and Instagram Clone App and also in our Instagram Clone App.