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

Redundant streams fallback behavior for alternate renditions with multiple media groups #1630

Merged
merged 47 commits into from
Jun 11, 2018

Conversation

tchakabam
Copy link
Collaborator

@tchakabam tchakabam commented Mar 26, 2018

Description of the Changes

Until now, we are not supporting a typical live broadcast use-case: using backup streams for all alternate audio and subtitle rendition options, grouping these into respective group-IDs. For example group-ID A groups all the primary renditions and video-levels, B all the back ones. In this case, group-IDs of selected audio and subtitle renditions need to be matched to the group-ID of the running level and vice versa. So if we fallback on a level, we have to re-select the rendition so to match the group ID, and if we fallback on audio we may have to switch to the matching redundant level. That is because the grouped renditions/levels actually run on sync'ed encoders, while if the groups don't match, there can easily be A/V-sync issues. In fact, the clocks of the redundants streams across group IDs are not actually even specified to have anything like matching clocks or so, they could operate in completely different timeline.

=> This PR will fix the above and allow proper fallback behavior across all directions (video-levels <-> audio-and-subs-renditions)

CheckLists

  • changes have been done against master branch, and PR does not conflict
  • no commits have been done in dist folder (we will take care of updating it)
  • new unit / functional tests have been added (whenever applicable)
  • Travis tests are passing (or test results are not worse than on master branch :))
  • API or design changes are documented in API.md

@tchakabam tchakabam requested a review from mangui March 26, 2018 21:39
@tchakabam
Copy link
Collaborator Author

May fix parts of #1438

@tchakabam tchakabam changed the title Audio track fallback to non-blacklisted on network failure Audio track fallback to non-blacklisted on network failure, fixes #1438 Mar 26, 2018
@@ -152,24 +152,26 @@ class PlaylistLoader extends EventHandler {
}

onManifestLoading (data) {
this.load(data.url, { type: ContextType.MANIFEST });
this.load(data.url, { type: ContextType.MANIFES, level: null, id: null });
Copy link
Contributor

Choose a reason for hiding this comment

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

There is a little type error, should be:
this.load(data.url, { type: ContextType.MANIFEST, level: null, id: null });

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

which is why stuff currently fails 👍

get audioTrack () {
return this.trackId;
}

/** select an audio track, based on its index in audio track lists**/
set audioTrack (audioTrackId) {
if (this.trackId !== audioTrackId || this.tracks[audioTrackId].details === undefined)
Copy link
Collaborator Author

@tchakabam tchakabam Mar 27, 2018

Choose a reason for hiding this comment

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

@mangui how did the condition to run the set internal make sense: this.tracks[audioTrackId].details === undefined ?

wouldn't you still want to switch even if the metadata is not loaded yet?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

condition is :
call set internal if new track id OR if same track id but details not available yet (in that case it will force a reload of the details)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

of course, makes sense :) thanks for the explanation. i have added the check for the details prop in this branch now as well to make sure we are allowing to switch track in this case as well.

@tchakabam tchakabam force-pushed the feature/audio-track-fallback branch from 266d64c to 556cd06 Compare April 23, 2018 16:59
@tchakabam
Copy link
Collaborator Author

@mangui @johnBartos @tjenkinson Hey guys, do you think you could check this out? just to make sure i didn't break something badly :)

Will also add unit tests for this behavior hopefully as soon as I get to it!

… details lack (recovers previous logic after refactor)
@@ -31,7 +31,12 @@ class AbrController extends EventHandler {
}

onFragLoading (data) {
let frag = data.frag;
const hls = this.hls;
Copy link
Member

Choose a reason for hiding this comment

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

hls and config objects were only initialized if useful before.
now you will have extra processing everytime a subtitle or audio frag is loading.
you could argue that this might look like micro-optimization, but still ;-)

isLive = hls.levels[level].details.live,
config = hls.config,
ewmaFast, ewmaSlow;
let level = data.frag.level;
Copy link
Member

Choose a reason for hiding this comment

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

let level = frag.level is shorter

@@ -55,8 +57,9 @@ class AbrController extends EventHandler {
}
this._bwEstimator = new EwmaBandWidthEstimator(hls, ewmaSlow, ewmaFast, config.abrEwmaDefaultEstimate);
}
this.fragCurrent = frag;
this._bwEstimator = new EwmaBandWidthEstimator(hls, ewmaSlow, ewmaFast, config.abrEwmaDefaultEstimate);
Copy link
Member

Choose a reason for hiding this comment

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

looks like bwEstimator is created twice now ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

true. was debugging this stuff and left some mess around :/

}
this.fragCurrent = frag;
Copy link
Member

Choose a reason for hiding this comment

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

we should not update this.fragCurrent if frag is not from main

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

i was actually wondering about that. i think there was a bug however here, we were sometimes hitting an undefined fragCurrent..? but maybe it's fixed now, i was trying to debug this part and something was fishy.

@tchakabam tchakabam changed the title Audio track fallback to non-blacklisted on network failure, fixes #1438 Redundant streams fallback behavior for alternate rendition with multiple media groups May 17, 2018
@tchakabam tchakabam changed the title Redundant streams fallback behavior for alternate rendition with multiple media groups Redundant streams fallback behavior for alternate renditions with multiple media groups May 17, 2018
…her controllers to ensure they are on the correct group ID
@axten
Copy link

axten commented May 23, 2018

Hi @tchakabam,
can you confirm that with this great PR a master playlist with multiple media groups (GROUP-ID) for video (with subs) and audio is handled correctly?
https://zdfhlstest2-i.akamaihd.net/hls/live/226660/zdf_ahw/high/master.m3u8

And if yes, does hls.js ensure that all active medias are from the same group?
Example: video, audio and subs from group A are playing. Now video fails and hls.js switches to fallback video stream from group B. Does hls.js switch also audio and subtitle streams to Group B in this case?

@tchakabam
Copy link
Collaborator Author

tchakabam commented May 23, 2018

Hi @axten Yes. That's exactly the idea. In this branch this should work as expected now.

@axten
Copy link

axten commented May 23, 2018

ok great! this is exactly what we need! cannot await this to land :)

@tchakabam
Copy link
Collaborator Author

tchakabam commented May 24, 2018

@axten That's what I had been told as well 👍 Very well, then this will need some clean-up and polishing, but the functionality is now there. We were actually missing the information inside our Quality-Level data-model where we are handling redundant streams by nesting various URLs for the same quality, but as a result were compressing away the various audio-group IDs for the respective redundants streams. This information is now being patched onto the data-model along with the redundant URLs.

Furthermore, the audio-track switched event is being handled now by the level-controller (which is triggered also when we switch for failure fallback) in order to make sure that the current level played is actually for the group ID of the latest selected audio-track. If that is not the case, we will correct that setting internally and re-load the video levels accordingly.

In a way like a mirror, the analogous logic is implemented for audio-tracks. As a quality level is switched (wether it be for any reason, manually or by recovery/fallback mechanisms) we check which actual stream is being played of the redundant options, and which audio-group-id that corresponds. If that doesn't match the audio-group-ID of the currently selected audio-track, we switch it accordingly, while trying to preserve the actual name/language setting which indicates the logical "role" of this audio track (to avoid switching say from original audio to audio for visibility handicap etc).

Further things to do now would be:

  • Add the analoguous logic for subtitles redundant groups (will be pretty much a copy of what we added for audio-tracks, so very easy)

  • Add some unit tests to spec this behavior clearly and avoid regression

  • Add these test cases of yours to our acceptance test suite

=> Then we should be able to merge this into our master branch.

You could anyhow already use this branch here for your testing to validate we got it all right, and in principle also for your prod player already, if it works for you (and after we removed a few hard-coded log lines maybe :)).

@tchakabam tchakabam mentioned this pull request Jun 4, 2018
4 tasks
}
const newTrack = this.tracks[i];
if (newTrack.name === name &&
newTrack.language === language) {
Copy link

Choose a reason for hiding this comment

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

Why check for both name and language here but only name on _selectInitialAudioTrack? From what I see in the spec only name is required to both exist and be consistent across groups. Any sane manifest will have matching and unique names and languages, but it may be worth being consistent

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point. Name should be sufficient, you're right.

@tchakabam
Copy link
Collaborator Author

Build pipeline succeeded (not sure why it's greyed out however): https://travis-ci.org/video-dev/hls.js/builds/389764334

@tchakabam tchakabam merged commit 741d531 into master Jun 11, 2018
@tchakabam tchakabam added this to the 0.10.0 milestone Jun 13, 2018
@tchakabam tchakabam deleted the feature/audio-track-fallback branch June 16, 2018 16:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants