mirror of
https://github.com/mifi/lossless-cut.git
synced 2024-11-21 18:02:35 +01:00
allow selecting track when only one
this allows the user to enable ffmpeg assisted playback when audio track is not supported closes #2144
This commit is contained in:
parent
cc202b4845
commit
be5dadcc84
@ -1,5 +1,5 @@
|
|||||||
import { memo, useEffect, useState, useCallback, useRef, useMemo, CSSProperties, ReactEventHandler, FocusEventHandler } from 'react';
|
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 { MdRotate90DegreesCcw } from 'react-icons/md';
|
||||||
import { AnimatePresence } from 'framer-motion';
|
import { AnimatePresence } from 'framer-motion';
|
||||||
import { ThemeProvider } from 'evergreen-ui';
|
import { ThemeProvider } from 'evergreen-ui';
|
||||||
@ -60,7 +60,7 @@ import {
|
|||||||
RefuseOverwriteError, extractSubtitleTrackToSegments,
|
RefuseOverwriteError, extractSubtitleTrackToSegments,
|
||||||
mapRecommendedDefaultFormat,
|
mapRecommendedDefaultFormat,
|
||||||
} from './ffmpeg';
|
} 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 { exportEdlFile, readEdlFile, loadLlcProject, askForEdlImport } from './edlStore';
|
||||||
import { formatYouTube, getFrameCountRaw, formatTsv } from './edlFormats';
|
import { formatYouTube, getFrameCountRaw, formatTsv } from './edlFormats';
|
||||||
import {
|
import {
|
||||||
@ -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 zoomRel = useCallback((rel: number) => setZoom((z) => Math.min(Math.max(z + (rel * (1 + (z / 10))), 1), zoomMax)), []);
|
||||||
const compatPlayerRequired = usingDummyVideo;
|
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 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(() => {
|
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);
|
if (compatPlayerEnabled) setHideMediaSourcePlayer(false);
|
||||||
}, [compatPlayerEnabled]);
|
}, [compatPlayerEnabled]);
|
||||||
|
|
||||||
@ -505,17 +509,17 @@ function App() {
|
|||||||
}
|
}
|
||||||
}, [subtitlesByStreamId, subtitleStreams, workingRef, setWorking, filePath, loadSubtitle]);
|
}, [subtitlesByStreamId, subtitleStreams, workingRef, setWorking, filePath, loadSubtitle]);
|
||||||
|
|
||||||
const onActiveVideoStreamChange = useCallback((index?: number) => {
|
const onActiveVideoStreamChange = useCallback((videoStreamIndex?: number) => {
|
||||||
invariant(videoRef.current);
|
invariant(videoRef.current);
|
||||||
setHideMediaSourcePlayer(index == null || getVideoTrackForStreamIndex(videoRef.current, index) != null);
|
setHideMediaSourcePlayer(false);
|
||||||
enableVideoTrack(videoRef.current, index);
|
enableVideoTrack(videoRef.current, videoStreamIndex);
|
||||||
setActiveVideoStreamIndex(index);
|
setActiveVideoStreamIndex(videoStreamIndex);
|
||||||
}, [videoRef]);
|
}, [videoRef]);
|
||||||
const onActiveAudioStreamChange = useCallback((index?: number) => {
|
const onActiveAudioStreamChange = useCallback((audioStreamIndex?: number) => {
|
||||||
invariant(videoRef.current);
|
invariant(videoRef.current);
|
||||||
setHideMediaSourcePlayer(index == null || getAudioTrackForStreamIndex(videoRef.current, index) != null);
|
setHideMediaSourcePlayer(false);
|
||||||
enableAudioTrack(videoRef.current, index);
|
enableAudioTrack(videoRef.current, audioStreamIndex);
|
||||||
setActiveAudioStreamIndex(index);
|
setActiveAudioStreamIndex(audioStreamIndex);
|
||||||
}, [videoRef]);
|
}, [videoRef]);
|
||||||
|
|
||||||
const allFilesMeta = useMemo(() => ({
|
const allFilesMeta = useMemo(() => ({
|
||||||
@ -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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -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} />
|
<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 && (
|
{!showRightBar && (
|
||||||
<FaAngleLeft
|
<FaAngleLeft
|
||||||
title={t('Show sidebar')}
|
title={t('Show sidebar')}
|
||||||
|
@ -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 { MdSubtitles } from 'react-icons/md';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import Select from './Select';
|
import Select from './Select';
|
||||||
@ -36,16 +36,16 @@ function PlaybackStreamSelector({
|
|||||||
timeoutRef.current = window.setTimeout(() => setControlVisible(false), 7000);
|
timeoutRef.current = window.setTimeout(() => setControlVisible(false), 7000);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onChange = useCallback((e, fn) => {
|
const onChange = useCallback((e: ChangeEvent<HTMLSelectElement>, fn: (a: number | undefined) => void) => {
|
||||||
resetTimer();
|
resetTimer();
|
||||||
const index = e.target.value ? parseInt(e.target.value, 10) : undefined;
|
const index = e.target.value ? parseInt(e.target.value, 10) : undefined;
|
||||||
fn(index);
|
fn(index);
|
||||||
e.target.blur();
|
e.target.blur();
|
||||||
}, [resetTimer]);
|
}, [resetTimer]);
|
||||||
|
|
||||||
const onActiveSubtitleChange2 = useCallback((e) => onChange(e, onActiveSubtitleChange), [onActiveSubtitleChange, onChange]);
|
const onActiveSubtitleChange2 = useCallback<ChangeEventHandler<HTMLSelectElement>>((e) => onChange(e, onActiveSubtitleChange), [onActiveSubtitleChange, onChange]);
|
||||||
const onActiveVideoStreamChange2 = useCallback((e) => onChange(e, onActiveVideoStreamChange), [onActiveVideoStreamChange, onChange]);
|
const onActiveVideoStreamChange2 = useCallback<ChangeEventHandler<HTMLSelectElement>>((e) => onChange(e, onActiveVideoStreamChange), [onActiveVideoStreamChange, onChange]);
|
||||||
const onActiveAudioStreamChange2 = useCallback((e) => onChange(e, onActiveAudioStreamChange), [onActiveAudioStreamChange, onChange]);
|
const onActiveAudioStreamChange2 = useCallback<ChangeEventHandler<HTMLSelectElement>>((e) => onChange(e, onActiveAudioStreamChange), [onActiveAudioStreamChange, onChange]);
|
||||||
|
|
||||||
const onIconClick = useCallback(() => {
|
const onIconClick = useCallback(() => {
|
||||||
resetTimer();
|
resetTimer();
|
||||||
@ -71,7 +71,7 @@ function PlaybackStreamSelector({
|
|||||||
</Select>
|
</Select>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{videoStreams.length > 1 && (
|
{videoStreams.length > 0 && (
|
||||||
<Select
|
<Select
|
||||||
value={activeVideoStreamIndex ?? ''}
|
value={activeVideoStreamIndex ?? ''}
|
||||||
onChange={onActiveVideoStreamChange2}
|
onChange={onActiveVideoStreamChange2}
|
||||||
@ -84,7 +84,7 @@ function PlaybackStreamSelector({
|
|||||||
</Select>
|
</Select>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{audioStreams.length > 1 && (
|
{audioStreams.length > 0 && (
|
||||||
<Select
|
<Select
|
||||||
value={activeAudioStreamIndex ?? ''}
|
value={activeAudioStreamIndex ?? ''}
|
||||||
onChange={onActiveAudioStreamChange2}
|
onChange={onActiveAudioStreamChange2}
|
||||||
|
@ -263,8 +263,15 @@ const getHtml5TrackId = (ffmpegTrackIndex: number) => String(ffmpegTrackIndex +
|
|||||||
const getHtml5VideoTracks = (video: ChromiumHTMLVideoElement) => [...(video.videoTracks ?? [])];
|
const getHtml5VideoTracks = (video: ChromiumHTMLVideoElement) => [...(video.videoTracks ?? [])];
|
||||||
const getHtml5AudioTracks = (audio: ChromiumHTMLAudioElement) => [...(audio.audioTracks ?? [])];
|
const getHtml5AudioTracks = (audio: ChromiumHTMLAudioElement) => [...(audio.audioTracks ?? [])];
|
||||||
|
|
||||||
export const getVideoTrackForStreamIndex = (video: ChromiumHTMLVideoElement, index) => getHtml5VideoTracks(video).find((videoTrack) => videoTrack.id === getHtml5TrackId(index));
|
const getVideoTrackForStreamIndex = (video: ChromiumHTMLVideoElement, index: number) => getHtml5VideoTracks(video).find((videoTrack) => videoTrack.id === getHtml5TrackId(index));
|
||||||
export const getAudioTrackForStreamIndex = (audio: ChromiumHTMLAudioElement, index) => getHtml5AudioTracks(audio).find((audioTrack) => audioTrack.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) {
|
function resetVideoTrack(video: ChromiumHTMLVideoElement) {
|
||||||
console.log('Resetting video track');
|
console.log('Resetting video track');
|
||||||
|
Loading…
Reference in New Issue
Block a user