mirror of
https://github.com/mifi/lossless-cut.git
synced 2024-11-22 02:12:30 +01:00
make it more explicit when changing mp4 to mov
and show a notification #1075
This commit is contained in:
parent
590e6a87cd
commit
69f600a0c0
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user