Skip to content

Commit

Permalink
Merge branch 'main' into refactor/decoupling
Browse files Browse the repository at this point in the history
  • Loading branch information
JoaquinBCh committed Dec 20, 2024
2 parents 7665464 + fddb003 commit cc1fbf6
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 42 deletions.
11 changes: 9 additions & 2 deletions lib/playback/audio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import registerMyAudioWorklet from "audio-worklet:./worklet/index.ts"
export class Audio {
context: AudioContext
worklet: Promise<AudioWorkletNode>
volumeNode: GainNode

constructor(config: Message.ConfigAudio) {
this.context = new AudioContext({
latencyHint: "interactive",
sampleRate: config.sampleRate,
})
this.volumeNode = this.context.createGain()
this.volumeNode.gain.value = 1.0

this.worklet = this.load(config)
}
Expand All @@ -31,8 +34,8 @@ export class Audio {
}

// Connect the worklet to the volume node and then to the speakers
worklet.connect(volume)
volume.connect(this.context.destination)
worklet.connect(this.volumeNode)
this.volumeNode.connect(this.context.destination)

worklet.port.postMessage({ config })

Expand All @@ -42,4 +45,8 @@ export class Audio {
private on(_event: MessageEvent) {
// TODO
}

public setVolume(newVolume: number) {
this.volumeNode.gain.setTargetAtTime(newVolume, this.context.currentTime, 0.01)
}
}
10 changes: 9 additions & 1 deletion lib/playback/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ export default class Backend {
}

pause() {
this.send({ pause: true })
this.send({ play: false })
}

play() {
this.send({ play: true })
}

async mute() {
Expand All @@ -86,6 +90,10 @@ export default class Backend {
this.send({ segment }, segment.stream)
}

setVolume(newVolume: number) {
this.#audio?.setVolume(newVolume)
}

async close() {
this.#worker.terminate()
await this.#audio?.context.close()
Expand Down
19 changes: 15 additions & 4 deletions lib/playback/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,12 +303,23 @@ export default class Player {
this.subscribeFromTrackName(this.#audioTrackName)
await this.#backend.unmute()
}
this.#backend.play()
} else {
await this.unsubscribeFromTrack(this.#videoTrackName)
await this.unsubscribeFromTrack(this.#audioTrackName)
await this.#backend.mute()
this.#backend.pause()
this.#paused = true
this.#backend.pause()
const mutePromise = this.#backend.mute()
const audioPromise = this.unsubscribeFromTrack(this.#audioTrackName)
const videoPromise = this.unsubscribeFromTrack(this.#videoTrackName)
await Promise.all([mutePromise, audioPromise, videoPromise])
}
}

async setVolume(newVolume: number) {
this.#backend.setVolume(newVolume)
if (newVolume == 0 && !this.#muted) {
await this.mute(true)
} else if (newVolume > 0 && this.#muted) {
await this.mute(false)
}
}

Expand Down
17 changes: 13 additions & 4 deletions lib/playback/worker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Worker {

on(e: MessageEvent) {
const msg = e.data as Message.ToWorker
// console.log("message: ", msg)

if (msg.config) {
this.#onConfig(msg.config)
Expand All @@ -30,8 +31,10 @@ class Worker {
this.#onInit(msg.init)
} else if (msg.segment) {
this.#onSegment(msg.segment).catch(console.warn)
} else if (msg.pause) {
this.#onPause(msg.pause)
} else if (msg.play === false) {
this.#onPause(msg.play)
} else if (msg.play === true) {
this.#onPlay(msg.play)
} else {
throw new Error(`unknown message: + ${JSON.stringify(msg)}`)
}
Expand Down Expand Up @@ -103,11 +106,17 @@ class Worker {
await segment.close()
}

#onPause(pause: boolean) {
if (this.#video && pause) {
#onPause(play: boolean) {
if (this.#video && !play) {
this.#video.pause()
}
}

#onPlay(play: boolean) {
if (this.#video && play) {
this.#video.play()
}
}
}

// Pass all events to the worker
Expand Down
2 changes: 1 addition & 1 deletion lib/playback/worker/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export interface ToWorker {
// Sent on each init/data stream
init?: Init
segment?: Segment
pause?: boolean
play?: boolean

/*
// Sent to control playback
Expand Down
16 changes: 13 additions & 3 deletions lib/playback/worker/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ export class Renderer {

#decoderConfig?: DecoderConfig
#waitingForKeyframe: boolean = true
#paused: boolean

constructor(config: Message.ConfigVideo, timeline: Component) {
this.#canvas = config.canvas
this.#timeline = timeline
this.#paused = false

this.#queue = new TransformStream({
start: this.#start.bind(this),
Expand All @@ -41,14 +43,22 @@ export class Renderer {
}

pause() {
console.log("pause")
this.#paused = true
this.#decoder.flush().catch((err) => {
console.error(err)
})
this.#waitingForKeyframe = true
}

play() {
this.#paused = false
}

async #run() {
const reader = this.#timeline.frames.pipeThrough(this.#queue).getReader()
for (;;) {
const { value: frame, done } = await reader.read()
if (this.#paused) continue
if (done) break

self.requestAnimationFrame(() => {
Expand All @@ -74,8 +84,8 @@ export class Renderer {
}

#transform(frame: Frame) {
if (this.#decoder.state === "closed") {
console.warn("Decoder is closed. Skipping frame.")
if (this.#decoder.state === "closed" || this.#paused) {
console.warn("Decoder is closed or paused. Skipping frame.")
return
}

Expand Down
65 changes: 51 additions & 14 deletions web/src/components/volume.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,66 @@
import { createSignal } from "solid-js"

type VolumeButtonProps = {
type VolumeControlProps = {
mute: (isMuted: boolean) => void
setVolume: (newVolume: number) => void
}

export const VolumeButton = (props: VolumeButtonProps) => {
export const VolumeControl = (props: VolumeControlProps) => {
const [isMuted, setIsMuted] = createSignal(false)
const [currentVolume, setCurrentVolume] = createSignal(1)
const [previousVolume, setPreviousVolume] = createSignal(1)

const toggleMute = () => {
const newIsMuted = !isMuted()
setIsMuted(newIsMuted)
props?.mute(newIsMuted)
props.mute(newIsMuted)

if (newIsMuted) {
setPreviousVolume(currentVolume())
props.setVolume(0)
setCurrentVolume(0)
} else {
const restoredVolume = previousVolume()
setCurrentVolume(restoredVolume)
props.setVolume(restoredVolume)
}
}

const handleVolumeChange = (e: InputEvent & { currentTarget: HTMLInputElement }) => {
const volume = parseFloat(e.currentTarget.value)
if (volume == 0) {
setIsMuted(true)
} else {
setIsMuted(false)
}
setCurrentVolume(volume)
props.setVolume(volume)
}

return (
<button
class="
flex h-4 w-0 items-center justify-center rounded bg-transparent
p-4 text-white hover:bg-black/80
focus:bg-black/80 focus:outline-none
"
onClick={toggleMute}
aria-label={isMuted() ? "Unmute" : "Mute"}
>
{isMuted() ? "🔇" : "🔊"}
</button>
<div class="flex items-center gap-2">
<button
class="
flex h-4 w-0 items-center justify-center rounded bg-transparent
p-4 text-white hover:bg-black/80
focus:bg-black/80 focus:outline-none
"
onClick={toggleMute}
aria-label={isMuted() ? "Unmute" : "Mute"}
>
{isMuted() ? "🔇" : "🔊"}
</button>

<input
type="range"
min="0"
max="1"
step="0.1"
style={{ padding: "0" }}
value={currentVolume()}
onInput={handleVolumeChange}
class="h-1 w-24 cursor-pointer"
/>
</div>
)
}
29 changes: 16 additions & 13 deletions web/src/components/watch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import Player from "moq-player"

import Fail from "./fail"
import { createEffect, createMemo, createSignal, onCleanup, Show } from "solid-js"
import { VolumeButton } from "./volume"
import { VolumeControl } from "./volume"
import { PlayButton } from "./play-button"
import { TrackSelect } from "./track-select"
import { promise } from "astro/zod"

export default function Watch(props: { name: string }) {
// Use query params to allow overriding environment variables.
Expand Down Expand Up @@ -37,6 +38,10 @@ export default function Watch(props: { name: string }) {
player()?.mute(state).catch(setError)
}

const setVolume = (newVolume: number) => {
player()?.setVolume(newVolume).catch(setError)
}

const switchTrack = (track: string) => {
void player()?.switchTrack(track)
}
Expand All @@ -48,17 +53,15 @@ export default function Watch(props: { name: string }) {
const handlePlayPause = () => {
const playerInstance = player()
if (!playerInstance) return

if (playerInstance.isPaused()) {
playerInstance
.play()
.then(() => setIsPlaying(true))
.catch(setError)
} else {
playerInstance
.play()
.then(() => setIsPlaying(false))
.catch(setError)
try {
void playerInstance.play()
if (playerInstance.isPaused()) {
setIsPlaying(false)
} else {
setIsPlaying(true)
}
} catch (err) {
setError(err instanceof Error ? err : new Error(String(err)))
}
}

Expand Down Expand Up @@ -109,7 +112,7 @@ export default function Watch(props: { name: string }) {
>
<PlayButton onClick={handlePlayPause} isPlaying={isPlaying()} />
<div class="absolute bottom-0 right-4 flex h-[32px] w-fit items-center justify-evenly gap-[4px] rounded bg-black/70 p-2">
<VolumeButton mute={mute} />
<VolumeControl mute={mute} setVolume={setVolume} />
<TrackSelect trackNum={tracknum} getVideoTracks={getVideoTracks} switchTrack={switchTrack} />
</div>
</div>
Expand Down

0 comments on commit cc1fbf6

Please sign in to comment.