- Published on
An Audio Player with react-native-video
- Authors
- Name
- Ghayoor ul Haq
react-native-video
for Audio?
Why There are several audio libraries like react-native-audio-api
and react-native-track-player
, react-native-video is a simple, all-in-one solution for handling both audio and video playback in React Native apps.
Following are some of the points that make react-native-video
a great choice for audio playback over the other audio libraries:
- Unified API: It provides a consistent API for both audio and video, making it easier to manage media playback in your app.
- Background Playback: Handles background playback with lock screen, Control Center, and Android notification controls. You don’t need to manually manage playback event listeners like onPlay or onPause.
- Audio Streaming: Supports streaming audio formats like HLS, providing a seamless user experience.
- Active Maintenance: The library is actively maintained and has a large community, ensuring ongoing support and updates.
Before diving into the code, install following packages that we will be using in the example:
yarn add react-native-video @react-native-community/slider
Below is the component that uses react-native-video
to play audio:
import React, { useRef, useState, useEffect } from 'react';
import { View, Text, TouchableOpacity, ActivityIndicator } from 'react-native';
import Slider from '@react-native-community/slider';
import Video from 'react-native-video';
type Status = 'idle' | 'loading' | 'playing' | 'paused' | 'error';
// Function to format seconds into a user-friendly time string
const formatSecondsToPlayerTime = (seconds: number) => {
let timeString = '';
// get hours, minutes, and seconds from total seconds
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = Math.floor(seconds % 60);
// construct the time string based on whether hours are present
if (hours > 0) {
timeString += `${hours}:`;
}
// always show minutes and seconds as two digits
if (hours > 0) {
timeString += `${String(minutes).padStart(2, '0')}:`;
} else {
timeString += `${minutes}:`;
}
timeString += `${String(remainingSeconds).padStart(2, '0')}`;
return timeString;
};
const AudioPlayer = () => {
// Using useRef to hold the Video component reference
const videoRef = useRef<Video>(null);
// State variables to manage playback state
const [paused, setPaused] = useState(true);
const [duration, setDuration] = useState(0);
const [currentTime, setCurrentTime] = useState(0);
const [status, setStatus] = useState<Status>('idle');
const [isSeeking, setIsSeeking] = useState(false);
// Handle video load event to set duration and initial state
const handleLoad = (meta: any) => {
setDuration(meta.duration || 0);
setStatus('paused');
};
// Update current time as the video plays and user is not seeking
const handleProgress = (progress: any) => {
if (!isSeeking) {
setCurrentTime(progress.currentTime);
}
};
// Handle sliding start to indicate user is sliding the slider
const handleSlidingStart = () => {
setIsSeeking(true);
};
// Handle sliding complete to seek the video to the new position using slider
const handleSlidingComplete = (value: number) => {
videoRef.current?.seek(value);
setCurrentTime(value);
setIsSeeking(false);
};
// When the audio ends, reset the player states
const handleEnd = () => {
setPaused(true);
setCurrentTime(0);
setStatus('paused');
};
// Handle any errors during playback
const handleError = (error: any) => {
console.warn('Audio playback error', error);
setStatus('error');
};
// Toggle playback state between paused and playing
const togglePlayback = () => {
setPaused((prev) => {
const next = !prev;
setStatus(next ? 'paused' : 'playing');
return next;
});
};
// Seek to a specific time in the audio and update the state
const seek = (value: number) => {
videoRef.current?.seek(value);
setCurrentTime(value);
setPaused(false);
setStatus('playing');
};
// Effect to update the status when paused changes
useEffect(() => {
if (!paused) {
setStatus('playing');
}
}, [paused]);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 20 }}>
<Video
ref={videoRef}
source={{
uri: 'AUDIO_FILE_URL', // TODO: Replace with your audio file URL
metadata: {
title: 'React Native Audio Player',
artist: 'ghayoor.com',
imageUri: 'https://ghayoor.com/_next/image?url=%2Fstatic%2Fimages%2Favatar.png&w=384&q=75',
},
}}
paused={paused}
onLoad={handleLoad}
onProgress={handleProgress}
onEnd={handleEnd}
onError={handleError}
playInBackground // Allow playback in the background
showNotificationControls // Show controls in notification area
playWhenInactive // Allow playback when the app is inactive
ignoreSilentSwitch="ignore"
progressUpdateInterval={250}
/>
<Slider
style={{ width: '100%', marginVertical: 12 }}
minimumValue={0}
maximumValue={duration}
value={currentTime}
onSlidingStart={handleSlidingStart}
onSlidingComplete={handleSlidingComplete}
minimumTrackTintColor={"#1942e6"}
maximumTrackTintColor={"#e8e8e8"}
thumbTintColor={"#1942e6"}
/>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', width: '100%' }}>
<Text>{formatSecondsToPlayerTime(currentTime)}</Text>
<Text>{formatSecondsToPlayerTime(duration)}</Text>
</View>
<View style={{ flexDirection: 'row', marginTop: 16, alignItems: 'center' }}>
<TouchableOpacity onPress={() => seek(Math.max(0, currentTime - 10))} style={{ marginHorizontal: 20 }}>
<Text style={{ fontSize: 18 }}>-10s</Text>
</TouchableOpacity>
<TouchableOpacity onPress={togglePlayback} disabled={status === 'loading'}>
{status === 'loading' ? (
<ActivityIndicator color="#1C45E6" size="small" />
) : (
<Text style={{ fontSize: 32 }}>{paused ? '▶️' : '⏸️'}</Text>
)}
</TouchableOpacity>
<TouchableOpacity onPress={() => seek(Math.min(duration, currentTime + 10))} style={{ marginHorizontal: 20 }}>
<Text style={{ fontSize: 18 }}>+10s</Text>
</TouchableOpacity>
</View>
</View>
);
};
export default AudioPlayer;
Explanation of the Code
- Imports: We import necessary components from React, React Native, and the
react-native-video
library. - State Management: We use
useState
to manage playback state, duration, current time, and status of the audio player. - Video Component: The
Video
component fromreact-native-video
is used to handle audio playback. We pass various props to control playback, handle events like loading, progress, end, and error. - Slider: We use
@react-native-community/slider
to allow users to scrub through the audio. It updates the current time and seeks to the new position when sliding is complete. - Playback Controls: We provide buttons to play/pause the audio and seek backward or forward by 10 seconds. The playback state is toggled using the
togglePlayback
function. - Formatting Time: The
formatSecondsToPlayerTime
function formats the current time and duration into a player-friendly string format. - Background Playback: The
playInBackground
andshowNotificationControls
props allow the audio to continue playing when the app is in the background, with controls available in the notification area.
Usage
Change the AUDIO_FILE_URL
in the source
prop of the Video
component to the URL of your audio file. This can be a remote URL or a local file path. Although we added playInBackground
and showNotificationControls
to Video component, you still need to configure additional settings in your app for background audio playback, especially on iOS. This typically involves adding Background Modes
in your Xcode project settings and enabling Audio, AirPlay, and Picture in Picture
.
To use this AudioPlayer
component, simply import it into your main application file (e.g., App.tsx
) and include it in your component tree:
import React from 'react';
import { SafeAreaView } from 'react-native';
import AudioPlayer from './AudioPlayer'; // Adjust the path as necessary
const App = () => {
return (
<SafeAreaView style={{ flex: 1 }}>
<AudioPlayer />
</SafeAreaView>
);
};
export default App;
Note: At the time of writing this blog,
react-native-video
is at version 6.16.0
Following are screenshots of the audio player in action:
