An example app using the Expo.Audio & Expo.Video API.
See App.js for the good stuff.
Home Page: https://expo.io/@community/playlist
An example app using the Expo.Audio & Expo.Video API.
See App.js for the good stuff.
I would like to try your demo app, however the video playback feature is no longer working.
Could you kindly, update the script to EXPO 31 SKD Version?
Thanks so much!
i keep getting this error about sound.stopAsync is not an object but I can use it to stop playback so I don't know what the issue is or how to fix it.
import { Text, View, StyleSheet, Pressable, Alert} from 'react-native';
import Colors from '../constants/Colors';
import {Ionicons } from '@expo/vector-icons';
import { Audio } from 'expo-av';
import React, { useState, useEffect } from 'react';
const Timer = (props) => {
const [seconds, setSeconds] = useState(props.time);
const [isActive, setIsActive] = useState(false);
const [sound, setSound] = useState();
async function playSound() {
const { sound } = await Audio.Sound.createAsync(
require('../assets/mysterious_bass.wav'),
);
setSound(sound);
await sound.playAsync();
}
useEffect(() => {
return sound
? () => {
sound.unloadAsync();
}
: undefined;
}, [sound]);
useEffect(()=>{
if(seconds === 0){
playSound();
}
},[seconds])
const toggle= async()=> {
setIsActive(!isActive);
try {
await sound.stopAsync();
} catch (error) {
console.error(error);
}
}
const reset = async () => {
setSeconds(props.time);
setIsActive(false);
try {
await sound.stopAsync();
} catch (error) {
console.error(error);
}
}
useEffect(() => {
let interval = null;
if (isActive) {
interval = setInterval(() => {
setSeconds(seconds => seconds - 1);
}, 1000);
} else if (!isActive && seconds !== 0) {
clearInterval(interval);
}
return () => clearInterval(interval);
}, [isActive, seconds]);
return (
{isActive ? : <Ionicons size={59}name='play'/>}
00:{seconds}
Reset
);
};
package.json
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"eject": "expo eject"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.3.0",
"@fortawesome/free-regular-svg-icons": "^6.0.0-beta3",
"@fortawesome/free-solid-svg-icons": "^6.0.0",
"@fortawesome/react-fontawesome": "^0.1.17",
"@fortawesome/react-native-fontawesome": "^0.2.7",
"@react-native-community/masked-view": "^0.1.11",
"@react-navigation/bottom-tabs": "^6.2.0",
"axios": "^0.26.0",
"expo": "~44.0.0",
"expo-app-loading": "~1.3.0",
"expo-av": "~10.2.0",
"expo-linear-gradient": "~11.0.3",
"expo-status-bar": "~1.2.0",
"firebase": "^9.6.7",
"firebase-functions": "^3.18.1",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-icons": "^4.3.1",
"react-native": "0.64.3",
"react-native-elements": "^3.4.2",
"react-native-gesture-handler": "~2.1.0",
"react-native-linear-gradient": "^2.5.6",
"react-native-reanimated": "~2.3.1",
"react-native-safe-area-context": "^3.3.2",
"react-native-screens": "~3.10.1",
"react-native-svg": "^12.1.1",
"react-native-web": "0.17.1",
"react-navigation": "^4.4.4",
"react-navigation-header-buttons": "^6.3.1",
"react-navigation-material-bottom-tabs": "^2.3.5",
"react-navigation-stack": "^2.10.4",
"react-redux": "^7.2.6",
"redux": "^4.1.2",
"redux-thunk": "^2.4.1"
},
"devDependencies": {
"@babel/core": "^7.12.9",
"typescript": "^4.5.5"
},
"private": true
}
@brentvatne I generate this (.wav) audio file https://drive.google.com/file/d/1yXmoV6tk30n4klJFtzA8-t4DLtGgkJwh/view using C# code. when I try to play it using Expo.Audio.Sound.create(require('./GUIdump.wav')
but I can't hear any voice and I got no errors just this warning https://drive.google.com/file/d/1pm0Uwe-xLaEFAY_BtvgXFoe9V4snYqTG/view?usp=sharing
Great example. Only problem is that the playlist stops when you put the app into the background. Any idea on how to get it to keep playing the current item, AND advance to the next item all while in the background?
"Warning: git+https://github.com/exponentjs/react-native#sdk-15 is not a valid version. Version must be in the form of sdk-x.y.z. Please update your package.json file."
Wonderful example. Is there a way to load a json file that can be imported and fetched in at the time the user opens the app to get a fresh and updated list of streams? I would appreciate if you can give me some pointers on how to accomplish that. Thanks and great work!
Get the error cannot find "babel-preset-exponent" when I try to run the react native packager. Shouldn't it be babel-preset-expo?
After cloning the folder, i ran the command yarn install.
when i am trying to run expo start, i am facing the following error
error Invalid regular expression: /(.\fixtures\.|node_modules[\]react[\]dist[\].|website\node_modules\.|heapCapture\bundle.js|.\tests\.)$/: Unterminated character class.
I need to show live streaming video in Expo react native app. I tried expo-video but it only plays mp4 videos not live stream if i'm not wrong. I don't want to eject from expo. Is there any expo library or any other way to play hls m3u8 or rtmp live streaming?
I copied the contents of main.js into one of the screens in my app and the player gets stuck on "... loading ..." with all of the buttons grayed out. I am running SDK 18... is this not expected to be fixed in SDK 19?
import React from 'react';
import {
Dimensions,
Image,
Slider,
StyleSheet,
Text,
TouchableHighlight,
View,
} from 'react-native';
import Expo, { Asset, Audio, Font, Video } from 'expo';
class Icon {
constructor(module, width, height) {
this.module = module;
this.width = width;
this.height = height;
Asset.fromModule(this.module).downloadAsync();
}
}
class PlaylistItem {
constructor(name, uri, isVideo) {
this.name = name;
this.uri = uri;
this.isVideo = isVideo;
}
}
const PLAYLIST = [
new PlaylistItem(
'Comfort Fit - “Sorry”',
'https://s3.amazonaws.com/exp-us-standard/audio/playlist-example/Comfort_Fit_-_03_-_Sorry.mp3',
false
),
new PlaylistItem(
'Big Buck Bunny',
'http://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4',
true
),
new PlaylistItem(
'Mildred Bailey – “All Of Me”',
'https://ia800304.us.archive.org/34/items/PaulWhitemanwithMildredBailey/PaulWhitemanwithMildredBailey-AllofMe.mp3',
false
),
new PlaylistItem(
"Popeye - I don't scare",
'https://ia800501.us.archive.org/11/items/popeye_i_dont_scare/popeye_i_dont_scare_512kb.mp4',
true
),
new PlaylistItem(
'Podington Bear - “Rubber Robot”',
'https://s3.amazonaws.com/exp-us-standard/audio/playlist-example/Podington_Bear_-_Rubber_Robot.mp3',
false
),
];
const ICON_PLAY_BUTTON = new Icon(
require('./assets/images/play_button.png'),
34,
51
);
const ICON_PAUSE_BUTTON = new Icon(
require('./assets/images/pause_button.png'),
34,
51
);
const ICON_STOP_BUTTON = new Icon(
require('./assets/images/stop_button.png'),
22,
22
);
const ICON_FORWARD_BUTTON = new Icon(
require('./assets/images/forward_button.png'),
33,
25
);
const ICON_BACK_BUTTON = new Icon(
require('./assets/images/back_button.png'),
33,
25
);
const ICON_LOOP_ALL_BUTTON = new Icon(
require('./assets/images/loop_all_button.png'),
77,
35
);
const ICON_LOOP_ONE_BUTTON = new Icon(
require('./assets/images/loop_one_button.png'),
77,
35
);
const ICON_MUTED_BUTTON = new Icon(
require('./assets/images/muted_button.png'),
67,
58
);
const ICON_UNMUTED_BUTTON = new Icon(
require('./assets/images/unmuted_button.png'),
67,
58
);
const ICON_TRACK_1 = new Icon(require('./assets/images/track_1.png'), 166, 5);
const ICON_THUMB_1 = new Icon(require('./assets/images/thumb_1.png'), 18, 19);
const ICON_THUMB_2 = new Icon(require('./assets/images/thumb_2.png'), 15, 19);
const LOOPING_TYPE_ALL = 0;
const LOOPING_TYPE_ONE = 1;
const LOOPING_TYPE_ICONS = { 0: ICON_LOOP_ALL_BUTTON, 1: ICON_LOOP_ONE_BUTTON };
const { width: DEVICE_WIDTH, height: DEVICE_HEIGHT } = Dimensions.get('window');
const BACKGROUND_COLOR = '#FFF8ED';
const DISABLED_OPACITY = 0.5;
const FONT_SIZE = 14;
const LOADING_STRING = '... loading ...';
const BUFFERING_STRING = '...buffering...';
const RATE_SCALE = 3.0;
const VIDEO_CONTAINER_HEIGHT = DEVICE_HEIGHT * 2.0 / 5.0 - FONT_SIZE * 2;
class MediaPlayer extends React.Component {
constructor(props) {
super(props);
this.index = 0;
this.isSeeking = false;
this.shouldPlayAtEndOfSeek = false;
this.playbackInstance = null;
this.state = {
showVideo: false,
playbackInstanceName: LOADING_STRING,
loopingType: LOOPING_TYPE_ALL,
muted: false,
playbackInstancePosition: null,
playbackInstanceDuration: null,
shouldPlay: false,
isPlaying: false,
isBuffering: false,
isLoading: true,
fontLoaded: false,
shouldCorrectPitch: true,
volume: 1.0,
rate: 1.0,
videoWidth: DEVICE_WIDTH,
videoHeight: VIDEO_CONTAINER_HEIGHT,
poster: false,
useNativeControls: false,
fullscreen: false,
};
}
componentDidMount() {
Audio.setAudioModeAsync({
allowsRecordingIOS: false,
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
playsInSilentModeIOS: true,
shouldDuckAndroid: true,
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX,
});
(async () => {
await Font.loadAsync({
'cutive-mono-regular': require('./assets/fonts/CutiveMono-Regular.ttf'),
});
this.setState({ fontLoaded: true });
})();
}
async _loadNewPlaybackInstance(playing) {
if (this.playbackInstance != null) {
await this.playbackInstance.unloadAsync();
this.playbackInstance.setCallback(null);
this.playbackInstance = null;
}
const source = { uri: PLAYLIST[this.index].uri };
const initialStatus = {
shouldPlay: playing,
rate: this.state.rate,
shouldCorrectPitch: this.state.shouldCorrectPitch,
volume: this.state.volume,
isMuted: this.state.muted,
isLooping: this.state.loopingType === LOOPING_TYPE_ONE,
};
if (PLAYLIST[this.index].isVideo) {
this._video.setCallback(this._callback);
await this._video.loadAsync(source, initialStatus);
this.playbackInstance = this._video;
const status = await this._video.getStatusAsync();
} else {
const { sound, status } = await Audio.Sound.create(
source,
initialStatus,
this._callback
);
this.playbackInstance = sound;
}
this._updateScreenForLoading(false);
}
_mountVideo = component => {
this._video = component;
this._loadNewPlaybackInstance(false);
};
_updateScreenForLoading(isLoading) {
if (isLoading) {
this.setState({
showVideo: false,
isPlaying: false,
playbackInstanceName: LOADING_STRING,
playbackInstanceDuration: null,
playbackInstancePosition: null,
isLoading: true,
});
} else {
this.setState({
playbackInstanceName: PLAYLIST[this.index].name,
showVideo: PLAYLIST[this.index].isVideo,
isLoading: false,
});
}
}
_callback = status => {
if (status.isLoaded) {
this.setState({
playbackInstancePosition: status.positionMillis,
playbackInstanceDuration: status.durationMillis,
shouldPlay: status.shouldPlay,
isPlaying: status.isPlaying,
isBuffering: status.isBuffering,
rate: status.rate,
muted: status.isMuted,
volume: status.volume,
loopingType: status.isLooping ? LOOPING_TYPE_ONE : LOOPING_TYPE_ALL,
shouldCorrectPitch: status.shouldCorrectPitch,
});
if (status.didJustFinish && !status.isLooping) {
this._advanceIndex(true);
this._updatePlaybackInstanceForIndex(true);
}
} else {
if (status.error) {
console.log(`FATAL PLAYER ERROR: ${status.error}`);
}
}
};
_onLoadStart = () => {
console.log(`ON LOAD START`);
};
_onLoad = status => {
console.log(`ON LOAD : ${JSON.stringify(status)}`);
};
_onError = error => {
console.log(`ON ERROR : ${error}`);
};
_onReadyForDisplay = event => {
const widestHeight =
DEVICE_WIDTH * event.naturalSize.height / event.naturalSize.width;
if (widestHeight > VIDEO_CONTAINER_HEIGHT) {
this.setState({
videoWidth:
VIDEO_CONTAINER_HEIGHT *
event.naturalSize.width /
event.naturalSize.height,
videoHeight: VIDEO_CONTAINER_HEIGHT,
});
} else {
this.setState({
videoWidth: DEVICE_WIDTH,
videoHeight:
DEVICE_WIDTH * event.naturalSize.height / event.naturalSize.width,
});
}
};
_onFullscreenUpdate = event => {
console.log(
`FULLSCREEN UPDATE : ${JSON.stringify(event.fullscreenUpdate)}`
);
};
_advanceIndex(forward) {
this.index =
(this.index + (forward ? 1 : PLAYLIST.length - 1)) % PLAYLIST.length;
}
async _updatePlaybackInstanceForIndex(playing) {
this._updateScreenForLoading(true);
this.setState({
videoWidth: DEVICE_WIDTH,
videoHeight: VIDEO_CONTAINER_HEIGHT,
});
this._loadNewPlaybackInstance(playing);
}
_onPlayPausePressed = () => {
if (this.playbackInstance != null) {
if (this.state.isPlaying) {
this.playbackInstance.pauseAsync();
} else {
this.playbackInstance.playAsync();
}
}
};
_onStopPressed = () => {
if (this.playbackInstance != null) {
this.playbackInstance.stopAsync();
}
};
_onForwardPressed = () => {
if (this.playbackInstance != null) {
this._advanceIndex(true);
this._updatePlaybackInstanceForIndex(this.state.shouldPlay);
}
};
_onBackPressed = () => {
if (this.playbackInstance != null) {
this._advanceIndex(false);
this._updatePlaybackInstanceForIndex(this.state.shouldPlay);
}
};
_onMutePressed = () => {
if (this.playbackInstance != null) {
this.playbackInstance.setIsMutedAsync(!this.state.muted);
}
};
_onLoopPressed = () => {
if (this.playbackInstance != null) {
this.playbackInstance.setIsLoopingAsync(
this.state.loopingType !== LOOPING_TYPE_ONE
);
}
};
_onVolumeSliderValueChange = value => {
if (this.playbackInstance != null) {
this.playbackInstance.setVolumeAsync(value);
}
};
_trySetRate = async (rate, shouldCorrectPitch) => {
if (this.playbackInstance != null) {
try {
await this.playbackInstance.setRateAsync(rate, shouldCorrectPitch);
} catch (error) {
// Rate changing could not be performed, possibly because the client's Android API is too old.
}
}
};
_onRateSliderSlidingComplete = async value => {
this._trySetRate(value * RATE_SCALE, this.state.shouldCorrectPitch);
};
_onPitchCorrectionPressed = async value => {
this._trySetRate(this.state.rate, !this.state.shouldCorrectPitch);
};
_onSeekSliderValueChange = value => {
if (this.playbackInstance != null && !this.isSeeking) {
this.isSeeking = true;
this.shouldPlayAtEndOfSeek = this.state.shouldPlay;
this.playbackInstance.pauseAsync();
}
};
_onSeekSliderSlidingComplete = async value => {
if (this.playbackInstance != null) {
this.isSeeking = false;
const seekPosition = value * this.state.playbackInstanceDuration;
if (this.shouldPlayAtEndOfSeek) {
this.playbackInstance.playFromPositionAsync(seekPosition);
} else {
this.playbackInstance.setPositionAsync(seekPosition);
}
}
};
_getSeekSliderPosition() {
if (
this.playbackInstance != null &&
this.state.playbackInstancePosition != null &&
this.state.playbackInstanceDuration != null
) {
return (
this.state.playbackInstancePosition /
this.state.playbackInstanceDuration
);
}
return 0;
}
_getMMSSFromMillis(millis) {
const totalSeconds = millis / 1000;
const seconds = Math.floor(totalSeconds % 60);
const minutes = Math.floor(totalSeconds / 60);
const padWithZero = number => {
const string = number.toString();
if (number < 10) {
return '0' + string;
}
return string;
};
return padWithZero(minutes) + ':' + padWithZero(seconds);
}
_getTimestamp() {
if (
this.playbackInstance != null &&
this.state.playbackInstancePosition != null &&
this.state.playbackInstanceDuration != null
) {
return `${this._getMMSSFromMillis(
this.state.playbackInstancePosition
)} / ${this._getMMSSFromMillis(this.state.playbackInstanceDuration)}`;
}
return '';
}
_onPosterPressed = () => {
this.setState({ poster: !this.state.poster });
};
_onUseNativeControlsPressed = () => {
this.setState({ useNativeControls: !this.state.useNativeControls });
};
_onFullscreenPressed = () => {
try {
this._video.presentIOSFullscreenPlayer();
} catch (error) {
console.log(error.toString());
}
};
render() {
return !this.state.fontLoaded
? <View style={styles.emptyContainer} />
: <View style={styles.container}>
<View />
<View style={styles.nameContainer}>
<Text
style={[styles.text, { ...Font.style('cutive-mono-regular') }]}>
{this.state.playbackInstanceName}
</Text>
</View>
<View style={styles.space} />
<View style={styles.videoContainer}>
<Video
ref={this._mountVideo}
style={[
styles.video,
{
opacity: this.state.showVideo ? 1.0 : 0.0,
width: this.state.videoWidth,
height: this.state.videoHeight,
},
]}
resizeMode={Video.RESIZE_MODE_CONTAIN}
callback={this._callback}
onLoadStart={this._onLoadStart}
onLoad={this._onLoad}
onError={this._onError}
onFullscreenUpdate={this._onFullscreenUpdate}
onReadyForDisplay={this._onReadyForDisplay}
useNativeControls={this.state.useNativeControls}
/>
</View>
<View
style={[
styles.playbackContainer,
{
opacity: this.state.isLoading ? DISABLED_OPACITY : 1.0,
},
]}>
<Slider
style={styles.playbackSlider}
trackImage={ICON_TRACK_1.module}
thumbImage={ICON_THUMB_1.module}
value={this._getSeekSliderPosition()}
onValueChange={this._onSeekSliderValueChange}
onSlidingComplete={this._onSeekSliderSlidingComplete}
disabled={this.state.isLoading}
/>
<View style={styles.timestampRow}>
<Text
style={[
styles.text,
styles.buffering,
{ ...Font.style('cutive-mono-regular') },
]}>
{this.state.isBuffering ? BUFFERING_STRING : ''}
</Text>
<Text
style={[
styles.text,
styles.timestamp,
{ ...Font.style('cutive-mono-regular') },
]}>
{this._getTimestamp()}
</Text>
</View>
</View>
<View
style={[
styles.buttonsContainerBase,
styles.buttonsContainerTopRow,
{
opacity: this.state.isLoading ? DISABLED_OPACITY : 1.0,
},
]}>
<TouchableHighlight
underlayColor={BACKGROUND_COLOR}
style={styles.wrapper}
onPress={this._onBackPressed}
disabled={this.state.isLoading}>
<Image style={styles.button} source={ICON_BACK_BUTTON.module} />
</TouchableHighlight>
<TouchableHighlight
underlayColor={BACKGROUND_COLOR}
style={styles.wrapper}
onPress={this._onPlayPausePressed}
disabled={this.state.isLoading}>
<Image
style={styles.button}
source={
this.state.isPlaying
? ICON_PAUSE_BUTTON.module
: ICON_PLAY_BUTTON.module
}
/>
</TouchableHighlight>
<TouchableHighlight
underlayColor={BACKGROUND_COLOR}
style={styles.wrapper}
onPress={this._onStopPressed}
disabled={this.state.isLoading}>
<Image style={styles.button} source={ICON_STOP_BUTTON.module} />
</TouchableHighlight>
<TouchableHighlight
underlayColor={BACKGROUND_COLOR}
style={styles.wrapper}
onPress={this._onForwardPressed}
disabled={this.state.isLoading}>
<Image
style={styles.button}
source={ICON_FORWARD_BUTTON.module}
/>
</TouchableHighlight>
</View>
<View
style={[
styles.buttonsContainerBase,
styles.buttonsContainerMiddleRow,
]}>
<View style={styles.volumeContainer}>
<TouchableHighlight
underlayColor={BACKGROUND_COLOR}
style={styles.wrapper}
onPress={this._onMutePressed}>
<Image
style={styles.button}
source={
this.state.muted
? ICON_MUTED_BUTTON.module
: ICON_UNMUTED_BUTTON.module
}
/>
</TouchableHighlight>
<Slider
style={styles.volumeSlider}
trackImage={ICON_TRACK_1.module}
thumbImage={ICON_THUMB_2.module}
value={1}
onValueChange={this._onVolumeSliderValueChange}
/>
</View>
<TouchableHighlight
underlayColor={BACKGROUND_COLOR}
style={styles.wrapper}
onPress={this._onLoopPressed}>
<Image
style={styles.button}
source={LOOPING_TYPE_ICONS[this.state.loopingType].module}
/>
</TouchableHighlight>
</View>
<View
style={[
styles.buttonsContainerBase,
styles.buttonsContainerBottomRow,
]}>
<TouchableHighlight
underlayColor={BACKGROUND_COLOR}
style={styles.wrapper}
onPress={() =>
this._trySetRate(1.0, this.state.shouldCorrectPitch)}>
<View style={styles.button}>
<Text
style={[
styles.text,
{ ...Font.style('cutive-mono-regular') },
]}>
Rate:
</Text>
</View>
</TouchableHighlight>
<Slider
style={styles.rateSlider}
trackImage={ICON_TRACK_1.module}
thumbImage={ICON_THUMB_1.module}
value={this.state.rate / RATE_SCALE}
onSlidingComplete={this._onRateSliderSlidingComplete}
/>
<TouchableHighlight
underlayColor={BACKGROUND_COLOR}
style={styles.wrapper}
onPress={this._onPitchCorrectionPressed}>
<View style={styles.button}>
<Text
style={[
styles.text,
{ ...Font.style('cutive-mono-regular') },
]}>
PC: {this.state.shouldCorrectPitch ? 'yes' : 'no'}
</Text>
</View>
</TouchableHighlight>
</View>
<View />
{this.state.showVideo
? <View>
<View
style={[
styles.buttonsContainerBase,
styles.buttonsContainerTextRow,
]}>
<View />
<TouchableHighlight
underlayColor={BACKGROUND_COLOR}
style={styles.wrapper}
onPress={this._onPosterPressed}>
<View style={styles.button}>
<Text
style={[
styles.text,
{ ...Font.style('cutive-mono-regular') },
]}>
Poster: {this.state.poster ? 'yes' : 'no'}
</Text>
</View>
</TouchableHighlight>
<View />
<TouchableHighlight
underlayColor={BACKGROUND_COLOR}
style={styles.wrapper}
onPress={this._onFullscreenPressed}>
<View style={styles.button}>
<Text
style={[
styles.text,
{ ...Font.style('cutive-mono-regular') },
]}>
Fullscreen
</Text>
</View>
</TouchableHighlight>
<View />
</View>
<View style={styles.space} />
<View
style={[
styles.buttonsContainerBase,
styles.buttonsContainerTextRow,
]}>
<View />
<TouchableHighlight
underlayColor={BACKGROUND_COLOR}
style={styles.wrapper}
onPress={this._onUseNativeControlsPressed}>
<View style={styles.button}>
<Text
style={[
styles.text,
{ ...Font.style('cutive-mono-regular') },
]}>
Native Controls:
{' '}
{this.state.useNativeControls ? 'yes' : 'no'}
</Text>
</View>
</TouchableHighlight>
<View />
</View>
</View>
: null}
</View>;
}
}
const styles = StyleSheet.create({
emptyContainer: {
alignSelf: 'stretch',
backgroundColor: BACKGROUND_COLOR,
},
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'space-between',
alignItems: 'center',
alignSelf: 'stretch',
backgroundColor: BACKGROUND_COLOR,
},
wrapper: {},
nameContainer: {
height: FONT_SIZE,
},
space: {
height: FONT_SIZE,
},
videoContainer: {
height: VIDEO_CONTAINER_HEIGHT,
},
video: {
maxWidth: DEVICE_WIDTH,
},
playbackContainer: {
flex: 1,
flexDirection: 'column',
justifyContent: 'space-between',
alignItems: 'center',
alignSelf: 'stretch',
minHeight: ICON_THUMB_1.height * 2.0,
maxHeight: ICON_THUMB_1.height * 2.0,
},
playbackSlider: {
alignSelf: 'stretch',
},
timestampRow: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
alignSelf: 'stretch',
minHeight: FONT_SIZE,
},
text: {
fontSize: FONT_SIZE,
minHeight: FONT_SIZE,
},
buffering: {
textAlign: 'left',
paddingLeft: 20,
},
timestamp: {
textAlign: 'right',
paddingRight: 20,
},
button: {
backgroundColor: BACKGROUND_COLOR,
},
buttonsContainerBase: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
buttonsContainerTopRow: {
maxHeight: ICON_PLAY_BUTTON.height,
minWidth: DEVICE_WIDTH / 2.0,
maxWidth: DEVICE_WIDTH / 2.0,
},
buttonsContainerMiddleRow: {
maxHeight: ICON_MUTED_BUTTON.height,
alignSelf: 'stretch',
paddingRight: 20,
},
volumeContainer: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
minWidth: DEVICE_WIDTH / 2.0,
maxWidth: DEVICE_WIDTH / 2.0,
},
volumeSlider: {
width: DEVICE_WIDTH / 2.0 - ICON_MUTED_BUTTON.width,
},
buttonsContainerBottomRow: {
maxHeight: ICON_THUMB_1.height,
alignSelf: 'stretch',
paddingRight: 20,
paddingLeft: 20,
},
rateSlider: {
width: DEVICE_WIDTH / 2.0,
},
buttonsContainerTextRow: {
maxHeight: FONT_SIZE,
alignItems: 'center',
paddingRight: 20,
paddingLeft: 20,
minWidth: DEVICE_WIDTH,
maxWidth: DEVICE_WIDTH,
},
});
export default MediaPlayer
The playlist does not keep playing in the background even after changing the Audio Mode.
Audio.setAudioModeAsync({
allowsRecordingIOS: false,
staysActiveInBackground: true, <--- Changed this
playsInSilentModeIOS: true,
shouldDuckAndroid: true,
interruptionModeAndroid: InterruptionModeAndroid.DoNotMix,
playThroughEarpieceAndroid: false
});
I cloned the project, changed that value and that's all I did.
Is there anything extra that needs to be done in this example to have it work in the background?
Thank you!
Hi, is there a way to play a local bundled audio file as soon as the component is loaded or rendered?
similar to on html
So that it is played as an intro sound without any required interaction from the user.
Thanks
Tested on a Google Pixel 3A, when seeking the player freezes and doesn't respond well.
Error while loading in Iphone 6S
The app doesn't output any sound on 10.0.2
Attempting to copy contents of main.js into my code and to test this out and I'm unable to play the audio or video from the playlist.
It either gets stuck on "... loading ..." when I navigate to the screen, or it loads the playlist item and then gets stuck on "...buffering..." and am unable to play play the playlist item.
Sometimes, when navigating to another playlist item all the icons gray out and it gets stuck with both "...loading..." and "...buffering..." displayed.
When it is in the web version, in safari it takes a long time to load the song (more than 10 seconds), is there a way to stream it?
I'm getting this error on startup:
Native module cannot be null.
NativeEventEmitter
NativeEventEmitter.js:35:16
EventEmitter
EventEmitter.js:21:48
<unknown>
Location.js:8:46
loadModuleImplementation
require.js:214:12
<unknown>
Expo.js:5
loadModuleImplementation
require.js:214:12
<unknown>
AppEntry.js:1
loadModuleImplementation
require.js:214:12
guardedLoadModule
require.js:141:45
global code
<unknown file>:0
Hi , how to call a function onseek ! I am not able to use react-native-video
How could I use the Audio API to play the musics in the background?
Example app links to the old audio-only playlist that doesn't include the video updates. Is there an expo link for the updated version that includes video?
Hi,
could you add the license to this repo?
Thanks
Hey Expo team, it looks like the upgraded sdk version needs to be published as the demo app still uses SDK 15.
White screen with warning message
[06:13:58] [Unhandled promise rejection: TypeError: undefined is not an object (evaluating 'ExpoFontLoader.loadAsync')]
- node_modules/expo-font/src/Font.js:141:23 in _loadSingleFontAsync$
- node_modules/regenerator-runtime/runtime.js:62:44 in tryCatch
- ... 14 more stack frames from framework internals
This code not working when having parameter but works without it ?token=SOME_TOKEN
<Video
ref={videoRef}
source={{uri: 'https://www.something.com/video.mp4?token=SOME_TOKEN'}}
rate={1.0}
volume={1.0}
isMuted={false}
resizeMode="cover"
shouldPlay
isLooping
style={{ width: '100%', height: '100%' }}
/>
Please update to SDK 38, because the sample documentation is in error.
Example in doc: https://expo.io/@documentation/playlist-example
Doc URL: https://docs.expo.io/versions/v38.0.0/sdk/audio/
I want to disable useNativeControls in full screen but normal portrate view its working. But full screen view the seekingbar, pause, play, forward, backward button is displayin
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.