React-native-track-player: [iOS] play() after stop() doesn't work

Created on 11 Apr 2019  路  7Comments  路  Source: react-native-kit/react-native-track-player

Hi All,

I'm new to react-native and I'm trying to create an App for a friend who would like to start a web radio.

Android: emulator/device everything works as expected.

iOS: when I fire the play() function for the first time everything works. Then fire stop() and then play() again the player remains in buffering state forever.

Any suggestion?

This is the configuration:
iOS: 10.14.4
react-native-cli: 2.0.1
react-native: 0.59.3
react-native-track-player: 1.1.3

Here the App.js:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow
 */

import React, {Component} from 'react';
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native';
import TrackPlayer from 'react-native-track-player';


function resolveTrackPlayerState(state) {
    if(state == TrackPlayer.STATE_PLAYING) {
        return 'Playing';
    } else if(state == TrackPlayer.STATE_PAUSED) {
        return 'Paused';
    } else if(state == TrackPlayer.STATE_STOPPED) {
        return 'Stopped';
    } else if(state == TrackPlayer.STATE_BUFFERING) {
        return 'Buffering...';
    }
    return 'None';
};


TrackPlayer.setupPlayer().then(async () => {
    console.log('Player ready!');

    TrackPlayer.updateOptions({
        // An array of media controls capabilities
        capabilities: [
            TrackPlayer.CAPABILITY_PLAY,
            TrackPlayer.CAPABILITY_STOP
        ],

        // An array of capabilities that will show up when the notification is in the compact form on Android
        compactCapabilities: [
            TrackPlayer.CAPABILITY_PLAY,
            TrackPlayer.CAPABILITY_STOP
        ]
    });

    await TrackPlayer.add({
        id: 'rml',
        url: 'http://37.187.93.104:8652/stream',
        title: 'Radio Music Lab',
        artist: '***',
        artwork: require('./assets/ico/icona_02.png')
    });
});


type Props = {};
export default class App extends Component<Props> {
    constructor(){
        super();

        var tpState = TrackPlayer.getState();

        this.state={
            playback: tpState,
            isPlaying: tpState == TrackPlayer.STATE_PLAYING ? true : false,
            isBuffering: tpState == TrackPlayer.STATE_BUFFERING ? true : false,
        }
    }

    componentDidMount() {
        // Adds an event handler
        this.onPlaybackState = TrackPlayer.addEventListener('playback-state', async (data) => {
            var tpState = data.state;

            this.setState({
                playback: tpState,
            });

            if(tpState == TrackPlayer.STATE_PLAYING)
            {
                this.setState({
                    isPlaying: true,
                    isBuffering: false,
                });
            }
            else if(tpState == TrackPlayer.STATE_BUFFERING)
            {
                this.setState({
                    isPlaying: false,
                    isBuffering: true,
                });
            }
            else
            {
                this.setState({
                    isPlaying: false,
                    isBuffering: false,
                });
            }
        });
    }

    componentWillUnmount() {
        // Removes the event handler
        this.onPlaybackState.remove();
    }

    render() {
        return (

            <View style={styles.container}>
                <TouchableOpacity onPress={() => this.playMusic()}>
                    <Text style={styles.play_btn}>Play</Text>
                </TouchableOpacity>

                <TouchableOpacity onPress={() => this.stopMusic()}>
                    <Text style={styles.stop_btn}>Stop</Text>
                </TouchableOpacity>

                <Text style={styles.info}>{resolveTrackPlayerState(this.state.playback)}</Text>

            </View>


        );
    }


    playMusic()
    {
        TrackPlayer.play();
    }

    stopMusic()
    {
        TrackPlayer.stop();
    }
}


const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
    info: {
        fontSize: 15,
        textAlign: 'center',
    },
    play_btn: {
        color: 'white',
        backgroundColor: 'green',
        fontSize: 20,
        padding: 10,
        margin: 10,
    },
    stop_btn: {
        color: 'white',
        backgroundColor: 'red',
        fontSize: 20,
        padding: 10,
        margin: 10,
    }
});


TrackPlayer.registerEventHandler(require('./player-handler.js'));

Here the player-handler.js:

import TrackPlayer from "react-native-track-player";

module.exports = async (data) => {
    if(data.type === 'remote-play') {
        TrackPlayer.play();
    } else if(data.type === 'remote-pause') {
        TrackPlayer.pause();
    } else if(data.type === 'remote-stop') {
        TrackPlayer.stop();
    }
};
iOS

Most helpful comment

With help of @cristiankmb I was able to solve my problem like this:

let refreshPlay = setInterval(async () => {
    let playerState = await TrackPlayer.getState();
    if (playerState !== TrackPlayer.STATE_STOPPED &&  playerState !== TrackPlayer.STATE_PLAYING) {
        await TrackPlayer.play();
        this.setState({playing: true});
    } else {
        clearInterval(refreshPlay);
    }
}, 1000);

Basically the promise which is returned at TrackPlayer.add() is messed up and you cannot be sure if track is truly added or not. I put TrackPlayer.play() inside an interval to try playing stream multiple times until it is successful.

All 7 comments

Same issue and context for me!

@besh81 stop() is use to kill any track array Sequence. If you need only stop the audio you can use pause(). So if you really need use stop() function you need do this after using it:

const trackStructure = { id: 'trackId', url: '.mp3', title: '', artist: '', artwork: '', }; const currentTrack = await TrackPlayer.getCurrentTrack(); if (currentTrack === null || currentTrack === undefined) { await TrackPlayer.reset(); await TrackPlayer.add(trackStructure); }

@cristiankmb thanks for the tips.
Yes I need to use stop. I'm playing a webradio stream source and I tried to use pause but then starts a buffering process that misalign the stream. I want to do something that act like a classic fm radio.

I've tried your suggestions and it works very well on android.
On iOs sometimes I have to push play twice to start the stream. The first time the player go to buffering state then returns to ready. Any idea?

Thanks

Ok, @besh81 I had the same problem, temporarily i used a different behavior for iOS:

componentDidMount() {
  this.getTrack();
}

async getTrack() {
  const trackStructure = {
    id: 'trackId',
    url: 'YourUrlStream',
    title: '',
    artist: '',
    artwork: '',
  };
  const currentTrack = await TrackPlayer.getCurrentTrack();
  if (currentTrack === null || currentTrack === undefined) {
      await TrackPlayer.reset();
      await TrackPlayer.add(trackStructure);
  }

  return currentTrack;
}

  async getPlayback() {
    const { playerState } = this.props;

    if (playerState === 'ready') {
      await TrackPlayer.play();
    } else if (playerState === TrackPlayer.STATE_NONE || playerState === TrackPlayer.STATE_STOPPED) {
      this.getTrack().then(await TrackPlayer.play());
    } else if (playerState === TrackPlayer.STATE_PAUSED) {
      await TrackPlayer.play();
    } else if (playerState === TrackPlayer.STATE_STOPPED) {
      await TrackPlayer.play();
    } else {
      await TrackPlayer.pause();
    }
  }

where do you call getPlayback()? can you share your code please? this bug with streams is hard to resolve on ios

@DNA-h complement this code with the code above:

render() {
    const { playerState } = this.props;

    return (
      <View style={StyleSheet.flatten(Styles.viewContainer)}>
          <TouchableOpacity
            activeOpacity={0.7}
            onPress={() => this.getPlayback()}
            style={StyleSheet.flatten(Styles.btnPlay)}
          />
      </View> 
    );
  }

Remember that the playerState prop is the state that you handle with TrackPlayer state.

With help of @cristiankmb I was able to solve my problem like this:

let refreshPlay = setInterval(async () => {
    let playerState = await TrackPlayer.getState();
    if (playerState !== TrackPlayer.STATE_STOPPED &&  playerState !== TrackPlayer.STATE_PLAYING) {
        await TrackPlayer.play();
        this.setState({playing: true});
    } else {
        clearInterval(refreshPlay);
    }
}, 1000);

Basically the promise which is returned at TrackPlayer.add() is messed up and you cannot be sure if track is truly added or not. I put TrackPlayer.play() inside an interval to try playing stream multiple times until it is successful.

Was this page helpful?
0 / 5 - 0 ratings