zackls / StreamDisco

A react webapp for hosting silent discos, no equipment required
MIT License
6 stars 1 forks source link

a few questions please #9

Open vesper8 opened 3 years ago

vesper8 commented 3 years ago

First of all thanks for making and sharing this, it seems to be exactly what I need.

My question is.. how do I host my own streams? What kind of streams are supported? Is it only Soundcloud? Does it also work with Shoutcast? Or anything else?

What if I have an MP3 playlist hosted somewhere on a server and I want to make a stream from it, and then I want multiple clients to be able to connect and sync so we're all listening to the same music at the same time.

Could you say a few words on how I may be able to accomplish this?

vesper8 commented 3 years ago

Ok so I answered my own question, I see you're using react-player and that supports a lot of different streams including links to mp3. I set it up on a server and it loads and plays the music. But I don't understand how I can make it sync between multiple clients. Is that all happening by setting a specific timestamp?

What happens when clients join after the timestamp has passed though?

vesper8 commented 3 years ago

I can see that the code attempts to sync based on the start timestamp but it doesn't work very well and the music is out of sync by at least one second but in most cases it's off by multiple seconds. It would be useful to have the current seek time displayed for debugging so that we can fine tune the syncing, possibly manually, or at least it would be helpful for coming up with a syncing strategy that is more precise

zackls commented 3 years ago

Hi @vesper8! I have a commit i realize now I haven't yet pushed to attempt to sync server time across devices (since devices all believe its a slightly different time than one-another). This commit doesn't entirely solve the problem but gets sync to <0.5 seconds roughly in the worst cases. This is canonically a really tough problem to solve, and unfortunately very important in this type of environment which requires perfectly synced devices.

Glad you were able to answer your question, I haven't explored much into different types of streams / files. Will push that commit next time I open up the project.

zackls commented 3 years ago

Oh hm i think i did push that commit after all, I think you would be seeing the current behavior. I am very open to solutions and suggestions to improve this

vesper8 commented 3 years ago

Hrm.. you did eh? You gave me hope there for a minute. I've done more tests and the audio is quite out of sync. This might be because the mp3 file is a big one (200mb). I haven't tried with a smaller file. You can see my test here https://music.cbass.dev/

As you can see if you open up the track on a phone and your laptop they are quite out of sync. It's not the best track to realize just out of sync they are but it feels like multi-seconds. It would be helpful to see the seek time (eg 00:20 / 58:32) so that we could visually tell how out of sync the two devices are.

It does seem like a tough challenge to crack. I'm currently imagining a complex solution that involves websockets to allow the clients to communicate with each other to try and correct the sync (when deemed out of sync). Not sure if the clients would connect directly with each other or coordinate with a server which would become the source of truth and decide which clients should continue as-is and which clients should make corrections)

Maybe there's a simpler way.

I also feel that if the data source was for example a live radio stream it might work better than if it's a large mp3. If it was a live stream then it's more likely everyone would by default be in sync.

I haven't found a nice open source solution for creating a live stream from a mp3 but I'm keeping an eye out for one.

Would love to hear your thoughts on.. well.. my thoughts : )

zackls commented 3 years ago

There is one more issue with iOS browsers I've run into. For some reason on iOS devices specifically, browsers are unable to play audio when it's not triggered synchronously by a user action. So playback on iOS devices is currently not working. I have a rewrite of the Player planned for this issue to make audio playback synchronous with the "Start listening" button.

Introducing a middleman between the clients would be a pretty large shift, as it would introduce a new server that clients communicate with, though this might solve the issue and could be worth the investment / complexity. I wonder if theres a server out there that we could use instead of defining our own?

You may view current player lag by flipping DEBUG_PLAYER_LOGGING, either by setting the environment variable or setting it to true locally, see https://github.com/zackls/StreamDisco/blob/master/src/Page.tsx#L128

Im curious how in-sync common streaming options like twitch / youtube / facebook / etc are. While they might be live streams, im not sure they really have to solve problems like this since you would want to keep clients close to one-another but not necessarily perfectly synced.

vesper8 commented 3 years ago

Another thing I thought might be exacerbating the out-of-sync issue is if whether the syncing logic occurs before the file is ready to play?

So if I click on a stream and it links to a 200mb mp3, does it give is the instruction to "seek ahead to X based on timestamp logic" immediately, before the file is "loaded" and ready to play? Or does it wait until the file is "ready", through some event listener if available, and only then does it check the server time and start time and come up with the logic to determine the correct seek?

Introducing websockets is for sure making matters a lot more complex but if it solves the problem then yea it may be worth it. I guess if you used something like Pusher's free tier, it may be possible to do it without using a server and all communications could be between peers only

zackls commented 3 years ago

It's a little complicated but I think makes sense. On track progress (the library fires this callback every second), we compare the current seek of the track to where we think we should be seeked to based on the start time of the track, our client time, and our offset from the server time (which is re-determined every 5s). If the difference here is greater than a threshold, we either:

  1. Set a timeout and wait until the stream catches up to us, if we're ahead of the stream.
  2. Seek forward to the stream time + some amount of time and then repeat our check of if were at the expected time if we're behind the stream. If we fail this check again by being behind (which will happen if we didnt load the stream in time), we seek forward again but this time we increase the offset by a factor to give ourselves a better chance of loading at the correct time.

I haven't seen pusher yet! I think my current top priority is to fix iOS streaming right now, and then look into a better server time solution

vesper8 commented 3 years ago

Hrm.. so where do you think the off-sync issue is occurring then? Could it be because the call to fetch("http://worldtimeapi.org/api/ip") adds an unpredictable latency which is hard to calculate for? It seems that your logic is quite solid and yet somehow there is a big out-of-sync issue

zackls commented 3 years ago

Im not entirely sure, that's definitely the most likely source of the issue imo. I estimate that the time is determined at the middle of that call, when in reality it could be different. It might be worthwhile to try out different server time sources (there are lots), or look into a different flavor of solution, like the ones you've suggested, though that would be a larger time investment.

fivefortyfour commented 2 years ago

I'd love to try this.... but I haven't a clue what to do with the files! Have you got a step by step guide please? thanks

zackls commented 2 years ago

@fivefortyfour this seems unrelated to OPs issue - did you define a data.ts file as described in https://github.com/zackls/StreamDisco#installation--development ? If you have more questions I'd love to help in a separate issue!