2

For the purposes of a project I am working, I want to find out via the JS console if the <audio> tag found on facebook.com is playing audio or not. (I can then move this code into a Chrome extension and also apply it against other websites such as silent HTML5 ads.)

You can find the element by typing:

$$("audio")[0] 

I have used the following code (adapted from this jsfiddle which I found during a search) to show when different event listeners are called:

var audio = $$("audio")[0]; 
var events = 'abort,canplay,canplaythrough,durationchange,emptied,ended,error,loadeddata,loadedmetadata,loadstart,pause,play,playing,progress,ratechange,seeked,seeking,stalled,suspend,timeupdate,volumechange,waiting'.split(',');

// event handler
var onEvent = function(e) {
    console.log(e.type);
};

// add event listener to audio for all events
for (var i = 0, len = events.length; i < len; i++) {
    audio.addEventListener(events[i], onEvent, false);
}

I would then play the audio by typing:

$$("audio")[0].play();

If I check $$("audio")[0].paused, it will show false, but I haven't found any properties or events that would indicate whether it played sound or not. (I wouldn't expect it to here since I don't think it has audio data to play, but if there was a way to check, I could compare that with an audio element that does play sound.)

Also, I have tried sending myself a message from an incognito tab and don't see a trace of this happening. (I assume the audio element gets used for that.)

Help is appreciated.

Jared Sohn
  • 443
  • 5
  • 17
  • that's a bit unclear what you ask. Do you only want to know if it is actually playing? If so you just have to check for its `paused` attribute. (`if(!yourAudio.paused)console.log('isPlaying')`) and [there are dupes](http://stackoverflow.com/questions/9437228/html5-check-if-audio-is-playing). Otherwise, please clarify your question. – Kaiido Oct 20 '15 at 03:17
  • I thought I was clear but will happily make edits if there is a better way to express it. I want to know whether or not it is playing sound or ever did play sound (i.e. do I actually hear anything?). I think this statement is the most relevant from my question: "If I check $$("audio")[0].paused, it will show false, but I haven't found any properties or events that would indicate whether it played sound or not." – Jared Sohn Oct 20 '15 at 07:41
  • what do you mean by "played sound"? Do you want to check if the media loaded has no sound in it ? (hard but feasible) Do you want to check if it is muted ? (easily checkable) do you want to check if it was already played once at least, whatever its state now ? (easy) – Kaiido Oct 20 '15 at 07:44
  • Ps : after looking at your profile page, you've to know that you won't be able to mute all sounds from a webpage : if it comes from flash you're stuck, if the audio element is not appended to the DOM nor accessible through global scope, you're stuck through external iframe, you're stuck, and there are probably other reasons why this would be impossible. – Kaiido Oct 20 '15 at 07:51
  • I now see your edited comment, the best solution for this IMO is to check the `audioElement.played` property which will return a TimeRange. You can then check the length of this TimeRange to know if it was played at least once (`if(audioElement.played.length>0){// has been played}`) – Kaiido Oct 20 '15 at 07:59
  • My question here is not about muting all sounds from a webpage. I have some code that looks at all iframes to grab audio and video elements and keeps track of which are playing and which are paused and I want to ignore those that have never produced sound before. (If the only element on the page that is playing has never produced sound, then I will say that the page is not playing.) One way to simplify this a bit that would help for the Facebook example would be to determine if the audio element has received any data or not (since it is audio, it isn't also going to have video data.) – Jared Sohn Oct 20 '15 at 07:59
  • Yeah, this seems to works; receiving a message actually creates a second element on the page which does have a played.length of 1 while my original audio element still has length 0. (I think I was looking for changes to my first audio element before rather than checking to see if I had a new audio element). Not sure what that first one is actually for, but I can just have my code ignore it. Please submit a solution. :) – Jared Sohn Oct 20 '15 at 08:05
  • It may have received data (fetched) and never played it, so either you do use the `played` attribute (not sure about compatibilty though, so I'm checking rn before posting as an answer) either you attach an event handler on page load on every audio element that will set a `hasPlayed` attribute on it on `playing` event – Kaiido Oct 20 '15 at 08:06
  • compatible with IE9 which was the first version of IE to implement HTMLAudioElement – Kaiido Oct 20 '15 at 08:18

1 Answers1

1

To check if an HTMLMediaElement (either video or audio) has ever been played on the page, the best solution is to check for its played attribute.

This will return a TimeRanges object that you can as well use to get how much of the media has been played.

If it has never been played, then the length property of this TimeRanges object will be set to 0 :

if(audioElement.played.length){
  // This media has already been played
}else{
  // Never
}
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • I wasn't asking if it ever played, though; I wanted to know if it ever made sound. For HTML5 audio, I think this approach works because if there is a time range then there was some data to play. For HTML5 video, I am less convinced (we know there was some video, but we don't know if it had sound as well or not). But it seems to work for the first example I checked: www.youtube.com/; will check some other test cases now). – Jared Sohn Oct 20 '15 at 08:33
  • Your answer is fine and I'll accept it in a moment. There are actually three different scenarios here: HTML5 audio that has never played, HTML video that has never played, and HTML5 video that played but that had no audio. Your answer is a solution for the first two of these three scenarios which is really what I asked about and care the most about. Thanks! – Jared Sohn Oct 20 '15 at 08:47
  • 1
    for third scenario, you will have to create a new `AudioContext().createMediaElementSource(yourVideoElement)` and then attach it to a `ScriptProcessorNode` and check if some values are outputted from this source while playing. Or firefox has a `videoElement.mozHasAudio` property. But I think videos without sound tracks are quite rare out there so you can just accept this error margin, **but**, since the audio output is your main interest, then you should also double check with the `muted` and `volume` attributes of the mediaElement – Kaiido Oct 20 '15 at 08:50
  • I'm already looking at muted. The use cases I am thinking about are vines, videos produced from animated gifs, and silent html5 ads. Your mozHasAudio was helpful (searching for it found http://stackoverflow.com/questions/21270048/html5-video-how-to-detect-when-there-is-no-audio-track). Specifically, checking the webkitAudioDecodedByteCount property. – Jared Sohn Oct 20 '15 at 08:59
  • Vine uses multiple video elements, and does mute only the original one (`document.querySelectorAll('video')[1]`) for videos from animated gif, I only see these non-standard-browsers-dependent-properties-that-may-be-removed-in-any-future-version and the over-heavy-AudioContext-hacky-workaround – Kaiido Oct 20 '15 at 09:32
  • A specific example for videos created from animated gifs: go here https://www.facebook.com/FallonTonight?fref=nf. These videos have a played.length but webkitAudioDecodedByteCount is zero. I think I just need to be careful to cache webkitAudioDecodedByteCount after it loads (if I check too soon, it will show zero even when there is data to load.) (The browser-dependent aspect is okay since it will be used in a Chrome extension and I will just have to deal with it breaking if they remove the property.) – Jared Sohn Oct 20 '15 at 15:16
  • My implementation seems to be working the way I want. I am just checking webkitAudioDecodedByteCount and I make sure to recalculate the state whenever the loadeddata event occurs. – Jared Sohn Oct 20 '15 at 16:46