From 48170b487c512ed5fb0786ea992eeb545cd369ac Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Fri, 27 Nov 2020 12:17:32 +0100 Subject: [PATCH] Provide feedback for audio/sub selection --- src/routes/player/Player.tsx | 25 ++++++++--- src/routes/player/subtitles/TtmlRenderer.tsx | 16 ++++--- src/routes/player/video/DashVideoElement.tsx | 25 +++++++---- src/routes/player/video/RawVideoElement.tsx | 7 ++- src/routes/player/video/VideoApi.ts | 4 +- src/util/media/useAudioTracks.ts | 45 ++++++++++++-------- 6 files changed, 81 insertions(+), 41 deletions(-) diff --git a/src/routes/player/Player.tsx b/src/routes/player/Player.tsx index c812b3e..2e9fd7d 100644 --- a/src/routes/player/Player.tsx +++ b/src/routes/player/Player.tsx @@ -40,7 +40,7 @@ export function Player( const position = usePosition(videoElement); const duration = useDuration(videoElement); - const audioTracks = useAudioTracks(videoElement); + const [audioTracks, currentTrack, setCurrentTrack] = useAudioTracks(videoElement); useDebugInfo("playerEl", videoElement); useDebugInfo("previewTrackEl", previewTrack); @@ -87,6 +87,7 @@ export function Player( <TtmlRenderer className={classes.subtitleCanvas} trackElement={subtitleTrack} + subtitle={subtitle} duration={duration} /> </div> @@ -101,18 +102,28 @@ export function Player( <strong>{track.lang}</strong> {track.labels} - <button onClick={() => videoElement?.setAudioTrack(track)}>Choose</button> + <button + disabled={currentTrack === track} + onClick={() => setCurrentTrack(track)} + > + Choose + </button> </li> ))} </ul> <h3>Subtitles</h3> <ul> - {content.subtitles.map(track => ( - <li key={track.src}> - <strong>{track.language}</strong> - {track.specifier} + {[null, ...content.subtitles].map(track => ( + <li key={track?.src || "none"}> + <strong>{track?.language || "none"}</strong> + {track?.specifier} - <button onClick={() => setSubtitle(track)}>Choose</button> + <button + disabled={subtitle === track} + onClick={() => setSubtitle(track)} + > + Choose + </button> </li> ))} </ul> diff --git a/src/routes/player/subtitles/TtmlRenderer.tsx b/src/routes/player/subtitles/TtmlRenderer.tsx index bd599e6..06c2112 100644 --- a/src/routes/player/subtitles/TtmlRenderer.tsx +++ b/src/routes/player/subtitles/TtmlRenderer.tsx @@ -1,24 +1,30 @@ -import { useEffect, useState } from "react"; +import {useEffect, useState} from "react"; import TtmlHelper from "./TtmlHelper"; +import {Subtitle} from "../../../api/models/Subtitle"; interface Props { trackElement: HTMLTrackElement | null, + subtitle: Subtitle | null, duration: number, className?: string, } export function TtmlRenderer( - {trackElement, duration, className}: Props + {trackElement, subtitle, duration, className}: Props ) { const [subtitleCanvas, setSubtitleCanvas] = useState<HTMLElement | null>(null); useEffect(() => { - if (subtitleCanvas && trackElement) { + if (subtitleCanvas && trackElement && subtitle) { return TtmlHelper.bindToTrack(trackElement, subtitleCanvas, duration) } - }, [subtitleCanvas, trackElement, duration]); + }, [subtitleCanvas, subtitle, trackElement, duration]); return ( - <div ref={setSubtitleCanvas} className={className}/> + <div + ref={setSubtitleCanvas} + lang={subtitle?.language || undefined} + className={className} + /> ) } diff --git a/src/routes/player/video/DashVideoElement.tsx b/src/routes/player/video/DashVideoElement.tsx index c6698ea..bca35e0 100644 --- a/src/routes/player/video/DashVideoElement.tsx +++ b/src/routes/player/video/DashVideoElement.tsx @@ -73,7 +73,22 @@ export const DashVideoElement = forwardRef<VideoApi, PropsWithChildren<Props>>(f } return !Number.isNaN(player.duration()); }, - setAudioTrack(track: MediaInfo) { + getAudioTracks(): MediaInfo[] { + if (!player.isReady()) { + return []; + } + return player.getTracksFor("audio").map((info: MediaInfo) => { + return info; + }) + }, + getCurrentAudioTrack(): MediaInfo | null { + if (!player.isReady()) { + return null; + } + + return player.getCurrentTrackFor("audio"); + }, + setCurrentAudioTrack(track: MediaInfo) { if (!player.isReady()) { return; } @@ -86,14 +101,6 @@ export const DashVideoElement = forwardRef<VideoApi, PropsWithChildren<Props>>(f removeEventListener(event: string, listener: () => void) { player.off(event, listener); }, - getAudioTracks(): MediaInfo[] { - if (!player.isReady()) { - return []; - } - return player.getTracksFor("audio").map((info: MediaInfo) => { - return info; - }) - }, debug(): any { return player; } diff --git a/src/routes/player/video/RawVideoElement.tsx b/src/routes/player/video/RawVideoElement.tsx index b957682..0d1c630 100644 --- a/src/routes/player/video/RawVideoElement.tsx +++ b/src/routes/player/video/RawVideoElement.tsx @@ -1,4 +1,4 @@ -import { forwardRef, PropsWithChildren, useImperativeHandle, useState } from "react"; +import {forwardRef, PropsWithChildren, useImperativeHandle, useState} from "react"; import {Media} from "../../../api/models/Media"; import {VideoApi} from "./VideoApi"; import {MediaInfo} from "dashjs"; @@ -84,7 +84,10 @@ export const RawVideoElement = forwardRef<VideoApi, PropsWithChildren<Props>>(fu getAudioTracks(): MediaInfo[] { return []; }, - setAudioTrack(track: MediaInfo) { + setCurrentAudioTrack(track: MediaInfo) { + }, + getCurrentAudioTrack(): MediaInfo | null { + return null; }, addEventListener(event: string, listener: () => void) { if (!videoElement) { diff --git a/src/routes/player/video/VideoApi.ts b/src/routes/player/video/VideoApi.ts index fd53529..24c7fb1 100644 --- a/src/routes/player/video/VideoApi.ts +++ b/src/routes/player/video/VideoApi.ts @@ -24,7 +24,9 @@ export interface VideoApi { getAudioTracks(): MediaInfo[] - setAudioTrack(track: MediaInfo): void + getCurrentAudioTrack(): MediaInfo | null + + setCurrentAudioTrack(track: MediaInfo): void addEventListener(event: string, listener: () => void): void diff --git a/src/util/media/useAudioTracks.ts b/src/util/media/useAudioTracks.ts index 5076217..d22fb0e 100644 --- a/src/util/media/useAudioTracks.ts +++ b/src/util/media/useAudioTracks.ts @@ -1,25 +1,36 @@ -import {useEffect, useState} from "react"; +import {useCallback, useEffect, useState} from "react"; import {VideoApi} from "../../routes/player/video/VideoApi"; import {MediaInfo} from "dashjs"; -export const useAudioTracks = (video: VideoApi | null) => { +export function useAudioTracks(video: VideoApi | null): + [MediaInfo[], MediaInfo | null, (track: MediaInfo) => void] { const [audioTracks, setAudioTracks] = useState<MediaInfo[]>([]); - useEffect(() => { - if (video !== null) { - if (video.canPlay()) { + const [currentTrack, setCurrentTrack] = useState<MediaInfo | null>(null); + + const listener = useCallback(() => { + window.requestAnimationFrame(() => { + if (video !== null) { setAudioTracks(video.getAudioTracks()); - } else { - const listener = () => { - window.requestAnimationFrame(() => { - setAudioTracks(video.getAudioTracks()); - }) - }; - video.addEventListener(video.METADATA_EVENT, listener) - return () => { - video.removeEventListener(video.METADATA_EVENT, listener) - } + setCurrentTrack(video.getCurrentAudioTrack); } - } + }) }, [video]); - return audioTracks; + + const updateAudioTracks = useCallback((track: MediaInfo) => { + video?.setCurrentAudioTrack(track); + listener(); + }, [video, listener]); + + useEffect(() => { + if (video?.canPlay()) { + setAudioTracks(video.getAudioTracks()); + setCurrentTrack(video.getCurrentAudioTrack); + } else { + video?.addEventListener(video.METADATA_EVENT, listener) + return () => { + video?.removeEventListener(video.METADATA_EVENT, listener) + } + } + }, [listener, video]); + return [audioTracks, currentTrack, updateAudioTracks]; } -- GitLab