---
title: "Playing videos in React Native"
description: "Playing videos in React Native"
canonical_url: "https://www.bigbinary.com/blog/playing-videos-in-react-native-using-cloudinary"
markdown_url: "https://www.bigbinary.com/blog/playing-videos-in-react-native-using-cloudinary.md"
---

# Playing videos in React Native

Playing videos in React Native

- Author: Chirag Bhaiji
- Published: September 14, 2021
- Categories: React Native

These days, a large number of mobile apps allow users to upload videos and play
those videos efficiently. While building these features in a React Native app,
we ran into some challenges. In this blog, we will discuss those challenges and
the solutions.

## Where to host the videos

Since we are dealing with video,s we need a service that will store, host,
encode and be a CDN. After looking at various service providers, we decided to
go with [Cloudinary](https://cloudinary.com/). Cloudinary is a service provider
with an end-to-end image/video-management solution for web and mobile
applications, covering everything from **_uploads, storage, manipulations,
optimizations to delivery_** with a fast content delivery network (**_CDN_**).

### Setting up react-native-video player

We decided to use
[react-native-video](https://github.com/react-native-video/react-native-video)
`v5.1.1` for playing videos in React Native application. Here is the
[guide](https://github.com/react-native-video/react-native-video#readme) to set
up the video player.

```jsx
import Video from "react-native-video";

const Player = ({ uri }) => {
  return (
    <SafeAreaView style={styles.container}>
      <Video
        style={styles.player}
        source={{ uri }}
        controls
        resizeMode="contain"
      />
    </SafeAreaView>
  );
};
```

The above code snippet works perfectly on iOS but not on Android. On Android, we
faced a
[known issue](https://github.com/react-native-video/react-native-video/issues/1032)
where the video doesn't play, but the audio plays with significant delay. This
issue can be resolved by setting
[exoplayer](https://developer.android.com/guide/topics/media/exoplayer) as the
default player for Android in `react-native.config.js` in the root directory of
the project.

```js
module.exports = {
  dependencies: {
    "react-native-video": {
      platforms: {
        android: {
          sourceDir: "../node_modules/react-native-video/android-exoplayer",
        },
      },
    },
  },
};
```

### Setting up Cloudinary

A Cloudinary account is required before proceeding. Once the account is ready,
following are the steps to enable unsigned upload for the account.

- Go to the _Settings_ of Cloudinary.
- Select the _Upload_ tab.
- Search for _Upload presets_ section.
- Click on _Enable unsigned uploading_.

This generates an upload preset with a random name, which will be required for
the unsigned upload.

## Setting up Client for Upload

### Selecting Video from the gallery

We decided to use
[react-native-image-crop-picker](https://github.com/ivpusic/react-native-image-crop-picker)
`v0.36.2` library to select the video from the gallery. Here is the
[guide](https://github.com/ivpusic/react-native-image-crop-picker#readme) for
setting it up.

```jsx
import ImagePicker from "react-native-image-crop-picker";

const selectVideo = ({ setVideoToUpload }) => {
  ImagePicker.openPicker({ mediaType: "video" })
    .then(setVideoToUpload)
    .catch(console.error);
};
```

### Uploading Video

```js
// Cloud Name: Found on the Dashboard of Cloudinary.
const URL = "https://api.cloudinary.com/v1_1/<CLOUD_NAME>/video/upload";
// Random Name: Generated After Enabling The Unsigned Uploading.
const UPLOAD_PRESET = "<UPLOAD_PRESET_FOR_UNSIGNED_UPLOAD>";

const uploadVideo = (fileInfo, onSuccess, onError) => {
  const { name, uri, type } = fileInfo;
  let formData = new FormData();

  if (uri) {
    formData.append("file", { name, uri, type });
    formData.append("upload_preset", UPLOAD_PRESET);
  }

  axios
    .post(URL, formData, {
      headers: { "Content-Type": "multipart/form-data" },
    })
    .then(res => onSuccess(res.data))
    .catch(error => onError(error));
};

export default { uploadVideo };
```

### Fetching Videos on client

```js
import base64 from "base-64";

// API Key and Secret: Found on the Dashboard of Cloudinary.
const API_KEY = "<API_KEY>";
const API_SECRET = "<API_SECRET>";

const URL = `https://api.cloudinary.com/v1_1/<CLOUD_NAME>/resources/video`;

const getVideos = (onRes, onError) => {
  axios
    .get(URL, {
      headers: {
        Authorization: base64.encode(`${API_KEY}:${API_SECRET}`),
      },
    })
    .then(res => onRes(res.data.resources))
    .catch(error => onError(error));
};

export default { getVideos };
```

Response:

```json
{
  "resources": [
    {
      "asset_id": "475675ddd87cb3bb380415736ed1e3dc",
      "public_id": "samples/elephants",
      "format": "mp4",
      "version": 1628233788,
      "resource_type": "video",
      "type": "upload",
      "created_at": "2021-08-06T07:09:48Z",
      "bytes": 38855178,
      "width": 1920,
      "height": 1080,
      "access_mode": "public",
      "url": "http://res.cloudinary.com/do77lourv/video/upload/v1628233788/samples/elephants.mp4",
      "secure_url": "https://res.cloudinary.com/do77lourv/video/upload/v1628233788/samples/elephants.mp4"
    }
    //...
  ]
}
```

### Transforming uploaded videos for faster delivery

One way to play a video is to first completely download the video on the
client's device and then play it locally. The biggest drawback of this approach
is that it could take a long time before the video is fully downloaded and till
then the device is not playing anything at all. This strategy also requires a
sophisticated algorithm to manage memory.

Another solution is to use
[Adaptive Bitrate Streaming](https://en.wikipedia.org/wiki/Adaptive_bitrate_streaming)(ABS)
for playing videos. As the name suggests in this case, the video is being
streamed. It is one of the most efficient ways to deliver videos based on the
client's internet bandwidth and device capabilities.

To generate ABS, we add an eager transformation to the upload preset we created
while setting up the Cloudinary account earlier.

An eager transformation runs automatically for all the videos uploaded using the
upload preset which has the transformation added.

### Setup Transformation

Here are the steps for adding an eager transformation to the upload preset.

- Go to the upload preset that was generated when the unsigned upload was
  enabled while setting up Cloudinary in the earlier section.
- Click on edit.
- Go to the Upload Manipulations section.
- Click on `Add Eager Transformation` to open the Edit Transaction window.
- Open the `Format` dropdown under the `Format & shape` section and select
  `M3U8 Playlist (HLS)`.
- Select any streaming profile from the dropdown under the `More options`. Any
  profile matching your maximum required quality can be selected here.

Next upload a video with the same preset, trigger the transformation to generate
The `M3U8` format, which is the one we need for playing the videos as streams.

### Consuming video streams

To play streams, the `type` property must be provided to the
`react-native-video`'s source prop. In this case, `type` will be `m3u8`. Also,
the URI needs to be updated with this same extension.

```diff
- source={{ uri }}
+ source={{ uri: uri.replace(`.${format}`, '.m3u8'), type: 'm3u8'}}
```

### Conclusion

Once all the above mentioned steps were performed, we were able to upload and
stream videos on both iOS and Android without any issues.

## Links

- [Human page](https://www.bigbinary.com/blog/playing-videos-in-react-native-using-cloudinary)
