Skip to content

Commit

Permalink
fix: CMAF HLS. Source buffer change type is called with wrong codecs …
Browse files Browse the repository at this point in the history
…sometimes when append segment without init data because of a race condition. (#1374)

* Get Codecs: Always use playlist for current pending segment instead of currently loaded playlist.

* fix eslint errors

* Clear active init segment id during reset everything

---------

Co-authored-by: Dzianis Dashkevich <ddashkevich@brightcove.com>
  • Loading branch information
dzianis-dashkevich and Dzianis Dashkevich authored Feb 27, 2023
1 parent 59d98df commit aa9dfbd
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 7 deletions.
16 changes: 9 additions & 7 deletions src/master-playlist-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -1725,9 +1725,11 @@ export class MasterPlaylistController extends videojs.EventTarget {
audio: this.audioSegmentLoader_.getCurrentMediaInfo_() || {}
};

const playlist = this.mainSegmentLoader_.getPendingSegmentPlaylist() || this.media();

// set "main" media equal to video
media.video = media.main;
const playlistCodecs = codecsForPlaylist(this.master(), this.media());
const playlistCodecs = codecsForPlaylist(this.master(), playlist);
const codecs = {};
const usingAudioLoader = !!this.mediaTypes_.AUDIO.activePlaylistLoader;

Expand All @@ -1748,7 +1750,7 @@ export class MasterPlaylistController extends videojs.EventTarget {
// no codecs, no playback.
if (!codecs.audio && !codecs.video) {
this.blacklistCurrentPlaylist({
playlist: this.media(),
playlist,
message: 'Could not determine codecs for playlist.',
blacklistDuration: Infinity
});
Expand All @@ -1773,13 +1775,13 @@ export class MasterPlaylistController extends videojs.EventTarget {
}
});

if (usingAudioLoader && unsupportedAudio && this.media().attributes.AUDIO) {
const audioGroup = this.media().attributes.AUDIO;
if (usingAudioLoader && unsupportedAudio && playlist.attributes.AUDIO) {
const audioGroup = playlist.attributes.AUDIO;

this.master().playlists.forEach(variant => {
const variantAudioGroup = variant.attributes && variant.attributes.AUDIO;

if (variantAudioGroup === audioGroup && variant !== this.media()) {
if (variantAudioGroup === audioGroup && variant !== playlist) {
variant.excludeUntil = Infinity;
}
});
Expand All @@ -1800,7 +1802,7 @@ export class MasterPlaylistController extends videojs.EventTarget {
}, '') + '.';

this.blacklistCurrentPlaylist({
playlist: this.media(),
playlist,
internal: true,
message,
blacklistDuration: Infinity
Expand All @@ -1825,7 +1827,7 @@ export class MasterPlaylistController extends videojs.EventTarget {

if (switchMessages.length) {
this.blacklistCurrentPlaylist({
playlist: this.media(),
playlist,
message: `Codec switching not supported: ${switchMessages.join(', ')}.`,
blacklistDuration: Infinity,
internal: true
Expand Down
5 changes: 5 additions & 0 deletions src/segment-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,7 @@ export default class SegmentLoader extends videojs.EventTarget {
*/
resetEverything(done) {
this.ended_ = false;
this.activeInitSegmentId_ = null;
this.appendInitSegment_ = {
audio: true,
video: true
Expand Down Expand Up @@ -1982,6 +1983,10 @@ export default class SegmentLoader extends videojs.EventTarget {
return this.getCurrentMediaInfo_(segmentInfo) || this.startingMediaInfo_;
}

getPendingSegmentPlaylist() {
return this.pendingSegment_ ? this.pendingSegment_.playlist : null;
}

hasEnoughInfoToAppend_() {
if (!this.sourceUpdater_.ready()) {
return false;
Expand Down
40 changes: 40 additions & 0 deletions test/master-playlist-controller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5092,6 +5092,25 @@ QUnit.test('playlist codecs take priority over others', function(assert) {
assert.deepEqual(codecs, {video: 'avc1.4b400d', audio: 'mp4a.40.20'}, 'codecs returned');
});

QUnit.test('Current pending segment\'s playlist codecs take priority over others', function(assert) {
this.contentSetup({
mainStartingMedia: {videoCodec: 'avc1.4c400d', hasVideo: true, hasAudio: false},
audioStartingMedia: {audioCodec: 'mp4a.40.5', hasVideo: false, hasAudio: true},
mainPlaylist: {attributes: {CODECS: 'avc1.4b400d', AUDIO: 'low-quality'}},
audioPlaylist: {attributes: {CODECS: 'mp4a.40.20'}}
});

const originalGetPendingSegmentPlaylist = this.mpc.mainSegmentLoader_.getPendingSegmentPlaylist.bind(this.mpc.mainSegmentLoader_);

this.mpc.mainSegmentLoader_.getPendingSegmentPlaylist = () => ({attributes: {CODECS: 'avc1.64001f', AUDIO: 'low-quality'}});

const codecs = this.mpc.getCodecsOrExclude_();

assert.deepEqual(this.blacklists, [], 'did not blacklist anything');
assert.deepEqual(codecs, {video: 'avc1.64001f', audio: 'mp4a.40.20'}, 'codecs returned');
this.mpc.mainSegmentLoader_.getPendingSegmentPlaylist = originalGetPendingSegmentPlaylist;
});

QUnit.test('uses default codecs if no codecs are found', function(assert) {
this.contentSetup({
mainStartingMedia: {hasVideo: true, hasAudio: false},
Expand Down Expand Up @@ -5123,6 +5142,27 @@ QUnit.test('excludes playlist without detected audio/video', function(assert) {
assert.deepEqual(codecs, void 0, 'no codecs returned');
});

QUnit.test('excludes current pending segment\'s playlist without detected audio/video', function(assert) {
this.contentSetup({
mainStartingMedia: {},
audioStartingMedia: {},
mainPlaylist: {attributes: {}}
});

const originalGetPendingSegmentPlaylist = this.mpc.mainSegmentLoader_.getPendingSegmentPlaylist.bind(this.mpc.mainSegmentLoader_);

this.mpc.mainSegmentLoader_.getPendingSegmentPlaylist = () => ({attributes: {CODECS: ''}});
const codecs = this.mpc.getCodecsOrExclude_();

assert.deepEqual(this.blacklists, [{
blacklistDuration: Infinity,
message: 'Could not determine codecs for playlist.',
playlist: {attributes: {CODECS: ''}}
}], 'blacklisted playlist');
assert.deepEqual(codecs, void 0, 'no codecs returned');
this.mpc.mainSegmentLoader_.getPendingSegmentPlaylist = originalGetPendingSegmentPlaylist;
});

QUnit.test('excludes unsupported muxer codecs for ts', function(assert) {
this.contentSetup({
mainStartingMedia: {
Expand Down

0 comments on commit aa9dfbd

Please sign in to comment.