Describe the bug
I am using RNTP to stream user-recorded audio that is stored in Azure blob storage. After a user records their audio, I upload the audio via uploadAudio() and then setup the player and add a track via loadPlayer(). The audio plays just fine. However, if the user deletes the recording and then records a new track (which calls uploadAudio() again when finished), I can never get the new audio track to play. When I log the player state, I see it immediately goes from 'playing' to 'paused'.
I'm using react-native-community/react-native-audio-toolkit to record the audio. I'm using RNTP for its background capabilities, plus I'm already using RNTP in another area of the same app.
Environment
System:
OS: macOS 10.15.5
CPU: (8) x64 Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz
Memory: 212.79 MB / 16.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 12.18.0 - /usr/local/bin/node
Yarn: 1.22.4 - /usr/local/bin/yarn
npm: 6.14.4 - /usr/local/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
SDKs:
iOS SDK:
Platforms: iOS 13.5, DriverKit 19.0, macOS 10.15, tvOS 13.4, watchOS 6.2
IDEs:
Android Studio: 3.5 AI-191.8026.42.35.5900203
Xcode: 11.5/11E608c - /usr/bin/xcodebuild
npmPackages:
react: 16.8.6 => 16.8.6
react-native: 0.60.5 => 0.60.5
I'm using react-native-track-player version 1.2.3
I am testing on a real iOS device, iPhone SE 2020.
Code (simplified)
async uploadAudio(filePath){
const audioURL = [URL OF AUDIO FILE IN BLOB STORAGE]
await RNFetchBlob.fetch('PUT', audioURL,
{
'x-ms-blob-type': 'BlockBlob',
'content-type': 'application/octet-stream'
}, RNFetchBlob.wrap(filePath))
.then(async (res) => {
await this._loadPlayer(audioURL);
});
}
async loadPlayer(audioURL){
await TrackPlayer.destroy();
await TrackPlayer.setupPlayer().then(async () => {
await TrackPlayer.updateOptions({
stopWithApp: true,
capabilities: [
TrackPlayer.CAPABILITY_PLAY,
TrackPlayer.CAPABILITY_PAUSE,
TrackPlayer.CAPABILITY_STOP
],
notificationCapabilities: [
TrackPlayer.CAPABILITY_PLAY,
TrackPlayer.CAPABILITY_PAUSE,
TrackPlayer.CAPABILITY_STOP
]
});
});
await TrackPlayer.reset();
await TrackPlayer.add({
id: audioURL,
url: audioURL,
title: 'example',
artist: 'example',
duration: (this.state.audioDuration)
});
}
async onPlayClicked(){
if (await TrackPlayer.getState() !== STATE_NONE){
TrackPlayer.play();
this._playbackProgressInterval = setInterval(this.updateProgress.bind(this), 250);
}
else{
await this._loadPlayer();
this.onPlayClicked();
}
}
async onStopClicked(){
TrackPlayer.stop();
if (this._playbackProgressInterval){
clearInterval(this._playbackProgressInterval);
this._playbackProgressInterval = null;
}
}
async deleteRecording(){
await this.onPlayerStop();
[update my state]
}
Could you try using the waitForBuffer option in the setupPlayer(options) function? See https://github.com/react-native-kit/react-native-track-player/issues/616
No, setting waitForBuffer to true in the setupPlayer didn't fix the issue.
I noticed similar issue when playing a radio (live audio) stream.
I was having the same issue when calling TrackPlayer.add and TrackPlayer.play next to each other with async/await.
My solution was to add a setTimeout of 1 second around the call made to play. Seems that the .add takes a little more to be ready to play after finishing its promise.
We're seeing this issue and one dev has reported that 5 seconds was necessary to solve this race condition. What's the contributing factor here? Is this related to buffering time? This issue only exists for livestreams.
Having the same issue, and I can confirm that after a few seconds (perhaps 5) you can click the play button successfully.
Same error on stream. Waiting for a fix.
I can confirm the issue. I have my playback code very similar to the example project in this repo. I'm using a shoutcast/icecast source for playback.
On Android it works fine.
Anyone got a solution for this??
Beside putting the timer/pause to play the stream.
I noticed similar issue when playing a radio (live audio) stream.
Hello guys, by any chance, has anyone managed to clarify this point? Thx! @neoassyrian @sgerendasy @dodgex
I still have this issue. Never got it it fixed
For me after hours of debugging and testing it decided to finally work. The final code only uses the waitForBuffer in the setup call. tried it multiple times and with other changes in my as well as rn-track-player code but it didn't work. no idea why it just started to work and keept working after removing all my changes.
For me after hours of debugging and testing it decided to finally work. The final code only uses the
waitForBufferin the setup call. tried it multiple times and with other changes in my as well as rn-track-player code but it didn't work. no idea why it just started to work and keept working after removing all my changes.
@dodgex
Can you share the setup code please
let playerActive: boolean = false;
function setupPlayer() {
if (playerActive) {
return;
}
playerActive = true;
TrackPlayer.setupPlayer({waitForBuffer: true}).then(async () => {
await TrackPlayer.updateOptions({
stopWithApp: false,
capabilities: [TrackPlayer.CAPABILITY_PLAY, TrackPlayer.CAPABILITY_PAUSE, TrackPlayer.CAPABILITY_STOP],
compactCapabilities: [
TrackPlayer.CAPABILITY_PLAY,
TrackPlayer.CAPABILITY_PAUSE,
TrackPlayer.CAPABILITY_STOP,
],
notificationCapabilities: [
TrackPlayer.CAPABILITY_PLAY,
TrackPlayer.CAPABILITY_PAUSE,
TrackPlayer.CAPABILITY_STOP,
],
});
});
}
export async function togglePlay() {
setupPlayer();
const playbackState = await TrackPlayer.getState();
const currentTrack = await TrackPlayer.getCurrentTrack();
if (currentTrack == null) {
await TrackPlayer.reset();
await TrackPlayer.add(createTrack(currentStation));
await TrackPlayer.play();
} else {
if (playbackState === TrackPlayer.STATE_PAUSED) {
await TrackPlayer.stop(); // Force Restart
await TrackPlayer.reset();
await togglePlay();
} else {
await TrackPlayer.pause();
}
}
}
This is the code we currently have in our app. Works for Android and iOS. createTrack returns a Track object based on the radio station that is selected (live streaming).
the "waitForBuffer" fixes the issue.
This is not documented and probably a lot of people miss adding it to the setup call.
Thanks @dodgex
let playerActive: boolean = false; function setupPlayer() { if (playerActive) { return; } playerActive = true; TrackPlayer.setupPlayer({waitForBuffer: true}).then(async () => { await TrackPlayer.updateOptions({ stopWithApp: false, capabilities: [TrackPlayer.CAPABILITY_PLAY, TrackPlayer.CAPABILITY_PAUSE, TrackPlayer.CAPABILITY_STOP], compactCapabilities: [ TrackPlayer.CAPABILITY_PLAY, TrackPlayer.CAPABILITY_PAUSE, TrackPlayer.CAPABILITY_STOP, ], notificationCapabilities: [ TrackPlayer.CAPABILITY_PLAY, TrackPlayer.CAPABILITY_PAUSE, TrackPlayer.CAPABILITY_STOP, ], }); }); } export async function togglePlay() { setupPlayer(); const playbackState = await TrackPlayer.getState(); const currentTrack = await TrackPlayer.getCurrentTrack(); if (currentTrack == null) { await TrackPlayer.reset(); await TrackPlayer.add(createTrack(currentStation)); await TrackPlayer.play(); } else { if (playbackState === TrackPlayer.STATE_PAUSED) { await TrackPlayer.stop(); // Force Restart await TrackPlayer.reset(); await togglePlay(); } else { await TrackPlayer.pause(); } } }This is the code we currently have in our app. Works for Android and iOS.
createTrackreturns aTrackobject based on the radio station that is selected (live streaming).
I cannot get createTrack to work, as it says it is not a function.
How do you get the currentStation values to work?
@maendamedia: you have to implement your own createTrack that returns a Track. the currentStation might not be relevant to you.
That code is from an internet radio app I work on.
@maendamedia: you have to implement your own
createTrackthat returns aTrack. the currentStation might not be relevant to you.That code is from an internet radio app I work on.
Got it. Thanks for reply. That part is working.
Except the main issue still exists: immediately pause after run play. Even with waitForBuffer enabled
Most helpful comment
I noticed similar issue when playing a radio (live audio) stream.