1
0
mirror of https://github.com/mifi/lossless-cut.git synced 2024-11-21 18:02:35 +01:00

make it more explicit when changing mp4 to mov

and show a notification #1075
This commit is contained in:
Mikael Finstad 2024-09-04 00:24:42 +02:00
parent 590e6a87cd
commit 69f600a0c0
No known key found for this signature in database
GPG Key ID: 25AB36E3E81CBC26
5 changed files with 30 additions and 23 deletions

View File

@ -87,9 +87,9 @@ Smart cut is experimental, so don't expect too much. But if you're having proble
- If Smart cut gives you repeated (duplicate) segments, you can try to enable the Export Option "Shift all start times".
- Sometimes it helps to convert (remux) your videos [to mp4 first](https://github.com/mifi/lossless-cut/discussions/1292#discussioncomment-10425084) (e.g. from mkv) using LosslessCut, before smart cutting them.
## My file changes from MP4 to MOV
## MP4/MOV issues
Some MP4 files ffmpeg is not able to export as MP4 and therefore needs to use MOV instead. Unfortunately I don't know any way to fix this.
Some MP4 files FFmpeg is not able to export as MP4 and MOV needs to be selected instead. Unfortunately I don't know any way to fix this. Sometimes certain players are not able to play back certain exported `.mov` files ([Adobe Premiere](https://github.com/mifi/lossless-cut/issues/1075#issuecomment-2327459890) 👀). You can try to rename the exported MOV file extension to `.mp4` and see if it helps. Or vice versa, rename an exported MP4 file to `.mov`.
## Output file name is missing characters

View File

@ -58,6 +58,7 @@ import {
isIphoneHevc, isProblematicAvc1, tryMapChaptersToEdl,
getDuration, getTimecodeFromStreams, createChaptersFromSegments,
RefuseOverwriteError, extractSubtitleTrackToSegments,
mapRecommendedDefaultFormat,
} from './ffmpeg';
import { shouldCopyStreamByDefault, getAudioStreams, getRealVideoStreams, isAudioDefinitelyNotSupported, willPlayerProperlyHandleVideo, doesPlayerSupportHevcPlayback, getSubtitleStreams, getVideoTrackForStreamIndex, getAudioTrackForStreamIndex, enableVideoTrack, enableAudioTrack } from './util/streams';
import { exportEdlFile, readEdlFile, loadLlcProject, askForEdlImport } from './edlStore';
@ -1305,7 +1306,6 @@ function App() {
// console.log('file meta read', fileMeta);
const fileFormatNew = await getDefaultOutFormat({ filePath: fp, fileMeta });
if (!fileFormatNew) throw new Error('Unable to determine file format');
const timecode = autoLoadTimecode ? getTimecodeFromStreams(fileMeta.streams) : undefined;
@ -1373,8 +1373,14 @@ function App() {
if (!haveVideoStream) setWaveformMode('big-waveform');
setMainFileMeta({ streams: fileMeta.streams, formatData: fileMeta.format, chapters: fileMeta.chapters });
setCopyStreamIdsForPath(fp, () => copyStreamIdsForPathNew);
setFileFormat(outFormatLocked || fileFormatNew);
setDetectedFileFormat(fileFormatNew);
if (outFormatLocked) {
setFileFormat(outFormatLocked);
} else {
const recommendedDefaultFormat = mapRecommendedDefaultFormat({ sourceFormat: fileFormatNew, streams: fileMeta.streams });
if (recommendedDefaultFormat.message) showNotification({ icon: 'info', text: recommendedDefaultFormat.message });
setFileFormat(recommendedDefaultFormat.format);
}
// only show one toast, or else we will only show the last one
if (existingHtml5FriendlyFile) {

View File

@ -8,7 +8,7 @@ import invariant from 'tiny-invariant';
import Checkbox from './Checkbox';
import { ReactSwal } from '../swal';
import { readFileMeta, getDefaultOutFormat } from '../ffmpeg';
import { readFileMeta, getDefaultOutFormat, mapRecommendedDefaultFormat } from '../ffmpeg';
import useFileFormatState from '../hooks/useFileFormatState';
import OutputFormatSelect from './OutputFormatSelect';
import useUserSettings from '../hooks/useUserSettings';
@ -69,8 +69,8 @@ function ConcatDialog({ isShown, onHide, paths, onConcat, alwaysConcatMultipleFi
const fileFormatNew = await getDefaultOutFormat({ filePath: firstPath, fileMeta: fileMetaNew });
if (aborted) return;
setFileMeta(fileMetaNew);
setFileFormat(fileFormatNew);
setDetectedFileFormat(fileFormatNew);
setFileFormat(mapRecommendedDefaultFormat({ sourceFormat: fileFormatNew, streams: fileMetaNew.streams }).format);
setUniqueSuffix(Date.now());
})().catch(console.error);

View File

@ -236,27 +236,28 @@ export async function createChaptersFromSegments({ segmentPaths, chapterNames }:
}
/**
* ffmpeg only supports encoding certain formats, and some of the detected input
* formats are not the same as the muxer name used for encoding.
* Some of the detected input formats are not the same as the muxer name used for encoding.
* Therefore we have to map between detected input format and encode format
* See also ffmpeg -formats
*/
function mapDefaultFormat({ streams, requestedFormat }: { streams: FFprobeStream[], requestedFormat: string | undefined }) {
if (requestedFormat === 'mp4') {
// Only MOV supports these codecs, so default to MOV instead https://github.com/mifi/lossless-cut/issues/948
// eslint-disable-next-line unicorn/no-lonely-if
if (streams.some((stream) => pcmAudioCodecs.includes(stream.codec_name))) {
return 'mov';
}
}
// see sample.aac
function mapInputToOutputFormat(requestedFormat: string | undefined) {
// see file aac raw adts.aac
if (requestedFormat === 'aac') return 'adts';
return requestedFormat;
}
async function determineOutputFormat(ffprobeFormatsStr: string | undefined, filePath: string) {
export function mapRecommendedDefaultFormat({ streams, sourceFormat }: { streams: FFprobeStream[], sourceFormat: string | undefined }) {
// Certain codecs cannot be muxed by ffmpeg into mp4, but in MOV they can
// so we default to MOV instead in those cases https://github.com/mifi/lossless-cut/issues/948
if (sourceFormat === 'mp4' && streams.some((stream) => pcmAudioCodecs.includes(stream.codec_name))) {
return { format: 'mov', message: i18n.t('This file contains an audio track that FFmpeg is unable to mux into the MP4 format, so MOV has been auto-selected as the default output format.') };
}
return { format: sourceFormat };
}
async function determineSourceFileFormat(ffprobeFormatsStr: string | undefined, filePath: string) {
const ffprobeFormats = (ffprobeFormatsStr || '').split(',').map((str) => str.trim()).filter(Boolean);
const [firstFfprobeFormat] = ffprobeFormats;
@ -342,10 +343,10 @@ async function determineOutputFormat(ffprobeFormatsStr: string | undefined, file
}
}
export async function getDefaultOutFormat({ filePath, fileMeta: { format, streams } }: { filePath: string, fileMeta: { format: Pick<FFprobeFormat, 'format_name'>, streams: FFprobeStream[] } }) {
const assumedFormat = await determineOutputFormat(format.format_name, filePath);
export async function getDefaultOutFormat({ filePath, fileMeta: { format } }: { filePath: string, fileMeta: { format: Pick<FFprobeFormat, 'format_name'> } }) {
const assumedFormat = await determineSourceFileFormat(format.format_name, filePath);
return mapDefaultFormat({ streams, requestedFormat: assumedFormat });
return mapInputToOutputFormat(assumedFormat);
}
export async function readFileMeta(filePath: string) {

View File

@ -221,7 +221,7 @@ export function getOutFileExtension({ isCustomFormatSelected, outFormat, filePat
// https://github.com/mifi/lossless-cut/issues/1075#issuecomment-1072084286
const hasMovIncorrectExtension = outFormat === 'mov' && inputExt.toLowerCase() !== '.mov';
// OK, just keep the current extension. Because most players will not care about the extension
// OK, just keep the current extension. Because most other players will not care about the extension
if (!hasMovIncorrectExtension) return inputExt;
}