Jump to content
justinh

audio networkState and readyState not working as expected

Recommended Posts

I'm trying to check for an audio stream, to see if it is active.  I found two methods, .networkState and .readyState, that work against an audio tag or an Audio() object.  Things look okay when the stream is off and never been listened to (not in browser cache).  I have both methods being executed every 4s to catch any change in the stream.

The trouble is that I can't get either method to correctly update the status of the stream once it has changed, that is, from off to on or on to off.  I have to refresh to the page to get a real update.  Then, after you've listened to the stream and it is cached, it always shows as 'on'.  (The counters are updating, so I know the page is updating, but the stream detection is not updating dynamically.)

Why wouldn't the status change when the stream changes?

Is there a way to force a check against the actual object instead of the cached object?

(excuse my code - I quickly chunked this together and I am a newbie)  (I went thru all the below to test the diff methods and for debugging, so you can look at only function to get the drift)

<audio id="myAudio"  controls>
  <source src="http://myserver.com:8080/live.mp3" type="audio/mpeg">
  Your browser does not support the audio element.
</audio>

<!-- BEGINS: AUTO-GENERATED MUSES RADIO PLAYER CODE -->
<script src="https://myserver.com/stream/mrp.js"></script>
<script>
MRP.insert({
'url':'http://myserver.com:8080/live.mp3',
[ . . .]
});
</script>
<!-- ENDS: AUTO-GENERATED MUSES RADIO PLAYER CODE -->

<!-- this is what does not update correctly on the page -->
<p>StreamN0 is: <span id="netstate">OFFLINE</span></p>
<p>StreamN1 is: <span id="netstate1">OFFLINE</span></p>
<p>StreamR0 is: <span id="readystate">OFFLINE</span></p>
<p>StreamR1 is: <span id="readystate1">OFFLINE</span></p>

<script>
var myTimerVar = setInterval(myTimer, 4000);
var myTimerVar1 = setInterval(myTimer1, 4000);
var myFunctionVar = setInterval(myFunction, 4000);
var myFunctionVar1 = setInterval(myFunction1, 4000);
var aud = new Audio('http://myserver.com:8080/live.mp3');

var n0 = 0; var n1 = 0; var r0 = 0; var r1 = 0;
function myTimer() {
    var acheck = document.getElementById("myAudio").networkState;
    if (acheck == 2) {  //(acheck == 2 || acheck == 3)
      document.getElementById("netstate").innerHTML = "buffering " + acheck + " - " + n0++;
      document.getElementById("netstate").style.backgroundColor = "yellow";
    }
    else if (acheck == 3) {
      document.getElementById("netstate").innerHTML = "OFFLINE " + acheck + " - " + n0++;
      document.getElementById("netstate").style.backgroundColor = "yellow";
    }
    else if (acheck == 1) {   // works in local env but says online when on server; not reliable
      document.getElementById("netstate").innerHTML = "ONLINE " + acheck + " - " + n0++;
      document.getElementById("netstate").style.backgroundColor = "MediumSeaGreen";
    }
    else {
      document.getElementById("netstate").innerHTML = "unknown " + acheck + " - " + n0++;
    }
}
function myTimer1() {
    var acheck1 = aud.networkState;
    if (acheck1 == 2) {
      document.getElementById("netstate1").innerHTML = "buffering " + acheck1 + " - " + n1++;
      document.getElementById("netstate1").style.backgroundColor = "yellow";
    }
    else if (acheck1 == 3) {
      document.getElementById("netstate1").innerHTML = "OFFLINE " + acheck1 + " - " + n1++;
      document.getElementById("netstate1").style.backgroundColor = "yellow";
    }
    else if (acheck1 == 1) {
      document.getElementById("netstate1").innerHTML = "ONLINE " + acheck1 + " - " + n1++;
      document.getElementById("netstate1").style.backgroundColor = "MediumSeaGreen";
    }
    else {
      document.getElementById("netstate1").innerHTML = "unknown " + acheck1 + " - " + n1++;
    }
}
function myFunction() {
    var x = document.getElementById("myAudio").readyState;
    if (x == 0) {
      document.getElementById("readystate").innerHTML = "OFFLINE " + x + " - " + r0++;
      document.getElementById("readystate").style.backgroundColor = "yellow";
    }
    else if (x == 4) {
      document.getElementById("readystate").innerHTML = "ONLINE " + x + " - " + r0++;
      document.getElementById("readystate").style.backgroundColor = "MediumSeaGreen";
    }
    else {
      document.getElementById("readystate").innerHTML = "unknown " + x + " - " + r0++;
    }
  }
function myFunction1() {
   var x1 = aud.readyState;
    if (x1 == 0) {
      document.getElementById("readystate1").innerHTML = "OFFLINE " + x1 + " - " + r1++;
      document.getElementById("readystate1").style.backgroundColor = "yellow";
    }
    else if (x1 == 4) {
      document.getElementById("readystate1").innerHTML = "ONLINE " + x1 + " - " + r1++;
      document.getElementById("readystate1").style.backgroundColor = "MediumSeaGreen";
    }
    else {
      document.getElementById("readystate1").innerHTML = "unknown " + x1 + " - " + r1++;
    }
}
</script>

I'd appreciate any help.

Edited by justinh

Share this post


Link to post
Share on other sites

I'm trying to check for an audio stream, to see if it is active.

What does "active" mean, are you trying to determine if it's currently being played?  

I have both methods being executed every 4s to catch any change in the stream.

There are event handlers for play, pause, etc that you can use to handle any events as they happen.

Share this post


Link to post
Share on other sites

Yes, active = stream is live (playing).

But is there an event handler to detect when the stream is turned and turned off?  Something equivalent to what I have now?

Share this post


Link to post
Share on other sites

No.  I need to DETECT if the stream exists or not (is it available to play?).  I don't care if the browser is actually playing it, that is not the issue.

I'm not sure if any of the Media events will do what I need, because it looks like the user has to interact with the page at some time or another for any of the events to trigger.  I'll have to try, after I figure out how to use event handlers.  :-)

Share this post


Link to post
Share on other sites

I don't think you're using common terminology, you're asking if the stream is active, live, and exists, but not actually playing.

Just go through the media events to see what's available, that is your toolbox. Check canplay and canplaythrough, for example.  Look at the various load events.  Look at all of them to see what is available.  

it looks like the user has to interact with the page at some time or another for any of the events to trigger

Not with all of the media events.  Those events aren't user interaction, they are events for what is happening with the media object.  They fire when various events occur, such as the progress or timeupdate events.

Share this post


Link to post
Share on other sites

Well, I just want to know if the audio is available.  It doesn't matter if it is playing (in my browser).  It's like checking for a PNG file on the server; it doesn't have to be loaded on my page to be available for download, it just has to exist, so I can call it when I want it.  The audio (if available) won't play until I hit play on the player.  I'm sorry if I'm using non-standard verbiage, but it is the only way I know.

Yes, canplay looks like it might be suitable.  So, to use it, my code would look something like:

var v = document.getElementsByTagName("audio")[0];
v.addEventListener("canplay", myfunction());

1) When the page loads, this event handler will automatically start?

2) The event handler will, on it's own, just continue to check the event forever?

For #2, this is my confusion.  The audio could be available, but it doesn't push itself to the page, does it?  If it doesn't, then how would the event be triggered?  Upon page load, does the 'page' try to hook up to the stream or is it ignorant of the stream until I hit play?

I do appreciate the help.

Share this post


Link to post
Share on other sites

The "canplay" event fires only once, as soon as enough content has been downloaded that the play button can be pressed.

If the server opens a connection and streams empty data to you any time you try to connect, there is no way the browser can know whether the content that is being streamed is meaningful to the user or not.

Share this post


Link to post
Share on other sites

Right, so what causes the download to start?  Wouldn't the user have to hit play for the download to even start?  (To be sure, audio will not start playing by itself, the user will have to initiate playback.)

In my case, if the server opens a connection, there is data in the stream.  (Can a stream actually have empty data??)

Thanks, Ingolme.  Your explanation of the event is better than MDN's.

Share this post


Link to post
Share on other sites

It might fire an onerror event if the server is unavailable

A server might decide to stream a string of zeroes to anybody who opens a connection, I can't be sure because I haven't carefully researched streaming protocols.

Share this post


Link to post
Share on other sites

Thanks.  I found that onstalled fires if the stream is not available upon page load.

It also appears that the audio tag starts looking for the source as soon as the page loads, instead of when the user hits play.  Is that correct?  That might explain the disconnect between me and justsomeguy 😟

Here's what I think to be the problem with theses events.

  • the page loads - stream is not available, JS is used to show error on page
  • 10 seconds later the stream becomes available (the sever is now sending a stream)
    • Are the events going to be in effect now?
  • Will the on oncanplay event now fire?  If so, JS could now be used to show an updated status on the page

Share this post


Link to post
Share on other sites

I confirmed that event handlers don't continuously monitor changes.  This is so frustrating.

<audio id="myAudio" oncanplay="myTimer0()" onprogress="myTimer00()" onerror="myTimer000()" controls>

If stream is on, upon page load onprogress fires.

Stop the stream (at server).

Reload the page and onprogress still fires.

This is the same behavior as with the original methods - I have to clear the cache to get a true reading!

Edited by justinh

Share this post


Link to post
Share on other sites

Have you tried the ended event?

Share this post


Link to post
Share on other sites

It's like checking for a PNG file on the server; it doesn't have to be loaded on my page to be available for download, it just has to exist, so I can call it when I want it.

That's fine if it's on your own server, you could use ajax to just send a head request for a URL and check the response status.  If it's not on your own server, then you can't use Javascript to determine if a particular URL is valid and returns a 200 response.  You need to use a server-side language for that.

All of these events that we're talking about are not used to determine the status of resources on other domains, they are only used to determine status of resources being loaded on the current page (even if the resources comes from another domain, that's OK). 

So...

When the page loads, this event handler will automatically start?

It will fire as soon as the browser has downloaded enough data to start playing the media object.  No other user interaction is required.  Again, this is only used for looking at the status of an object currently being loaded on the page.  You can not use it to look at whether or not some arbitrary URL is valid, that's not what it's for.

The audio could be available, but it doesn't push itself to the page, does it?

Nothing pushes itself to the page, the page pulls everything.  HTTP is a request/response model, so your browser needs to send requests for the various resources and the other servers respond appropriately.  If the browser didn't send a request for something, like a media object you've added, there's no way for it to get added to the page otherwise.

Right, so what causes the download to start?

Probably just the fact that it's on the page, the browser will send a request for the source file.  Maybe, depending on the various attributes you can set, it might wait for the user to press play first.  I don't know if the media object specification defines that behavior for when the download should start or if the specification leaves that up to the browser vendors.

To be sure, audio will not start playing by itself, the user will have to initiate playback.

Even if you have autoplay set to off, the browser still might start downloading the first bit of it to be ready in case the user does start.  Maybe it will wait for other things to finish loading first.  It can also send requests for partial content, so it can send one request just to get the size, then maybe a request to download the first 1MB or so in case the user plays it.  While it's playing it will send requests to download the rest.  

This is the same behavior as with the original methods - I have to clear the cache to get a true reading!

Well, sure, the progress event fires even with cached data because the browser already has that data.  Consider the onprogress event to be what you use to show a download progress bar.  If the file is cached, you want to show that on the progress bar, you don't want to show 0% progress if the data is already cached.

If you don't want the stream to be cached, you control that on the streaming server.

Share this post


Link to post
Share on other sites

Also, you might want to become familiar with the Network tab in your browser's developer tools.  You can look at every request the browser is sending and see things like the timing, headers, etc.

Share this post


Link to post
Share on other sites

Yes, I've been using the browser's dev tools; that's how I discovered some of the findings above.

The stream originate from my domain, it is not an arbitrary URL.  JS can work, as described above, but it is just not reliable.  BTW, I tried many event handlers, but I mentioned what I found to do something useful for me.

The ended event would be useful when the stream is ends of course, either because it is at the end or because of a network interruption onerror also.  But, the scenario is not that simple.  The problem is whatever I use has to account for any situation, this it why it has to be like a monitor.  The stream could be on or off any time (before or after the page is loaded, interrupted and recovered, etc.).

Share this post


Link to post
Share on other sites

If you're just trying to determine the presence of a file, you can use an ajax request to send a head request.  That will return the status and headers without downloading the actual file.

If you have a streaming server that is always going to respond regardless of whether or not a stream is available then you can't use these kinds of techniques, and you need an API or another way for that server to communicate the status of the stream.  Those options will depend on the capabilities of the streaming server.

Share this post


Link to post
Share on other sites

Checking for the header may work.  Ajax is dependent on jQuery, correct?  Is it possible to do the same thing with the native Fetch API?  Would I have to check for a specific header property (like status) or if there is no header it would return false and I could just use that?

The server is not always streaming.  The server is running only when it is streaming.

Share this post


Link to post
Share on other sites

Ajax is dependant on JavaScript and a server script language, jQuery is a JavaScript library that provides cross browser oddities support (mainly IE, Edge), but! It can easily be done using vanilla JavaScript on its own.

Share this post


Link to post
Share on other sites

With a head request you don't even need anything on the server, a head request is basically like a get request but it only returns the headers, not the content.  So you're just asking the server if a file exists and what the size etc is.

You can use the fetch API, I think the major difference is that it returns a promise.  I assume you can still specify the request method.  IE does not support the fetch API if that matters to you.

Quote

if there is no header it would return false and I could just use that?

If there's no header then there's a problem.  If the file doesn't exist the server will return a 404 status with the appropriate headers.  If it can't reach the server at all to get a response then I think it will timeout after 30 seconds, but you should be able to detect that.

Share this post


Link to post
Share on other sites

You're right.  I forgot about the timeout in the case of the server being off.  My experience is it takes 21s to return an error.  I need to find a way for the head request to abort after so much time (less than 21s).  Then that might work.  It's not elegant, but it might serve the purpose.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×