Skip to content

Commit

Permalink
feat: support latency chasing by changing playbackRate
Browse files Browse the repository at this point in the history
  • Loading branch information
FredZeng authored and xqq committed Sep 23, 2023
1 parent d41cae0 commit 4f16e45
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 1 deletion.
3 changes: 3 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ In multipart mode, `duration` `filesize` `url` field in `MediaDataSource` struct
| `liveBufferLatencyChasing?` | `boolean` | `false` | Chasing the live stream latency caused by the internal buffer in HTMLMediaElement. `isLive` should also be set to `true` |
| `liveBufferLatencyMaxLatency?` | `number` | `1.5` | Maximum acceptable buffer latency in HTMLMediaElement, in seconds. Effective only if `isLive: true` and `liveBufferLatencyChasing: true` |
| `liveBufferLatencyMinRemain?` | `number` | `0.5` | Minimum buffer latency to be keeped in HTMLMediaElement, in seconds. Effective only if `isLive: true` and `liveBufferLatencyChasing: true` |
| `liveSync?` | `boolean` | `false` | Chasing the live stream latency caused by the internal buffer in HTMLMediaElement by changing the playbackRate. `isLive` should also be set to `true` |
| `liveSyncMaxLatency` | `number` | `1` | Maximum acceptable buffer latency in HTMLMediaElement, in seconds. Effective only if `isLive: true` and `liveSync: true` |
| `liveSyncPlaybackRate` | `number` | `1.2` | PlaybackRate limited between [1, 2] will be used for latency chasing. Effective only if `isLive: true` and `liveSync: true` |
| `lazyLoad?` | `boolean` | `true` | Abort the http connection if there's enough data for playback. |
| `lazyLoadMaxDuration?` | `number` | `3 * 60` | Indicates how many seconds of data to be kept for `lazyLoad`. |
| `lazyLoadRecoverDuration?` | `number` | `30` | Indicates the `lazyLoad` recover time boundary in seconds. |
Expand Down
4 changes: 4 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export const defaultConfig = {
liveBufferLatencyMaxLatency: 1.5,
liveBufferLatencyMinRemain: 0.5,

liveSync: false,
liveSyncMaxLatency: 1,
liveSyncPlaybackRate: 1.2,

lazyLoad: true,
lazyLoadMaxDuration: 3 * 60,
lazyLoadRecoverDuration: 30,
Expand Down
31 changes: 30 additions & 1 deletion src/player/mse-player.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ class MSEPlayer {
onvSeeking: this._onvSeeking.bind(this),
onvCanPlay: this._onvCanPlay.bind(this),
onvStalled: this._onvStalled.bind(this),
onvProgress: this._onvProgress.bind(this)
onvProgress: this._onvProgress.bind(this),
onvTimeupdate: this._onvTimeupdate.bind(this),
};

if (self.performance && self.performance.now) {
Expand Down Expand Up @@ -140,6 +141,9 @@ class MSEPlayer {
mediaElement.addEventListener('canplay', this.e.onvCanPlay);
mediaElement.addEventListener('stalled', this.e.onvStalled);
mediaElement.addEventListener('progress', this.e.onvProgress);
if (this._config.isLive && this._config.liveSync) {
mediaElement.addEventListener('timeupdate', this.e.onvTimeupdate);
}

this._msectl = new MSEController(this._config);

Expand Down Expand Up @@ -365,6 +369,21 @@ class MSEPlayer {
return Object.assign({}, this._statisticsInfo);
}

get latency() {
if (!this._mediaElement) {
return 0;
}
let buffered = this._mediaElement.buffered;
let currentTime = this._mediaElement.currentTime;

if (buffered.length > 0) {
let buffered_end = buffered.end(buffered.length - 1);
return buffered_end - currentTime;
}

return 0;
}

_fillStatisticsInfo(statInfo) {
statInfo.playerType = this._type;

Expand Down Expand Up @@ -647,6 +666,16 @@ class MSEPlayer {
this._checkAndResumeStuckPlayback();
}

_onvTimeupdate(e) {
const latency = this.latency;

if (latency > this._config.liveSyncMaxLatency + 0.05) {
const playbackRate = Math.min(2, Math.max(1, this._config.liveSyncPlaybackRate));
this._mediaElement.playbackRate = playbackRate;
} else if (this._mediaElement.playbackRate !== 1 && this._mediaElement.playbackRate !== 0) {
this._mediaElement.playbackRate = 1;
}
}
}

export default MSEPlayer;

0 comments on commit 4f16e45

Please sign in to comment.