Skip to content

Commit

Permalink
Merge branch 'main' of github.com:videojs/http-streaming into multi-p…
Browse files Browse the repository at this point in the history
…eriod-dash-vod-fixes
  • Loading branch information
dzianis-dashkevich committed Nov 20, 2024
2 parents 140eec3 + 2f8d0af commit d72e2e9
Show file tree
Hide file tree
Showing 9 changed files with 320 additions and 145 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
<a name="3.16.0"></a>
# [3.16.0](https://github.com/videojs/http-streaming/compare/v3.15.0...v3.16.0) (2024-11-18)

### Features

* parse mp4 webvtt segments ([#1545](https://github.com/videojs/http-streaming/issues/1545)) ([9f1c4ad](https://github.com/videojs/http-streaming/commit/9f1c4ad))

### Bug Fixes

* issues with live playback timing ([#1553](https://github.com/videojs/http-streaming/issues/1553)) ([6b4b7e2](https://github.com/videojs/http-streaming/commit/6b4b7e2))

<a name="3.15.0"></a>
# [3.15.0](https://github.com/videojs/http-streaming/compare/v3.14.2...v3.15.0) (2024-10-10)

Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@videojs/http-streaming",
"version": "3.15.0",
"version": "3.16.0",
"description": "Play back HLS and DASH with Video.js, even where it's not natively supported",
"main": "dist/videojs-http-streaming.cjs.js",
"module": "dist/videojs-http-streaming.es.js",
Expand Down
140 changes: 81 additions & 59 deletions src/playlist-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -1681,9 +1681,73 @@ export class PlaylistController extends videojs.EventTarget {
return this.seekable_;
}

onSyncInfoUpdate_() {
let audioSeekable;
getSeekableRange_(playlistLoader, mediaType) {
const media = playlistLoader.media();

if (!media) {
return null;
}

const mediaSequenceSync = this.syncController_.getMediaSequenceSync(mediaType);

if (mediaSequenceSync && mediaSequenceSync.isReliable) {
const start = mediaSequenceSync.start;
const end = mediaSequenceSync.end;

if (!isFinite(start) || !isFinite(end)) {
return null;
}

const liveEdgeDelay = Vhs.Playlist.liveEdgeDelay(this.mainPlaylistLoader_.main, media);

// Make sure our seekable end is not negative
const calculatedEnd = Math.max(0, end - liveEdgeDelay);

if (calculatedEnd < start) {
return null;
}

return createTimeRanges([[start, calculatedEnd]]);
}

const expired = this.syncController_.getExpiredTime(media, this.duration());

if (expired === null) {
return null;
}

const seekable = Vhs.Playlist.seekable(
media,
expired,
Vhs.Playlist.liveEdgeDelay(this.mainPlaylistLoader_.main, media)
);

return seekable.length ? seekable : null;
}

computeFinalSeekable_(mainSeekable, audioSeekable) {
if (!audioSeekable) {
return mainSeekable;
}

const mainStart = mainSeekable.start(0);
const mainEnd = mainSeekable.end(0);
const audioStart = audioSeekable.start(0);
const audioEnd = audioSeekable.end(0);

if (audioStart > mainEnd || mainStart > audioEnd) {
// Seekables are far apart, rely on main
return mainSeekable;
}

// Return the overlapping seekable range
return createTimeRanges([[
Math.max(mainStart, audioStart),
Math.min(mainEnd, audioEnd)
]]);
}

onSyncInfoUpdate_() {
// TODO check for creation of both source buffers before updating seekable
//
// A fix was made to this function where a check for
Expand All @@ -1707,87 +1771,45 @@ export class PlaylistController extends videojs.EventTarget {
return;
}

let media = this.mainPlaylistLoader_.media();

if (!media) {
return;
}

let expired = this.syncController_.getExpiredTime(media, this.duration());
const mainSeekable = this.getSeekableRange_(this.mainPlaylistLoader_, 'main');

if (expired === null) {
// not enough information to update seekable
if (!mainSeekable) {
return;
}

const main = this.mainPlaylistLoader_.main;
const mainSeekable = Vhs.Playlist.seekable(
media,
expired,
Vhs.Playlist.liveEdgeDelay(main, media)
);

if (mainSeekable.length === 0) {
return;
}
let audioSeekable;

if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
media = this.mediaTypes_.AUDIO.activePlaylistLoader.media();
expired = this.syncController_.getExpiredTime(media, this.duration());

if (expired === null) {
return;
}
audioSeekable = this.getSeekableRange_(this.mediaTypes_.AUDIO.activePlaylistLoader, 'audio');

audioSeekable = Vhs.Playlist.seekable(
media,
expired,
Vhs.Playlist.liveEdgeDelay(main, media)
);

if (audioSeekable.length === 0) {
if (!audioSeekable) {
return;
}
}

let oldEnd;
let oldStart;
const oldSeekable = this.seekable_;

if (this.seekable_ && this.seekable_.length) {
oldEnd = this.seekable_.end(0);
oldStart = this.seekable_.start(0);
}
this.seekable_ = this.computeFinalSeekable_(mainSeekable, audioSeekable);

if (!audioSeekable) {
// seekable has been calculated based on buffering video data so it
// can be returned directly
this.seekable_ = mainSeekable;
} else if (audioSeekable.start(0) > mainSeekable.end(0) ||
mainSeekable.start(0) > audioSeekable.end(0)) {
// seekables are pretty far off, rely on main
this.seekable_ = mainSeekable;
} else {
this.seekable_ = createTimeRanges([[
(audioSeekable.start(0) > mainSeekable.start(0)) ? audioSeekable.start(0) :
mainSeekable.start(0),
(audioSeekable.end(0) < mainSeekable.end(0)) ? audioSeekable.end(0) :
mainSeekable.end(0)
]]);
if (!this.seekable_) {
return;
}

// seekable is the same as last time
if (this.seekable_ && this.seekable_.length) {
if (this.seekable_.end(0) === oldEnd && this.seekable_.start(0) === oldStart) {
if (oldSeekable && oldSeekable.length && this.seekable_.length) {
if (oldSeekable.start(0) === this.seekable_.start(0) &&
oldSeekable.end(0) === this.seekable_.end(0)) {
// Seekable range hasn't changed
return;
}
}

this.logger_(`seekable updated [${Ranges.printableRange(this.seekable_)}]`);

const metadata = {
seekableRanges: this.seekable_
};

this.trigger({type: 'seekablerangeschanged', metadata});
this.trigger({ type: 'seekablerangeschanged', metadata });
this.tech_.trigger('seekablechanged');
}

Expand Down
1 change: 1 addition & 0 deletions src/segment-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,7 @@ export default class SegmentLoader extends videojs.EventTarget {
return;
}


if (this.playlist_ &&
this.playlist_.endList &&
newPlaylist.endList &&
Expand Down
25 changes: 21 additions & 4 deletions src/util/media-sequence-sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export class MediaSequenceSync {
return this.updateStorage_(
segments,
mediaSequence,
this.calculateBaseTime_(mediaSequence, currentTime)
this.calculateBaseTime_(mediaSequence, segments, currentTime)
);
}

Expand Down Expand Up @@ -228,7 +228,7 @@ export class MediaSequenceSync {
this.diagnostics_ = newDiagnostics;
}

calculateBaseTime_(mediaSequence, fallback) {
calculateBaseTime_(mediaSequence, segments, fallback) {
if (!this.storage_.size) {
// Initial setup flow.
return 0;
Expand All @@ -239,6 +239,23 @@ export class MediaSequenceSync {
return this.storage_.get(mediaSequence).segmentSyncInfo.start;
}

const minMediaSequenceFromStorage = Math.min(...this.storage_.keys());

// This case captures a race condition that can occur if we switch to a new media playlist that is out of date
// and still has an older Media Sequence. If this occurs, we extrapolate backwards to get the base time.
if (mediaSequence < minMediaSequenceFromStorage) {
const mediaSequenceDiff = minMediaSequenceFromStorage - mediaSequence;
let baseTime = this.storage_.get(minMediaSequenceFromStorage).segmentSyncInfo.start;

for (let i = 0; i < mediaSequenceDiff; i++) {
const segment = segments[i];

baseTime -= segment.duration;
}

return baseTime;
}

// Fallback flow.
// There is a gap between last recorded playlist and a new one received.
return fallback;
Expand All @@ -256,7 +273,7 @@ export class DependantMediaSequenceSync extends MediaSequenceSync {
this.parent_ = parent;
}

calculateBaseTime_(mediaSequence, fallback) {
calculateBaseTime_(mediaSequence, segments, fallback) {
if (!this.storage_.size) {
const info = this.parent_.getSyncInfoForMediaSequence(mediaSequence);

Expand All @@ -267,6 +284,6 @@ export class DependantMediaSequenceSync extends MediaSequenceSync {
return 0;
}

return super.calculateBaseTime_(mediaSequence, fallback);
return super.calculateBaseTime_(mediaSequence, segments, fallback);
}
}
Loading

0 comments on commit d72e2e9

Please sign in to comment.