Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix edge-case in main audio-only with track descriptions #2943

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion demo/chart/timeline-chart.ts
Expand Up @@ -274,7 +274,7 @@ export class TimelineChart {
return;
}
// eslint-disable-next-line no-restricted-properties
const fragData = arrayFind(levelDataSet.data, fragData => fragData.relurl === frag.relurl);
const fragData = arrayFind(levelDataSet.data, fragData => fragData.relurl === frag.relurl && fragData.sn === frag.sn);
if (fragData && fragData !== frag) {
Object.assign(fragData, frag);
}
Expand Down
1 change: 1 addition & 0 deletions demo/index.html
Expand Up @@ -169,6 +169,7 @@ <h3>
</p>
<p>
<span>
<button type="button" class="btn btn-xs btn-default" onclick="$('#streamSelect')[0].selectedIndex++;$('#streamSelect').change()">Next video</button>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a big fan of inline html event handlers, but this is just personal opinion, and I see we do it all over.
Maybe add to the demo js file as util method?
Easier for linters and other tools to pick up on things.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just something quick and dirty for now to speed up running manual tests on all test streams in the demo page. The button should be next to the select menu, no inline html js, ditch jQuery... all the nice to haves.

<button type="button" class="btn btn-xs btn-default btn-dump" title="Save dumped audio mp4 appends" onclick="createfMP4('audio');">Create audio-fmp4</button>
<button type="button" class="btn btn-xs btn-default btn-dump" title="Save dumped video mp4 appends" onclick="createfMP4('video')">Create video-fmp4</button>
</span>
Expand Down
5 changes: 4 additions & 1 deletion demo/main.js
Expand Up @@ -58,10 +58,13 @@ $(document).ready(function () {

chart = setupTimelineChart();

Object.keys(testStreams).forEach((key) => {
Object.keys(testStreams).forEach((key, index) => {
const stream = testStreams[key];
const option = new Option(stream.description, key);
$('#streamSelect').append(option);
if (stream.url === sourceURL) {
$('#streamSelect')[0].selectedIndex = index + 1;
}
});

$('#streamSelect').change(function () {
Expand Down
11 changes: 7 additions & 4 deletions src/controller/audio-stream-controller.js
Expand Up @@ -58,7 +58,7 @@ class AudioStreamController extends BaseStreamController {
// Can change due level switch
this.initPTS[cc] = initPTS;
this.videoTrackCC = cc;
logger.log(`InitPTS for cc: ${cc} found from video track: ${initPTS}`);
logger.log(`InitPTS for cc: ${cc} found from main: ${initPTS}`);

// If we are waiting we need to demux/remux the waiting frag
// With the new initPTS
Expand Down Expand Up @@ -264,7 +264,9 @@ class AudioStreamController extends BaseStreamController {
// we force a frag loading in audio switch as fragment tracker might not have evicted previous frags in case of quick audio switch
this.fragCurrent = frag;
if (audioSwitch || this.fragmentTracker.getState(frag) === FragmentState.NOT_LOADED) {
logger.log(`Loading ${frag.sn}, cc: ${frag.cc} of [${trackDetails.startSN} ,${trackDetails.endSN}],track ${trackId}, currentTime:${pos},bufferEnd:${bufferEnd.toFixed(3)}`);
logger.log(`Loading ${frag.sn}, cc: ${frag.cc} of [${trackDetails.startSN} ,${trackDetails.endSN}],track ${trackId}, ${
this.loadedmetadata ? 'currentTime' : 'nextLoadPosition'
}: ${pos}, bufferEnd: ${bufferEnd.toFixed(3)}`);

if (frag.sn !== 'initSegment') {
this.startFragRequested = true;
Expand Down Expand Up @@ -593,9 +595,10 @@ class AudioStreamController extends BaseStreamController {
logger.log(`parsed ${data.type},PTS:[${data.startPTS.toFixed(3)},${data.endPTS.toFixed(3)}],DTS:[${data.startDTS.toFixed(3)}/${data.endDTS.toFixed(3)}],nb:${data.nb}`);
LevelHelper.updateFragPTSDTS(track.details, fragCurrent, data.startPTS, data.endPTS);

let audioSwitch = this.audioSwitch, media = this.media, appendOnBufferFlush = false;
const media = this.media;
let appendOnBufferFlush = false;
// Only flush audio from old audio tracks when PTS is known on new audio track
if (audioSwitch) {
if (this.audioSwitch) {
if (media && media.readyState) {
let currentTime = media.currentTime;
logger.log('switching audio track : currentTime:' + currentTime);
Expand Down
26 changes: 18 additions & 8 deletions src/controller/stream-controller.js
Expand Up @@ -426,7 +426,7 @@ class StreamController extends BaseStreamController {
}

_loadKey (frag, levelDetails) {
logger.log(`Loading key for ${frag.sn} of [${levelDetails.startSN} ,${levelDetails.endSN}],level ${this.level}`);
logger.log(`Loading key for ${frag.sn} of [${levelDetails.startSN}-${levelDetails.endSN}], level ${this.level}`);
this.state = State.KEY_LOADING;
this.hls.trigger(Event.KEY_LOADING, { frag });
}
Expand All @@ -449,7 +449,9 @@ class StreamController extends BaseStreamController {
frag.autoLevel = this.hls.autoLevelEnabled;
frag.bitrateTest = this.bitrateTest;

logger.log(`Loading ${frag.sn} of [${levelDetails.startSN} ,${levelDetails.endSN}],level ${this.level}, currentTime:${pos.toFixed(3)},bufferEnd:${bufferEnd.toFixed(3)}`);
logger.log(`Loading ${frag.sn} of [${levelDetails.startSN}-${levelDetails.endSN}], level ${this.level}, ${
this.loadedmetadata ? 'currentTime' : 'nextLoadPosition'
}: ${parseFloat(pos.toFixed(3))}, bufferEnd: ${parseFloat(bufferEnd.toFixed(3))}`);

this.hls.trigger(Event.FRAG_LOADING, { frag });
// lazy demuxer init, as this could take some time ... do it during frag loading
Expand Down Expand Up @@ -1085,8 +1087,9 @@ class StreamController extends BaseStreamController {

onAudioTrackSwitching (data) {
// if any URL found on new audio track, it is an alternate audio track
let altAudio = !!data.url,
trackId = data.id;
const fromAltAudio = this.altAudio;
const altAudio = !!data.url;
const trackId = data.id;
// if we switch on main audio, ensure that main fragment scheduling is synced with media.buffered
// don't do anything if we switch to alt audio: audio stream controller is handling it.
// we will just have to change buffer scheduling on audioTrackSwitched
Expand All @@ -1111,10 +1114,17 @@ class StreamController extends BaseStreamController {
this.state = State.IDLE;
}
let hls = this.hls;
// switching to main audio, flush all audio and trigger track switched
hls.trigger(Event.BUFFER_FLUSHING, { startOffset: 0, endOffset: Number.POSITIVE_INFINITY, type: 'audio' });
hls.trigger(Event.AUDIO_TRACK_SWITCHED, { id: trackId });
this.altAudio = false;
// If switching from alt to main audio, flush all audio and trigger track switched
if (fromAltAudio) {
hls.trigger(Event.BUFFER_FLUSHING, {
startOffset: 0,
endOffset: Number.POSITIVE_INFINITY,
type: 'audio'
});
}
hls.trigger(Event.AUDIO_TRACK_SWITCHED, {
id: trackId
});
}
}

Expand Down
4 changes: 2 additions & 2 deletions tests/functional/auto/setup.js
Expand Up @@ -123,10 +123,10 @@ async function testSmoothSwitch (url, config) {
const callback = arguments[arguments.length - 1];
window.startStream(url, config, callback);
const video = window.video;
window.hls.once(window.Hls.Events.FRAG_CHANGED, (event, data) => {
window.hls.once(window.Hls.Events.FRAG_CHANGED, function (event, data) {
window.switchToHighestLevel('next');
});
window.hls.on(window.Hls.Events.LEVEL_SWITCHED, (event, data) => {
window.hls.on(window.Hls.Events.LEVEL_SWITCHED, function (event, data) {
console.log('[test] > level switched: ' + data.level);
let currentTime = video.currentTime;
if (data.level === window.hls.levels.length - 1) {
Expand Down
169 changes: 75 additions & 94 deletions tests/test-streams.js
Expand Up @@ -35,116 +35,89 @@ function createTestStreamWithConfig (target, config) {
}

module.exports = {
bbb: createTestStreamWithConfig({
bbb: {
url: 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8',
description: 'Big Buck Bunny - adaptive qualities'
description: 'Big Buck Bunny - adaptive qualities',
abr: true
},
{
// try to workaround test failing because of slow seek on Chrome/Win10
nudgeMaxRetry: 5
}
),
fdr: {
url: 'https://cdn.jwplayer.com/manifests/pZxWPRg4.m3u8',
description: 'FDR - CDN packaged, 4s segments, 180p - 1080p',
live: false,
abr: true
},
bigBuckBunny480p: {
'url': 'https://test-streams.mux.dev/x36xhzz/url_6/193039199_mp4_h264_aac_hq_7.m3u8',
'description': 'Big Buck Bunny - 480p only',
'live': false,
'abr': false,
'blacklist_ua': ['internet explorer']
url: 'https://test-streams.mux.dev/x36xhzz/url_6/193039199_mp4_h264_aac_hq_7.m3u8',
description: 'Big Buck Bunny - 480p only',
abr: false,
blacklist_ua: ['internet explorer']
},
arte: {
'url': 'https://test-streams.mux.dev/test_001/stream.m3u8',
'description': 'ARTE China,ABR',
'live': false,
'abr': true
url: 'https://test-streams.mux.dev/test_001/stream.m3u8',
description: 'ARTE China,ABR',
abr: true
},
deltatreDAI: {
'url': 'https://test-streams.mux.dev/dai-discontinuity-deltatre/manifest.m3u8',
'description': 'Ad-insertion in event stream',
'live': false,
'abr': false,
'blacklist_ua': ['internet explorer']
url: 'https://test-streams.mux.dev/dai-discontinuity-deltatre/manifest.m3u8',
description: 'Ad-insertion in event stream',
abr: false,
blacklist_ua: ['internet explorer']
},
issue666: {
'url': 'https://playertest.longtailvideo.com/adaptive/issue666/playlists/cisq0gim60007xzvi505emlxx.m3u8',
'description': 'Surveillance footage - https://github.com/video-dev/hls.js/issues/666',
'live': false,
'abr': false,
'blacklist_ua': ['internet explorer']
},
/* // went offline for us :( would be good to replace this for regression test with something mimicking the issue
issue649: {
'url': 'https://cdn3.screen9.com/media/c/W/cW87csHkxsgu5TV1qs78aA_auto_hls.m3u8?auth=qlUjeCtbVdtkDfZYrtveTIVUXX1yuSqgF8wfWabzKpX72r-d5upW88-FHuyRRdnZA_1PKRTGAtTt_6Z-aj22kw',
'description': 'hls.js/issues/649',
'live': false,
'abr': false
url: 'https://playertest.longtailvideo.com/adaptive/issue666/playlists/cisq0gim60007xzvi505emlxx.m3u8',
description: 'Surveillance footage - https://github.com/video-dev/hls.js/issues/666',
abr: false,
blacklist_ua: ['internet explorer']
},
*/
closedCaptions: {
'url': 'https://playertest.longtailvideo.com/adaptive/captions/playlist.m3u8',
'description': 'CNN special report, with CC',
'live': false,
'abr': false
url: 'https://playertest.longtailvideo.com/adaptive/captions/playlist.m3u8',
description: 'CNN special report, with CC',
abr: false
},
customIvBadDts: {
'url': 'https://playertest.longtailvideo.com/adaptive/customIV/prog_index.m3u8',
'description': 'Custom IV with bad PTS DTS',
'live': false,
'abr': false,
'blacklist_ua': ['safari']
url: 'https://playertest.longtailvideo.com/adaptive/customIV/prog_index.m3u8',
description: 'Custom IV with bad PTS DTS',
abr: false
},
oceansAES: {
'url': 'https://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8',
'description': 'AES encrypted,ABR',
'live': false,
'abr': true
url: 'https://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8',
description: 'AES encrypted,ABR',
abr: true
},
/*
bbbAES: {
'url': 'https://test-streams.mux.dev/bbbAES/playlists/sample_aes/index.m3u8',
'description': 'SAMPLE-AES encrypted',
'live': false,
'abr': false
url: 'https://test-streams.mux.dev/bbbAES/playlists/sample_aes/index.m3u8',
description: 'SAMPLE-AES encrypted',
live: false,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete live?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's the default. This streams no up anymore though, so this comment is more a reminder that we are missing a Sample-AES test stream.

abr: false
},
*/
mp3Audio: {
'url': 'https://playertest.longtailvideo.com/adaptive/vod-with-mp3/manifest.m3u8',
'description': 'MP3 VOD demo',
'live': false,
'abr': false,
'blacklist_ua': ['safari']
url: 'https://playertest.longtailvideo.com/adaptive/vod-with-mp3/manifest.m3u8',
description: 'MP3 VOD demo',
abr: false
},
mpegAudioOnly: {
'url': 'https://pl.streamingvideoprovider.com/mp3-playlist/playlist.m3u8',
'description': 'MPEG Audio Only demo',
'live': false,
'abr': false,
'blacklist_ua': ['internet explorer', 'MicrosoftEdge', 'safari', 'firefox']
url: 'https://pl.streamingvideoprovider.com/mp3-playlist/playlist.m3u8',
description: 'MPEG Audio Only demo',
abr: false,
blacklist_ua: ['internet explorer', 'MicrosoftEdge', 'firefox']
},
fmp4: {
'url': 'https://storage.googleapis.com/shaka-demo-assets/angel-one-hls/hls.m3u8',
'description': 'HLS fMP4 Angel-One multiple audio-tracks',
'live': false,
'abr': false,
'blacklist_ua': ['safari', 'internet explorer']
url: 'https://storage.googleapis.com/shaka-demo-assets/angel-one-hls/hls.m3u8',
description: 'HLS fMP4 Angel-One multiple audio-tracks',
abr: true,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ABR was false before, did you mean to put true?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this stream has multiple variants. It was probably set to false to skip tests. If the smooth switch test fails on this stream, I want to know.

blacklist_ua: ['internet explorer']
},
fmp4Bitmovin: {
'url': 'https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s-fmp4/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8',
'description': 'HLS fMP4 by Bitmovin',
'live': false,
'abr': true,
'blacklist_ua': ['safari', 'internet explorer']
url: 'https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s-fmp4/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8',
description: 'HLS fMP4 by Bitmovin',
abr: true,
blacklist_ua: ['internet explorer']
},
offset_pts: {
'url': 'https://test-streams.mux.dev/pts_shift/master.m3u8',
'description': 'DK Turntable, PTS shifted by 2.3s',
'live': false,
'abr': false
url: 'https://test-streams.mux.dev/pts_shift/master.m3u8',
description: 'DK Turntable, PTS shifted by 2.3s',
abr: true
},
/*
uspHLSAteam: createTestStream(
Expand All @@ -155,59 +128,67 @@ module.exports = {
angelOneShakaWidevine: createTestStreamWithConfig({
url: 'https://storage.googleapis.com/shaka-demo-assets/angel-one-widevine-hls/hls.m3u8',
description: 'Shaka-packager Widevine DRM (EME) HLS-fMP4 - Angel One Demo',
abr: true,
blacklist_ua: ['firefox', 'safari', 'internet explorer']
},
{
}, {
widevineLicenseUrl: 'http://cwip-shaka-proxy.appspot.com/no_auth',
emeEnabled: true
}
),
}),
audioOnlyMultipleLevels: {
'url': 'https://s3.amazonaws.com/qa.jwplayer.com/~alex/121628/new_master.m3u8',
'description': 'Multiple non-alternate audio levels',
'live': false,
'abr': false
url: 'https://s3.amazonaws.com/qa.jwplayer.com/~alex/121628/new_master.m3u8',
description: 'Multiple non-alternate audio levels',
abr: true
},
pdtDuplicate: {
url: 'https://playertest.longtailvideo.com/adaptive/artbeats/manifest.m3u8',
description: 'Stream with duplicate sequential PDT values'
description: 'Stream with duplicate sequential PDT values',
abr: false
},
pdtLargeGap: {
url: 'https://playertest.longtailvideo.com/adaptive/boxee/playlist.m3u8',
description: 'PDTs with large gaps following discontinuities'
description: 'PDTs with large gaps following discontinuities',
abr: false
},
pdtBadValues: {
url: 'https://playertest.longtailvideo.com/adaptive/progdatime/playlist2.m3u8',
description: 'PDTs with bad values'
description: 'PDTs with bad values',
abr: false
},
pdtOneValue: {
url: 'https://playertest.longtailvideo.com/adaptive/aviion/manifest.m3u8',
description: 'One PDT, no discontinuities'
description: 'One PDT, no discontinuities',
abr: false
},
noTrackIntersection: {
noTrackIntersection: createTestStreamWithConfig({
url: 'https://s3.amazonaws.com/qa.jwplayer.com/~alex/123633/new_master.m3u8',
description: 'Audio/video track PTS values do not intersect; 10 second start gap',
abr: false
}, {
avBufferOffset: 10.5
},
}),
// altAudioNoVideoCodecSignaled: {
// url: 'https://d35u71x3nb8v2y.cloudfront.net/4b711b97-513c-4d36-ad29-298ab23a2e5e/3cbf1114-b2f4-4320-afb3-f0f7eeeb8630/playlist.m3u8',
// description: 'Alternate audio track, but no video codec is signaled in the master manifest'
// },
altAudioAndTracks: {
url: 'https://wowzaec2demo.streamlock.net/vod-multitrack/_definst_/smil:ElephantsDream/elephantsdream2.smil/playlist.m3u',
description: 'Alternate audio tracks, and multiple VTT tracks'
description: 'Alternate audio tracks, and multiple VTT tracks',
abr: true
},
altAudioAudioOnly: {
url: 'https://playertest.longtailvideo.com/adaptive/alt-audio-no-video/sintel/playlist.m3u8',
description: 'Audio only with alternate audio track (Sintel)'
description: 'Audio only with alternate audio track (Sintel)',
abr: false
},
altAudioMultiAudioOnly: {
url: 'https://playertest.longtailvideo.com/adaptive/alt-audio-no-video/angel-one.m3u8',
description: 'Audio only with multiple alternate audio tracks (Angel One)'
description: 'Audio only with multiple alternate audio tracks (Angel One)',
abr: false
},
muxedFmp4: {
url: 'https://s3.amazonaws.com/qa.jwplayer.com/hlsjs/muxed-fmp4/hls.m3u8',
description: 'Muxed av fmp4 - appended to "audiovideo" SourceBuffer'
description: 'Muxed av fmp4 - appended to "audiovideo" SourceBuffer',
abr: false
},
altAudioWithPdtAndStartGap: {
url: 'https://playertest.longtailvideo.com/adaptive/hls-test-streams/test-audio-pdt/playlist.m3u8',
Expand Down