static getLanguageAndRolesFrom_(tracks) {
    /** @type {!Map<string, !Set>} */
    const languageToRoles = new Map();
    /** @type {!Map<string, !Map<string, string>>} */
    const languageRoleToLabel = new Map();

    for (let i = 0; i < tracks.length; i++) {
      const track = /** @type {shaka.extern.Track} */(tracks[i]);
      let language = 'und';
      let roles = [];

      if (track.language) {
        language = shaka.util.LanguageUtils.normalize(track.language);
      }

      if (track.type == 'variant') {
        roles = track.audioRoles;
      } else {
        roles = track.roles;
      }

      if (!roles || !roles.length) {
        // We must have an empty role so that we will still get a language-role
        // entry from our Map.
        roles = [''];
      }

      if (!languageToRoles.has(language)) {
        languageToRoles.set(language, new Set());
      }

      for (const role of roles) {
        languageToRoles.get(language).add(role);
        if (track.label) {
          if (!languageRoleToLabel.has(language)) {
            languageRoleToLabel.set(language, new Map());
          }
          languageRoleToLabel.get(language).set(role, track.label);
        }
      }
    }

    // Flatten our map to an array of language-role pairs.
    const pairings = [];
    languageToRoles.forEach((roles, language) => {
      for (const role of roles) {
        let label = null;
        if (languageRoleToLabel.has(language) &&
            languageRoleToLabel.get(language).has(role)) {
          label = languageRoleToLabel.get(language).get(role);
        }
        pairings.push({language, role, label});
      }
    });
    return pairings;
  }
Get all permutations of normalized languages and role for a group of
tracks.

@param {!Array<?(shaka.extern.Track|shaka.extern.TextTrack)>} tracks
@return {!Array<shaka.extern.LanguageRole>}
@private