audio element's 'readystatechange' is not called on devices running ios 17, readyState stays at 0

29 Views Asked by At

I have a recorder app, that records the user onmousedown and plays back the audio onmouseup - it also does stuff with the recording in the backend, but on the client side that's pretty much it. anyway, up until ios 17, everything was working fine - i'd create a recording using MediaRecorder, set up the supported options according to each browser, and the user would be able to record and play themselves back immediately. I know about apple's strict 'no autoplay' rules, but since the user would have to be interacting with the page for the recording to start and finish, it was considered good enough. Now however, it does not. and I'm at a loss understanding why.

I added some event subscribers to see what's going on, and for some reason, the reason it's not playing is because the audio native element's ready state never goes past 0, even though load() is called on it. investigating both the recording object and the blob itself doesn't show anything out of the ordinary, the blob has data in it and is in the correct audio format for ios, plus the url seems correct and of course - the code literally works every where else. but nothing changes the fact that on ios - ready state does not go past 0. I've added some more event subscribers, but nothing gets called, except 'loadedmetadata', and that's it. no error, no progress, no nothing - again only on ios 17+. Finally, i've added a debug button that calls play on the audio element when clicked - and obviously, it works. it breaks other things, but i haven't looked into it too much.the point is - same blob, same audio element, same recording - loads and works when clicked on specifically.

my question is, then, is that it? is ios preventing data to load unless an on click event is directly related to playing the audio? why doesn't 'onmouseup' count as a good enough user interaction, seeing as play happens within that event responder? why isn't there any error popping up, stating what's going on or why? does anybody have any idea what might be the issue here?

Code:

const mp3BlobPromise = this.recording.end()
.then(async (Blob) => {
  try {
    console.log('recroding ended, blob acquired', Blob)
    this.setAudio.emit(Blob);
    // hear the recording immediately
    this.audio.nativeElement.src = this.createObjectURL(Blob); // URL.createObjectURL(Blob);
    this.audio.nativeElement.autoplay = false; //should always be false 
    this.audio.nativeElement.load();

    const waitForMediaReady = new Promise<void>((resolve) => {
      console.log('waiting on media ready... ')
      console.log('Current readyState:', this.audio.nativeElement.readyState);
      console.log('audio bloc url: ', this.audio.nativeElement.src)
      this.audio.nativeElement.addEventListener('readystatechange', (event) => {
        if (this.audio.nativeElement.readyState >= 2) {
          console.log('readystatechange event fired');
          resolve();
        }
      }, { once: true });
    });
    const nativeElement = this.audio.nativeElement
    nativeElement.addEventListener('loadedmetadata', () => {
      console.log('Audio metadata loaded');
    });
    nativeElement.addEventListener('error', (e) => {
      console.error('Error with audio playback:', nativeElement.error);
      console.error(e);
    });
    nativeElement.addEventListener("canplay", (event) => {
      console.log('can play')
    });
    nativeElement.addEventListener('stalled', () => {
      console.log('Playback stalled');
    });
    nativeElement.addEventListener('waiting', () => {
      console.log('Waiting for data');
    });
    waitForMediaReady.then(() => {
      console.log('media is ready, trying to play')
      var isPlaying = nativeElement.currentTime > 0 && !nativeElement.paused && !nativeElement.ended
                            && nativeElement.readyState > nativeElement.HAVE_CURRENT_DATA
      if (!isPlaying) {
        this.audio.nativeElement.play().then(() => {
          console.log('audio has played, nice')
          this.isAudioPlayed = true;
        }).catch(err => {
          console.error('Native element play error: ', err);
          this.showPlayButton();
        });
        this.selfAudioPlayStart.emit();
      }
    })
  } catch (err) {
    console.error('Record button error: ', err);
  }
})
.catch(error => {
  this.mic.ensureInit();
  this.MouseUp.emit(null);
  this.setAudio.emit(null);
  console.log('an error occured while ending the recording.', error)
  return null;
});
0

There are 0 best solutions below