Skip to content

Commit

Permalink
feat: add audio tracks API (#742)
Browse files Browse the repository at this point in the history
This adds the audio tracks API. Not the UI yet.
test url:
https://elements-demo-vanilla-git-fork-luwes-audio-tracks-api-mux.vercel.app/mux-player-audio-tracks.html

Co-authored-by: Christian Pillsbury <[email protected]>
  • Loading branch information
luwes and cjpillsbury authored Aug 16, 2023
1 parent f584e94 commit 13bfbdb
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 15 deletions.
1 change: 1 addition & 0 deletions examples/vanilla-ts-esm/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ <h1><a href="/">Elements</a></h1>
<li><a href="./mux-player-audio.html" class="player">&lt;mux-player audio&gt;</a></li>
<li><a href="./mux-player-cuepoints.html" class="player">&lt;mux-player&gt; (cuePoints)</a></li>
<li><a href="./mux-player-renditions.html" class="player">&lt;mux-player&gt; (renditions)</a></li>
<li><a href="./mux-player-audio-tracks.html" class="player">&lt;mux-player&gt; (audio tracks)</a></li>
<li><a href="./mux-player-theme.html" class="player">&lt;mux-player&gt; (microvideo theme)</a></li>
<li><a href="./mux-player-theme-2023.html" class="player">&lt;mux-player&gt; (2023 theme)</a></li>
<li><a href="./mux-uploader-simple.html" class="uploader">&lt;mux-uploader&gt;</a></li>
Expand Down
83 changes: 83 additions & 0 deletions examples/vanilla-ts-esm/public/mux-player-audio-tracks.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>&lt;mux-player&gt; audio tracks example</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/water.css">
<link rel="stylesheet" href="./styles.css">
<script
defer
src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"
></script>
<script type="module" src="./dist/mux-player.js"></script>
<style>
mux-player {
display: block;
width: 100%;
margin: 1rem 0 2rem;
background-color: #000;
line-height: 0;
}

mux-player:not([audio]) {
aspect-ratio: 16 / 9;
}
</style>
</head>
<body>
<header>
<div class="left-header">
<a class="mux-logo" href="https://www.mux.com/player" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/360826/233653989-11cd8603-c20f-4008-8bf7-dc15b743c52b.svg">
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/360826/233653583-50dda726-cbe7-4182-a113-059a91ae83e6.svg">
<img alt="Mux Logo" src="https://user-images.githubusercontent.com/360826/233653583-50dda726-cbe7-4182-a113-059a91ae83e6.svg">
</picture>
</a>
<h1><a href="/">Elements</a></h1>
</div>
<div class="right-header">
<a class="github-logo" href="https://github.com/muxinc/elements" target="_blank">
<img width="32" height="32" src="./images/github-logo.svg" alt="Github logo">
</a>
</div>
</header>

<mux-player
id="muxPlayer"
stream-type="on-demand"
src="https://playertest.longtailvideo.com/adaptive/elephants_dream_v4/index.m3u8"
></mux-player>

<br>

<select id="audioselect"></select>

<br>
<br>

<script type="module">

muxPlayer.audioTracks.addEventListener('removetrack', ({ track }) => {
audioselect.querySelector(`[value="${track.id}"]`).remove();
});

muxPlayer.audioTracks.addEventListener('addtrack', ({ track }) => {
audioselect.append(new Option(
track.label,
track.id,
track.enabled,
track.enabled
));
});

audioselect.addEventListener('change', () => {
for (let track of muxPlayer.audioTracks) {
track.enabled = audioselect.value == track.id;
}
});
</script>

<a href="../">Browse Elements</a>
</body>
</html>
12 changes: 6 additions & 6 deletions packages/playback-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ import type { HlsInterface } from './hls';
import { MediaError } from './errors';
import { setupAutoplay } from './autoplay';
import { setupPreload } from './preload';
import { setupRenditions } from './renditions';
import { setupMediaTracks } from './media-tracks';
import {
setupTracks,
setupTextTracks,
addTextTrack,
removeTextTrack,
addCuePoints,
getCuePoints,
getActiveCuePoint,
setupCuePoints,
getCuePointsTrack,
} from './tracks';
} from './text-tracks';
import { getStartDate, getCurrentPdt } from './pdt';
import {
inSeekableRange,
Expand All @@ -35,7 +35,6 @@ import {
type PlaybackCore,
type MuxMediaProps,
type MuxMediaPropsInternal,
type MediaTracks,
HlsPlaylistTypes,
MediaTypes,
} from './types';
Expand Down Expand Up @@ -534,6 +533,7 @@ export const loadMedia = (
| 'subtitleTracks'
| 'subtitleTrack'
| 'userConfig'
| 'audioTrack'
| 'autoLevelEnabled'
| 'nextLevel'
| 'levels'
Expand Down Expand Up @@ -636,8 +636,8 @@ export const loadMedia = (
});
mediaEl.addEventListener('error', handleInternalError);

setupRenditions(props as HTMLMediaElement, hls);
setupTracks(mediaEl, hls);
setupMediaTracks(props as HTMLMediaElement, hls);
setupTextTracks(mediaEl, hls);

hls.attachMedia(mediaEl);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import Hls from './hls';
import type { VideoRenditionList } from 'media-tracks';

export function setupRenditions(
export function setupMediaTracks(
customMediaEl: HTMLMediaElement,
hls: Pick<Hls, 'autoLevelEnabled' | 'nextLevel' | 'levels' | 'on' | 'once'>
hls: Pick<Hls, 'audioTrack' | 'autoLevelEnabled' | 'nextLevel' | 'levels' | 'on' | 'once'>
) {
if (!('videoTracks' in customMediaEl)) return;

Expand All @@ -13,7 +13,7 @@ export function setupRenditions(
const levelIdMap = new WeakMap();

hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
removeAllVideoTracks();
removeAllMediaTracks();

const videoTrack = customMediaEl.addVideoTrack('main');
videoTrack.selected = true;
Expand All @@ -31,6 +31,21 @@ export function setupRenditions(
levelIdMap.set(level, `${id}`);
videoRendition.id = `${id}`;
}

for (const [id, a] of data.audioTracks.entries()) {
// hls.js doesn't return a `kind` property for audio tracks yet.
const kind = a.default ? 'main' : 'alternative';
const audioTrack = customMediaEl.addAudioTrack(kind, a.name, a.lang);
audioTrack.id = `${id}`;

if (a.default) {
audioTrack.enabled = true;
}
}
});

customMediaEl.audioTracks.addEventListener('change', () => {
hls.audioTrack = [...customMediaEl.audioTracks].find((t) => t.enabled).id;
});

// Fired when a level is removed after calling `removeLevel()`
Expand Down Expand Up @@ -61,14 +76,15 @@ export function setupRenditions(

customMediaEl.videoRenditions.addEventListener('change', switchRendition);

const removeAllVideoTracks = () => {
const removeAllMediaTracks = () => {
for (const videoTrack of customMediaEl.videoTracks) {
customMediaEl.removeVideoTrack(videoTrack);
}
for (const audioTrack of customMediaEl.audioTracks) {
customMediaEl.removeAudioTrack(audioTrack);
}
};

// NOTE: Since this is only relevant for hls, using destroying event (CJP).
hls.once(Hls.Events.DESTROYING, () => {
removeAllVideoTracks();
});
hls.once(Hls.Events.DESTROYING, removeAllMediaTracks);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Hls from './hls';
import { CuePoint } from './types';
import { addEventListenerWithTeardown } from './util';

export function setupTracks(
export function setupTextTracks(
mediaEl: HTMLMediaElement,
hls: Pick<Hls, 'on' | 'once' | 'subtitleTracks' | 'subtitleTrack'>
) {
Expand Down
2 changes: 1 addition & 1 deletion packages/playback-core/test/tracks.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { assert, nextFrame, oneEvent, aTimeout, waitUntil } from '@open-wc/testing';
import { addCuePoints, getCuePoints, getActiveCuePoint } from '../src/tracks.ts';
import { addCuePoints, getCuePoints, getActiveCuePoint } from '../src/text-tracks.ts';

describe('textTracks', () => {
describe('cuePoints', () => {
Expand Down

5 comments on commit 13bfbdb

@vercel
Copy link

@vercel vercel bot commented on 13bfbdb Aug 16, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

elements-demo-svelte-kit – ./examples/svelte-kit

elements-demo-svelte-kit-git-main-mux.vercel.app
elements-demo-svelte-kit.vercel.app
elements-demo-svelte-kit-mux.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 13bfbdb Aug 16, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

elements-demo-vanilla – ./examples/vanilla-ts-esm

elements-demo-vanilla-mux.vercel.app
elements-demo-vanilla-git-main-mux.vercel.app
elements-demo-vanilla.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 13bfbdb Aug 16, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

elements-demo-vue – ./examples/vue-with-typescript

elements-demo-vue-mux.vercel.app
elements-demo-vue.vercel.app
elements-demo-vue-git-main-mux.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 13bfbdb Aug 16, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

elements-demo-create-react-app – ./examples/create-react-app-with-typescript

elements-demo-create-react-app-git-main-mux.vercel.app
elements-demo-create-react-app-mux.vercel.app
elements-demo-create-react-app.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 13bfbdb Aug 16, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

elements-demo-nextjs – ./examples/nextjs-with-typescript

elements-demo-nextjs-mux.vercel.app
elements-demo-nextjs-git-main-mux.vercel.app
elements-demo-nextjs.vercel.app

Please sign in to comment.