How to fix expo AV video sound on IOS in class components - react-native

This is a known problem - if an iPhone is in silent mode, the video won't playback with sound. There are various solutions including this one but the solutions are always provided in functional components and I've been unsuccessful in translating them to work in my class components. I understand the basic idea is to set the sound:
await Audio.setAudioModeAsync({ playsInSilentModeIOS: true });
But I don't know how to associate that with the video playback. The solutions make use of ref's so I tried something like:
async TriggerAudio() {
try {
await Audio.setAudioModeAsync({ playsInSilentModeIOS: true });
} catch (error) {
console.log("error for trigger audio " + error);
}
this.videoRef.current.playAsync();
}
And called that in a function that returns the Video tag - I declared the videoRef variable in the constructor, but I don't think I understand how to use ref's in a class component because there's no playAsync function associated with the ref. I also put the setAudioModeAsync in componentDidMount - even asked for permissions, but the video still plays with no sound when the phone is silenced.
How do I solve this for a class component?
UPDATE
If I create a ref in the Constructor:
this.videoRef = React.createRef();
And set the ref parameter of Video to that value:
<Video source={{
uri: url
}}
ref={this.videoRef}
useNativeControls
resizeMode="contain"
style={styles.videoComment}
/>
And create a button with onPress set to the above TriggerAudio function, it works great - the challenge I have is that the ref variables have to be unique for each video (I believe) but I don't know what videos will be loaded until I make API calls in the componentDidMount function, so how do I create ref's for each video so that each video's audio will start correctly?

Related

Getting refs to rendered videos in FlatList

I have an application where I am rendering a video component using expo-video-player via FlatList as this list could grow in the future and I want to keep things optimized as possible.
What I want to accomplish is that when I click to play on one video in the list, all others will pause if they are playing. I'm using all functional components and typically if it wasn't in a FlatList could reference a video with the following:
const videoRef = useRef(null);
...
<Video
ref={videoRef}
...
/>
and then call videoRef.current.pauseAsync() to pause it somewhere else in my code.
However, now that it is in a FlatList I am having trouble understanding how to link the ref for each video and be able to get the ref back to call pauseAsync() on the previous video that is playing.
Any direction/guidance would be appreciated.
How need to create a Array of Refs.
I would use a code link this:
const videoRefs = [];
const videos = ['url.com/xyz.mp4', 'url.com/abc.mp4'];
videos.forEach((video, index) => {
videoRefs.push(useRef(null));
});
...
renderItem = ({item, index}) => {
<Video
ref={videoRefs[index]}
...
/>
}

How to edit video player props in React Native Video using ref

I am using react-native-video. I am trying to change the props for each video onPress, which are in a FlatList of videos. So if I use state it will change all the video props in the entire FlatList, whereas I only want to change the one.
I created: const videoPlayer = [] and added a ref in each Video player like: ref={r => videoPlayer[item.id] = r}, and I know I can do videoPlayer[item.id].presentFullscreenPlayer() to play the video in fullscreen because it is a method of the player, but what about controlling other properties like resizeMode and paused?
I have tried videoPlayer[item.id].props.paused = false, but that has no effect. I also imagine I can create some sort of complex object in state, but then the entire list will update every time I change a property and that may cause slow running refreshes in a big list.
So, is there I way I can control props of each video like I explained?
You definitely could use setNativeProps like:
videoPlayer[item.id].setNativeProps({
paused: false
})
This is a doc page about it: https://facebook.github.io/react-native/docs/direct-manipulation

It is possible to manage the focus on the react-native-video player?

We are planning to use the react-native-video player on our app and we would like to show/hide the controls on focus, specifically:
On gaining focus - video player controls are displayed
On losing focus - video player controls are hidden
Do you guys know if this is possible, or how to do it ?
I read the react-native-video API, give you the following solution
constructor(props){
super(props)
this.state={
isShowControl:false
}
}
....
<Video
...
disableFocus={this.state.isShowControl} // disables audio focus and wake lock (default false)
onAudioFocusChanged={this.onAudioFocusChanged} // Callback when audio focus has been lost - pause if focus has been lost
/>
...
onAudioFocusChanged = (event: { hasAudioFocus: boolean }) => {
if(event.hasAudioFocus) {
this.setState({ isShowControl: false })
} else {
this.setState({ isShowControl: true })
}
if you want more control about the video component, you can use react-native-video-controls

react-native-audio-toolkit, isPlaying is not working

import React from "react";
import { Player } from "#react-native-community/audio-toolkit";
export default class RNAudiotoolkit extends React.Component {
componentDidMount(){
new Player("some_audio_file.mp3").play();
console.log(Player.isPlaying);
}
}
Above is the minimum code I've whittled down to, the audio track does play but, console.log(Player.isPlaying) always returns "false" but the audio file is running. Any input on why this isn't working. I can only suspect it has something to do with MediaStates, but have unsuccessfully gotten anything to work. If you have experience with this package your input is greatly appreciated.
documentation: https://github.com/react-native-community/react-native-audio-toolkit/blob/master/docs/API.md
Edit: Fixed and tested; answer is a combination of my original and mAhMoUdDaFeR's answer.
If you look above the documentation for the play method, you will see the documentation for prepare(Function callback). In it, it states:
...Otherwise the file is prepared when calling play() which may result in a small delay.
This means that if you check the .isPlaying property immediately after calling play() like you are doing, it is not guaranteed that the file is actually playing by time your console.log(Player.isPlaying) executes.
There is also the second issue that .isPlaying is not a static property on the Player class despite how it appears in the docs. It is actually a property of the Player instance that you need to create to play an audio file.
If you want to see that .isPlaying is indeed working correctly, one potential check is to run your code in a callback function passed into .play() as the docs show:
play(Function ?callback)
Start playback.
If callback is given, it is called when playback has started.
So a simple example would be to write your example code like this (saving the created instance and then logging in a callback):
componentDidMount(){
const p = new Player("some_audio_file.mp3").play(() => console.log('in callback', p.isPlaying));
console.log('immediately after play', p.isPlaying);
}
I created a new project to test this and if you run the above code, you'll see the following printed out illustrating the issue:
immediately after play false
in callback true
isPlaying is not a static method in the component Player, so you can't use Player.isPlaying, you can get isPlaying from the created instance (object) of this Player.
Try keeping a reference of the player object and then accessing its children:
this.player = new Player("some_audio_file.mp3").play()
and then log:
console.log(this.player.isPlaying)

React Native - Activity Indicator while FlatList loads images

Whenever my app mounts, I have set:
constructor(props) {
super(props);
this.state = {
loading: true
};
}
I have an activity indicator which animates until loading: false:
{this.state.loading &&
<View style={styles.loading}>
<ActivityIndicator color="red" animating={this.state.loading} />
</View>
}
I tried searching inside FlatList's documentation for a method would tell me if all items have been rendered so I can call this.setState({loading: false}); However, I did not manage to find such method.
Does anyone know how can I display my activity indicator whilst the list is loading its data?
Your problem is not react native related. Which is why you couldn't find any help regarding it in the react native documentation.
Assuming that your data is coming in asynchronously and in reference to your question you are not able to figure out when this asynchronous operation ends.
Use promises or async await or any other functionality like that, to figure out when this asynchronous operation ends and then use setState to disable the activity indicator and then show the flatList.
Let me know if you need more explanation or clarity on the above said.