Search code examples
iosionic-frameworkionic4html5-audio

Html5 audio does not loading first time in iPhone ionic 5


I am using HTML 5 audio in IONIC 5 to play audio. I do not use HTML audio controls. I create my own controls seek bar using ion-range. an ionic app running as expected in android but creating an issue in iPhone. When I deploy the app on iPhone audio does not load the first time. When the user visits another page of the app and then moves to the audio page, it loaded the audio as expected. my HTML tag is

<audio id="{{i}}" class="audio" playsinline>
        <source src="{{music.url}}" type="audio/mp3">
      </audio>

I am getting audio duration in ionViewWillEnter and also try ionViewDidLoad but getting the same error. How can I fix this. please have a look at my ts code.

ionViewWillEnter() {
    this.utils.showLoading('please wait...');
    setTimeout(() => {
      this.utils.hideLoading();
      this.musicList.forEach((music, index) => {
        var audio: any = document.getElementById((index);
        music.duration = this.calculateDuration(audio.duration);
        music.maxRange = audio.duration;
      });
    }, 1000);
  }

Solution

  • The problem there is that you don't know if the audio is ready or not to be played. You just set a 1 second loading assuming it will be enough when, in fact, it's not.

    Take a look at the Media events documentation. You need to create a listener/override the canplaythrough event so you know when the audio is loaded on the browser.

    On your case it'll be something like this

    //it's better to use viewDidLoad in your case because it only runs on the page instatiation
    async ionViewDidLoad() {
      await this.utils.showLoading('please wait...'); //remember loading instantiations and presentation returns a promise
      
      const MAX_WAIT = 10000 //maximum wait time in ms
      const loadings = []
    
      this.musicList.forEach((music, index) => {
        const audio: any = document.getelementById(index)
        const p = new Promise((resolve, reject) => {
          // creating a timeout handler
          const timeoutHandler = setTimeout(() => {
            const errorMsg = `Timeout waiting audio ${index} to load/buffer` 
            console.error(errorMsg)
            reject(new Error(errorMsg))
          }, MAX_WAIT)
          
          // overriding the oncanplaythrough event handler
          audio.oncanplaythrough = (event) => {
            music.duration = this.calculateDuration(audio.duration);
            music.maxRange = audio.duration;
    
            console.log(`audio ${index} is loaded and buffered enough to play without interruptions`)
            clearTimeout(timeoutHandler) //clearing the timeout handler
            resolve()
          }
        })
    
        // pushing the loading the promise arrays
        loadings.push(p)
      })
    
      //waiting all audios to load
      Promise.all(loadings).then(()=> {
        console.log('all audios loaded')
        this.utils.hideLoading()
      }).catch((error)=> {
        this.utils.hideLoading()
        console.error('error loading some files')
      })
    
    }