public static async Task ExtractSubtitles(string inputFile, string outFolder, Interpolate.OutMode outMode, bool showMsg = true) { try { string msg = "Extracting subtitles from video..."; if (showMsg) { Logger.Log(msg); } List <SubtitleTrack> subtitleTracks = await GetSubtitleTracks(inputFile); int counter = 1; int extractedSuccessfully = 0; foreach (SubtitleTrack subTrack in subtitleTracks) { if (Interpolate.canceled) { break; } string outPath = Path.Combine(outFolder, $"{subTrack.streamIndex}_{subTrack.lang}_{subTrack.encoding}.srt"); string[] trim = FfmpegExtract.GetTrimArgs(); string args = $"-sub_charenc {subTrack.encoding} {trim[0]} -i {inputFile.Wrap()} {trim[1]} -map 0:{subTrack.streamIndex} {outPath.Wrap()}"; await RunFfmpeg(args, LogMode.Hidden, "error"); if (subtitleTracks.Count > 4) { Program.mainForm.SetProgress(FormatUtils.RatioInt(counter, subtitleTracks.Count)); } counter++; if (IOUtils.GetFilesize(outPath) >= 32) { Logger.Log($"[FFCmds] Extracted subtitle track {subTrack.streamIndex} to {outPath} ({FormatUtils.Bytes(IOUtils.GetFilesize(outPath))})", true, false, "ffmpeg"); extractedSuccessfully++; } else { IOUtils.TryDeleteIfExists(outPath); // Delete if encode was not successful } } if (extractedSuccessfully > 0) { Logger.Log($"Extracted {extractedSuccessfully} subtitle tracks from the input video.", false, Logger.GetLastLine().Contains(msg)); Utils.ContainerSupportsSubs(Utils.GetExt(outMode), true); } } catch (Exception e) { Logger.Log("Error extracting subtitles: " + e.Message); } Program.mainForm.SetProgress(0); }
public static async Task ExtractAudioTracks(string inputFile, string outFolder, bool showMsg = true) { string msg = "Extracting audio from video..."; if (showMsg) { Logger.Log(msg); } List <AudioTrack> audioTracks = await GetAudioTracks(inputFile); int counter = 1; foreach (AudioTrack track in audioTracks) { if (Interpolate.canceled) { break; } string audioExt = Utils.GetAudioExt(inputFile, track.streamIndex); string outPath = Path.Combine(outFolder, $"{track.streamIndex}_{track.metadata}_audio.{audioExt}"); string[] trim = FfmpegExtract.GetTrimArgs(); string args = $"{trim[0]} -i {inputFile.Wrap()} {trim[1]} -map 0:{track.streamIndex} -vn -c:a copy {outPath.Wrap()}"; await RunFfmpeg(args, LogMode.Hidden, "panic"); if (File.Exists(outPath) && IOUtils.GetFilesize(outPath) < 512) { Logger.Log($"Failed to extract audio stream #{track.streamIndex} losslessly! Trying to re-encode."); File.Delete(outPath); outPath = Path.ChangeExtension(outPath, Utils.GetAudioExtForContainer(Path.GetExtension(inputFile))); args = $"{trim[0]} -i {inputFile.Wrap()} {trim[1]} -vn {Utils.GetAudioFallbackArgs(Interpolate.current.outMode)} {outPath.Wrap()}"; await RunFfmpeg(args, LogMode.Hidden, "panic", TaskType.ExtractOther, true); if (File.Exists(outPath) && IOUtils.GetFilesize(outPath) < 512) { Logger.Log($"Failed to extract audio stream #{track.streamIndex}, even with re-encoding. Will be missing from output."); IOUtils.TryDeleteIfExists(outPath); return; } Logger.Log($"Audio stream #{track.streamIndex} has been re-encoded as it can't be extracted losslessly. This may decrease the quality slightly.", false, true); } if (audioTracks.Count > 1) { Program.mainForm.SetProgress(FormatUtils.RatioInt(counter, audioTracks.Count)); } Logger.Log($"[FFCmds] Extracted audio track {track.streamIndex} to {outPath} ({FormatUtils.Bytes(IOUtils.GetFilesize(outPath))})", true, false, "ffmpeg"); counter++; } }
public static async Task MergeStreamsFromInput(string inputVideo, string interpVideo, string tempFolder) { if (!File.Exists(inputVideo) && !I.current.inputIsFrames) { Logger.Log("Warning: Input video file not found, can't copy audio/subtitle streams to output video!"); return; } string containerExt = Path.GetExtension(interpVideo); string tempPath = Path.Combine(tempFolder, $"vid{containerExt}"); string outPath = Path.Combine(tempFolder, $"muxed{containerExt}"); File.Move(interpVideo, tempPath); string inName = Path.GetFileName(tempPath); string outName = Path.GetFileName(outPath); string subArgs = "-c:s " + Utils.GetSubCodecForContainer(containerExt); bool audioCompat = Utils.ContainerSupportsAllAudioFormats(I.current.outMode, GetAudioCodecs(interpVideo)); string audioArgs = audioCompat ? "" : Utils.GetAudioFallbackArgs(I.current.outMode); if (!Config.GetBool("keepAudio")) { audioArgs = "-an"; } if (!Config.GetBool("keepSubs")) { subArgs = "-sn"; } string mkvFix = I.current.outMode == I.OutMode.VidMkv ? "-max_interleave_delta 0" : ""; // https://www.reddit.com/r/ffmpeg/comments/efddfs/starting_new_cluster_due_to_timestamp/ if (QuickSettingsTab.trimEnabled) { string otherStreamsName = $"otherStreams{containerExt}"; string[] trim = FfmpegExtract.GetTrimArgs(); string args1 = $"{trim[0]} -i {inputVideo.Wrap()} {trim[1]} -vn -map 0 -c copy {audioArgs} {subArgs} {otherStreamsName}"; // Extract trimmed await RunFfmpeg(args1, tempFolder, LogMode.Hidden); string args2 = $"-i {inName} -i {otherStreamsName} -map 0:v:0 -map 1:a:? -map 1:s:? -c copy {audioArgs} {subArgs} {mkvFix} {outName}"; // Merge interp + trimmed original await RunFfmpeg(args2, tempFolder, LogMode.Hidden); IOUtils.TryDeleteIfExists(Path.Combine(tempFolder, otherStreamsName)); } else // If trimming is disabled we can pull the streams directly from the input file { string args = $"-i {inName} -i {inputVideo.Wrap()} -map 0:v:0 -map 1:a:? -map 1:s:? -c copy {audioArgs} {subArgs} {mkvFix} {outName}"; await RunFfmpeg(args, tempFolder, LogMode.Hidden); } if (File.Exists(outPath) && IOUtils.GetFilesize(outPath) > 512) { File.Delete(tempPath); File.Move(outPath, interpVideo); } else { File.Move(tempPath, interpVideo); // Muxing failed, move unmuxed video file back } }
public static async Task MergeStreamsFromInput(string inputVideo, string interpVideo, string tempFolder, bool shortest) { if (!File.Exists(inputVideo) && !I.current.inputIsFrames) { Logger.Log("Warning: Input video file not found, can't copy audio/subtitle streams to output video!"); return; } string containerExt = Path.GetExtension(interpVideo); string tempPath = Path.Combine(tempFolder, $"vid{containerExt}"); string outPath = Path.Combine(tempFolder, $"muxed{containerExt}"); IoUtils.TryDeleteIfExists(tempPath); File.Move(interpVideo, tempPath); string inName = Path.GetFileName(tempPath); string outName = Path.GetFileName(outPath); string subArgs = "-c:s " + Utils.GetSubCodecForContainer(containerExt); bool audioCompat = Utils.ContainerSupportsAllAudioFormats(I.current.outMode, GetAudioCodecs(inputVideo)); bool slowmo = I.current.outItsScale != 0 && I.current.outItsScale != 1; string audioArgs = audioCompat && !slowmo ? "" : await Utils.GetAudioFallbackArgs(inputVideo, I.current.outMode, I.current.outItsScale); if (!audioCompat && !slowmo) { Logger.Log("Warning: Input audio format(s) not fully supported in output container - Will re-encode.", true, false, "ffmpeg"); } bool audio = Config.GetBool(Config.Key.keepAudio); bool subs = Config.GetBool(Config.Key.keepSubs); bool meta = Config.GetBool(Config.Key.keepMeta); if (!audio) { audioArgs = "-an"; } if (!subs || (subs && !Utils.ContainerSupportsSubs(containerExt))) { subArgs = "-sn"; } bool isMkv = I.current.outMode == I.OutMode.VidMkv; string mkvFix = isMkv ? "-max_interleave_delta 0" : ""; // https://reddit.com/r/ffmpeg/comments/efddfs/starting_new_cluster_due_to_timestamp/ string metaArg = (isMkv && meta) ? "-map 1:t?" : ""; // https://reddit.com/r/ffmpeg/comments/fw4jnh/how_to_make_ffmpeg_keep_attached_images_in_mkv_as/ string shortestArg = shortest ? "-shortest" : ""; if (QuickSettingsTab.trimEnabled) { string otherStreamsName = $"otherStreams{containerExt}"; string[] trim = FfmpegExtract.GetTrimArgs(); string args1 = $"{trim[0]} -i {inputVideo.Wrap()} {trim[1]} -map 0 -map -0:v -map -0:d -c copy {audioArgs} {subArgs} {otherStreamsName}"; // Extract trimmed await RunFfmpeg(args1, tempFolder, LogMode.Hidden); string args2 = $"-i {inName} -i {otherStreamsName} -map 0:v:0 -map 1:a:? -map 1:s:? {metaArg} -c copy {audioArgs} {subArgs} {mkvFix} {shortestArg} {outName}"; // Merge interp + trimmed original await RunFfmpeg(args2, tempFolder, LogMode.Hidden); IoUtils.TryDeleteIfExists(Path.Combine(tempFolder, otherStreamsName)); } else // If trimming is disabled we can pull the streams directly from the input file { string args = $"-i {inName} -i {inputVideo.Wrap()} -map 0:v:0 -map 1:a:? -map 1:s:? {metaArg} -c copy {audioArgs} {subArgs} {mkvFix} {shortestArg} {outName}"; await RunFfmpeg(args, tempFolder, LogMode.Hidden); } if (File.Exists(outPath) && IoUtils.GetFilesize(outPath) > 512) { File.Delete(tempPath); File.Move(outPath, interpVideo); } else { File.Move(tempPath, interpVideo); // Muxing failed, move unmuxed video file back } }