add fixCodecTag option

closes #2518
pull/2575/head
Mikael Finstad 8 months ago
parent 251106b037
commit 2511061f5e
No known key found for this signature in database
GPG Key ID: 25AB36E3E81CBC26

@ -120,6 +120,7 @@ const defaults: Config = {
preserveMetadata: 'default',
preserveMetadataOnMerge: false,
preserveMovData: false,
fixCodecTag: 'auto',
movFastStart: true,
avoidNegativeTs: 'make_zero',
hideNotifications: undefined,

@ -173,7 +173,7 @@ function App() {
const [selectedBatchFiles, setSelectedBatchFiles] = useState<string[]>([]);
const allUserSettings = useUserSettingsRoot();
const { captureFormat, customOutDir, keyframeCut, preserveMetadata, preserveMetadataOnMerge, preserveMovData, preserveChapters, movFastStart, avoidNegativeTs, autoMerge, timecodeFormat, invertCutSegments, autoExportExtraStreams, askBeforeClose, enableAskForImportChapters, enableAskForFileOpenAction, playbackVolume, autoSaveProjectFile, wheelSensitivity, waveformHeight, invertTimelineScroll, language, ffmpegExperimental, hideNotifications, hideOsNotifications, autoLoadTimecode, autoDeleteMergedSegments, exportConfirmEnabled, segmentsToChapters, simpleMode, cutFileTemplate, cutMergedFileTemplate, mergedFileTemplate, keyboardSeekAccFactor, keyboardNormalSeekSpeed, keyboardSeekSpeed2, keyboardSeekSpeed3, treatInputFileModifiedTimeAsStart, treatOutputFileModifiedTimeAsStart, outFormatLocked, safeOutputFileName, enableAutoHtml5ify, segmentsToChaptersOnly, keyBindings, enableSmartCut, customFfPath, storeProjectInWorkingDir, enableOverwriteOutput, mouseWheelZoomModifierKey, mouseWheelFrameSeekModifierKey, mouseWheelKeyframeSeekModifierKey, captureFrameMethod, captureFrameQuality, captureFrameFileNameFormat, enableNativeHevc, cleanupChoices, darkMode, preferStrongColors, outputFileNameMinZeroPadding, cutFromAdjustmentFrames, cutToAdjustmentFrames, waveformMode: waveformModePreference, thumbnailsEnabled, keyframesEnabled, reducedMotion } = allUserSettings.settings;
const { captureFormat, customOutDir, keyframeCut, preserveMetadata, preserveMetadataOnMerge, preserveMovData, fixCodecTag, preserveChapters, movFastStart, avoidNegativeTs, autoMerge, timecodeFormat, invertCutSegments, autoExportExtraStreams, askBeforeClose, enableAskForImportChapters, enableAskForFileOpenAction, playbackVolume, autoSaveProjectFile, wheelSensitivity, waveformHeight, invertTimelineScroll, language, ffmpegExperimental, hideNotifications, hideOsNotifications, autoLoadTimecode, autoDeleteMergedSegments, exportConfirmEnabled, segmentsToChapters, simpleMode, cutFileTemplate, cutMergedFileTemplate, mergedFileTemplate, keyboardSeekAccFactor, keyboardNormalSeekSpeed, keyboardSeekSpeed2, keyboardSeekSpeed3, treatInputFileModifiedTimeAsStart, treatOutputFileModifiedTimeAsStart, outFormatLocked, safeOutputFileName, enableAutoHtml5ify, segmentsToChaptersOnly, keyBindings, enableSmartCut, customFfPath, storeProjectInWorkingDir, enableOverwriteOutput, mouseWheelZoomModifierKey, mouseWheelFrameSeekModifierKey, mouseWheelKeyframeSeekModifierKey, captureFrameMethod, captureFrameQuality, captureFrameFileNameFormat, enableNativeHevc, cleanupChoices, darkMode, preferStrongColors, outputFileNameMinZeroPadding, cutFromAdjustmentFrames, cutToAdjustmentFrames, waveformMode: waveformModePreference, thumbnailsEnabled, keyframesEnabled, reducedMotion } = allUserSettings.settings;
const { setCaptureFormat, setCustomOutDir, setKeyframeCut, setPlaybackVolume, setExportConfirmEnabled, setSimpleMode, setOutFormatLocked, setSafeOutputFileName, setKeyBindings, resetKeyBindings, setStoreProjectInWorkingDir, setCleanupChoices, toggleDarkMode, setWaveformMode, setThumbnailsEnabled, setKeyframesEnabled, prefersReducedMotion } = allUserSettings;
const { withErrorHandling, handleError, genericError, setGenericError } = useErrorHandling();
@ -923,7 +923,7 @@ function App() {
await maybeMkDeepOutDir({ outputDir: outDir, fileOutPath: outPath });
const { haveExcludedStreams } = await concatFiles({ paths, outPath, outDir, outFormat, metadataFromPath, includeAllStreams, streams, ffmpegExperimental, onProgress: setProgress, preserveMovData, movFastStart, preserveMetadataOnMerge, chapters: chaptersFromSegments });
const { haveExcludedStreams } = await concatFiles({ paths, outPath, outDir, outFormat, metadataFromPath, includeAllStreams, streams, ffmpegExperimental, onProgress: setProgress, preserveMovData, movFastStart, fixCodecTag, preserveMetadataOnMerge, chapters: chaptersFromSegments });
const outputSize = await readFileSize(outPath); // * 1.06; // testing:)
const sizeCheckResult = checkFileSizes(inputSize, outputSize);
@ -967,7 +967,7 @@ function App() {
setWorking(undefined);
setProgress(undefined);
}
}, [workingRef, ensureWritableOutDir, customOutDir, setWorking, segmentsToChapters, concatFiles, ffmpegExperimental, preserveMovData, movFastStart, preserveMetadataOnMerge, closeBatch, enableOverwriteOutput, hideAllNotifications, t, showOsNotification, openConcatFinishedDialog, handleConcatFailed]);
}, [workingRef, ensureWritableOutDir, customOutDir, setWorking, segmentsToChapters, concatFiles, ffmpegExperimental, preserveMovData, movFastStart, fixCodecTag, preserveMetadataOnMerge, closeBatch, enableOverwriteOutput, hideAllNotifications, t, showOsNotification, openConcatFinishedDialog, handleConcatFailed]);
const cleanupFiles = useCallback(async (cleanupChoices2: CleanupChoicesType) => {
// Store paths before we reset state
@ -1093,6 +1093,7 @@ function App() {
preserveMovData,
preserveChapters,
movFastStart,
fixCodecTag,
avoidNegativeTs,
customTagsByFile,
paramsByStreamId,
@ -1128,6 +1129,7 @@ function App() {
ffmpegExperimental,
preserveMovData,
movFastStart,
fixCodecTag,
onProgress: setProgress,
chapterNames,
preserveMetadataOnMerge,
@ -1205,7 +1207,7 @@ function App() {
setWorking(undefined);
setProgress(undefined);
}
}, [filePath, numStreamsToCopy, haveInvalidSegs, workingRef, setWorking, segmentsToChaptersOnly, cutFileTemplateOrDefault, generateCutFileNames, cutMultiple, outputDir, customOutDir, fileFormat, fileDuration, isRotationSet, effectiveRotation, copyFileStreams, allFilesMeta, keyframeCut, segmentsToExport, shortestFlag, ffmpegExperimental, preserveMetadata, preserveMetadataOnMerge, preserveMovData, preserveChapters, movFastStart, avoidNegativeTs, customTagsByFile, paramsByStreamId, detectedFps, willMerge, enableOverwriteOutput, exportConfirmEnabled, mainFileFormatData, mainStreams, exportExtraStreams, areWeCutting, hideAllNotifications, simpleMode, prefersReducedMotion, cleanupChoices.cleanupAfterExport, cleanupFilesWithDialog, segmentsOrInverse.selected, t, cutMergedFileTemplateOrDefault, segmentsToChapters, invertCutSegments, generateCutMergedFileNames, concatCutSegments, autoDeleteMergedSegments, tryDeleteFiles, nonCopiedExtraStreams, extractStreams, showOsNotification, openCutFinishedDialog, handleExportFailed]);
}, [filePath, numStreamsToCopy, haveInvalidSegs, workingRef, setWorking, segmentsToChaptersOnly, cutFileTemplateOrDefault, generateCutFileNames, cutMultiple, outputDir, customOutDir, fileFormat, fileDuration, isRotationSet, effectiveRotation, copyFileStreams, allFilesMeta, keyframeCut, segmentsToExport, shortestFlag, ffmpegExperimental, preserveMetadata, preserveMetadataOnMerge, preserveMovData, preserveChapters, movFastStart, fixCodecTag, avoidNegativeTs, customTagsByFile, paramsByStreamId, detectedFps, willMerge, enableOverwriteOutput, exportConfirmEnabled, mainFileFormatData, mainStreams, exportExtraStreams, areWeCutting, hideAllNotifications, simpleMode, prefersReducedMotion, cleanupChoices.cleanupAfterExport, cleanupFilesWithDialog, segmentsOrInverse.selected, t, cutMergedFileTemplateOrDefault, segmentsToChapters, invertCutSegments, generateCutMergedFileNames, concatCutSegments, autoDeleteMergedSegments, tryDeleteFiles, nonCopiedExtraStreams, extractStreams, showOsNotification, openCutFinishedDialog, handleExportFailed]);
const onExportPress = useCallback(async () => {
if (!filePath) return;

@ -21,7 +21,7 @@ import styles from './ExportConfirm.module.css';
import { SegmentToExport } from '../types';
import { defaultCutFileTemplate, defaultCutMergedFileTemplate, GenerateOutFileNames } from '../util/outputNameTemplate';
import { FFprobeStream } from '../../../../ffprobe';
import { AvoidNegativeTs, PreserveMetadata } from '../../../../types';
import { AvoidNegativeTs, FixCodecTagOption, PreserveMetadata } from '../../../../types';
import TextInput from './TextInput';
import { UseSegments } from '../hooks/useSegments';
import ExportSheet from './ExportSheet';
@ -130,7 +130,7 @@ function ExportConfirm({
}) {
const { t } = useTranslation();
const { changeOutDir, keyframeCut, toggleKeyframeCut, preserveMovData, setPreserveMovData, preserveMetadata, setPreserveMetadata, preserveChapters, setPreserveChapters, movFastStart, setMovFastStart, avoidNegativeTs, setAvoidNegativeTs, autoDeleteMergedSegments, exportConfirmEnabled, toggleExportConfirmEnabled, segmentsToChapters, setSegmentsToChapters, preserveMetadataOnMerge, setPreserveMetadataOnMerge, enableSmartCut, setEnableSmartCut, effectiveExportMode, enableOverwriteOutput, setEnableOverwriteOutput, ffmpegExperimental, setFfmpegExperimental, cutFromAdjustmentFrames, setCutFromAdjustmentFrames, cutToAdjustmentFrames, setCutToAdjustmentFrames, setCutFileTemplate, setCutMergedFileTemplate, simpleMode } = useUserSettings();
const { changeOutDir, keyframeCut, toggleKeyframeCut, preserveMovData, setPreserveMovData, setFixCodecTag, fixCodecTag, preserveMetadata, setPreserveMetadata, preserveChapters, setPreserveChapters, movFastStart, setMovFastStart, avoidNegativeTs, setAvoidNegativeTs, autoDeleteMergedSegments, exportConfirmEnabled, toggleExportConfirmEnabled, segmentsToChapters, setSegmentsToChapters, preserveMetadataOnMerge, setPreserveMetadataOnMerge, enableSmartCut, setEnableSmartCut, effectiveExportMode, enableOverwriteOutput, setEnableOverwriteOutput, ffmpegExperimental, setFfmpegExperimental, cutFromAdjustmentFrames, setCutFromAdjustmentFrames, cutToAdjustmentFrames, setCutToAdjustmentFrames, setCutFileTemplate, setCutMergedFileTemplate, simpleMode } = useUserSettings();
const [showAdvanced, setShowAdvanced] = useState(!simpleMode);
@ -251,6 +251,12 @@ function ExportConfirm({
showHelpText({ text: exportModeDescription });
}, [exportModeDescription, showHelpText]);
// https://github.com/mifi/lossless-cut/issues/2518
// https://github.com/mifi/lossless-cut/issues/1444
const onFixCodecTagHelpPress = useCallback(() => {
showHelpText({ text: i18n.t('Some video files have incorrect codec tags which may cause problems playing back the video track in some players. On the flip side, fixing the codec tag will also cause problems in some players, so you might have to try both settings.') });
}, [showHelpText]);
const onAvoidNegativeTsHelpPress = useCallback(() => {
// https://ffmpeg.org/ffmpeg-all.html#Format-Options
// https://github.com/mifi/lossless-cut/issues/1206
@ -484,6 +490,22 @@ function ExportConfirm({
{renderNoticeIcon(notices.specific['preserveMovData'], rightIconStyle) ?? <HelpIcon onClick={onPreserveMovDataHelpPress} />}
</td>
</AnimatedTr>
<AnimatedTr>
<td>
{t('Fix video codec tag / ID')}
</td>
<td>
<Select value={fixCodecTag} onChange={(e) => setFixCodecTag(e.target.value as FixCodecTagOption)} style={{ height: 20, marginLeft: 5 }}>
<option value={'auto' satisfies FixCodecTagOption}>{t('Always')}</option>
<option value={'always' satisfies FixCodecTagOption}>{t('Always')}</option>
<option value={'never' satisfies FixCodecTagOption}>{t('Never')}</option>
</Select>
</td>
<td>
<HelpIcon onClick={onFixCodecTagHelpPress} />
</td>
</AnimatedTr>
</>
)}

@ -11,7 +11,7 @@ import { getMapStreamsArgs, getStreamIdsToCopy } from '../util/streams';
import { needsSmartCut, getCodecParams } from '../smartcut';
import { getGuaranteedSegments, isDurationValid } from '../segments';
import { FFprobeStream } from '../../../../ffprobe';
import { AvoidNegativeTs, Html5ifyMode, PreserveMetadata } from '../../../../types';
import { AvoidNegativeTs, FixCodecTagOption, Html5ifyMode, PreserveMetadata } from '../../../../types';
import { AllFilesMeta, Chapter, CopyfileStreams, CustomTagsByFile, LiteFFprobeStream, ParamsByStreamId, SegmentToExport } from '../types';
import { LossyMode } from '../../../main';
import { UserFacingError } from '../../errors';
@ -120,7 +120,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
const getOutputPlaybackRateArgs = useCallback(() => (outputPlaybackRate !== 1 ? ['-itsscale', String(1 / outputPlaybackRate)] : []), [outputPlaybackRate]);
const concatFiles = useCallback(async ({ paths, outDir, outPath, metadataFromPath, includeAllStreams, streams, outFormat, ffmpegExperimental, onProgress = () => undefined, preserveMovData, movFastStart, chapters, preserveMetadataOnMerge, videoTimebase }: {
const concatFiles = useCallback(async ({ paths, outDir, outPath, metadataFromPath, includeAllStreams, streams, outFormat, ffmpegExperimental, onProgress = () => undefined, preserveMovData, movFastStart, fixCodecTag, chapters, preserveMetadataOnMerge, videoTimebase }: {
paths: string[],
outDir: string | undefined,
outPath: string,
@ -132,6 +132,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
onProgress?: (a: number) => void,
preserveMovData: boolean,
movFastStart: boolean,
fixCodecTag: FixCodecTagOption,
chapters: Chapter[] | undefined,
preserveMetadataOnMerge: boolean,
videoTimebase?: number | undefined,
@ -188,6 +189,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
copyFileStreams: [{ path: metadataFromPath, streamIds: streamIdsToCopy }],
outFormat,
manuallyCopyDisposition: true,
fixCodecTag,
});
// Keep this similar to losslessCutSingle()
@ -245,7 +247,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
const losslessCutSingle = useCallback(async ({
keyframeCut: ssBeforeInput, avoidNegativeTs, copyFileStreams, cutFrom, cutTo, chaptersPath, onProgress, outPath,
fileDuration, rotation, allFilesMeta, outFormat, shortestFlag, ffmpegExperimental, preserveMetadata, preserveMovData, preserveChapters, movFastStart, customTagsByFile, paramsByStreamId, videoTimebase, detectedFps,
fileDuration, rotation, allFilesMeta, outFormat, shortestFlag, ffmpegExperimental, preserveMetadata, preserveMovData, preserveChapters, movFastStart, fixCodecTag, customTagsByFile, paramsByStreamId, videoTimebase, detectedFps,
}: {
keyframeCut: boolean,
avoidNegativeTs: AvoidNegativeTs | undefined,
@ -265,6 +267,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
preserveMovData: boolean,
preserveChapters: boolean,
movFastStart: boolean,
fixCodecTag: FixCodecTagOption,
customTagsByFile: CustomTagsByFile,
paramsByStreamId: ParamsByStreamId,
videoTimebase?: number | undefined,
@ -332,7 +335,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
...flatMap(Object.entries(customTagsByFile[filePath] || []), ([key, value]) => ['-metadata', `${key}=${value}`]),
];
const mapStreamsArgs = getMapStreamsArgs({ copyFileStreams: copyFileStreamsFiltered, allFilesMeta, outFormat, areWeCutting });
const mapStreamsArgs = getMapStreamsArgs({ copyFileStreams: copyFileStreamsFiltered, allFilesMeta, outFormat, areWeCutting, fixCodecTag });
const customParamsArgs = (() => {
const ret: string[] = [];
@ -492,7 +495,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
}, [appendFfmpegCommandLog, filePath]);
const cutMultiple = useCallback(async ({
outputDir, customOutDir, segments: segmentsIn, cutFileNames, fileDuration, rotation, detectedFps, onProgress: onTotalProgress, keyframeCut, copyFileStreams, allFilesMeta, outFormat, shortestFlag, ffmpegExperimental, preserveMetadata, preserveMetadataOnMerge, preserveMovData, preserveChapters, movFastStart, avoidNegativeTs, customTagsByFile, paramsByStreamId, chapters,
outputDir, customOutDir, segments: segmentsIn, cutFileNames, fileDuration, rotation, detectedFps, onProgress: onTotalProgress, keyframeCut, copyFileStreams, allFilesMeta, outFormat, shortestFlag, ffmpegExperimental, preserveMetadata, preserveMetadataOnMerge, preserveMovData, preserveChapters, movFastStart, fixCodecTag, avoidNegativeTs, customTagsByFile, paramsByStreamId, chapters,
}: {
outputDir: string,
customOutDir: string | undefined,
@ -513,6 +516,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
preserveMetadataOnMerge: boolean,
preserveChapters: boolean,
movFastStart: boolean,
fixCodecTag: FixCodecTagOption,
avoidNegativeTs: AvoidNegativeTs | undefined,
customTagsByFile: CustomTagsByFile,
paramsByStreamId: ParamsByStreamId,
@ -549,7 +553,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
// simple lossless cut
invariant(outFormat != null);
await losslessCutSingle({
cutFrom: desiredCutFrom, cutTo, chaptersPath, outPath: finalOutPath, copyFileStreams, keyframeCut, avoidNegativeTs, fileDuration, rotation, allFilesMeta, outFormat, shortestFlag, ffmpegExperimental, preserveMetadata, preserveMovData, preserveChapters, movFastStart, customTagsByFile, paramsByStreamId, onProgress: (progress) => onSingleProgress(i, progress),
cutFrom: desiredCutFrom, cutTo, chaptersPath, outPath: finalOutPath, copyFileStreams, keyframeCut, avoidNegativeTs, fileDuration, rotation, allFilesMeta, outFormat, shortestFlag, ffmpegExperimental, preserveMetadata, preserveMovData, preserveChapters, movFastStart, fixCodecTag, customTagsByFile, paramsByStreamId, onProgress: (progress) => onSingleProgress(i, progress),
});
return { path: finalOutPath, created: true };
}
@ -622,7 +626,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
// for smart cut we need to use keyframe cut here, and no avoid_negative_ts
await losslessCutSingle({
cutFrom: losslessCutFrom, cutTo, chaptersPath, outPath: losslessPartOutPath, copyFileStreams: copyFileStreamsFiltered, keyframeCut: true, avoidNegativeTs: undefined, fileDuration, rotation, allFilesMeta, outFormat, shortestFlag, ffmpegExperimental, preserveMetadata, preserveMovData, preserveChapters, movFastStart, customTagsByFile, paramsByStreamId, videoTimebase, onProgress,
cutFrom: losslessCutFrom, cutTo, chaptersPath, outPath: losslessPartOutPath, copyFileStreams: copyFileStreamsFiltered, keyframeCut: true, avoidNegativeTs: undefined, fileDuration, rotation, allFilesMeta, outFormat, shortestFlag, ffmpegExperimental, preserveMetadata, preserveMovData, preserveChapters, movFastStart, fixCodecTag, customTagsByFile, paramsByStreamId, videoTimebase, onProgress,
});
// We don't need to concat, just return the single cut file (we may need smart cut in other segments though)
@ -644,7 +648,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
// need to re-read streams because indexes may have changed. Using main file as source of streams and metadata
const { streams: streamsAfterCut } = await readFileMeta(losslessPartOutPath);
await concatFiles({ paths: smartCutSegmentsToConcat, outDir: outputDir, outPath: finalOutPath, metadataFromPath: losslessPartOutPath, outFormat, includeAllStreams: true, streams: streamsAfterCut, ffmpegExperimental, preserveMovData, movFastStart, chapters, preserveMetadataOnMerge, videoTimebase, onProgress: onConcatProgress });
await concatFiles({ paths: smartCutSegmentsToConcat, outDir: outputDir, outPath: finalOutPath, metadataFromPath: losslessPartOutPath, outFormat, includeAllStreams: true, streams: streamsAfterCut, ffmpegExperimental, preserveMovData, movFastStart, fixCodecTag, chapters, preserveMetadataOnMerge, videoTimebase, onProgress: onConcatProgress });
return { path: finalOutPath, created: true };
} finally {
await tryDeleteFiles(smartCutSegmentsToConcat);
@ -658,13 +662,14 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
}
}, [shouldSkipExistingFile, isEncoding, filePath, lossyMode, losslessCutSingle, cutEncodeSmartPart, encCustomBitrate, concatFiles]);
const concatCutSegments = useCallback(async ({ customOutDir, outFormat, segmentPaths, ffmpegExperimental, onProgress, preserveMovData, movFastStart, chapterNames, preserveMetadataOnMerge, mergedOutFilePath }: {
const concatCutSegments = useCallback(async ({ customOutDir, outFormat, segmentPaths, ffmpegExperimental, onProgress, preserveMovData, fixCodecTag, movFastStart, chapterNames, preserveMetadataOnMerge, mergedOutFilePath }: {
customOutDir: string | undefined,
outFormat: string | undefined,
segmentPaths: string[],
ffmpegExperimental: boolean,
onProgress: (p: number) => void,
preserveMovData: boolean,
fixCodecTag: FixCodecTagOption,
movFastStart: boolean,
chapterNames: (string | undefined)[] | undefined,
preserveMetadataOnMerge: boolean,
@ -680,7 +685,7 @@ function useFfmpegOperations({ filePath, treatInputFileModifiedTimeAsStart, trea
invariant(metadataFromPath != null);
// need to re-read streams because may have changed
const { streams } = await readFileMeta(metadataFromPath);
await concatFiles({ paths: segmentPaths, outDir, outPath: mergedOutFilePath, metadataFromPath, outFormat, includeAllStreams: true, streams, ffmpegExperimental, onProgress, preserveMovData, movFastStart, chapters, preserveMetadataOnMerge });
await concatFiles({ paths: segmentPaths, outDir, outPath: mergedOutFilePath, metadataFromPath, outFormat, includeAllStreams: true, streams, ffmpegExperimental, onProgress, preserveMovData, movFastStart, fixCodecTag, chapters, preserveMetadataOnMerge });
}, [concatFiles, filePath, shouldSkipExistingFile]);
const html5ify = useCallback(async ({ customOutDir, filePath: filePathArg, speed, hasAudio, hasVideo, onProgress }: {

@ -61,6 +61,8 @@ export default function useUserSettingsRoot() {
useEffect(() => safeSetConfig({ preserveMetadataOnMerge }), [preserveMetadataOnMerge]);
const [preserveMovData, setPreserveMovData] = useState(safeGetConfigInitial('preserveMovData'));
useEffect(() => safeSetConfig({ preserveMovData }), [preserveMovData]);
const [fixCodecTag, setFixCodecTag] = useState(safeGetConfigInitial('fixCodecTag'));
useEffect(() => safeSetConfig({ fixCodecTag }), [fixCodecTag]);
const [preserveChapters, setPreserveChapters] = useState(safeGetConfigInitial('preserveChapters'));
useEffect(() => safeSetConfig({ preserveChapters }), [preserveChapters]);
const [movFastStart, setMovFastStart] = useState(safeGetConfigInitial('movFastStart'));
@ -227,6 +229,7 @@ export default function useUserSettingsRoot() {
preserveMetadataOnMerge,
preserveMovData,
preserveChapters,
fixCodecTag,
movFastStart,
avoidNegativeTs,
autoMerge,
@ -301,6 +304,7 @@ export default function useUserSettingsRoot() {
setPreserveMetadataOnMerge,
setPreserveMovData,
setPreserveChapters,
setFixCodecTag,
setMovFastStart,
setAvoidNegativeTs,
setAutoMerge,

@ -52,6 +52,7 @@ test('getMapStreamsArgs', () => {
allFilesMeta,
copyFileStreams: [{ path, streamIds: streams1.map((stream) => stream.index) }],
outFormat,
fixCodecTag: 'auto',
})).toEqual([
'-map', '0:0', '-c:0', 'copy',
'-map', '0:1', '-c:1', 'copy',

@ -2,6 +2,7 @@ import invariant from 'tiny-invariant';
import { FFprobeStream, FFprobeStreamDisposition } from '../../../../ffprobe';
import { AllFilesMeta, ChromiumHTMLAudioElement, ChromiumHTMLVideoElement, CopyfileStreams, LiteFFprobeStream } from '../types';
import type { FileStream } from '../ffmpeg';
import { FixCodecTagOption } from '../../../../types';
// taken from `ffmpeg -codecs`
@ -106,8 +107,14 @@ export const isMatroska = (format: string | undefined) => format != null && ['ma
type GetVideoArgsFn = (a: { streamIndex: number, outputIndex: number }) => string[] | undefined;
function getPerStreamFlags({ stream, outputIndex, outFormat, manuallyCopyDisposition = false, getVideoArgs = () => undefined, areWeCutting }: {
stream: LiteFFprobeStream, outputIndex: number, outFormat: string | undefined, manuallyCopyDisposition?: boolean | undefined, getVideoArgs?: GetVideoArgsFn | undefined, areWeCutting: boolean | undefined
function getPerStreamFlags({ stream, outputIndex, outFormat, manuallyCopyDisposition = false, getVideoArgs = () => undefined, areWeCutting, fixCodecTag }: {
stream: LiteFFprobeStream,
outputIndex: number,
outFormat: string | undefined,
manuallyCopyDisposition?: boolean | undefined,
getVideoArgs?: GetVideoArgsFn | undefined,
areWeCutting: boolean | undefined,
fixCodecTag: FixCodecTagOption | undefined,
}) {
let args: string[] = [];
@ -179,7 +186,7 @@ function getPerStreamFlags({ stream, outputIndex, outFormat, manuallyCopyDisposi
if (isMov(outFormat)) {
// 0x31766568 see https://github.com/mifi/lossless-cut/issues/1444
// eslint-disable-next-line unicorn/prefer-switch, unicorn/no-lonely-if
if (['0x0000', '0x31637668', '0x31766568'].includes(stream.codec_tag) && stream.codec_name === 'hevc') {
if (fixCodecTag === 'always' || (fixCodecTag === 'auto' && ['0x0000', '0x31637668', '0x31766568'].includes(stream.codec_tag) && stream.codec_name === 'hevc')) {
addArgs(`-tag:${outputIndex}`, 'hvc1');
}
}
@ -200,7 +207,7 @@ function getPerStreamFlags({ stream, outputIndex, outFormat, manuallyCopyDisposi
return args;
}
export function getMapStreamsArgs({ startIndex = 0, outFormat, allFilesMeta, copyFileStreams, manuallyCopyDisposition, getVideoArgs, areWeCutting }: {
export function getMapStreamsArgs({ startIndex = 0, outFormat, allFilesMeta, copyFileStreams, manuallyCopyDisposition, getVideoArgs, areWeCutting, fixCodecTag }: {
startIndex?: number,
outFormat: string | undefined,
allFilesMeta: Record<string, Pick<AllFilesMeta[string], 'streams'>>,
@ -208,6 +215,7 @@ export function getMapStreamsArgs({ startIndex = 0, outFormat, allFilesMeta, cop
manuallyCopyDisposition?: boolean,
getVideoArgs?: GetVideoArgsFn,
areWeCutting?: boolean,
fixCodecTag?: FixCodecTagOption,
}) {
let args: string[] = [];
let outputIndex = startIndex;
@ -220,7 +228,7 @@ export function getMapStreamsArgs({ startIndex = 0, outFormat, allFilesMeta, cop
args = [
...args,
'-map', `${fileIndex}:${streamId}`,
...getPerStreamFlags({ stream, outputIndex, outFormat, manuallyCopyDisposition, getVideoArgs, areWeCutting }),
...getPerStreamFlags({ stream, outputIndex, outFormat, manuallyCopyDisposition, getVideoArgs, areWeCutting, fixCodecTag }),
];
outputIndex += 1;
});

@ -49,6 +49,8 @@ export type PreserveMetadata = 'default' | 'nonglobal' | 'none'
export type WaveformMode = 'big-waveform' | 'waveform';
export type FixCodecTagOption = 'always' | 'never' | 'auto';
export interface Config {
version: number,
@ -76,6 +78,7 @@ export interface Config {
preserveMetadata: PreserveMetadata,
preserveMetadataOnMerge: boolean,
preserveMovData: boolean,
fixCodecTag: FixCodecTagOption,
movFastStart: boolean,
avoidNegativeTs: AvoidNegativeTs,
hideNotifications: 'all' | undefined,

Loading…
Cancel
Save