Stefan Falk
Stefan Falk

Reputation: 25447

currentTime set to different value after loadmetadata event during seek

I am currently on an adventure with my sanity at stake where I try to simply play an mp3 file on my web-application. This mp3 is loaded chunk-wise in order to make the experience more responsive.

Even though the documentation of the MediaSource API is just remarkably, almost violently, useful and hundreds of well explained examples pave the way towards salvation, I find myself not able to make everything work properly.

The last issue I am facing is the seeking-functionality. The simple idea I had for this feature is to simply kill everything that's alive, burn everything down and revive again. To be more precise, if the user seeks, I stop the machinery and start playing everything from scratch, but with the desired offset (in seconds).

Everything is working so far except one last thing: currentTime, gets set to a different value after I have set it to the time that the user requested.

The track continues playing as expected but the problem is that currentTime is what's being used to visualize the current location within the track for the user.

This is how the events take place before and after the user seeks

[play-click]    <--- User clicks play button
 :
[timeupdate]    currentTime: 00.01
[timeupdate]    currentTime: 00.02
[timeupdate]    currentTime: 00.03
[seek-click]    <--- User seeks, stop() is called and then playFrom(seconds)
[loadstart]     currentTime: 33.33  # This is the (correct) time set in playFrom(seconds)
[loadmetadata]  currentTime: 10.12  # Now the time gets set to this (wrong) value
[seek]          currentTime: 10.12  # The triggered seek event comes in
[seeked]        currentTime: 10.12  
[canplay]       currentTime: 10.12
[playing]       currentTime: 10.12
[timeupdate]    currentTime: 10.13  # Track continues to play correctly but this value is still wrong
 :

This is how seeking takes place:

public play(track: Track) {
  this.track = track;
  this.playFrom(0);
}

public seekTo(seconds: number) {
  this.stop();
  this.playFrom(seconds);
}

public stop() {
  this.logger.debug('Stop ..');
  this.sourceBuffer.abort();
  this.audioObj.pause();
  this.removeEvents(this.audioObj, this.audioEvents, this.audioEventsHandler);
  this.audioObj = null;
  this.sourceBuffer = null;
  this.mediaSource = null;
}

Now, the playFrom(seconds) method is a bit more complicated than that but all it really does is re-create everything that I just destroyed:

private playFrom(seconds: number) {

  this.logger.debug(`Start playing from ${seconds.toFixed(2)} seconds`)

  // [:] (collapsed some less important code here)

  // Create the media source object
  this.mediaSource = new MediaSource();
  this.mediaSource.addEventListener('sourceopen', this.onSourceOpen);

  // Create audio element
  this.audioObj = document.createElement('audio');
  this.audioObj.src = URL.createObjectURL(this.mediaSource);
  this.audioObj.currentTime = seconds;
  this.addEvents(this.audioObj, this.audioEvents, this.audioEventsHandler);

  this.logger.debug(`Object-url: ${this.audioObj.src}`);

  this.state = {currentTime: seconds, playing: true};
}

How can I get the correct current-time here?

I tried another idea where I was tring to create and add a new SourceBuffer to the MediaSource and let it play the audio from there but I wasn't able to do that either.

Upvotes: 1

Views: 224

Answers (1)

Matt Wolenetz
Matt Wolenetz

Reputation: 121

What is the duration reported by the MediaSource (or by the media element) at the time of issuing the seek to the media element? If it is infinite, you may need to use the {set,clear}LiveSeekableRange APIs in MSE to let the implementation know if you wish to let the player seek beyond the currently buffered media's highest presentation end time. Alternatively, you could possibly set the MediaSource duration before seeking to be something finite, but at least as high as the highest currently buffered media presentation end time.

Upvotes: 1

Related Questions