// method 1: add preview as separate track with slightly bigger resolution (via Pituz) static void GeneratePreview(string filePath) { OutputProcessor sp = new SimpleProcessor(); ProcessingUnit pu = sp.CreateOne(); string fileName = Path.GetFileName(filePath), output = filePath.Substring(0, filePath.LastIndexOf('.') + 1) + "preview.webm", previewSource = ArgList.Get(Arg.PREVIEW_SOURCE) ? GetFullPath(ArgList.Get(Arg.PREVIEW_SOURCE).AsString()) : filePath, previewTiming = ArgList.Get(Arg.PREVIEW).AsString(); // Preview long time = DateTime.Now.ToFileTimeUtc(); string previewWebm = GetFolder(filePath) + "\\preview_" + time.ToString() + ".webm"; // Same scale Ffprober.Result result = Ffprober.Probe(filePath); string scale = result.Scale; scale = scale == null ? string.Empty : $",scale={result.WidthPix + 1}:-1"; scale = $"-filter:v:1 \"trim=end_frame=2{scale}\""; string args = $"-hide_banner -i \"{filePath}\" -ss {previewTiming} -i \"{previewSource}\" -c copy -map 0:v -map 0:a -map 1:v -c:v:1 vp9 -b:v:1 0 -crf 8 -speed 1 {scale} \"{output}\""; ExecuteFFMPEG(args, pu); sp.Destroy(pu); }
static void GeneratePreviewOld(string filePath) { OutputProcessor sp = new SimpleProcessor(); ProcessingUnit pu = sp.CreateOne(); string fileName = Path.GetFileName(filePath), output = filePath.Substring(0, filePath.LastIndexOf('.') + 1) + "preview.webm", previewSource = ArgList.Get(Arg.PREVIEW_SOURCE) ? GetFullPath(ArgList.Get(Arg.PREVIEW_SOURCE).AsString()) : filePath, previewTiming = ArgList.Get(Arg.PREVIEW).AsString(); // Preview long time = DateTime.Now.ToFileTimeUtc(); string previewWebm = GetFolder(filePath) + "\\preview_" + time.ToString() + ".webm"; // Same scale Ffprober.Result result = Ffprober.Probe(filePath); string scale = result.Scale; scale = scale == null ? string.Empty : (",scale=" + scale); scale = $"-vf \"trim=end_frame=2{scale}\""; string args = $"-hide_banner -ss {previewTiming} -i \"{previewSource}\" -c:v vp9 -pix_fmt +yuv420p -b:v 0 -crf 8 -speed 1 -an -sn {scale} \"{previewWebm}\""; ExecuteFFMPEG(args, pu); // Bad muxing fix string videoOnly = $"video_{time}.webm"; args = $"-hide_banner -i \"{filePath}\" -c copy -map 0:v \"{videoOnly}\""; ExecuteFFMPEG(args, pu); // Concat string concatedWebm = $"concat_{time}.webm"; string concatFile = $"concat_{time}.txt"; File.WriteAllText(concatFile, $"file '{previewWebm}'\r\nfile '{videoOnly}'", Encoding.Default); string fps = result.Framerate; string dur = Ffprober.Probe(previewWebm).EndTime; if (dur == null) { dur = "0.042"; } args = $"-hide_banner -f concat -i \"{concatFile}\" -c copy \"{concatedWebm}\""; ExecuteFFMPEG(args, pu); args = $"-hide_banner -y -itsoffset 0.5 -i \"{filePath}\" -i \"{concatedWebm}\" -map 1:v -map 0:a -c copy \"{output}\""; ExecuteFFMPEG(args, pu); // Delete File.Delete(concatFile); File.Delete(previewWebm); File.Delete(concatedWebm); File.Delete(videoOnly); sp.Destroy(pu); }
static void LookupEndTime(string filePath) { if (!ArgList.Get(Arg.END_TIME)) { string value = Ffprober.Probe(filePath).EndTime; if (value != null) { ArgList.Get(Arg.END_TIME).Value = value; } } }
static string Encode(long i, string file, string subs, TimeSpan start, TimeSpan end, int sizeLimit, int crf) { // subs = *.ass if (subs != null && subs.StartsWith("*")) { subs = file.Substring(0, file.LastIndexOf('.')) + subs.Substring(1); } // subs = same if (subs == "same") { subs = file; } bool subsWereCopied = false; string subsFilename = Path.GetFileName(subs); if (ArgList.Get(Arg.FIX_SUBS) && SubStationAlpha.IsAcceptable(subs)) { string subsNew = Path.Combine(Environment.CurrentDirectory, subsFilename); subsFilename += i.ToString() + "_ARIAL.ass"; SubStationAlpha ssa = new SubStationAlpha(subs); ssa.ChangeFontAndSave(subsNew); subs = subsNew; subsWereCopied = true; } string code = null; if (i < 10000) { code = $"{i}_{DateTime.Now.ToFileTimeUtc()}"; } else { code = i.ToString(); } string filePath = GetFolder(file), webmPath = Path.Combine(filePath, $"temp_{code}.webm"), oggPath = Path.Combine(filePath, $"temp_{code}.ogg"), finalPath = Path.Combine(filePath, $"{ArgList.Get(Arg.NAME_PREFIX).AsString()}{code}.webm"); TimeSpan timeLength = end - start; string startString = start.ToString("hh\\:mm\\:ss\\.fff"), timeLengthString = timeLength.ToString("hh\\:mm\\:ss\\.fff"); OutputProcessor sp = new SimpleProcessor(); ProcessingUnit pu = sp.CreateOne(); // Audio settings string mapAudio = ArgList.Get(Arg.MAP_AUDIO) ? $"-map 0:a:{ArgList.Get(Arg.MAP_AUDIO).AsInt()}" : string.Empty; int opusRate = ArgList.Get(Arg.OPUS_RATE).AsInt(); string audioFile = ArgList.Get(Arg.AUDIO_FILE) ? GetFullPath(ArgList.Get(Arg.AUDIO_FILE).AsString()) : file; string otherAudio = ArgList.Get(Arg.OTHER_AUDIO).AsString(); int vorbis = ArgList.Get(Arg.VORBIS) ? ArgList.Get(Arg.VORBIS).AsInt() : -1; string codecParams = vorbis == -1 ? $"-c:a opus -b:a {opusRate}K -vbr on" : $"-c:a libvorbis -q:a {vorbis}"; // Encode audio string args = $"-hide_banner -y -ss {startString} -i \"{audioFile}\" {mapAudio} -ac 2 {codecParams} -vn -sn -t {timeLengthString} {otherAudio} \"{oggPath}\""; // Audio cache Cache.ACKey aKey = new Cache.ACKey(args); bool aCached = Cache.Instance.CreateIfPossible(aKey, oggPath); if (!aCached) { ExecuteFFMPEG(args, pu); Cache.Instance.Save(aKey, oggPath); } // No upscale check string scale = ArgList.Get(Arg.SCALE).AsString(); if (scale != "no" && !ArgList.Get(Arg.UPSCALE)) { string oScale = Ffprober.Probe(file).Scale; string[] scaleSplit = oScale.Split('x'); if (scaleSplit.Length == 2) { int oWidth = int.Parse(scaleSplit[0]); int oHeight = int.Parse(scaleSplit[1]); scaleSplit = scale.Split(':'); int width = int.Parse(scaleSplit[0]); int height = int.Parse(scaleSplit[1]); if (width > oWidth || height > oHeight) { scale = "no"; } } } // VideoFilter const string vfDefault = "-vf \""; StringBuilder vf = new StringBuilder(vfDefault); Action addSubs = () => { if (subs != null) { string format = subs.EndsWith("ass") || subs.EndsWith("ssa") ? "ass='{0}'{1}" : "subtitles='{0}'{1}"; format = string.Format(format, subs.Replace(@"\", @"\\").Replace(":", @"\:"), ArgList.Get(Arg.SUBS_INDEX).Command); format = string.Format(new CultureInfo("en"), "setpts=PTS+{0:0.######}/TB,{1},setpts=PTS-STARTPTS", start.TotalSeconds, format); vf.AppendIfPrev(",").AppendForPrev(format); } }; Action addScale = () => { if (scale != "no") { vf.AppendIfPrev(",").AppendForPrev($"scale={scale}:sws_flags=lanczos"); } }; if (ArgList.Get(Arg.CROP)) { string crop = GetCrop(file, startString, timeLengthString); if (crop != null) { vf.AppendIfPrev(",").AppendForPrev(crop); } pu.Write("CROP: " + crop); } if (ArgList.Get(Arg.CROP_V)) { vf.AppendIfPrev(",").AppendForPrev("crop=" + ArgList.Get(Arg.CROP_V).AsString()); } if (ArgList.Get(Arg.SUBS_FIRST)) { addSubs(); addScale(); } else { addScale(); addSubs(); } if (vf.Length == vfDefault.Length) { vf.Clear(); } else { vf.Append("\" "); } // Encode 2-pass video StringBuilder otherVideo = new StringBuilder(); otherVideo.AppendForPrev(ArgList.Get(Arg.OTHER_VIDEO).AsString()).AppendIfPrev(" "); if (crf < 4 || crf > 63) { crf = ushort.MaxValue; if (ArgList.Get(Arg.CRF_MODE)) { try { crf = ushort.Parse(ArgList.Get(Arg.CRF_MODE).Value); if (crf < 4 || crf > 63) { crf = ushort.MaxValue; } } catch { } } } string threadSettings; if (ArgList.Get(Arg.SINGLE_THREAD)) { threadSettings = "-tile-columns 0 -frame-parallel 0 -threads 1 -speed 1"; } else { threadSettings = $"-tile-columns {Environment.ProcessorCount} -frame-parallel 1 -threads {Environment.ProcessorCount} -speed 1"; } // Pass 1 cache string logPath = Path.Combine(Environment.CurrentDirectory, $"temp_{code}-0.log"); Cache.FPCKey key = new Cache.FPCKey(file, vf.ToString(), startString, timeLengthString); bool cached = Cache.Instance.CreateIfPossible(key, logPath); // If CRF_MODE if (crf != ushort.MaxValue) { if (!cached) { args = $"-hide_banner -y -ss {startString} -i \"{file}\" -c:v vp9 -pix_fmt +yuv420p {vf} -crf {crf} -b:v 0 {threadSettings} -an -t {timeLengthString} -sn -lag-in-frames 25 -pass 1 -auto-alt-ref 1 -passlogfile temp_{code} -f null -y NUL"; ExecuteFFMPEG(args, pu); Cache.Instance.Save(key, logPath); } args = $"-hide_banner -y -ss {startString} -i \"{file}\" -c:v vp9 -pix_fmt +yuv420p {vf} -crf {crf} -b:v 0 {threadSettings} -an -t {timeLengthString} -sn -lag-in-frames 25 -pass 2 -auto-alt-ref 1 -passlogfile temp_{code} \"{webmPath}\""; ExecuteFFMPEG(args, pu); } else { double audioSize = GetSizeKB(oggPath); int bitrate = (int)((sizeLimit - audioSize) * 8 / timeLength.TotalSeconds); string bitrateString = $"-b:v {bitrate}K"; if (!cached) { args = $"-hide_banner -y -ss {startString} -i \"{file}\" -c:v vp9 -pix_fmt +yuv420p {bitrateString} {threadSettings} -an {vf} -t {timeLengthString} -sn {otherVideo} -lag-in-frames 25 -pass 1 -auto-alt-ref 1 -passlogfile temp_{code} -f null -y NUL"; ExecuteFFMPEG(args, pu); Cache.Instance.Save(key, logPath); } args = $"-hide_banner -y -ss {startString} -i \"{file}\" -c:v vp9 -pix_fmt +yuv420p {bitrateString} {threadSettings} -an {vf} -t {timeLengthString} -sn {otherVideo} -lag-in-frames 25 -pass 2 -auto-alt-ref 1 -passlogfile temp_{code} \"{webmPath}\""; ExecuteFFMPEG(args, pu); } // Concat args = $"-hide_banner -y -i \"{webmPath}\" -i \"{oggPath}\" -c copy -metadata title=\"{Path.GetFileNameWithoutExtension(file)} [github.com/CherryPerry/ffmpeg-vp9-wrap]\" \"{finalPath}\""; ExecuteFFMPEG(args, pu); // Delete if (subsWereCopied) { File.Delete(subs); } File.Delete(webmPath); File.Delete(oggPath); File.Delete(logPath); sp.Destroy(pu); return(finalPath); }