Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Proposal) Support Multiple Sources for HLS Segments #6

Open
ferrohd opened this issue Oct 15, 2023 · 1 comment
Open

(Proposal) Support Multiple Sources for HLS Segments #6

ferrohd opened this issue Oct 15, 2023 · 1 comment

Comments

@ferrohd
Copy link

ferrohd commented Oct 15, 2023

What

Currently, wasp-hls fetches segments only from HTTP (as per HLS spec). My proposal is to make the WASM module extensible, allowing different sources to gather the segments from.
I.e. some sources could be WebTransport, WebSocket, WebRTC or Local/Session Storage

Why

Some examples why this could be useful:

  • Integrating WebRTC streaming unlocks P2P connectivity between users allows HLS segments to be shared directly among peers, reducing the load the server, like a distributed CDN.
  • Local Storage provides caching capabilities enabling offline playback when network connectivity is temporarily unavailable.

How

Proposal 1 (better)

A possible implementation could be to generalize the Requester struct into a RequesterTrait enabling the user to implement different behaviours (i.e HTTPRequester, WebRTCRequester, LocalRequester ecc.) or a combination of them.

As of now the process of fetching a segment goes through:
Dispatcher -> Requester -> Rs Binding -> Ts Bindings -> Dispatcher
where the Requester is tighly coupled with the JS Bindings due to the fetch being executed at Worker-level and returning the data with callbacks.

A possible solution could be to execute the fetch inside the WASM (rs-core) enviroment instead of the JS one.

Proposal 2 (janky af)

Intercept the fetch request in the Worker and pass it to rs-core where multiple handlers can be implemented and send back the response.

@peaBerberian
Copy link
Owner

Hello,

Interesting, thanks.
I also did integrate in the past on another player an API allowing Peer-to-Peer segment sharing through WebRTC, though it was on a full JS DASH player (the RxPlayer).

To my understanding, the WebRTC APIs still need to be called in JS (they are not available through WebAssembly directly), so here the WebRTC code would probably be in the ts-worker part. We may provide another method (or several methods) than fetch for this if it makes sense or reuse fetch in some ways if it's not necessary.
I don't know that much the complexities behind real media P2P code and as such I'm not sure of whether there is some logic-heavy code that might profit from being executed in WebAssembly here.

By the way, it's the same situation with the fetch API.
I guess the wasm-bindgen example automatically generates the JS bindings to make it transparent but after compilation, it would still go the WASM -> JS -> WASM route. Even if it could be more readable in a way to rely on a fetch call directly in Rust, I'm wary of adding such library magic for requests as it is a very important part of an adaptive player - I prefer wasp-hls to stay fully in control (as in: understanding as much as possible what is done) on that part.
As a tangent, the wasm-bindgen example seems to transform a JS Promise into a Rust future, which is something I only explored initially in wasp-hls. I now try to avoid at maximum the Rust async can of worms as it leads to a lot of complexities. I'm happy that by relying on the browser's event loop for everything IO, we can keep the WebAssembly code in a unique thread and only rely on synchronous code for Rust.
We can then easily stay compatible with Rust rules by keeping a synchronous Rust code -> Asynchronous TS binding -> synchronous Rust code pattern and ensuring that TS bindings never calls back into Rust code synchronously (e.g. to avoid mutating values still used by the method which made the original ts-binding call from Rust, which might not yet have finished to run).

To go back to P2P, what we did at the time we implemented it on the other DASH player, was to let an application define its own fetching logic and provide it as a callback through a player API which was then called by the player at request time.
That way, various P2P logic (for example, coming from several competing P2P-specialized companies) could be plugged in with no much effort on our side.
Though the same solution is not that easy to implement on wasp-hls, due to the fact that the fetching happens in a WebWorker but the application providing the fetching logic would be in the main thread. Transferring closures in-between JS realms is impossible so tricks have to be found here (as a side-note, I did also encounter the exact same worker situation in an ongoing development on the RxPlayer. We decided there for now to still allow such JS callbacks to be defined as a string, as long as it is guaranteed that no outer scope is captured by it. The player's main thread logic then transmits that string to the worker and creates, through a Function constructor, the function on the worker-side).

Local Storage provides caching capabilities enabling offline playback when network connectivity is temporarily unavailable.

The Local Storage API specifically is fairly limited in size (10 MB?) and as such is not adapted for storing large media segments for offline playback, yet the IndexedDB API allows much more storage so you're right that segment retrieval from storage may be an useful feature. That's something implemented for example by the shaka-player for offline playback.
Here also the API would only be in-JS anyway, so I guess either plugging a specific logic in fetch of having a separate method in the TS bindings would work.

Thoughts?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants