How to Record Videos in ReactJS with Ionic Capacitor and Cordova Plugins

Image for post
Image for post

I often see Ionic Framework developers getting confused about how to use the Cordova plugins and Ionic Native plugins in Reactjs or Vuejs, which is still in beta, but there is not much to it.

Below is a quick step by step on how to integrate the video-capture-plus plugin into a reactjs application and deploy it to an ios device using Ionic Capacitor

We will be using a project from a previous post I wrote utilizing a custom react hook to upload files to firebase. The project will be modified to include the video capture plugin and then we will modify the firebase hook to upload the blob from the video data

I have seen people in the past recommend the cordova-plugin-media-capture but the quality of the video you get is horrible

Install the components, plugins

npm install cordova-plugin-video-capture-plus
npm install @ionic-native/video-capture-plus
npm install @ionic-native/core

Add the imports

import {
MediaFile,
VideoCapturePlusOptions,
VideoCapturePlus,
} from "@ionic-native/video-capture-plus";

When using the VideoCapturePlus components in ReactJS, typescript is your friend because it shows you what properties and methods are available on the objects.

In this case what you want is MediaCapture.captureVideo which returns a promise that is the media you were looking for or the error that was generated

const doMediaCapture = async () => {
let options: VideoCapturePlusOptions = {
limit: 1, duration: 30
};
let capture:any = await VideoCapturePlus.captureVideo(options);
console.log((capture[0] as MediaFile).fullPath)
};

The basic code for the Home Component containing the button. The click event triggers the function to activate the camera and we console log the output to confirm we got the data. We are taking this first step since we need to deploy to device to actually record video.

//.. removed ionic imports for brevity
const Home: React.FC<RouteComponentProps> = ({ history }) => {
return (
<IonPage id="home-page">
<IonHeader>
<IonToolbar>
<IonTitle>Inbox</IonTitle>
</IonHeader>
<IonContent>
<IonButton onClick={() => doMediaCapture()}>
VIDEO CAPATURE
</IonButton>
</IonContent>
</IonPage>
);
};
export default Home;

If your code ran fine and you saw the output in the console, then you are ready for the next step which is to deploy the app to you device so you can actually record a video.

Below are the steps to get the app running on ios, the steps for android are similar. Please see capacitor documentation for additional details

Deploying To Device Using Capacitor/CLI

ionic build
ionic cap sync ios

After you make changes to web code, you typically run the following command.

ionic cap copy ios

After changes to native code/plugins

ionic cap sync ios

Open up the IDE

ionic cap open ios

Using Live Reload To Debug Application

When developing the remainder of the application, I used live reload commands which are listed below.

ionic cap run ios -l --externalionic cap run android -l --external

Changes To The Firebase File Upload Hook

There is a need to modify the react-hook to handle videos being uploaded. In an attempt to keep the interface of the hook simple, we will derive the additional information of mime type and file name for the the video in the hook.

By looking at the type of the blob, one can determine if there is a need to add the proper extension on the file name when we upload it. we run into this issue because on IOS the mime type “quicktime” needs to be mapped to the extension “.mov”

In this example, we are assuming if it is a dataUrl we get, then it is an image so there is no need for me to make any changes to the hook at this point, but for the Blob, we need to account for the change in the mime type with the following code.

console.log("processing as File/Blob");
let fName = `${new Date().getTime()}`;
if (_value instanceof Blob) {
if (_value.type.split("/")[1] === "quicktime") {
fName = fName + ".mov";
} else {
fName = fName + "." + _value.type.split("/")[1];
}
}
// setting the firebase properties for the file upload
let ref = storageRef.child("media/" + fName);
return ref.put(_value);}

Getting the Blob From The File

We need to make some additional imports to get information regarding the current platform, Capacitor.getPlatform and we are going to be using the file system so we need the File and DirectoryEntry to be imported also.

import { CameraResultType, Capacitor } from "@capacitor/core";
import { File, DirectoryEntry } from "@ionic-native/file";

Pretty certain there is a better way to handle this mess of converting the file path, but this is what I have so far.

We need to get the path and the file name from the media file and use the combination to resolve it to a format that can be read by the mobile device’s file system. We are accomplishing this by getting the `DirectoryEntry using the File plugin

let resolvedPath: DirectoryEntry;
let path = media.fullPath.substring(0, media.fullPath.lastIndexOf("/"));
if (Capacitor.getPlatform() === "ios") {
resolvedPath = await File.resolveDirectoryUrl("file://" + path);
} else {
resolvedPath = await File.resolveDirectoryUrl(path);
}

Once we get the resolved path, we can then read the file into a blob using File.readAsArrayBuffer and upload the blob to firebase.

return File.readAsArrayBuffer(resolvedPath.nativeURL, media.name).then(
(buffer: any) => {
// get the buffer and make a blob to be saved
let imgBlob = new Blob([buffer], {
type: media.type,
});
setFileData(imgBlob);
},
(error: any) => console.log(error)
)

The hook attempts to upload the file to firebase when ever setFileData is called with a dataUrl or Blob, so the last part is to call the function exposed by the useFirebaseUpload hook and start the upload.

Conclusion

The real purpose of the blog post was to try and explain what is going on in the associated video and hopefully it has been helpful. Please take a look at the video and the source code provided and leave comments and questions below

Android Quirks

The configuration in the plugin.xml is causing issues with the VideoCapturePlus plugin and needs to be edited in order to work properly. For now I just quickly edited the file in side the node_modules directory to have the android section look like this

<!-- android -->
<platform name="android">
<config-file target="res/xml/config.xml" parent="/*">
<feature name="VideoCapturePlus">
<param name="android-package" value="nl.xservices.plugins.videocaptureplus.VideoCapturePlus"/>
</feature>
</config-file>
<config-file target="AndroidManifest.xml" parent="/*">
<uses-permission
android:name="android.permission.RECORD_AUDIO" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
</config-file>
<source-file
src="src/android/nl/xservices/plugins/videocaptureplus/VideoCapturePlus.java"
target-dir="src/nl/xservices/plugins/videocaptureplus"/>
<source-file src="src/android/nl/xservices/plugins/videocaptureplus/FileHelper.java"
target-dir="src/nl/xservices/plugins/videocaptureplus"/>
<source-file src="src/android/nl/xservices/plugins/videocaptureplus/xml/provider_paths.xml" target-dir="res/xml" />
</platform>

🔥 Related Tutorials 🔥

🔥 Links 🔥

Written by

DC based software agency utilizing #Javascript, #Typescript, #HTML5, #Ionicframework, #NodeJS, #Firebase to build web & mobile solutions. https://buff.ly/300Zru

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store