我正在用Expo创建一个音频播放器,几乎所有东西都能按预期工作。我可以推动前进,后退,暂停/播放等。
但是,如果我听了整个音频文件,然后尝试转到该音频文件末尾的下一个音轨,它将失败并显示以下错误:“ AVPlayerItem实例失败,错误代码为-1102和域” NSURLErrorDomain“”
我几乎已经尝试了所有方法来修复此错误。
我正在从远程uri加载音频文件。
代码如下:
class PlaylistItem {
constructor(identifier, name, uri, isVideo) {
this.identifier = this.identifier;
this.name = name;
this.uri = uri;
this.isVideo = isVideo;
}
}
let SLIDER_ITEM_WIDTH = Math.round(Dimensions.get('window').width);
class PlayAudioScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
showVideo: false,
muted: false,
playbackInstancePosition: null,
playbackInstanceDuration: null,
shouldPlay: false,
isPlaying: false,
isBuffering: false,
isLoading: true,
fontLoaded: false,
shouldCorrectPitch: true,
volume: 1.0,
rate: 1.0,
poster: false,
useNativeControls: false,
fullscreen: false,
throughEarpiece: false,
isReady: false,
guid: null,
startAt: 0,
audioIsLoaded: false,
showPresentation: false,
finishedPresentationLoading: false,
};
this.playbackInstance = null;
this.timeout = null;
this.lastUpdate = new Date();
this.lastUpdatedUsage = null;
this.isChanging = false;
this.index = 0;
this.Playlist = props.navigation.state.params.items.map(item => {
return new PlaylistItem(
item.identifier,
item.title,
item.media,
false
);
});
}
async componentDidMount() {
const { items, start, id } = this.props.navigation.state.params;
Audio.setAudioModeAsync({
allowsRecordingIOS: false,
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
playsInSilentModeIOS: true,
shouldDuckAndroid: true,
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX,
playThroughEarpieceAndroid: false,
});
this._loadNewPlaybackInstance(false);
}
async componentWillUnmount() {
if (this.playbackInstance != null) {
await this.playbackInstance.unloadAsync();
// this.playbackInstance.setOnPlaybackStatusUpdate(null);
this.playbackInstance = null;
}
}
_onPlaybackStatusUpdate = 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,
audioIsLoaded: true,
});
if (status.didJustFinish && !status.isLooping) {
this._advanceIndex(true);
this._updatePlaybackInstanceForIndex(true);
}
} else {
if (status.error) {
console.log(`FATAL PLAYER ERROR: ${status.error}`);
}
}
};
async _updatePlaybackInstanceForIndex(playing) {
this._updateScreenForLoading(true);
this._loadNewPlaybackInstance(playing);
}
_updateScreenForLoading(isLoading) {
if (isLoading) {
this.setState({
isPlaying: false,
playbackInstanceDuration: 0,
playbackInstancePosition: 0,
audioIsLoaded: false,
});
} else {
this.setState({
audioIsLoaded: true,
});
}
}
_advanceIndex(forward) {
this.index =
(this.index + (forward ? 1 : this.Playlist.length - 1)) %
this.Playlist.length;
}
async _loadNewPlaybackInstance(playing) {
if (this.playbackInstance != null) {
await this.playbackInstance.unloadAsync();
// this.playbackInstance.setOnPlaybackStatusUpdate(null);
this.playbackInstance = null;
}
const source = {
uri: this.Playlist[this.index].uri,
};
const initialStatus = {
shouldPlay: playing,
rate: this.state.rate,
shouldCorrectPitch: this.state.shouldCorrectPitch,
volume: this.state.volume,
isMuted: this.state.muted,
startAt: this.state.startAt,
};
const GUID = uuid();
const { sound, status } = await Audio.Sound.createAsync(
source,
initialStatus,
this._onPlaybackStatusUpdate
);
this.playbackInstance = sound;
this.setState({
guid: GUID,
});
}
_onPlayPausePressed = () => {
if (this.playbackInstance != null) {
if (this.state.isPlaying) {
this.playbackInstance.pauseAsync();
} else {
this.playbackInstance.playAsync();
}
}
};
_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);
}
};
_renderSliderItem = item => (
<SliderItemWrapper key={item.identifier} width={SLIDER_ITEM_WIDTH}>
<ShadowImage
height={wp(88)}
width={wp(88)}
imageUrl={this.props.navigation.state.params.image}
noToRadius
/>
</SliderItemWrapper>
);
render() {
const { isReady, audioIsLoaded } = this.state;
const { title } = this.props.navigation.state.params;
if (!isReady) {
return (
<>
<StatusBar
backgroundColor={colors.light}
barStyle="dark-content"
/>
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: colors.light,
}}
></View>
</>
);
}
return (
<ScreenWrapper style={{ flex: 1, paddingBottom: 0 }}>
<StatusBar
backgroundColor={colors.light}
barStyle="dark-content"
/>
<MyHeader
showLeftContent
onLeftClick={() => this.props.navigation.goBack()}
backgroundColor={colors.light}
/>
<TopSection>
<Container style={{ width: '100%', flex: 1 }}>
<ContentWrapper>
<TopSectionContainer>
<Slider
renderItem={this._renderSliderItem}
data={this.Playlist}
totalLength={this.Playlist.length}
initialScrollIndex={this.index}
activeIndex={this.index}
keyExtractor={(item, index) =>
item.identifier
}
itemWidth={SLIDER_ITEM_WIDTH}
/>
</TopSectionContainer>
<BottomSection disabled={!audioIsLoaded}>
<PlayActiveHeadingContainer>
<Heading1>{`${
this.Playlist[this.index].name
}`}</Heading1>
<Paragraph>{`${this.index + 1}/${
this.Playlist.length
} ${title}`}</Paragraph>
<PlayerTrack
playedMillis={
this.state.playbackInstancePosition
}
durationMillis={
this.state.playbackInstanceDuration
}
disabled={!audioIsLoaded}
/>
</PlayActiveHeadingContainer>
<PlayerControls
onBackFunc={this._onBackPressed}
onForwardFunc={this._onForwardPressed}
isPlaying={this.state.isPlaying}
onPress={this._onPlayPausePressed}
leftDisabled={this.index === 0}
rightDisabled={
this.index === this.Playlist.length - 1
}
disabled={!audioIsLoaded}
/>
</BottomSection>
</ContentWrapper>
</Container>
</TopSection>
</ScreenWrapper>
);
}
}