Skip to content

Commit

Permalink
Merge branch 'main' into chore-latency-estimate
Browse files Browse the repository at this point in the history
  • Loading branch information
adrums86 authored Oct 22, 2024
2 parents 57c0553 + 8456cb3 commit ab8de9b
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 11 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
<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)

### Features

* Add Airplay support when overriding native HLS in Safari/iOS ([#1543](https://github.com/videojs/http-streaming/issues/1543)) ([bfc17b4](https://github.com/videojs/http-streaming/commit/bfc17b4))
* Add support for ManagedMediaSource 'startstreaming' and 'endstream' event handling ([#1542](https://github.com/videojs/http-streaming/issues/1542)) ([ae1ae70](https://github.com/videojs/http-streaming/commit/ae1ae70))

### Chores

* update mpd-parser to v1.3.1 ([#1544](https://github.com/videojs/http-streaming/issues/1544)) ([a9dd790](https://github.com/videojs/http-streaming/commit/a9dd790))

<a name="3.14.2"></a>
## [3.14.2](https://github.com/videojs/http-streaming/compare/v3.14.1...v3.14.2) (2024-09-17)

Expand Down
14 changes: 7 additions & 7 deletions package-lock.json

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

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@videojs/http-streaming",
"version": "3.14.2",
"version": "3.15.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 Expand Up @@ -62,12 +62,12 @@
"aes-decrypter": "^4.0.2",
"global": "^4.4.0",
"m3u8-parser": "^7.2.0",
"mpd-parser": "^1.3.0",
"mpd-parser": "^1.3.1",
"mux.js": "7.0.3",
"video.js": "^7 || ^8"
},
"peerDependencies": {
"video.js": "^8.14.0"
"video.js": "^8.19.0"
},
"devDependencies": {
"@babel/cli": "^7.21.0",
Expand Down
22 changes: 22 additions & 0 deletions src/playlist-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ export class PlaylistController extends videojs.EventTarget {
// Airplay source not yet implemented. Remote playback must be disabled.
this.tech_.el_.disableRemotePlayback = true;
this.mediaSource = new window.ManagedMediaSource();

videojs.log('Using ManagedMediaSource');
} else if (window.MediaSource) {
this.mediaSource = new window.MediaSource();
Expand All @@ -223,12 +224,16 @@ export class PlaylistController extends videojs.EventTarget {
this.handleDurationChange_ = this.handleDurationChange_.bind(this);
this.handleSourceOpen_ = this.handleSourceOpen_.bind(this);
this.handleSourceEnded_ = this.handleSourceEnded_.bind(this);
this.load = this.load.bind(this);
this.pause = this.pause.bind(this);

this.mediaSource.addEventListener('durationchange', this.handleDurationChange_);

// load the media source into the player
this.mediaSource.addEventListener('sourceopen', this.handleSourceOpen_);
this.mediaSource.addEventListener('sourceended', this.handleSourceEnded_);
this.mediaSource.addEventListener('startstreaming', this.load);
this.mediaSource.addEventListener('endstreaming', this.pause);
// we don't have to handle sourceclose since dispose will handle termination of
// everything, and the MediaSource should not be detached without a proper disposal

Expand Down Expand Up @@ -1056,14 +1061,31 @@ export class PlaylistController extends videojs.EventTarget {
*/
load() {
this.mainSegmentLoader_.load();

if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
this.audioSegmentLoader_.load();
}

if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
this.subtitleSegmentLoader_.load();
}
}

/**
* Call pause on our SegmentLoaders
*/
pause() {
this.mainSegmentLoader_.pause();

if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
this.audioSegmentLoader_.pause();
}

if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
this.subtitleSegmentLoader_.pause();
}
}

/**
* Re-tune playback quality level for the current player
* conditions. This method will perform destructive actions like removing
Expand Down
14 changes: 13 additions & 1 deletion src/videojs-http-streaming.js
Original file line number Diff line number Diff line change
Expand Up @@ -1079,7 +1079,19 @@ class VhsHandler extends Component {

this.mediaSourceUrl_ = window.URL.createObjectURL(this.playlistController_.mediaSource);

this.tech_.src(this.mediaSourceUrl_);
// If we are playing HLS with MSE in Safari, add source elements for both the blob and manifest URLs.
// The latter will enable Airplay playback on receiver devices.
if ((
videojs.browser.IS_ANY_SAFARI || videojs.browser.IS_IOS) &&
this.options_.overrideNative &&
this.options_.sourceType === 'hls' &&
typeof this.tech_.addSourceElement === 'function'
) {
this.tech_.addSourceElement(this.mediaSourceUrl_);
this.tech_.addSourceElement(this.source_.src);
} else {
this.tech_.src(this.mediaSourceUrl_);
}
}

createKeySessions_() {
Expand Down
36 changes: 36 additions & 0 deletions test/playlist-controller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7693,3 +7693,39 @@ QUnit.test('uses ManagedMediaSource only when opted in', function(assert) {
mmsSpy.restore();
mms.restore();
});

QUnit.test('ManagedMediaSource startstreaming and endstreaming events start and pause segment loading respectively', function(assert) {
const mms = useFakeManagedMediaSource();
const options = {
src: 'test.m3u8',
tech: this.player.tech_,
player_: this.player,
experimentalUseMMS: true
};

const controller = new PlaylistController(options);

controller.mediaTypes_.AUDIO.activePlaylistLoader = {};
controller.mediaTypes_.SUBTITLES.activePlaylistLoader = {};

const mainLoadSpy = sinon.spy(controller.mainSegmentLoader_, 'load');
const audioLoadSpy = sinon.spy(controller.audioSegmentLoader_, 'load');
const subtitleLoadSpy = sinon.spy(controller.subtitleSegmentLoader_, 'load');
const mainPauseSpy = sinon.spy(controller.mainSegmentLoader_, 'pause');
const audioPauseSpy = sinon.spy(controller.audioSegmentLoader_, 'pause');
const subtitlePauseSpy = sinon.spy(controller.subtitleSegmentLoader_, 'pause');

controller.mediaSource.trigger('startstreaming');

assert.ok(mainLoadSpy.calledOnce, 'Segment loading started on startstreaming event');
assert.ok(audioLoadSpy.calledOnce, 'Audio segment loading started on startstreaming event');
assert.ok(subtitleLoadSpy.calledOnce, 'Subtitle segment loading started on startstreaming event');

controller.mediaSource.trigger('endstreaming');

assert.ok(mainPauseSpy.calledOnce, 'Main segment loading paused on endstreaming event');
assert.ok(audioPauseSpy.calledOnce, 'Audio segment loading paused on endstreaming event');
assert.ok(subtitlePauseSpy.calledOnce, 'Subtitle segment loading paused on endstreaming event');

mms.restore();
});
39 changes: 39 additions & 0 deletions test/videojs-http-streaming.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3121,6 +3121,45 @@ QUnit.test(
}
);

QUnit.test('uses source elements when overriding native HLS in Safari/iOS', function(assert) {
const origIsAnySafari = videojs.browser.IS_ANY_SAFARI;
const addSourceElementCalls = [];
let srcCalls = 0;

videojs.browser.IS_ANY_SAFARI = true;

const player = createPlayer({ html5: { vhs: { overrideNative: true } } });

player.tech_.addSourceElement = function(url) {
addSourceElementCalls.push(url);
};

player.tech_.src = function() {
srcCalls++;
};

player.src({
src: 'http://example.com/manifest/main.m3u8',
type: 'application/x-mpegURL'
});

this.clock.tick(1);

assert.equal(addSourceElementCalls.length, 2, '2 source elements added');
assert.equal(srcCalls, 0, 'tech.src() not called');

const blobUrl = addSourceElementCalls[0];
const manifestUrl = addSourceElementCalls[1];

assert.ok(blobUrl.startsWith('blob:'), 'First source element is a blob URL');
assert.equal(manifestUrl, 'http://example.com/manifest/main.m3u8', 'Second source element is the manifest URL');

// Clean up and restore original flags
player.dispose();

videojs.browser.IS_ANY_SAFARI = origIsAnySafari;
});

QUnit.test('re-emits mediachange events', function(assert) {
let mediaChanges = 0;

Expand Down

0 comments on commit ab8de9b

Please sign in to comment.