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

feat(ui): Add quality selection for audio-only content #3649

Merged
merged 1 commit into from Apr 21, 2022
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
16 changes: 0 additions & 16 deletions test/ui/ui_integration.js
Expand Up @@ -470,22 +470,6 @@ describe('UI', () => {
expect(isChosen).not.toBe(null);
});

it('restores the resolutions menu after audio-only playback', async () => {
/** @type {HTMLElement} */
const resolutionButton = shaka.util.Dom.getElementByClassName(
'shaka-resolution-button', videoContainer);

// Load an audio-only clip. The menu should be hidden.
await player.load('test:sintel_audio_only_compiled');
expect(player.isAudioOnly()).toBe(true);
expect(resolutionButton.classList.contains('shaka-hidden')).toBe(true);

// Load an audio-video clip. The menu should be visible again.
await player.load('test:sintel_multi_lingual_multi_res_compiled');
expect(player.isAudioOnly()).toBe(false);
expect(resolutionButton.classList.contains('shaka-hidden')).toBe(false);
});

/**
* @return {Element}
*/
Expand Down
27 changes: 27 additions & 0 deletions test/ui/ui_unit.js
Expand Up @@ -658,6 +658,33 @@ describe('UI', () => {
expect(getResolutions()).toEqual(['540p']);
});

it('displays audio quality based on current stream', async () => {
const manifest =
shaka.test.ManifestGenerator.generate((manifest) => {
manifest.addVariant(0, (variant) => {
variant.addAudio(0);
variant.bandwidth = 100000;
});
manifest.addVariant(1, (variant) => {
variant.addAudio(1);
variant.bandwidth = 200000;
});
});

shaka.media.ManifestParser.registerParserByMime(
fakeMimeType, () => new shaka.test.FakeManifestParser(manifest));

await player.load(
/* uri= */ 'fake', /* startTime= */ 0, fakeMimeType);

const qualityButtons = videoContainer.querySelectorAll(
joeyparrish marked this conversation as resolved.
Show resolved Hide resolved
'button.explicit-resolution > span');
const qualityOptions =
Array.from(qualityButtons).map((btn) => btn.innerText);

expect(qualityOptions).toEqual(['200 kbits/s', '100 kbits/s']);
});

/**
* Use internals to update the resolution menu. Our fake manifest can
* cause problems with startup where the Player will get stuck using
Expand Down
1 change: 1 addition & 0 deletions ui/locales/en.json
Expand Up @@ -27,6 +27,7 @@
"PICTURE_IN_PICTURE": "Picture-in-Picture",
"PLAY": "Play",
"PLAYBACK_RATE": "Playback speed",
"QUALITY": "Quality",
"REPLAY": "Replay",
"RESOLUTION": "Resolution",
"REWIND": "Rewind",
Expand Down
5 changes: 5 additions & 0 deletions ui/locales/source.json
Expand Up @@ -113,6 +113,11 @@
"message": "Playback speed",
"description": "Label for a button used to navigate to a submenu to choose the playback speed in the video player."
},
"QUALITY": {
"description": "Label for a button used to open a submenu to choose audio quality in the video player.",
"meaning": "Audio quality",
"message": "Quality"
},
"REPLAY": {
"description": "Label for a button used to replay the video in a video player.",
"message": "Replay"
Expand Down
64 changes: 31 additions & 33 deletions ui/resolution_selection.js
Expand Up @@ -60,9 +60,6 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
});

this.updateResolutionSelection_();

// Set up all the strings in the user's preferred language.
this.updateLocalizedStrings_();
}


Expand All @@ -71,27 +68,6 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
/** @type {!Array.<shaka.extern.Track>} */
let tracks = this.player.getVariantTracks();

// Hide resolution menu and button for audio-only content and src= content
// without resolution information.
// TODO: for audio-only content, this should be a bitrate selection menu
// instead.
if (tracks.length && !tracks[0].height) {
shaka.ui.Utils.setDisplay(this.menu, false);
shaka.ui.Utils.setDisplay(this.button, false);
return;
}
// Otherwise, restore it.
shaka.ui.Utils.setDisplay(this.button, true);

tracks.sort((t1, t2) => {
// We have already screened for audio-only content, but the compiler
// doesn't know that.
goog.asserts.assert(t1.height != null, 'Null height');
goog.asserts.assert(t2.height != null, 'Null height');

return t2.height - t1.height;
});

// If there is a selected variant track, then we filter out any tracks in
// a different language. Then we use those remaining tracks to display the
// available resolutions.
Expand All @@ -103,14 +79,31 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
track.channelsCount == selectedTrack.channelsCount);
}

// Remove duplicate entries with the same height. This can happen if
// we have multiple resolutions of audio. Pick an arbitrary one.
// Remove duplicate entries with the same resolution or quality depending
// on content type. Pick an arbitrary one.
tracks = tracks.filter((track, idx) => {
// Keep the first one with the same height.
const otherIdx = tracks.findIndex((t) => t.height == track.height);
// Keep the first one with the same height or bandwidth.
const otherIdx = this.player.isAudioOnly() ?
tracks.findIndex((t) => t.bandwidth == track.bandwidth) :
tracks.findIndex((t) => t.height == track.height);
return otherIdx == idx;
});

// Sort the tracks by height or bandwith depending on content type.
if (this.player.isAudioOnly()) {
tracks.sort((t1, t2) => {
goog.asserts.assert(t1.bandwidth != null, 'Null bandwidth');
goog.asserts.assert(t2.bandwidth != null, 'Null bandwidth');
return t2.bandwidth - t1.bandwidth;
});
} else {
tracks.sort((t1, t2) => {
goog.asserts.assert(t1.height != null, 'Null height');
goog.asserts.assert(t2.height != null, 'Null height');
return t2.height - t1.height;
});
}

// Remove old shaka-resolutions
// 1. Save the back to menu button
const backButton = shaka.ui.Utils.getFirstDescendantWithClassName(
Expand All @@ -132,7 +125,8 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
() => this.onTrackSelected_(track));

const span = shaka.util.Dom.createHTMLElement('span');
span.textContent = track.height + 'p';
span.textContent = this.player.isAudioOnly() ?
Math.round(track.bandwidth / 1000) + ' kbits/s' : track.height + 'p';
button.appendChild(span);

if (!abrEnabled && track == selectedTrack) {
Expand Down Expand Up @@ -176,6 +170,8 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
shaka.ui.Utils.focusOnTheChosenItem(this.menu);
this.controls.dispatchEvent(
new shaka.util.FakeEvent('resolutionselectionupdated'));

this.updateLocalizedStrings_();
}


Expand All @@ -197,13 +193,15 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
*/
updateLocalizedStrings_() {
const LocIds = shaka.ui.Locales.Ids;
const locId = this.player.isAudioOnly() ?
LocIds.QUALITY : LocIds.RESOLUTION;

this.button.ariaLabel = this.localization.resolve(LocIds.RESOLUTION);
this.backButton.ariaLabel = this.localization.resolve(LocIds.RESOLUTION);
this.button.ariaLabel = this.localization.resolve(locId);
this.backButton.ariaLabel = this.localization.resolve(locId);
this.backSpan.textContent =
this.localization.resolve(LocIds.RESOLUTION);
this.localization.resolve(locId);
this.nameSpan.textContent =
this.localization.resolve(LocIds.RESOLUTION);
this.localization.resolve(locId);
this.abrOnSpan_.textContent =
this.localization.resolve(LocIds.AUTO_QUALITY);

Expand Down