React Native course · Module 6: Photos, camera and audio

expo-av - Audio and Video

2 min read ·
In this lesson5

Play audio and video in your application!

Installation

npx expo install expo-av

Video Playback

import { Video, ResizeMode } from 'expo-av';
import { useState, useRef } from 'react';

function VideoPlayer() {
  const videoRef = useRef<Video>(null);
  const [status, setStatus] = useState<any>({});

  return (
    <View style={styles.container}>
      <Video
        ref={videoRef}
        style={styles.video}
        source={{ uri: 'https://example.com/video.mp4' }}
        useNativeControls
        resizeMode={ResizeMode.CONTAIN}
        isLooping
        onPlaybackStatusUpdate={(status) => setStatus(() => status)}
      />

      <View style={styles.controls}>
        <Pressable
          onPress={() =>
            status.isPlaying
              ? videoRef.current?.pauseAsync()
              : videoRef.current?.playAsync()
          }
        >
          <Ionicons
            name={status.isPlaying ? 'pause' : 'play'}
            size={32}
          />
        </Pressable>
      </View>
    </View>
  );
}

Audio Playback

import { Audio } from 'expo-av';

function AudioPlayer() {
  const [sound, setSound] = useState<Audio.Sound>();
  const [isPlaying, setIsPlaying] = useState(false);

  const playSound = async () => {
    const { sound } = await Audio.Sound.createAsync(
      require('@/assets/audio/ocean.mp3')
    );
    setSound(sound);
    await sound.playAsync();
    setIsPlaying(true);
  };

  const pauseSound = async () => {
    if (sound) {
      await sound.pauseAsync();
      setIsPlaying(false);
    }
  };

  // Cleanup
  useEffect(() => {
    return sound
      ? () => {
          sound.unloadAsync();
        }
      : undefined;
  }, [sound]);

  return (
    <Pressable onPress={isPlaying ? pauseSound : playSound}>
      <Ionicons name={isPlaying ? 'pause' : 'play'} size={48} />
    </Pressable>
  );
}

Background Audio

import { Audio } from 'expo-av';

async function setupAudio() {
  await Audio.setAudioModeAsync({
    playsInSilentModeIOS: true,
    staysActiveInBackground: true,
    shouldDuckAndroid: true,
  });
}

Audio Recording

import { Audio } from 'expo-av';

function AudioRecorder() {
  const [recording, setRecording] = useState<Audio.Recording>();
  const [recordingUri, setRecordingUri] = useState<string>();

  const startRecording = async () => {
    try {
      const permission = await Audio.requestPermissionsAsync();
      if (!permission.granted) return;

      await Audio.setAudioModeAsync({
        allowsRecordingIOS: true,
        playsInSilentModeIOS: true,
      });

      const { recording } = await Audio.Recording.createAsync(
        Audio.RecordingOptionsPresets.HIGH_QUALITY
      );

      setRecording(recording);
    } catch (error) {
      console.error('Recording error:', error);
    }
  };

  const stopRecording = async () => {
    if (!recording) return;

    await recording.stopAndUnloadAsync();
    const uri = recording.getURI();
    setRecordingUri(uri ?? undefined);
    setRecording(undefined);
  };

  return (
    <View>
      <Pressable
        onPress={recording ? stopRecording : startRecording}
        style={[styles.recordButton, recording && styles.recording]}
      >
        <Ionicons name={recording ? 'stop' : 'mic'} size={32} color="white" />
      </Pressable>

      {recordingUri && <Text>Recorded: {recordingUri}</Text>}
    </View>
  );
}

expo-av handles all your audio/video needs!

Code for this lesson: App.tsx
import React, { useState, useEffect } from 'react';
import { View, Text, Pressable, StyleSheet } from 'react-native';
import { Audio } from 'expo-av';

export default function App() {
  const [sound, setSound] = useState<Audio.Sound>();
  const [isPlaying, setIsPlaying] = useState(false);

  const playSound = async () => {
    // TODO: create a sound using Audio.Sound.createAsync(...)
    //   - pass { uri: 'https://example.com/ocean.mp3' } as the source
    //   - destructure the result: const { sound } = await Audio.Sound.createAsync(...)
    //   - store it: setSound(sound)
    //   - start playback: await sound.playAsync()
    //   - set setIsPlaying(true)

  };

  const pauseSound = async () => {
    if (sound) {
      await sound.pauseAsync();
      setIsPlaying(false);
    }
  };

  // Release the sound resource on unmount
  useEffect(() => {
    return sound ? () => { sound.unloadAsync(); } : undefined;
  }, [sound]);

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Sounds of Atlantis</Text>
      <Pressable
        style={styles.button}
        onPress={isPlaying ? pauseSound : playSound}
      >
        <Text style={styles.buttonText}>{isPlaying ? 'Pause' : 'Play'}</Text>
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#1a5276' },
  title: { color: '#aed6f1', fontSize: 20, marginBottom: 24 },
  button: { backgroundColor: '#2980b9', paddingVertical: 14, paddingHorizontal: 40, borderRadius: 8 },
  buttonText: { color: '#fff', fontSize: 16, fontWeight: '600' },
});

Docs entries used in this lesson

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. Which prop enables native controls in the Video component?

Hands-on tasks in the game

  • Click in order

    Click the elements for creating a sound:

  • Code editor

    Complete the Audio.Sound code with play/pause.

Useful articles