Giter Club home page Giter Club logo

icecast-metadata-js's Introduction

Icecast Metadata JS

Icecast Metadata JS is a collection of Javascript modules for streaming audio playback with metadata.

Checkout the demos here!

Modules:

  • Icecast Metadata Player (click to read more)
    • Browser library that plays streaming audio with full cross-platform codec support and real-time metadata updates.
    • NPM Package - Install using npm i icecast-metadata-player
    • LICENSE LGPL 3.0 or Greater
  • Icecast Metadata JS (click to read more)
    • Browser and NodeJS library for reading audio and metadata from an Icecast response body.
    • NPM Package - Install using npm i icecast-metadata-js
    • This module actively used here to display real-time metadata updates: https://dsmrad.io
    • LICENSE LGPL 3.0 or Greater
  • Icecast Metadata Stats (click to read more)
    • Browser and NodeJS library that queries an Icecast compatible server for metadata and statistics.
    • NPM Package - Install using npm i icecast-metadata-stats
    • LICENSE LGPL 3.0 or Greater
  • Stream Recorder (click to read more)
    • NodeJS based application for recording / archiving Icecast audio and metadata.
    • NPM Package coming soon!
    • LICENSE GPL 3.0 or Greater

Troubleshooting


Demo

The Demo is a React application that demonstrates how to use icecast-metadata-player in a React application in a plain HTML webpage. You can view the demo source code here.

Developing Locally

Requirements

  • Latest LTS version of NodeJS
  • Unix like shell
    • Linux / MacOS: any shell should work to run npm commands
    • Windows: Git BASH is recommended

Developing with the React Demo

The icecast-metadata-player module is installed using a relative path and will automatically update with any changes made to the package.

  • git clone https://github.com/eshaz/icecast-metadata-js.git
  • cd src/demo
  • npm i
  • npm start -> Runs a local server on http://localhost:3000

Developing with the HTML demos

The HTML demos use the <script> tag method to import icecast-metadata-player which is built independently.

  • Follow the steps above to run and start the React server.
  • Navigate to either of the demo pages
  • In a new terminal window, run the below commands to install and build icecast-metadata-player.
  • cd src/icecast-metadata-player
  • npm i
  • npm run build
  • Refresh your browser to pull in the latest changes.
  • Each time you make a change, run the npm run build and refresh your browser to view the change.

Troubleshooting

HTTP and HTTPS Mixed Content

Browsers are configured by default to disallow mixed security content when the origin is being served from HTTPS. This means that any requests using HTTP that are being accessed from a HTTPS origin will be blocked and an error will be shown in the browser console. This affects many Icecast streams since the default is to serve a stream via HTTP and not HTTPS.

The simplest and most secure way to fix this is to configure Icecast to serve only over HTTPS. HTTP, unlike HTTPS, is sent in clear text and can be easily intercepted, viewed, and / or modified by any party in between you and the server potentially injecting unwanted data in your request and corrupting your stream. See the Icecast documentation for more information on how to configure HTTPS.

See Also:

CORS

Cross-Origin Response Sharing is a client side security mechanism to prevent scripts from accessing other websites outside of the website the script originated from. Websites can opt-in to CORS by responding with various Access-Control headers. Browsers will send an pre-flight OPTIONS request to the cross-origin resource when a script attempts to access a cross-origin resource. The actual request will be allowed only if the OPTIONS response contains the appropriate Access-Control headers.

Read more about CORS here: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

Ogg Metadata is not dependent on requesting or reading any headers, but still relies on CORS for reading the response cross-origin.

ICY metadata is dependent on being able to request and read headers (specifically the Icy-* headers). If you intend on serving your Icecast stream on a website that is not on the same origin as your Icecast server, you will need to add the below CORS HTTP response headers.

CORS configuration for Ogg metadata:

  • Ogg Metadata will not work in a browser without this configuration.
Access-Control-Allow-Origin: '*'
Access-Control-Allow-Methods: 'GET, OPTIONS'
Access-Control-Allow-Headers: 'Content-Type'

CORS configuration for ICY metadata:

  • ICY Metadata will not work in a browser without this configuration.
Access-Control-Allow-Origin: '*'
Access-Control-Allow-Methods: 'GET, OPTIONS'
Access-Control-Allow-Headers: 'Content-Type, Icy-Metadata'

CORS configuration for authenticated streams:

Access-Control-Allow-Origin: '*'
Access-Control-Allow-Credentials: 'true'
Access-Control-Allow-Methods: 'GET, OPTIONS'
Access-Control-Allow-Headers: 'Content-Type, Icy-Metadata, Authorization'

Examples of common and invalid CORS configuration


Problem

Invalid duplication of headers containing *. This is caused by a proxy such as Nginx adding additional headers to an otherwise valid CORS configuration. This will prevent any cross origin playback for your stream.

Example invalid CORS response headers due to invalid configuration:

access-control-allow-credentials: *
access-control-allow-credentials: true
access-control-allow-headers: *
access-control-allow-headers: *
access-control-allow-origin: *
access-control-allow-origin: *

Fix

Only add the CORS headers once, either in Icecast or in your proxy, not both.


Example Nginx reverse proxy configuration for ICY metadata

# Match your stream location(s)
location ~ "^/stream/(stream.mp3|stream.ogg|stream.aac|stream.opus|stream.flac.ogg)$" {
    # Remove all headers from Icecast response
    proxy_hide_header Access-Control-Allow-Origin;
    proxy_hide_header Access-Control-Allow-Methods;
    proxy_hide_header Access-Control-Allow-Headers;
    proxy_hide_header Access-Control-Allow-Credentials;

    # Response to CORS OPTIONS request made by browser
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET,OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'Icy-Metadata';
        return 204;
    }

    # Add CORS headers for stream GET response
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Methods' 'GET';
    add_header 'Access-Control-Allow-Headers' 'Icy-Metadata';
    add_header 'Access-Control-Expose-Headers' 'Icy-MetaInt,Icy-Br,Icy-Description,Icy-Genre,Icy-Name,Ice-Audio-Info,Icy-Url';

    resolver 1.1.1.1;
    proxy_pass https://icecast-server.example.com:8443/$1
}

icecast-metadata-js's People

Contributors

bajakigabesz avatar dependabot[bot] avatar eshaz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

icecast-metadata-js's Issues

Add option to retry after successful stream end

In IcecastMetadataPlayer, a new option should be added that triggers retry logic after a successful disconnect of a stream. This may help listeners reconnect when a disconnect is caused by something other than a network error, such as the Icecast server / source disconnecting and restarting the stream.

I've seen that the majority of Icecast streams are meant to continue indefinitely, such as with a radio station. In these cases, any disconnect should be treated as an error. This option might make sense to default to true because of this usage pattern.

options.alwaysReconnect: default true

  • Set to true to reconnect after a stream is disconnected for any reason.
    • Retry logic will take place when the stream ends with or without a fetch request error.
    • The stream will end without an error if the Icecast server initiates the disconnect (i.e when a source ends or the Icecast server is restarted).
  • Set to false to stop playback after a stream is disconnected successfully on the server side.
    • Retry logic will take place when the stream ends only with a fetch request error.

IcecastMetadataPlayer: Retry logic

There should be some logic to retry the request if the connection is dropped or some other recoverable network error occurs. The below options are an initial idea to configure retries.

Options

retryTimeout:

  • Number of seconds to attempt retries.
  • Set to a value greater than 0 to enable retries.

retryInterval:

  • Number of seconds to wait between each retry.
  • 1 (default)

Metadata without playing

Several users have expressed interest in using this library to retrieve a stream's metadata without playing. This already exists in Icecast through a JSON api, but it would be nice to offer an implementation that utilizes this. There also may be cases where that API is unavailable, and the only way to retrieve metadata is to use the stream request.

  • Should default to using the Icecast JSON api, and default to settings that use the least amount of request data.
  • Interval to check for metadata is configurable.
  • Option to fetch the beginning of a stream, and then abort as soon as the first metadata comes in.
  • Option to retrieve at least the full burst on connect data to build a metadata queue, possibly using codec-parser to calculate the timestamps.

See #45 and #62

onStream, onError seemingly fail to be invoked

I've been able to use the player, get audio, and parse incoming metadata events. However, onStream and onError do not seem to produce any result. I'm looking for a way to detect whenever the stream goes down for instance, and I thought onError could do just that (and I suppose onStream could notify when the stream is back up). However if the player is playing and I cut the stream, onError is not triggered. Furthermore onStream never seems to be triggered by any event.

const player = new IcecastMetadataPlayer(endpoint, { 
   onStream: (input) => { console.log(input); }, 
   onError: (input) => { console.log(input); },
   onMetadata: (input) => { onMetadata(input); }, 
   metadataTypes: ['ogg'] 
});

Opus / Vorbis metadata

icecast-metadata-js should also support extracting metadata from an Opus / Vorbis stream in addition to Icy Metadata. This functionality should be able to be added to IcecastMetadataReader and acomidate for minimal user knowledge of the actual metadata source.

How to use icecast-metadata-js in-browser?

So far I have been unable to figure out how to use the metadata scripts in-browser, specifically IcecastMetadataReader.

I've tried to include IcecastMetadataReader.js in the html document, but it is a Node.js-specific file and results in console errors when included.

I also tried to "browserify" the script using the Node.js browserify tool. While including the resulting .js file no longer results in a console error on page load, I cannot use the IcecastMetadataReader class in any other scripts and it errors out as being undefined.

Help would be greatly appreciated in getting started here. I'm likely missing something simple but I can't figure out what it is.

Handle autoplay exceptions

Hey,

newer browsers need a permission to play audio automatically on site load. I've made an audio player that remembers its latest play state and resumes playing on site reload. If the site doesn't have the autoplay enabled, calling play() will result in an unhandled promise exception:

image

Console message: Uncaught (in promise) DOMException: play() failed because the user didn't interact with the document first. https://goo.gl/xX8pDD

You might want to look into this and handle the exception properly so I can act on it from within my player code.

Thanks!

NodeJS playback support

It should be possible to add support for desktop playback using the NodeJS Speaker and decoding the audio with WASM. This could be based on the webaudio playback method.

Any changes to enable NodeJS playback should not add additional dependencies nor break / modify the existing API in any significant way.

iOS / Webkit - No Media Source Extensions Support

icecast-metadata-player uses the Media Source Extensions API to play the stream audio in the browser. This works great in most browsers, except iOS / Webkit, which is the only modern browser that doesn't support MSE. iOS should still play the stream, but there will not be any metadata updates. An alternative will need to be found in order to support real-time metadata updates in iOS.

This is a continuation of #40.

Possible Alternatives:

  • Use a service worker to intercept the fetch request, parse / schedule / trigger the metadata using icecast-metadata-js, and send the stream data as the response to an HTML5 Audio element.
  • Decode the stream data into PCM using a WASM module, and then play the PCM via a ScriptProcessorNode or AudioWorkletNode.
  • Make two requests, one for metadata, and one for audio playback.
    • The audio request can just be the src of an HTML5 Audio element.
    • The metadata request can be parsed through icecast-metadata-js
    • The two requests can be synchronized by matching the current / buffered time in the audio element with the audio time of the metadata request.
      • Audio time of the metadata request can be determined by reading the frame headers. This would involve reading the audio data frame by frame and determining the duration of each frame. This is already being done in mse-audio-wrapper to support the current MSE solution and it could be adapted to expose the frame by frame durations and byte offsets.

Wrap MP3 in MP4 container for MediaSource API compatibility

The browser implementation (see example in readme.md) that is able to read and display metadata relies on the MediaSource API. This is a huge limitation since MP3 is widely used as an Icecast source. Only the Chrome MediaSource API (to my knowledge) supports audio/mpeg for a MediaSource. To be able to show Icecast metadata from a MP3 source on any other browser it will need to be wrapped in a container.

In Firefox MediaSource.isTypeSupported('audio/mp4; codecs="mp3"') returns true, so maybe the right container to use is MP4?

An ideal encoder would take in raw mp3 data, wrap it in an mp4 container, and return the result. The consumer of this should just be able to send raw MP3 bytes, and then pipe the result into a SourceBuffer as audio/mp4.

Existing audio html element

Hi,

is there any way to attach an existing audio html element to your component? So you have a metadataChange event? Without using IcecastMetadataStats...

"Worker is not defined"

ReferenceError: Worker is not defined
from node_modules/icecast-metadata-player/build/icecast-metadata-player-1.10.6.min.js (21:7898)

  • Next.js 12
  • Node 14.18.1

Stream never gets buffered state and stream not starting

When starting a stream on mobile devices, it never gets buffered state, and the stream does not start to play.
It was working properly in version 1.8.0, but not in 1.9.0 and later. I saw there were some changes around the codec. Do you have any ideas about what causes this issue?
If I change the playback method to html5, it looks like it's started but there is no sound.
This issue comes up especially on iPhones and some TVs inbuilt browsers, on Android it's working properly.

Legacy support

Do you can add a option for browser that not support Regex-Groups and Exponentiation (**) ?

Two players on a page

I'm having trouble to put two audio players on a single page. No example nor docs provide info for this case. Can anyone share a correct example of IcecastMetadataPlayer() init when there are audio1 and audio2 elements on a page ? I tried:

    const audioElement1 = document.getElementById("audio1");
    icecastMetadataPlayerEng = new IcecastMetadataPlayer("https://dsmrad.io/stream/isics-all", audioElement: audioElement1);

but this won't work.

Volume control?

Hello, this is a great tool and thank you very much. I use it in my stations.

Is there any way to get/set the volume of the player?

iOS 15 Web Audio Playback Stops while locked

Problem

Web Audio playback stops after playing for a few minutes after the screen is locked on iOS 15 simulator. This issue was not present in iOS 14.8.

Workaround

Set the playbackMethod to "html5" when instantiating.

new IcecastMetadataPlayer("https://example.com/stream", {
  playbackMethod: "html5"
});

iOS 15.5 webaudio playback broken

A change in iOS 15.5 has broken webaudio playback. Audio playback fails to start. Earlier versions of iOS do not have this issue.

This can be reproduced with a MP3 or Opus stream on the demo page.

EROR connection/_handle_connection HTTP request parsing failed

Hi. I hope you're ok. I've been testing my streaming with the "Bare Minimum HTML Demo" but the only response I get is this blocking responses from my server. Maybe I'm missing something in the icecast.xml ??

image

I have also looked at the logs, specifically /var/log/icecast/error.log
After taking a look at it I find that there's an error in the parsing (EROR connection/_handle_connection):

[2021-10-01  16:51:59] DBUG stats/modify_node_event update global clients (0)
[2021-10-01  16:51:59] DBUG stats/modify_node_event update global clients (1)
[2021-10-01  16:51:59] DBUG stats/modify_node_event update global connections (59)
[2021-10-01  16:51:59] DBUG stats/modify_node_event update global clients (0)
[2021-10-01  16:51:59] DBUG stats/modify_node_event update global clients (1)
[2021-10-01  16:51:59] DBUG stats/modify_node_event update global connections (60)
[2021-10-01  16:51:59] DBUG stats/modify_node_event update global clients (0)
[2021-10-01  16:51:59] EROR connection/_handle_connection HTTP request parsing failed
[2021-10-01  16:51:59] EROR connection/_handle_connection HTTP request parsing failed
[2021-10-01  16:51:59] EROR connection/_handle_connection HTTP request parsing failed

Is it something in the icecast.xml, I don't know if I'm missing some permissions, have you come across this problem before?
Thanks for your piece of software. I would like to use it like a charm. If you know something let me know please.

[Player] Next.js: Module not found: Can't resolve 'icecast-metadata-player'

Hi

First of all, thank you so much for this library!

It worked flawlessly on a plain HTML/JS page as a proof-of-concept.
But unfortunately, it seems to not work out using Next.js. I get the error:

Module not found: Can't resolve 'icecast-metadata-player'

You can reproduce the error here:
https://codesandbox.io/s/silly-gates-4wppo?file=/pages/index.js

This is probably because Node.js itself cannot import the player, as seen here:
https://codesandbox.io/s/admiring-hugle-64d86?file=/src/index.js

Is there a way to make this work? Maybe by exporting a dummy object, like the HLS player is doing (https://codesandbox.io/s/admiring-hugle-64d86?file=/src/index.js)

Sync old request and new request with retries (icecast-metadata-player)

When a stream resumes after a retry event it may be possible to sync-up the current audio buffer with the new request data coming in from the new request. Given the new response contains audio that has already been processed in the previous response, and the audio buffer is not emptied, the two streams can be concatenated with no gaps.

  • Parse all incoming audio data into codec frames before sending to mse-audio-wrapper
  • Save hashes of each audio frame that has been received and sent to the SourceBuffer
    • 10 seconds of audio should be enough
    • MD5 hash, or what ever is the least computational effort since 10 seconds of audio is not likely to have hash collisions.
  • When a retry event is successful, compare the hashes of the new audio data with the existing hashes.
  • Remove any matching hashes and append the remaining new data to the SourceBuffer.

Probably will add the audio frame hashing in codec-parser so it can be reused for other use cases.
It's possible that codec-parser could be updated with a dedupliate flag that doesn't reprocess data that's already been processed within the last n seconds.
Will need to update mse-audio-wrapper to accept an array of CodecFrames returned from codec-parser as input.

Icecast only loading or no sound on iOS in Angular

Hello Ethan,

first of all thank you for your hard work. I really like it and it works perfectly on Android. Unfortunately I can't get it to work on iOS. I am using Ionic and Angular. The stream is loading (network tab of developer console), but I can't hear anything. The event listener gives success on play, no error. The stream keeps on loading.

If I use a standard html5 audio without your script the stream plays fine. I really look forward to use your icecast-metadata-player.

Do you have any idea?

CORS Issue with metadata

Using the plugin to stream a ICECAST2 Stream, The meta Data will not load on Chrome:
Falling back to HTML5 audio with no metadata updates. See the console for details on the error.
Chrome Developer JS Console:
Access to fetch at 'http://ICESERVERIP:8000/STREAM.mp3' from origin 'http://WEBSERVERIP' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

Doing some research has lead to this for normal HTML5 Audio Player to disable CORS. Is there a way to implement this into the JS?

<audio controls crossorigin="anonymous">
<source src="http://myicecastserver.dns/mount" type="audio/mpeg" />
</audio>

iOS 10, 12 and 13 playbackMethod error.

On these versions of iOS, the webaudio and html5 method mainly works. If you set an endpoint with the received audio/aac data, then webaudio will be selected in player.js:94, and it does not have aac support in the mapping, because of this, the HTML5 player is eventually selected, because of this an error occurs.

Also on version 10 of iOS, icecastMetadataPlayer.js:55 is checked against window.EventTarget, but somehow it doesn’t work like that. Because of this, errors occur and the player crashes. I solved it by overriding window.EventTarget with a polyfill.

Icy Metadata interval CORS workaround

Some Icecast servers do not allow the ICY metadata interval to be read from the headers. The Icecast server needs to respond with Access-Control-Expose-Header: Icy-MetaInt to allow this value to be determined. Some highly used versions of Icecast do not allow configuration of the CORS response. Additionally, users of Icecast versions that do allow this configuration, would have to manually add the configuration to their CORS policy in Icecast.

As a work around, it should be possible to dynamically determine the Ice-MetaInt based on the below criteria:

  1. ICY Metadata interval is set only once in the headers and does not change while connected to the stream
  2. ICY Metadata always (at least seems to) contains the StreamTitle value as the first value in the metadata payload

ICY metadata could be detected by searching through the response for StreamTitle and either dynamically returning metadata without regards to the metadata interval, or determining the metadata interval based on the incoming data. This would be an adequate workaround for being unable to read the Icy-MetaInt from the response header due to an inadequate CORS policy.

Multi-channel Streaming Demo

The ogg-opus-decoder library supports multichannel decoding and this should enable a multichannel Ogg Opus Icecast stream to be played using the webaudio playback method.

The demo should contain a stream that loops through a multichannel audio track. There should be separate endpoints for 4, 5.1, 7.1, and 2 channel versions of the track.

An option might also be added to support stereo upmixing to the max channels for the AudioContext.

Looking for a way to update metadata without playing

Would it be possible to enable a way to receive metadata without actively playing the audio stream?

I need a simple page displaying data on the current track from my server, to be displayed as a widget on a livestream. For this to work the widget must be always active without any input from the user. This requires forcing some sort of autoplay right now, which is blocked by the chromium based browser being used, meaning no metadata event is received. I've tried setting audioElement: null when instantiating the player but it did not have the desired effect.

Thank you for your work.

Icecast Player

It would be helpful to provide a package that does it all i.e. using MSE, isobmff-audio, and icecast-metadata-js to play an icecast stream with metadata. This should be the very similar to the code already existing in the React based demo.

Key Features:

  • NPM package and vanilla JS script
  • User provides minimal information to use the Icecast Player
    • Required:
      • Icecast endpoint
    • Optional
      • HTML5 Audio element reference, or Audio() instance
      • Metadata types ["icy," "ogg"]
      • ICY Metadata interval
      • ICY Metadata detection timeout
      • onMetadata callback
      • onStream callback
      • onMetadataEnqueue callback
      • onAudioCodecUpdate callback
  • Simple API
    • const player = new IcecastMetadataPlayer(endpoint, options)
      • options:
        {
          audioElement,
          metadataTypes,
          icyMetaInt,
          icyDetectionTimeout,
          onStream,
          onMetadata,
          onMetadataEnqueue,
          onAudioCodecUpdate
        }
        
    • player.play()
    • player.pause()
    • player.stop()
  • Fallback to normal HTML5 audio without metadata when there are CORS or MSE issues

Metadata: Character decoding issues

First of all: thanks for your life-safer library, it really helps me a lot.

Anyway, I found an issue that may have an easy workaround:
Some Icecast versions could send different kinds of character sets than UTF-8 which could happen because of the source of the stream or the Icecast configuration. It makes it harder to decode some Central-European or other kinds of characters, for example in our case the character set is ISO-8859-2, but when the MetadataParser decoding the metadata, it's forced to read it as UTF-8.
As I see it's possible to add a new parameter to be able to set the decoder charset.
In this case, the IcecastMetadataPlayer can be configurable to be able to show accents and other characters properly for other languages as well. By default, it could stay as it is and it should be available to set it up by a parameter in the IcecastMetadataPlayer constructor.

What do you think about it? Do you have another workaround or is it easy to handle as I described?

Thanks for your help!

Sometimes the audio starts to lag behind on the stream

In IcecastMetadataPlayer sometimes the audio is not in sync anymore with the stream, and the gap becomes larger over time.

It happens randomly but I've experienced it a few times on Chrome (windows). I don't know if it's related to the browser (I can figure that out with some more testing). In my app I measure the time difference between where the stream (the icecast server) is at and where IcecastMetadataPlayer is at. I listen to the audio with my ears, but more precisely, I record the moment the metadata event is fired, indicating the start of a new song. Over time the time difference with the stream slowly increases and can reach a gap of 1 minute after an hour of playing, meaning the audio is 1 minute behind on the actual stream.

My personal app relies on the switching of songs (ad hoc) and it is important that the audio for every listener to the stream is somewhat in sync (within 10 seconds precision). But some listeners might be out of sync considerably. I've tested with 2 devices side by side. The issue can be fixed by restarting (re-instantiating) the IcecastMetadataPlayer.

I will implement a workaround that will stop-start the audio (or reinstantiate the player object) when the gap becomes more than 10 seconds, but it will not be a smooth transition for the listener. I'm hoping there's a better solution.

The gap never decreases after it has increased (the audio doesn't catch up), but it would be nice if there was a catch-up mechanism. A somewhat smooth re-sync mechanism.

Hopefully this explains the issue well. Let me know if I can provide more info.

Change source after stopping?

Hi,

thank you for this module, it's great! I'm building for myself a little web player with multiple channels, and I wondered if there is a way to stop the player, change its source (the URL it plays), and then start playing the other source?

I was looking for a function or a property but no avail so far.

What's your advice on this? Thanks in advance.

IcecastMetadataPlayer: Event handling

  • onError should be called when the connection is dropped, or there was a 4xx or 5xx response code.
  • New callbacks can be created to better describe the different scenarios that may occur
    • onStreamStart - called when the stream request start loading data
    • onStreamEnd - called when the stream request ends i.e. stops loading data (success, or error)

FLAC streams stop on Chromecast / Firestick

MediaSource has a very small buffer size on devices like Chromecast or Firestick. Some FLAC streams reach this limit due to a large burst on connect value and throw a QuotaExceededError that is currently not handled and stops playback.

Potential solutions:

  • Only append n seconds of audio to the SourceBuffer and store the rest in a separate queue.
  • Append audio until a QuotaExceededError is encountered, and the save the audio to a separate queue.
    • This is probably the ideal solution since this will be guaranteed to handle any QuotaExceededError without needing to track an arbitrary buffer second value. This obviously won't work if another MSE supporting platform doesn't throw the same error type.

The queueing will need to happen after the frame sync logic, and any audio in this separate queue should be considered "buffered" for playback.

See: https://developers.google.com/web/updates/2017/10/quotaexceedederror

Play through buffer on successful stream end

IcecastMetadataPlayer should continue to play until the audio buffer is emptied when a stream ends successfully (i.e. when there is no error reported from the fetch request).

When a stream disconnects / stops, the audio buffer should be exhausted before the stop event is called and the playback is stopped. Currently, playback stops immediately after a successful disconnect, and the remaining audio in the buffer is never played.

Multple stream support

It should be possible to allow for multiple streams of different bitrates that can be used to switch to lower or higher bitrates depending on connection speed.

  • If there is a stall in a higher bitrate stream, then a lower bitrate stream could be switched to.
  • Maybe there's an api or way to know the connection speed that could be used to select the stream.
  • Maybe there's an api for knowing if a user is connected on mobile / wifi, or a metered connection.

If the streams are all of the same underlying audio source and are generally synchronized, they could be switched over seemlessly by syncing the decoded audio. I don't think it would be perfect because two streams are not guaranteed to have the audio aligned on a frame basis.

  • AcoustID might be a way to compare the two streams to find the sync point and the streams could be roughly synced on a frame basis.
  • A profile of the decoded audio for the previous stream and new stream could be created and then aligned.

Perfect syncing might be achieved by decoding both streams entirely so the raw PCM could be spliced at any point regardless of the incoming codec and then fed into the web audio api.

onMetadataFailed Support!

Hi, Im trying to get feedback after calling onMetadataFailed! Does it work for the metadata only? And not the entire Stream?

Thank you!

iOS Safari Compatibility

It's me again, with another interesting issue.

I have the IcecastReadableStream object working flawlessly on every desktop browser I can test with. I'm compiling the javascript using webpack for Node, as opposed to the recently-created standalone JS player, because I'm lazy and don't want to port over all my code.

Anyway, I'm now trying to get my stream player working on mobile devices, and iOS safari is the first browser I'm hitting (since I own apple devices).

Safari on iOS doesn't support MSE, but I believe I have a workaround using the web audio API. That remains to be seen, but I'm getting hung up with getting stream data at all.

I instantiate the reader in what I believe is a normal way:

// fetch the stream
    fetch(streamUrl, {
        method: "GET",
        headers: {
            "Icy-MetaData": "1",
        },
        signal: signal
    })
    // create the icecast metadata reader
    .then(async (response) => {
        console.log("creating icecast readable stream");
        const icecast = new IcecastReadableStream(
            response,
            {
                onStream: pushStream,
                onMetadata: pushMetadata,
                metadataTypes: ["icy"]
            }
        );
        await icecast.startReading();
    });

However, iOS gives me the following console error (I'm using the remotedebug-ios-webkit-adapter npm package for remote debugging) when I call icecast.startReading():

[native code]:1 Unhandled Promise Rejection: TypeError: icecast.startReading is not a function. (In 'icecast.startReading()', 'icecast.startReading' is undefined)

Again, this same code works flawlessly in desktop browsers. I'm guessing something inside the library doesn't play nice with iOS safari.

This is definitely a "nice to have" but it would be useful for my application to be able to display metadata in mobile browsers.

CORS enabled on Icecast Server but error

Hello, i receive this error if i try the player on my icecast server and can receive audio stream but not metadata. If I open Chrome with --disable-web-security all works without errors.

Chrome Console log

icecast-metadata-player-0.0.3.min.js:formatted:2011 GET URL net::ERR_EMPTY_RESPONSE
t	@	icecast-metadata-pla…n.js:formatted:2011
Ae	@	icecast-metadata-pla…n.js:formatted:2016
play	@	icecast-metadata-pla…n.js:formatted:1928
onclick	@	VM103 :125

icecast-metadata-player-0.0.3.min.js:formatted:1954 icecast-metadata-js 
  Network request failed, possibly due to a CORS issue. Trying again without ICY Metadata.

icecast-metadata-player-0.0.3.min.js:formatted:227 icecast-metadata-js 
  Passed in Icy-MetaInt is invalid. Attempting to detect ICY Metadata. 
  See https://github.com/eshaz/icecast-metadata-js for information on how to properly request ICY Metadata.

icecast-metadata-player-0.0.3.min.js:formatted:244 icecast-metadata-js 
  ICY Metadata not detected after searching 107800 bytes for 2.095 seconds. 
  Assuming stream does not contain ICY metadata. Audio errors will occur if there is ICY metadata.

Testing Icecast server with CURL seems to respond with correct headers:

curl -H "Icy-MetaData: 1" -v "URL"
*   Trying IP...
* TCP_NODELAY set
* Connected to host (ip) port #### (#0)
> GET /stream HTTP/1.1
> Host: host:port
> User-Agent: curl/7.64.1
> Accept: */*
> Icy-MetaData: 1
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: Icecast 2.4.4
< Connection: Close
< Date: Sat, 23 Jan 2021 10:37:46 GMT
< Content-Type: audio/mpeg
< Cache-Control: no-cache, no-store
< Expires: Mon, 26 Jul 1997 05:00:00 GMT
< Pragma: no-cache
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, OPTIONS
< Access-Control-Allow-Headers: Content-Type, Icy-Metadata
< Access-Control-Expose-Headers: Icy-MetaInt, Icy-Br, Icy-Description, Icy-Genre, Icy-Name, Ice-Audio-Info, Icy-Url, Icy-Sr, Icy-Vbr, Icy-Pub
< icy-br:160
< ice-audio-info: ice-bitrate=160;ice-channels=2;ice-samplerate=44100
< icy-name:no name
< icy-pub:0
< icy-metaint:16000

What am I doing wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.