eshaz / icecast-metadata-js

Browser and NodeJS packages for playing and reading Icecast compatible streaming audio with realtime metadata updates.
155 stars 20 forks source link

Creating a "slim", player only package without metadata #163

Closed Yahav closed 1 year ago

Yahav commented 1 year ago

So, you've created a cross-platform, cross-encoding stream player with a unified API here which is not something to be taken lightly. I feel like there should be a version of this which is simply and only the streaming playing capabilities, stripping the whole metadata related dependencies and code could make the package really lightweight which is very desirable. Will you consider this? I might be able to sponsor this fork as well should you decide its a good idea.

eshaz commented 1 year ago

I think this is a good idea. I'm thinking it could be implemented slightly differently though.

The bulk of the code size is consumed by the wasm-audio-decoders and the synaudio libraries. The metadata functionality is actually quite small in comparison, probably less than 10kB.

I think it would be good to have these features able to be selectively enabled / disabled so that when they are disabled, the extra code isn't bundled when used with a bundler such as webpack or rollup.

One main example would be that if you only ever use Opus for your stream, you won't need the MP3 or FLAC decoders, which would save about 150kB. Or if you don't want iOS support / WebAudio, you could remove the decoders altogether.

This kind of change would require some significant refactoring of the existing code, but I'll take a look at this.

Yahav commented 1 year ago

@eshaz Is there anyway we can have a quick conversation about that?

eshaz commented 1 year ago

We can, but I would prefer to use this issue thread so that others can reference this in the future. Will that work?

Yahav commented 1 year ago

I think it would be good to have these features able to be selectively enabled / disabled so that when they are disabled, the extra code isn't bundled when used with a bundler such as webpack or rollup.

One main example would be that if you only ever use Opus for your stream, you won't need the MP3 or FLAC decoders, which would save about 150kB. Or if you don't want iOS support / WebAudio, you could remove the decoders altogether.

You are absolutely right, that is even better :) If we're going this route, imho the best way to achieve that is not to pre-configure which modules/decoders/mechanisms you're going to use, but rather have them dyanmically imported on a "need" basis, for instance, if an Opus stream is initiated and we're on a platform that doesn't support native playback, await import() the desired module to handle this situation, same goes for the metadata mechanisms and cross-play feature.

eshaz commented 1 year ago

await import() the desired module

That's a good idea, and probably the way to go.

It would be preferable to have the dependencies pulled in as needed, but I'm wondering if bundlers would be able to evaluate the condition whether or not to execute the import if it happens during runtime. There might have to be a hard coded parameter set on the library that bundlers could use to evaluate the conditional import statements and strip out those unused dependencies.

It seems reasonable for those who want to save a couple hundred kilobytes to explicitly enable / disable features if it can't be done at runtime. Just for reference, the minified bundle with all features including MP3, Flac and Opus audio decoders is 273KiB.

eshaz commented 1 year ago

@Yahav, it turns out the await import() solution works great with webpack.

If you want to take a look, I have #164 that splits the icecast-metadata-player into separate files that are downloaded as needed by the library. This should allow for a bit less data to be loaded on the initial page load.

Yahav commented 1 year ago

Amazing! I'll have a look into this later this week. This dynamic import loads the different kind of encoders, how about the rest of the optional mechanisms, like the metadata fetching, are those something that you might be able to dynamically import as well?

eshaz commented 1 year ago

The metadata fetching isn't all that much code, and it's tightly integrated with everything else so I'm going to leave that built in for now.

The main reason to do this is to reduce the initial load time and total size, which is enhanced the most by deferring the decoders.

Yahav commented 1 year ago

Not sure if that's intended. but when setting the playback method to html5, the mediasource.js file is loaded anyway. If you select mediasource as the playback method, it loads it twice, once when the page loads and again when you play the audio.

Yahav commented 1 year ago

@eshaz As an addition to the above reply. I found out that we can save further size by dynamically loading the EmscriptenWasm.js as well, it takes a rather large size of each encoder.. screenshot

eshaz commented 1 year ago

It should be good as is. That file is needed for the WASM audio decoders to function, so it all needs to load if one of the decoders is also needed.

Yahav commented 1 year ago

@eshaz ok. What about that one though: https://github.com/eshaz/icecast-metadata-js/issues/163#issuecomment-1518690986

eshaz commented 1 year ago

It preloads since most browsers use mediasource by default (everything except iOS), and then loads it again from cache in the import. The preload made starting playback a bit more responsive at the cost of just a few extra KB downloaded for iOS users.