I'm developing a music streaming app that uses React Native Track Player to handle music in app and in the background.
I'm fetching music from a server through Socket.IO and I get all the song as expected.
In this application, the admin can choose to start the streaming and then all the users connected to the app will receive the song and will start on their device. If a user connect when a song is already started, the user will receive the song and the current position. So, if a song is already started, will seek to the current position.
The problem is that not always the seek works. Sometimes it starts from 0, other times in an apparently random position. Only a few times the position seek well.
I thought that the song wasn't buffered completely, but if I try to output the current position and buffered position before and after the seek, it is right.
How could I resolve this? I should wait that all the song is buffered, and then seek?
If this module can't do this, are there other valid modules?
I tested this only on Android for now, idk if on iOS works.
Here the code:
This is the streaming function, listen from the server for songs...
socketStreaming.on("firstSong", async (song) => {
await TrackPlayer.reset();
await TrackPlayer.add({
id: song.id,
url: song.url,
title: song.title,
artist: song.artist,
artwork: song.artwork,
});
console.log(
`${await TrackPlayer.getBufferedPosition()} - ${await TrackPlayer.getPosition()}`
);
await TrackPlayer.getBufferedPosition().then(async () => {
if (song.seekTo !== undefined) await TrackPlayer.seekTo(~~song.seekTo);
console.log(
`${await TrackPlayer.getBufferedPosition()} - ${await TrackPlayer.getPosition()}`
);
});
await TrackPlayer.play();
console.log(
`${await TrackPlayer.getBufferedPosition()} - ${await TrackPlayer.getPosition()}`
);
setSrcButton(pauseImage);
});
socketStreaming.on("nextSong", async (song) => {
await TrackPlayer.add({
id: song.id,
url: song.url,
title: song.title,
artist: song.artist,
artwork: song.artwork,
});
});
socketStreaming.on("seek", async (second) => {
await TrackPlayer.seekTo(second);
await TrackPlayer.play();
setSrcButton(pauseImage);
});
This is the setup function, that setup the player...
await TrackPlayer.setupPlayer({
waitForBuffer: true,
});
await TrackPlayer.updateOptions({
capabilities: [
TrackPlayer.CAPABILITY_PLAY,
TrackPlayer.CAPABILITY_PAUSE,
TrackPlayer.CAPABILITY_SEEK_TO,
],
compactCapabilities: [
TrackPlayer.CAPABILITY_PLAY,
TrackPlayer.CAPABILITY_PAUSE,
],
notificationCapabilities: [
TrackPlayer.CAPABILITY_PLAY,
TrackPlayer.CAPABILITY_PAUSE,
],
});
Both functions are inside a useEffect(callback, []), so will be called only on startup
Thanks for the help :)
I was stuck with the same problem.
Resolved it by playing the track first and then seek to the required position.
Something like this,
await TrackPlayer.play();
await TrackPlayer.seekTo(second);
Related
My app downloads audio files to device and has a audio player which plays that audio.
So I am trying to add song to my playlist while audio is playing. I am using flutter package just_audio: ^0.9.7 . I query all the songs from device from a specific folder and use these two function in init state
Future<List<AudioSource>> _getList(List<SongModel> something) async {
List<AudioSource> _list = [];
something.forEach((element) {
_list.add(
AudioSource.uri(
Uri.directory(element.data.toString()),
tag: MediaItem(
id: element.id.toString(),
title: element.title.toString(),
),
),
);
print(element.data);
});
return _list;
}
Future<void> settingAudioPlayer() async {
List<SongModel> something = [];
something =
await OnAudioQuery().querySongs(sortType: SongSortType.DATA_ADDED);
something
.removeWhere((element) => !element.data.contains('StrikleDustiness'));
//print('${something[0].data}');
var items = await _getList(something);
await MainScreen.player.setAudioSource(
ConcatenatingAudioSource(useLazyPreparation: true, children: items),
);
}
by doing this I am able to play song whenever I restart the app. But I want to add song to the playlist whenever I download new song while playing the existing song . I want the new download song to be my next queue.
Sorry for the bad english and also I am new to flutter.
Thanks..
EDIT: I figured it out. I just had to create a separate list containing all the video sources.
We create an app with React Native WebRTC and Janus Gateway. It works as wanted. Our app is based on push to talk. So when an users in listen mode need to break microphone for other apps. And take back when anyone press to speak button.
Breaking Mic:
if (!globalTrack) {
globalTracks = config.myStream.getTracks();
}
config.myStream.getTracks().forEach(t => {
config.myStream.removeTrack(t);
});
Get Back the Tracks:
globalTracks.forEach(t => {
config.myStream.addTrack(t);
});
pluginHandle.createOffer({
media: { addVideo: true },
success: function(jsep) {
Janus.debug(jsep);
pluginHandle.send({message: {audio: true, video: false}, "jsep": jsep });
},
error: function(error) {
console.log("WebRTC error... " + JSON.stringify(error));
}
});
// also I have try this:
devices = await mediaDevices.getUserMedia({audio: true, video: false})
// devices output https://pastebin.ubuntu.com/p/KQqBq2QRy3/
devices._tracks.forEach(t => {
config.myStream.addTrack(t);
});
pluginHandle.createOffer({
media: {audio: {deviceId: devices._tracks[0]['id']}, replaceAudio: true},
success: function(jsep) {
pluginHandle.send({message: {audio: true, video: false}, "jsep": jsep});
},
error: function(error) {
console.log(("WebRTC error... " + JSON.stringify(error));
}
});
Problem:
Problem: E.g: when a call came to the phone and answered it. The user's voice does not pass anymore even after restarting the app.
On Janus Gateway every things is normal. I think problem is about renegotiation.
Problem occurred only on Android phones. And works after force stopping the app.
Janus Gateway Log which when I speak at room:
There's a message for JANUS AudioBridge plugin
Setting muted property: true (room 20, user 2301490876606211)
Notifying participant 329012611897879 (kardan)
Sending event to transport...
>> 0 (Success)
We can not find a generic solution.
For now restarting app is works with react-native-restart
I'm using this library for twilio video calling https://github.com/blackuy/react-native-twilio-video-webrtc
The participant view is showing black after connection. I'm not getting any error. Values are coming in _onParticipantAddedVideoTrack. Other callbacks are working.
_onParticipantAddedVideoTrack = ({ participant, track }) => {
//console.warn("onParticipantAddedVideoTrack: ", participant, track)
console.log("conference 10 ", participant, track);
this.setState({
videoTracks: new Map([
...this.state.videoTracks,
[track.trackSid, { participantSid: participant.sid, videoTrackSid: track.trackSid }]
]),
});
this.setState({
trackSid: track.trackSid, isParticipant: true,
trackIdentifier: { participantSid: participant.sid, videoTrackSid: track.trackSid }
});
}
<TwilioVideoParticipantView
style={styles.remoteVideo}
key={this.state.trackSid}
enabled={true}
trackIdentifier={this.state.trackIdentifier}
/>
I want to execute some task (e.g. fetch data from server) in background even app is closed in Flutter App.
So How can I achive this?
Its' better If anyone provide example for that.
I am trying using android_alarm_manager but facing below issue:
I have cloned ahttps://github.com/jsoref/flutter-plugins/tree/master/packages/android_alarm_manager/example
Modified code as below:
void printPeriodic() => printMessage("Periodic!");
void printonDelayed() async {
int i = 0;
while(i < 50) {
printMessage("printonDelayed:" + i.toString());
await sleep1();
i++;
}
Future<String> sleep1() {
return new Future.delayed(const Duration(seconds: 1), () => "1");
}
await AndroidAlarmManager.oneShot(
const Duration(seconds: 1), oneShotID, printOneShot, exact: true);
await AndroidAlarmManager.oneShot(
const Duration(seconds: 1), 2, printonDelayed, wakeup: true, exact: true);
Periodic! is printing even if I close app.
printonDelayed is not printing if I close app
Android Version: 8.1.0
I am about to build an app which locally stores multiple MySqlite *.db files. These are used to populate the app with data.
The app should regularly check if there is a newer version for one of these files and, if so, download it and replace the old version of the file.
This update process should happen in the background, while the app is inactive or even closed.
I have seen several plugins, which can be used to execute tasks in the Background (like https://www.npmjs.com/package/react-native-background-task). This could be used to check for updates regularly, but the time limit of 30s on iOS will probably not be enough to download the *.db file. Also, the plugin forces a minimum Android API Version of 21.
My question is: Is it possible to poll for updates in the background AND download them, replacing old files?
I found some useful plugins.
1. react-native-fetch-blob
https://github.com/wkh237/react-native-fetch-blob/wiki/Classes#rnfetchblobconfig
It has IOSBackgroundTask option.
RNFetchBlob
.config({
path : dest_file_path,
IOSBackgroundTask: true,
overwrite: true,
indicator: true,
})
.fetch('GET', download_url, {
//some headers ..
})
.progress( (received, total) => {
console.log('progress : '+ received + ' / ' + total);
})
.then((res) => {
console.log('# The file saved to :', file_path);
})
By the way, it looks doesn't work properly.
Not sure if I miss something...
2. react-native-fs
https://github.com/itinance/react-native-fs#downloadfileoptions-downloadfileoptions--jobid-number-promise-promisedownloadresult-
const ret = RNFS.downloadFile({
fromUrl: download_url,
toFile: dest_file_path,
connectionTimeout: 1000 * 10,
background: true,
discretionary: true,
progressDivider: 1,
resumable: (res) => {
console.log("# resumable :", res);
},
begin: (res) => {
// start event
},
progress: (data) => {
const percentage = ((100 * data.bytesWritten) / data.contentLength) | 0;
console.log("# percentage :", percentage);
},
});
jobId = ret.jobId;
ret.promise.then((res) => {
console.log('Download finished.');
RNFS.completeHandlerIOS(jobId);
jobId = -1;
}).catch(err => {
console.log('error');
jobId = -1;
});
It looks working well.
By the way, when I try download in background via push notification, it doesn't start download unless I open the app.
Anyone can solve this problem?
For download in the background, the best module I have used was react-native-background-downloader, which have pause, resume and the download percentage.
import RNBackgroundDownloader from 'react-native-background-downloader';
let task = RNBackgroundDownloader.download({
id: 'dbfile',
url: 'https://link-to-db/MySqlite.db'
destination: `${RNBackgroundDownloader.directories.documents}/MySqlite.db`
}).begin((expectedBytes) => {
console.log(`Going to download ${expectedBytes} bytes!`);
}).progress((percent) => {
console.log(`Downloaded: ${percent * 100}%`);
}).done(() => {
console.log('Download is done!');
}).error((error) => {
console.log('Download canceled due to error: ', error);
});
// Pause the task
task.pause();
// Resume after pause
task.resume();
// Cancel the task
task.stop();
This module is also suitable for iOS and you can download multiple files in a row in the background.