hyochan / react-native-audio-recorder-player

react-native native module for audio recorder and player.
MIT License
725 stars 215 forks source link

Keep recording after the phone is locked #566

Open imoxto opened 1 year ago

imoxto commented 1 year ago

Hello, I basically want to run the recording for a while even after the phone is locked. But i couldnt find any ideas on how to go about it in the docs. Any help is appreciated.

Version of react-native-audio-recorder-player

3.6.0

Version of React Native

0.72.4

Platforms you faced the error (IOS or Android or both?)

Android

Expected behavior

Press a button to start recording audio. Then lock the phone. I want to be able to record audio always after the app is locked.

Actual behavior

After locking the phone, it records for about 1 to 3 minutess after locking screen. but afterwards it doesn't record anymore untill the lock is lifted.

Steps to reproduce the behavior

Press a button to start recording audio. Then lock the phone and keep talking for 3 mins. Turn on the phone and stop recording and observe the playback file.

The following is the rough code i used:

import {View, PermissionsAndroid, Alert, Platform} from 'react-native';

import AudioRecorderPlayer, {
  AVEncoderAudioQualityIOSType,
  AVEncodingOption,
  AudioEncoderAndroidType,
  AudioSourceAndroidType,
  OutputFormatAndroidType,
} from 'react-native-audio-recorder-player';
import type {
  AudioSet,
  RecordBackType,
} from 'react-native-audio-recorder-player';

import {IconButton, Text} from 'react-native-paper';
import RNFetchBlob from 'rn-fetch-blob';
import {useUserStore} from '../utils/services';

import {useEffect, useState} from 'react';
import {formatTime} from '../utils';
import Toast from 'react-native-toast-message';

const permissions = [
  PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
  PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
  PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
];

const audioRecorderPlayer = new AudioRecorderPlayer();
audioRecorderPlayer.setSubscriptionDuration(0.5);

const LiveRecord = () => {
  const [permissionGranted, setPermissionGranted] = useState(false);
  const [isRecording, setIsRecording] = useState(false);
  const userId = useUserStore(state => state.user?.uid);

  const [recordTime, setRecordTime] = useState(0);

  useEffect(() => {
    const getPermissions = async () => {
      const granted = await PermissionsAndroid.requestMultiple(permissions);
      const recordAudioGranted =
        granted[PermissionsAndroid.PERMISSIONS.RECORD_AUDIO] === 'granted';

      const externalStorageWriteGranted =
        granted[PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE] ===
        'granted';
      const externalStorageReadGranted =
        granted[PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE] ===
        'granted';

      if (
        !(
          recordAudioGranted ||
          externalStorageWriteGranted ||
          externalStorageReadGranted
        )
      ) {
        Alert.alert('Permissions not granted');
      } else {
        setPermissionGranted(true);
      }
    };

    if (Platform.OS === 'android') {
      getPermissions();
    } else {
      setPermissionGranted(true);
    }
  }, []);

  useEffect(() => {
    if (!permissionGranted) {
      return;
    }
  }, [permissionGranted]);

  const startRecording = async () => {
    const dirs = RNFetchBlob.fs.dirs;
    const path = Platform.select({
      android: `${dirs.CacheDir}/record-${userId}-${new Date().getTime()}.mp3`,
    });
    const audioSet: AudioSet = {
      AudioEncoderAndroid: AudioEncoderAndroidType.AAC,
      AudioSourceAndroid: AudioSourceAndroidType.MIC,
      AVEncoderAudioQualityKeyIOS: AVEncoderAudioQualityIOSType.low,
      AVNumberOfChannelsKeyIOS: 2,
      AVFormatIDKeyIOS: AVEncodingOption.aac,
      OutputFormatAndroid: OutputFormatAndroidType.AAC_ADTS,
    };
    audioRecorderPlayer.removeRecordBackListener();
    audioRecorderPlayer.startRecorder(path, audioSet);
    audioRecorderPlayer.addRecordBackListener((e: RecordBackType) => {
      setRecordTime(e.currentPosition);
    });
    setIsRecording(true);
  };

  const onStopRecord = async () => {
    try {
      const result = await audioRecorderPlayer.stopRecorder();
      audioRecorderPlayer.removeRecordBackListener();
      console.log(result);
    } catch (err) {
      console.error(err);
      Toast.show({
        type: 'error',
        text1: 'Unxpected Error',
        text2: 'Please try again.',
      });
    }
    setIsRecording(false);
    setRecordTime(0);
  };

  return (
    <View style={{flex: 1, backgroundColor: '#1b1e2b'}}>
      <View className="flex flex-col items-center mt-10">
        <View
          className="rounded-full"
          style={{
            elevation: 20,
            shadowColor: '#000000',
            shadowOffset: {
              width: 0,
              height: 10,
            },
            shadowOpacity: 0.3,
            shadowRadius: 10,
            margin: 15,
            // borderRadius: 100,
            backgroundColor: '#2e3246',
          }}>
          {isRecording ? (
            <IconButton
              onPress={onStopRecord}
              icon="microphone"
              size={200}
              iconColor={'#e60000'}
            />
          ) : (
            <IconButton
              onPress={startRecording}
              icon="microphone"
              size={200}
              iconColor={'#613eea'}
            />
          )}
        </View>
      </View>
      <Text
        className="text-center text-5xl pt-10 pb-5 tracking-wider font-normal"
        style={{
          color: isRecording ? '#ffffff' : '#7878A3',
        }}>
        {formatTime(recordTime)}
      </Text>
    </View>
  );
};

export default LiveRecord;
rohitashPrajapati commented 1 year ago

Hi, have you figured out any workaround for this?

imoxto commented 1 year ago

Hi, have you figured out any workaround for this?

For now im just keeping the screen awake while recording

vijaystroup commented 10 months ago

+1

Submitted this a few days ago as well.

TheScalion commented 10 months ago

Does this NPM supports android API level 33

Fabian15-03 commented 10 months ago

Al parecer no es un tema de la librería como tal, creería que es debido a como el SO lo cataloga. Al usarlo en React Native y tenerlo en segundo plano puede ser que sea menos prioritario que una llamada telefónica que también usa el micrófono pero en una app nativa. De todas formas también tengo el mismo problema (+1). Veo como solución usar un modulo nativo de Kotlin y hacer un bridge a mi app de React Native.

samadRopstam commented 7 months ago

have you enabled "Audio, AirPlay, and Picture in Picture" option in xcode> Signing&Capabilities > Background Modes?

jondrews commented 7 months ago

+1 Experiencing basically the same issue here. Audio keeps recording when the device is locked, but a short time after locking (usually 30secs to 2mins) the audio levels become zero - it just records silence. The audio file then continues to be created, recording silence while the device is locked. When the device is later unlocked, the audio file continues to be created, and instead of recording silence the audio signal is restored to normal expected behaviour.

Any suggestions for improving this behaviour (other than keeping the screen unlocked)?

react-native version: 0.72.4 react-native-audio-recorder-player version: 3.6.0 release build tested on Oppo A52 (CPH2069) w/ Android 11, ColorOS v11.1

Yeasirarafat53 commented 1 week ago

Anyone solved this issue? i am also facing same issue

Yeasirarafat53 commented 1 week ago

I did the solution like this and it works fine in my case. anyone can apply this way

react-native version: 0.72.6 react-native-audio-recorder-player version: 3.6.12


  import React, {useState, useEffect, useRef} from 'react';
  import { AppState} from 'react-native';
  import AudioRecorderPlayer from 'react-native-audio-recorder-player';
  import {useIsFocused} from '@react-navigation/native';

const isFocused = useIsFocused();
const [isPlaying, setIsPlaying] = useState(false);

const appState = useRef(AppState.currentState);

  // Handle stopping audio when navigating away or unmounting the component
      useEffect(() => {
        if (!isFocused) {
          // Component is not focused, stop audio playback
          stopAudioPlayback();
        }
    // Cleanup function to stop audio playback when component unmounts
        return () => {
          stopAudioPlayback();
        };
      }, [isFocused]);

  // Function to stop audio playback
        const stopAudioPlayback = () => {
          try {
            audioRecorderPlayerRef.current.stopPlayer();
            audioRecorderPlayerRef.current.removePlayBackListener();
            setIsPlaying(false);
          } catch (error) {
            console.warn('Error stopping audio playback:', error);
          }
        };

  // background playback issue solution

useEffect(() => {
  const handleAppStateChange = (nextAppState) => {
    if (appState.current.match(/inactive|background/) && nextAppState === 'active') {
      // App comes to the foreground, do nothing
    } else if (nextAppState.match(/inactive|background/)) {
      // App goes to the background, stop audio playback
      audioRecorderPlayerRef.current.stopPlayer();
      audioRecorderPlayerRef.current.removePlayBackListener();
      setIsPlaying(false);
    }
    appState.current = nextAppState;
  };

  const appStateListener = AppState.addEventListener('change', handleAppStateChange);

  return () => {
    // Cleanup the AppState listener on component unmount
    appStateListener.remove();
  };
}, []);