示例#1
0
        public static async Task MergeAudioAndSubs(string inputFile, string tempFolder)    // https://superuser.com/a/277667
        {
            string containerExt = Path.GetExtension(inputFile);
            string tempPath     = Path.Combine(tempFolder, $"vid{containerExt}");
            string outPath      = Path.Combine(tempFolder, $"muxed{containerExt}");

            File.Move(inputFile, tempPath);
            string inName  = Path.GetFileName(tempPath);
            string outName = Path.GetFileName(outPath);

            bool audio = Config.GetBool("keepAudio");
            bool subs  = Utils.ContainerSupportsSubs(containerExt, false) && Config.GetBool("keepSubs");

            string[] audioTracks = audio ? IOUtils.GetFilesSorted(tempFolder, false, "*_audio.*") : new string[0]; // Find audio files
            string[] subTracks   = subs ? IOUtils.GetFilesSorted(tempFolder, false, "*.srt") : new string[0];      // Find subtitle files

            Dictionary <int, string> trackFiles = new Dictionary <int, string>();                                  // Dict holding all track files with their index

            foreach (string audioTrack in audioTracks)                                                             // Loop through audio streams to add them to the dict
            {
                trackFiles[Path.GetFileNameWithoutExtension(audioTrack).Split('_')[0].GetInt()] = audioTrack;      // Add file, dict key is stream index
            }
            foreach (string subTrack in subTracks)                                                                 // Loop through subtitle streams to add them to the dict
            {
                trackFiles[Path.GetFileNameWithoutExtension(subTrack).Split('_')[0].GetInt()] = subTrack;          // Add file, dict key is stream index
            }
            string trackInputArgs = "";
            string trackMapArgs   = "";
            string trackMetaArgs  = "";

            SortedDictionary <int, string> sortedTrackFiles = new SortedDictionary <int, string>(trackFiles);

            int inputIndex = 1; // Input index (= output stream index) - Start at 1 since 0 is the video stream

            foreach (KeyValuePair <int, string> track in sortedTrackFiles)
            {
                int    streamIndex = track.Key;
                string trackFile   = track.Value;

                trackInputArgs += $" -i {Path.GetFileName(trackFile)}";     // Input filename

                if (Path.GetFileNameWithoutExtension(trackFile).Contains("_audio"))
                {
                    trackMapArgs += $" -map {inputIndex}:a";  // Map input file (audio track)
                }
                else
                {
                    trackMapArgs += $" -map {inputIndex}:s";  // Map input file (subtitle track)
                }
                string meta = Path.GetFileNameWithoutExtension(trackFile).Split('_')[1];

                if (!string.IsNullOrWhiteSpace(meta))
                {
                    if (Path.GetFileNameWithoutExtension(trackFile).Contains("_audio"))
                    {
                        trackMetaArgs += $" -metadata:s:{inputIndex} {meta}"; // Metadata
                    }
                    else
                    {
                        trackMetaArgs += $" -metadata:s:{inputIndex} language={meta}"; // Language
                    }
                }

                inputIndex++;
            }

            bool allAudioCodecsSupported = true;

            foreach (string audioTrack in audioTracks)
            {
                if (!Utils.ContainerSupportsAudioFormat(Interpolate.current.outMode, Path.GetExtension(audioTrack)))
                {
                    allAudioCodecsSupported = false;
                }
            }

            if (!allAudioCodecsSupported)
            {
                Logger.Log("Warning: Input audio format(s) not fully supported in output container. Audio transfer will not be lossless.", false, false, "ffmpeg");
            }

            string subArgs   = "-c:s " + Utils.GetSubCodecForContainer(containerExt);
            string audioArgs = allAudioCodecsSupported ? "-c:a copy" : Utils.GetAudioFallbackArgs(Path.GetExtension(inputFile));

            string args = $" -i {inName} {trackInputArgs} -map 0:v {trackMapArgs} -c:v copy {audioArgs} {subArgs} {trackMetaArgs} -shortest {outName}";

            await RunFfmpeg(args, tempFolder, LogMode.Hidden);


            // if (File.Exists(outPath) && IOUtils.GetFilesize(outPath) < 1024)
            // {
            //     Logger.Log("Failed to merge audio losslessly! Trying to re-encode.", false, false, "ffmpeg");
            //
            //     args = $" -i {inName} -stream_loop {looptimes} -i {audioName.Wrap()}" +
            //     $"{trackInputArgs} -map 0:v -map 1:a {trackMapArgs} -c:v copy {Utils.GetAudioFallbackArgs(Path.GetExtension(inputFile))} -c:s {subCodec} {trackMetaArgs} -shortest {outName}";
            //
            //     await RunFfmpeg(args, tempFolder, LogMode.Hidden);
            //
            //     if (File.Exists(outPath) && IOUtils.GetFilesize(outPath) < 1024)
            //     {
            //         Logger.Log("Failed to merge audio, even with re-encoding. Output will not have audio.", false, false, "ffmpeg");
            //         IOUtils.TryMove(tempPath, inputFile);   // Move temp file back
            //         IOUtils.TryDeleteIfExists(tempPath);
            //         return;
            //     }
            //
            //     string audioExt = Path.GetExtension(audioPath).Remove(".").ToUpper();
            //     Logger.Log($"Source audio ({audioExt}) has been re-encoded to fit into the target container ({containerExt.Remove(".").ToUpper()}). This may decrease the quality slightly.", false, true, "ffmpeg");
            // }

            if (File.Exists(outPath) && IOUtils.GetFilesize(outPath) > 512)
            {
                File.Delete(tempPath);
                File.Move(outPath, inputFile);
            }
            else
            {
                File.Move(tempPath, inputFile);
            }
        }
示例#2
0
        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
            }
        }