Skip to content

Commit

Permalink
allow selecting track when only one
Browse files Browse the repository at this point in the history
this allows the user to enable ffmpeg assisted playback when audio track is not supported
closes #2144
  • Loading branch information
mifi committed Sep 29, 2024
1 parent cc202b4 commit be5dadc
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 25 deletions.
36 changes: 20 additions & 16 deletions src/renderer/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { memo, useEffect, useState, useCallback, useRef, useMemo, CSSProperties, ReactEventHandler, FocusEventHandler } from 'react';
import { FaAngleLeft, FaWindowClose } from 'react-icons/fa';
import { FaAngleLeft, FaRegTimesCircle } from 'react-icons/fa';
import { MdRotate90DegreesCcw } from 'react-icons/md';
import { AnimatePresence } from 'framer-motion';
import { ThemeProvider } from 'evergreen-ui';
Expand Down Expand Up @@ -60,7 +60,7 @@ import {
RefuseOverwriteError, extractSubtitleTrackToSegments,
mapRecommendedDefaultFormat,
} from './ffmpeg';
import { shouldCopyStreamByDefault, getAudioStreams, getRealVideoStreams, isAudioDefinitelyNotSupported, willPlayerProperlyHandleVideo, doesPlayerSupportHevcPlayback, getSubtitleStreams, getVideoTrackForStreamIndex, getAudioTrackForStreamIndex, enableVideoTrack, enableAudioTrack } from './util/streams';
import { shouldCopyStreamByDefault, getAudioStreams, getRealVideoStreams, isAudioDefinitelyNotSupported, willPlayerProperlyHandleVideo, doesPlayerSupportHevcPlayback, getSubtitleStreams, enableVideoTrack, enableAudioTrack, canHtml5PlayerPlayStreams } from './util/streams';
import { exportEdlFile, readEdlFile, loadLlcProject, askForEdlImport } from './edlStore';
import { formatYouTube, getFrameCountRaw, formatTsv } from './edlFormats';
import {
Expand Down Expand Up @@ -299,13 +299,17 @@ function App() {

const zoomRel = useCallback((rel: number) => setZoom((z) => Math.min(Math.max(z + (rel * (1 + (z / 10))), 1), zoomMax)), []);
const compatPlayerRequired = usingDummyVideo;
const compatPlayerWanted = (isRotationSet || activeVideoStreamIndex != null || activeAudioStreamIndex != null) && !hideMediaSourcePlayer;
const compatPlayerWanted = (
isRotationSet
// if user selected a custom video/audio stream, use compat player if the html5 player does not have any track index corresponding to the selected stream indexes
|| ((activeVideoStreamIndex != null || activeAudioStreamIndex != null) && videoRef.current != null && !canHtml5PlayerPlayStreams(videoRef.current, activeVideoStreamIndex, activeAudioStreamIndex))
) && !hideMediaSourcePlayer;
const compatPlayerEnabled = (compatPlayerRequired || compatPlayerWanted) && (activeVideoStream != null || activeAudioStream != null);

const shouldShowPlaybackStreamSelector = videoStreams.length > 1 || audioStreams.length > 1 || (subtitleStreams.length > 0 && !compatPlayerEnabled);
const shouldShowPlaybackStreamSelector = videoStreams.length > 0 || audioStreams.length > 0 || (subtitleStreams.length > 0 && !compatPlayerEnabled);

useEffect(() => {
// Reset the user preference when the state changes to true
// Reset the user preference when we go from not having compat player to having it
if (compatPlayerEnabled) setHideMediaSourcePlayer(false);
}, [compatPlayerEnabled]);

Expand Down Expand Up @@ -505,17 +509,17 @@ function App() {
}
}, [subtitlesByStreamId, subtitleStreams, workingRef, setWorking, filePath, loadSubtitle]);

const onActiveVideoStreamChange = useCallback((index?: number) => {
const onActiveVideoStreamChange = useCallback((videoStreamIndex?: number) => {
invariant(videoRef.current);
setHideMediaSourcePlayer(index == null || getVideoTrackForStreamIndex(videoRef.current, index) != null);
enableVideoTrack(videoRef.current, index);
setActiveVideoStreamIndex(index);
setHideMediaSourcePlayer(false);
enableVideoTrack(videoRef.current, videoStreamIndex);
setActiveVideoStreamIndex(videoStreamIndex);
}, [videoRef]);
const onActiveAudioStreamChange = useCallback((index?: number) => {
const onActiveAudioStreamChange = useCallback((audioStreamIndex?: number) => {
invariant(videoRef.current);
setHideMediaSourcePlayer(index == null || getAudioTrackForStreamIndex(videoRef.current, index) != null);
enableAudioTrack(videoRef.current, index);
setActiveAudioStreamIndex(index);
setHideMediaSourcePlayer(false);
enableAudioTrack(videoRef.current, audioStreamIndex);
setActiveAudioStreamIndex(audioStreamIndex);
}, [videoRef]);

const allFilesMeta = useMemo(() => ({
Expand Down Expand Up @@ -2421,7 +2425,9 @@ function App() {
</>
)}

{!compatPlayerRequired && <FaWindowClose role="button" style={{ cursor: 'pointer', pointerEvents: 'initial', verticalAlign: 'middle', padding: 10 }} onClick={() => setHideMediaSourcePlayer(true)} />}
<div style={{ cursor: 'pointer', pointerEvents: 'initial', color: 'white', opacity: 0.7, padding: '.2em', marginLeft: '.5em' }} role="button" onClick={() => incrementMediaSourceQuality()} title={t('Select playback quality')}>{mediaSourceQualities[mediaSourceQuality]}</div>

{!compatPlayerRequired && <FaRegTimesCircle role="button" style={{ cursor: 'pointer', pointerEvents: 'initial', verticalAlign: 'middle', padding: '.2em' }} onClick={() => setHideMediaSourcePlayer(true)} />}
</div>
)}

Expand All @@ -2433,8 +2439,6 @@ function App() {
<PlaybackStreamSelector subtitleStreams={subtitleStreams} videoStreams={videoStreams} audioStreams={audioStreams} activeSubtitleStreamIndex={activeSubtitleStreamIndex} activeVideoStreamIndex={activeVideoStreamIndex} activeAudioStreamIndex={activeAudioStreamIndex} onActiveSubtitleChange={onActiveSubtitleChange} onActiveVideoStreamChange={onActiveVideoStreamChange} onActiveAudioStreamChange={onActiveAudioStreamChange} />
)}

{compatPlayerEnabled && <div style={{ color: 'white', opacity: 0.7, padding: '.5em' }} role="button" onClick={() => incrementMediaSourceQuality()} title={t('Select playback quality')}>{mediaSourceQualities[mediaSourceQuality]}</div>}

{!showRightBar && (
<FaAngleLeft
title={t('Show sidebar')}
Expand Down
14 changes: 7 additions & 7 deletions src/renderer/src/components/PlaybackStreamSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { memo, useState, useCallback, useRef, useEffect } from 'react';
import { memo, useState, useCallback, useRef, useEffect, ChangeEventHandler, ChangeEvent } from 'react';
import { MdSubtitles } from 'react-icons/md';
import { useTranslation } from 'react-i18next';
import Select from './Select';
Expand Down Expand Up @@ -36,16 +36,16 @@ function PlaybackStreamSelector({
timeoutRef.current = window.setTimeout(() => setControlVisible(false), 7000);
}, []);

const onChange = useCallback((e, fn) => {
const onChange = useCallback((e: ChangeEvent<HTMLSelectElement>, fn: (a: number | undefined) => void) => {
resetTimer();
const index = e.target.value ? parseInt(e.target.value, 10) : undefined;
fn(index);
e.target.blur();
}, [resetTimer]);

const onActiveSubtitleChange2 = useCallback((e) => onChange(e, onActiveSubtitleChange), [onActiveSubtitleChange, onChange]);
const onActiveVideoStreamChange2 = useCallback((e) => onChange(e, onActiveVideoStreamChange), [onActiveVideoStreamChange, onChange]);
const onActiveAudioStreamChange2 = useCallback((e) => onChange(e, onActiveAudioStreamChange), [onActiveAudioStreamChange, onChange]);
const onActiveSubtitleChange2 = useCallback<ChangeEventHandler<HTMLSelectElement>>((e) => onChange(e, onActiveSubtitleChange), [onActiveSubtitleChange, onChange]);
const onActiveVideoStreamChange2 = useCallback<ChangeEventHandler<HTMLSelectElement>>((e) => onChange(e, onActiveVideoStreamChange), [onActiveVideoStreamChange, onChange]);
const onActiveAudioStreamChange2 = useCallback<ChangeEventHandler<HTMLSelectElement>>((e) => onChange(e, onActiveAudioStreamChange), [onActiveAudioStreamChange, onChange]);

const onIconClick = useCallback(() => {
resetTimer();
Expand All @@ -71,7 +71,7 @@ function PlaybackStreamSelector({
</Select>
)}

{videoStreams.length > 1 && (
{videoStreams.length > 0 && (
<Select
value={activeVideoStreamIndex ?? ''}
onChange={onActiveVideoStreamChange2}
Expand All @@ -84,7 +84,7 @@ function PlaybackStreamSelector({
</Select>
)}

{audioStreams.length > 1 && (
{audioStreams.length > 0 && (
<Select
value={activeAudioStreamIndex ?? ''}
onChange={onActiveAudioStreamChange2}
Expand Down
11 changes: 9 additions & 2 deletions src/renderer/src/util/streams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,15 @@ const getHtml5TrackId = (ffmpegTrackIndex: number) => String(ffmpegTrackIndex +
const getHtml5VideoTracks = (video: ChromiumHTMLVideoElement) => [...(video.videoTracks ?? [])];
const getHtml5AudioTracks = (audio: ChromiumHTMLAudioElement) => [...(audio.audioTracks ?? [])];

export const getVideoTrackForStreamIndex = (video: ChromiumHTMLVideoElement, index) => getHtml5VideoTracks(video).find((videoTrack) => videoTrack.id === getHtml5TrackId(index));
export const getAudioTrackForStreamIndex = (audio: ChromiumHTMLAudioElement, index) => getHtml5AudioTracks(audio).find((audioTrack) => audioTrack.id === getHtml5TrackId(index));
const getVideoTrackForStreamIndex = (video: ChromiumHTMLVideoElement, index: number) => getHtml5VideoTracks(video).find((videoTrack) => videoTrack.id === getHtml5TrackId(index));
const getAudioTrackForStreamIndex = (audio: ChromiumHTMLAudioElement, index: number) => getHtml5AudioTracks(audio).find((audioTrack) => audioTrack.id === getHtml5TrackId(index));

// although not technically correct, if video and audio index is null, assume that we can play
// the user can select an audio/video track if they want ffmpeg assisted playback
export const canHtml5PlayerPlayStreams = (videoEl: ChromiumHTMLVideoElement, videoIndex: number | undefined, audioIndex: number | undefined) => (
(videoIndex == null || getVideoTrackForStreamIndex(videoEl, videoIndex) != null)
&& (audioIndex == null || getAudioTrackForStreamIndex(videoEl, audioIndex) != null)
);

function resetVideoTrack(video: ChromiumHTMLVideoElement) {
console.log('Resetting video track');
Expand Down

0 comments on commit be5dadc

Please sign in to comment.