19

I've been working on using the html audio tag to play some audio files. The audio plays alright, but the duration property of the audio tag is always returning infinity.

I tried the accepted answer to this question but with the same result. Tested with Chrome, IE and Firefox.

Is this a bug with the audio tag, or am I missing something?

Some of the code I'm using to play the audio files.

javascript function when playbutton is pressed

function playPlayerV2(src) {
document.getElementById("audioplayerV2").addEventListener("loadedmetadata", function      (_event) {
console.log(player.duration);
});
var player = document.getElementById("audioplayer");

    player.src = "source";
    player.load();
    player.play();
}

the audio tag in html

<audio controls="true" id="audioplayerV2" style="display: none;" preload="auto">

note: I'm hiding the standard audio player with the intend of using custom layout and make use of the player via javascript, this does not seem to be related to my problem.

dako
  • 1,081
  • 1
  • 12
  • 22
KristianMedK
  • 1,659
  • 6
  • 24
  • 52
  • Are you looking at the right spot? I would suspect the problem to be on the server side. Is the resource streamed? Then probably there is no duration by design. – Marcel Feb 03 '14 at 08:18
  • 1
    turns out i had to set Content-length in the header when i returned the file. – KristianMedK Feb 04 '14 at 06:55

8 Answers8

18

try this

var getDuration = function (url, next) {
    var _player = new Audio(url);
    _player.addEventListener("durationchange", function (e) {
        if (this.duration!=Infinity) {
           var duration = this.duration
           _player.remove();
           next(duration);
        };
    }, false);      
    _player.load();
    _player.currentTime = 24*60*60; //fake big time
    _player.volume = 0;
    _player.play();
    //waiting...
};

getDuration ('/path/to/audio/file', function (duration) {
    console.log(duration);
});
gest
  • 429
  • 6
  • 4
11

I think this is due to a chrome bug. Until it's fixed:

if (video.duration === Infinity) {
    video.currentTime = 10000000;
    setTimeout(() => {
        video.currentTime = 0; // to reset the time, so it starts at the beginning
    }, 1000);
}
let duration = video.duration;
Jacob Hornbeck
  • 398
  • 2
  • 19
8

This works for me

const audio = document.getElementById("audioplayer");

audio.addEventListener('loadedmetadata', () => {
  if (audio.duration === Infinityс || isNaN(Number(audio.duration)) {
    audio.currentTime = 1e101
    audio.addEventListener('timeupdate', getDuration)
  }
})

function getDuration() {
  audio.currentTime = 0
  this.voice.removeEventListener('timeupdate', getDuration)
  console.log(audio.duration)
},
MAZ
  • 643
  • 5
  • 18
  • What's the meaning of "1e101"? – Shani Kehati May 18 '23 at 13:33
  • 1
    @ShaniKehati 1e101 is equal to 1 followed by 101 zeros, which is an extremely large number. In the context of the code, setting audio.currentTime to 1e101 is essentially setting the current time of the audio to a very large value, attempting to skip ahead in the audio. This is done as a workaround to check if the audio.duration property is reporting Infinity. – MAZ May 20 '23 at 11:14
  • Thanks! That helped us (setting to 0 or Infinity didn't work of-course). Notice that for iOS, `audio.duration` might be `NaN`. So our validation is `audio.duration === Infinity || isNaN(Number(audio.duration))` to cover all cases that it's not a number AND might be infinity (which **is** a number) – Shani Kehati May 22 '23 at 05:47
2

In case you control the server and can make it to send proper media header - this what helped the OP.

I faced this problem with files stored in Google Drive when getting them in Mobile version of Chrome. I cannot control Google Drive response and I have to somehow deal with it.

I don't have a solution that satisfies me yet, but I tried the idea from both posted answers - which basically is the same: make audio/video object to seek the real end of the resource. After Chrome finds the real end position - it gives you the duration. However the result is unsatisfying.

What this hack really makes - it forces Chrome to load the resource into the memory completely. So, if the resource is too big, or connection is too slow you end up waiting a long time for the file to be downloaded behind the scenes. And you have no control over that file - it is handled by Chrome and once it decides that it is no longer needed - it will dispose it, so the bandwidth may be spent ineficciently.

So, in case you can load the file yourself - it is better to download it (e.g. as blob) and feed it to your audio/video control.

Artemix
  • 2,113
  • 2
  • 23
  • 34
2

Not a direct answer but in case anyone using blobs came here, I managed to fix it using a package called webm-duration-fix

import fixWebmDuration from "webm-duration-fix";

...

 fixedBlob = await fixWebmDuration(blob);

...


scr2em
  • 974
  • 8
  • 17
0

If this is a Twilio mp3, try the .wav version. The mp3 is coming across as a stream and it fools the audio players.

To use the .wav version, just change the format of the source url from .mp3 to .wav (or leave it off, wav is the default)

Note - the wav file is 4x larger, so that's the downside to switching.

Mark Swardstrom
  • 17,217
  • 6
  • 62
  • 70
0

//If you want to modify the video file completely, you can use this package "webmFixDuration" Other methods are applied at the display level only on the video tag With this method, the complete video file is modified

webmFixDuration github example

 mediaRecorder.onstop = async () => {
        const duration = Date.now() - startTime;
        const buggyBlob = new Blob(mediaParts, { type: 'video/webm' });
    
        const fixedBlob = await webmFixDuration(buggyBlob, duration);
        displayResult(fixedBlob);
      };
hassan khademi
  • 1,156
  • 12
  • 14
0

This work with Angular !

   @ViewChild('myAudio') audioElement: ElementRef<HTMLAudioElement>;

  async ngAfterViewInit() {
    const audio = this.audioElement.nativeElement;
    const audioFilePath = 'your_audio.mp3';

    const audioContext = new (window.AudioContext || window.webkitAudioContext)();

    try {
      const response = await fetch(audioFilePath);
      const arrayBuffer = await response.arrayBuffer();

      audioContext.decodeAudioData(arrayBuffer, ({ duration }) => {
        console.log(duration);
      });
    } catch (error) {
      console.error('error here', e);
    }
  }