示例#1
0
        private string GetMediaInfoAnalysis(MediaAnalyzeInfo mediaInfo)
        {
            if (mediaInfo == null)
            {
                return("");
            }

            StringBuilder sb = new StringBuilder();

            sb.AppendLine($"{nameof(mediaInfo.Filename)}: {mediaInfo.Filename}");
            sb.AppendLine("######################");
            sb.AppendLine($"Resolution: {mediaInfo.VideoInfo?.Width} x {mediaInfo.VideoInfo?.Height}");
            sb.AppendLine($"{nameof(mediaInfo.VideoInfo.Bitrate)}: {mediaInfo.VideoInfo?.Bitrate}");
            sb.AppendLine($"{nameof(mediaInfo.VideoInfo.Length)}: {new TimeSpan(0, 0, 0, 0, (int)mediaInfo.VideoInfo?.Length)}");
            sb.AppendLine($"{nameof(mediaInfo.VideoInfo.CodecID)}: {mediaInfo.VideoInfo?.CodecID}");
            sb.AppendLine($"{nameof(mediaInfo.VideoInfo.FrameRateMode)}: {mediaInfo.VideoInfo?.FrameRateMode}");
            sb.AppendLine("######################");
            sb.AppendLine($"{nameof(mediaInfo.NeedsVideoReencode)}: {mediaInfo.NeedsVideoReencode}");
            sb.AppendLine($"TargetResolution: {mediaInfo.TargetVideoWidth} x {mediaInfo.TargetVideoHeight}");
            sb.AppendLine($"{nameof(mediaInfo.TargetVideoBitrate)}: {mediaInfo.TargetVideoBitrate}");
            if (mediaInfo.AudioInfo != null)
            {
                sb.AppendLine("######################");
                sb.AppendLine($"{nameof(mediaInfo.NeedsAudioReencode)}: {mediaInfo.NeedsAudioReencode}");
                sb.AppendLine($"{nameof(mediaInfo.TargetAudioBitrate)}: {mediaInfo.TargetAudioBitrate}");
                sb.AppendLine($"{nameof(mediaInfo.AudioInfo.Length)}: {new TimeSpan(0, 0, 0, 0, (int)mediaInfo.AudioInfo?.Length)}");
            }
            sb.AppendLine("######################");
            sb.AppendLine($"{nameof(mediaInfo.Size)}: {Math.Round((double)mediaInfo.Size / 1024.0 / 1024.0, 2, MidpointRounding.AwayFromZero)} MB");
            sb.AppendLine($"{nameof(mediaInfo.TargetSize)}: {Math.Round((double)mediaInfo.TargetSize / 1024.0 / 1024.0, 2, MidpointRounding.AwayFromZero)} MB");

            return(sb.ToString());
        }
示例#2
0
        public int Encode(MediaAnalyzeInfo mediaAnalyzeInfo, string x264FileName, Action <string> logAction, Action <string> progressAction, out string outputFileName)
        {
            // Get AviSynth script
            AviSynthScriptService aviSynthScriptService = ServiceFactory.GetService <AviSynthScriptService>();

            string avsScript = aviSynthScriptService.CreateAviSynthVideoScript(mediaAnalyzeInfo);

            outputFileName = $"{mediaAnalyzeInfo.Filename}.reencode.mkv".GetNewFileName();

            X264ProcessRunnerService service = ServiceFactory.GetService <X264ProcessRunnerService>();

            var parameters = service.GetAllParameters(x264FileName);

            // Pass 1
            parameters
            .ResetParameters()
            .IncludeParameterWithValue("infile", avsScript)
            .IncludeParameterWithValue("output", outputFileName)

            .IncludeParameterWithValue("pass", "1")

            .IncludeParameterWithValue("preset", "placebo")
            .IncludeParameterWithValue("bitrate", Math.Ceiling(mediaAnalyzeInfo.TargetVideoBitrateInKbps).ToString("#0"))
            .IncludeParameterWithValue("deblock", "-1:-1")

            .IncludeParameterWithValue("bframes", "3")
            .IncludeParameterWithValue("ref", "3")
            .IncludeParameterWithValue("qpmin", "10")
            .IncludeParameterWithValue("qpmax", "51")

            .IncludeParameterWithValue("vbv-bufsize", "50000")
            .IncludeParameterWithValue("vbv-maxrate", "50000")
            .IncludeParameterWithValue("ratetol", "2.0")
            .IncludeParameterWithValue("rc-lookahead", "40")
            .IncludeParameterWithValue("merange", "16")
            .IncludeParameterWithValue("me", "umh")
            .IncludeParameterWithValue("subme", "6")
            .IncludeParameterWithValue("trellis", "1")

            .IncludeParameterWithNoValue("no-dct-decimate")

            .IncludeParameterWithValue("muxer", "mkv");

            logAction?.Invoke($"Encoding {mediaAnalyzeInfo.Filename} with x264 1st pass...");

            DefaultProcessRunnerService defaultProcessRunnerService = ServiceFactory.GetService <DefaultProcessRunnerService>();

            defaultProcessRunnerService.RunProcess(parameters, new Action <Process, string>((process, line) => progressAction?.Invoke(line)));

            // Pass 2
            parameters.IncludeParameterWithValue("pass", "2");

            logAction?.Invoke($"Encoding {mediaAnalyzeInfo.Filename} with x264 2nd pass...");

            return(defaultProcessRunnerService.RunProcess(parameters, new Action <Process, string>((process, line) => progressAction?.Invoke(line))));
        }
        public int Encode(MediaAnalyzeInfo mediaAnalyzeInfo, IAudioEncoder audioEncoder, IAudioEncoderSettings settings, Action <string> logAction, Action <string> progressAction, out string outputFileName)
        {
            // Get AviSynth script
            AviSynthScriptService aviSynthScriptService = ServiceFactory.GetService <AviSynthScriptService>();

            // Open the AviSynth script
            AviSynthFileService aviSynthFileService = ServiceFactory.GetService <AviSynthFileService>();

            // Get the AviSynth audio script
            string avsScript = aviSynthScriptService.CreateAviSynthAudioScript(mediaAnalyzeInfo);

            // Try to open the Avs Script
            IAviSynthAudioSourceService audioSourceService = null;

            while (true)
            {
                try
                {
                    using (var avsFile = aviSynthFileService.OpenAviSynthScriptFile(avsScript))
                    {
                        break;
                    }
                }
                catch (Exception)
                {
                    // Check if we already tried again
                    if (audioSourceService != null)
                    {
                        throw;
                    }

                    // In case it fails, try to create audio AviSynth script with the DirectShowSource
                    audioSourceService = ServiceFactory.GetService <AviSynthDirectShowAudioSourceService>();

                    avsScript = aviSynthScriptService.CreateAviSynthAudioScript(mediaAnalyzeInfo, audioSourceService: audioSourceService);

                    continue;
                }
            }

            // Determine the output filename
            outputFileName = $"{mediaAnalyzeInfo.Filename}.reencode.{settings.FileExtension}".GetNewFileName();

            // Open the AviSynth Script to generate the timecodes
            using (var avsFile = aviSynthFileService.OpenAviSynthScriptFile(avsScript))
            {
                // Check for audio existence
                if (avsFile.Clip.AudioSamplesCount == 0)
                {
                    throw new ApplicationException("Can't find audio stream!");
                }

                // Calculate Size in Bytes
                long totalSizeInBytes = avsFile.Clip.AudioSamplesCount * avsFile.Clip.AudioBytesPerSample * avsFile.Clip.AudioChannelsCount;

                // Define format type tag
                // 1 for int, 3 for float
                int formatTypeTag = 1;
                if (avsFile.Clip.AudioSampleType == AvsAudioSampleType.FLOAT)
                {
                    formatTypeTag = 3;
                }

                using (var process = new Process())
                {
                    // Create the ProcessStartInfo object
                    ProcessStartInfo info = new ProcessStartInfo
                    {
                        // Command line arguments, to be passed to encoder
                        // {0} means output file name
                        // {1} means samplerate in Hz
                        // {2} means bits per sample
                        // {3} means channel count
                        // {4} means samplecount
                        // {5} means size in bytes
                        // {6} means format (1 int, 3 float)
                        // {7} means target bitrate
                        Arguments = string.Format(
                            audioEncoder.ExecutableArguments,
                            outputFileName,
                            avsFile.Clip.AudioSampleRate,
                            avsFile.Clip.AudioBitsPerSample,
                            avsFile.Clip.AudioChannelsCount,
                            avsFile.Clip.AudioSamplesCount,
                            totalSizeInBytes,
                            formatTypeTag,
                            mediaAnalyzeInfo.TargetAudioBitrate
                            ),

                        FileName = audioEncoder.EncoderFileName,

                        UseShellExecute        = false,
                        RedirectStandardInput  = true,
                        RedirectStandardOutput = true,
                        RedirectStandardError  = true,
                        CreateNoWindow         = true
                    };

                    process.StartInfo = info;

                    Debug.WriteLine(info.Arguments);

                    // Start the process
                    process.Start();

                    // TODO: Revisit that
                    //process.PriorityClass = m_processPriority;

                    // Read the Standard output character by character
                    Task.Run(() => process.ReadStreamPerCharacter(true, new Action <Process, string>((p, str) => Debug.WriteLine(str))));

                    // Read the Standard error character by character
                    Task.Run(() => process.ReadStreamPerCharacter(false, new Action <Process, string>((p, str) => Debug.WriteLine(str))));

                    try
                    {
                        using (Stream processInputStream = process.StandardInput.BaseStream)
                        {
                            // Check if we need to write WAV Header
                            if (audioEncoder.WriteHeader)
                            {
                                logAction?.Invoke($"Audio encoding: {mediaAnalyzeInfo.Filename} Writing header data to encoder's StdIn...");
                                WriteHeader(audioEncoder.HeaderType, processInputStream, avsFile, totalSizeInBytes, settings.ChannelMask, formatTypeTag);
                            }

                            logAction?.Invoke($"Audio encoding: {mediaAnalyzeInfo.Filename} Writing PCM data to encoder's StdIn...");

                            // Calculate the frame buffer total size
                            int frameBufferTotalSize = MAX_SAMPLES_PER_ONCE * avsFile.Clip.AudioChannelsCount * avsFile.Clip.AudioBitsPerSample / 8;

                            // Allocate the frame buffer
                            byte[] frameBuffer = new byte[frameBufferTotalSize];

                            // Get the handle for the frame buffer
                            GCHandle bufferHandle = GCHandle.Alloc(frameBuffer, GCHandleType.Pinned);

                            try
                            {
                                // Set a current frame sample indicator
                                int currentFrameSample = 0;

                                // Start passing the audio frames to the encoder's standard input stream
                                while (currentFrameSample < avsFile.Clip.AudioSamplesCount)
                                {
                                    // Check for unexpected process exit
                                    if (process != null && process.HasExited)
                                    {
                                        throw new ApplicationException($"Unexpected encoder termination with exit code: {process.ExitCode}");
                                    }

                                    // Calculate how many frame samples to read
                                    int framesSamplesToRead = Math.Min((int)(avsFile.Clip.AudioSamplesCount - currentFrameSample), MAX_SAMPLES_PER_ONCE);

                                    int bytesRead = framesSamplesToRead * avsFile.Clip.AudioBytesPerSample * avsFile.Clip.AudioChannelsCount;

                                    // Read the audio frame samples and copy them to the frame buffer
                                    avsFile.ReadAudioSamples(bufferHandle.AddrOfPinnedObject(), currentFrameSample, framesSamplesToRead);

                                    // Calculate the current progress
                                    double progress = ((double)currentFrameSample / (double)avsFile.Clip.AudioSamplesCount) * 100.0;
                                    progressAction?.Invoke($"Progress {progress:#0.00}%");

                                    // Write the frame samples to the encoder's standard input stream
                                    processInputStream.Write(frameBuffer, 0, bytesRead);
                                    processInputStream.Flush();

                                    // Advance the current frame sample indicator
                                    currentFrameSample += framesSamplesToRead;

                                    // Signal the OS to run other threads in our time slice
                                    Thread.Yield();
                                }
                            }
                            finally
                            {
                                // Free the frame buffer handle
                                bufferHandle.Free();
                            }
                        }

                        if (process != null)
                        {
                            logAction?.Invoke($"Audio encoding: {mediaAnalyzeInfo.Filename} Finalizing encoder");

                            // Wait for the process to exit
                            process.WaitForExit();

                            // Debug write the exit code
                            Debug.WriteLine($"Exit code: {process.ExitCode}");
                        }
                    }
                    finally
                    {
                        // Sanity check for non exited process
                        if (process != null && !process.HasExited)
                        {
                            // Kill the process
                            process.Kill();

                            // Wait for the process to exit
                            process.WaitForExit();

                            // Debug write the exit code
                            Debug.WriteLine($"Exit code: {process.ExitCode}");
                        }
                    }

                    // Return the process exit code
                    return(process?.ExitCode ?? 0);
                }
            }
        }
        private void AnalyzeVideoFileInternal(
            MediaAnalyzeFileRequest request,
            Func <double, double> targetFunction,
            MediaAnalyzeActions actions
            )
        {
            // Sanity checks
            if (request.MaxAllowedHeight > request.MaxAllowedWidth)
            {
                throw new ArgumentException(nameof(request), $"{nameof(request.MaxAllowedWidth)} must be always greater than {nameof(request.MaxAllowedHeight)}!");
            }

            actions.SetCurrentFileAction(request.MediaFile);

            using (gMediaInfo mi = new gMediaInfo(request.MediaFile))
            {
                if (mi == null)
                {
                    actions.LogErrorAction($"ERROR! {request.MediaFile}");
                    return;
                }

                MediaAnalyzeInfo result = new MediaAnalyzeInfo
                {
                    Filename           = request.MediaFile,
                    Size               = new FileInfo(request.MediaFile).Length,
                    NeedsVideoReencode = false,
                    NeedsAudioReencode = false
                };

                // Get first General track
                var generalTrack = mi?.GeneralTracks?.FirstOrDefault();
                if (generalTrack == null)
                {
                    actions.LogErrorAction($"ERROR! {request.MediaFile}");
                    return;
                }

                // Get more info
                result.FileExtension       = generalTrack.FileExtension;
                result.FileContainerFormat = generalTrack.Format;

                // Get first video track
                var videoTrack = mi?.VideoTracks?.FirstOrDefault();
                if (videoTrack == null)
                {
                    actions.LogErrorAction($"ERROR! {request.MediaFile}");
                    return;
                }

                // Check if we have valid video track info
                if (int.TryParse(videoTrack.Width, out int width) &&
                    int.TryParse(videoTrack.Height, out int height) &&
                    (
                        int.TryParse(videoTrack.BitRate, out int bitrate) ||
                        (videoTrack.BitRate.Contains("/") && int.TryParse(videoTrack.BitRate.Substring(videoTrack.BitRate.IndexOf("/") + 1), out bitrate)) ||
                        int.TryParse(videoTrack.BitRateNominal, out bitrate) ||
                        (videoTrack.BitRateNominal.Contains("/") && int.TryParse(videoTrack.BitRateNominal.Substring(videoTrack.BitRate.IndexOf("/") + 1), out bitrate))
                    )
                    )
                {
                    MediaAnalyzeVideoInfo videoResult = new MediaAnalyzeVideoInfo();
                    // Check for pixel aspect ration
                    if (videoTrack.PixelAspectRatio.TryParseDecimal(out decimal pixelAspectRatio))
                    {
                        // Check if pixel aspect ration is not equal to 1
                        if (pixelAspectRatio > 1.0m)
                        {
                            // Recalculate width based on the pixel aspect ration
                            width = Convert.ToInt32(Math.Ceiling(width * pixelAspectRatio));
                        }
                    }

                    videoResult.Width   = width;
                    videoResult.Height  = height;
                    videoResult.Bitrate = bitrate;
                    videoResult.CodecID = videoTrack.CodecID;
                    videoResult.Size    = long.TryParse(videoTrack.StreamSize, out long streamSize) ? streamSize : default;
                    videoResult.Length  = long.TryParse(videoTrack.Duration, out long videoDuration) ? videoDuration : default;
                    // Get the video FrameRate Mode
                    videoResult.FrameRateMode = videoTrack.FrameRateMode.ToLower().Equals("vfr") ? VideoFrameRateMode.VFR : VideoFrameRateMode.CFR;

                    videoResult.ChromaSubsampling = videoTrack.ChromaSubsampling;
                    videoResult.ColorSpace        = videoTrack.ColorSpace;

                    videoResult.Rotation = videoTrack.Rotation;

                    result.VideoInfo = videoResult;
                }
                else
                {
                    actions.LogErrorAction($"ERROR! {videoTrack.Width}x{videoTrack.Height} : {videoTrack.BitRate} : {videoTrack.CodecID} : {request.MediaFile}");
                    return;
                }

                // Get audio tracks
                var audioTracks = mi?.AudioTracks;

                // Get audio track
                var audioTrack = audioTracks?.FirstOrDefault();

                // Check if we have valid audio track info
                if (audioTrack != null &&
                    int.TryParse(audioTrack.Channels, out int audioChannels) &&
                    (int.TryParse(audioTrack.BitRate, out int audioBitrate) ||
                     int.TryParse(audioTrack.BitRateNominal, out audioBitrate)))
                {
                    MediaAnalyzeAudioInfo audioResult = new MediaAnalyzeAudioInfo();
                    audioResult.Channels = audioChannels;
                    audioResult.Bitrate  = audioBitrate;
                    audioResult.Codec    = audioTrack.FormatString;
                    audioResult.Size     = long.TryParse(audioTrack.StreamSize, out long audioSize) ? audioSize : default;
                    audioResult.Length   = long.TryParse(audioTrack.Duration, out long audioDuration) ? audioDuration : default;

                    result.AudioInfo = audioResult;
                }

                // Check if we need to re encode the video track
                bool isCandidateForVideoReencode = IsCandidateForVideoReencode(width, height, bitrate, request.MaxAllowedWidth, request.MaxAllowedHeight, request.BitratePercentageThreshold, targetFunction, out int targetBitrate, out int targetWidth, out int targetHeight);

                if (isCandidateForVideoReencode)
                {
                    // We only care for lower target bitrate and bitrate greater than the min allowed bitrate!
                    if (targetBitrate < bitrate &&
                        targetBitrate > request.MinAllowedBitrate)
                    {
                        // Check if the gain percentage is worth the reencode
                        double gainPercentage = Math.Abs(((double)(targetBitrate - bitrate) / (double)bitrate) * 100.0);
                        if (gainPercentage >= request.GainPercentageThreshold)
                        {
                            // Set that Video needs reencode
                            result.NeedsVideoReencode = true;
                            result.TargetVideoBitrate = targetBitrate;
                            result.TargetVideoWidth   = targetWidth;
                            result.TargetVideoHeight  = targetHeight;

                            _reEncodeFiles++;

                            actions.UpdateProgressAction(_reEncodeFiles, _totalFiles);
                            actions.HandleMediaForReencodeAction(result);

                            // Check for no audio, or more than one audio
                            if (audioTracks == null || !audioTracks.Any() || audioTracks.Count() > 1)
                            {
                                // No audio tracks, nothing to do here
                                return;
                            }

                            // Sanity check, we should have an AudioInfo
                            if (result?.AudioInfo == null)
                            {
                                // No audio info found, nothing to do here
                                return;
                            }

                            // Check if we need to re encode the audio track
                            bool isWindowsMedia = result.FileContainerFormat.ToLower().Trim().Equals("windows media");
                            bool isCandidateForAudioReencode = IsCandidateForAudioReencode(isWindowsMedia, result.AudioInfo.Channels, result.AudioInfo.Bitrate, out int targetAudioBitrate);

                            if (isCandidateForAudioReencode)
                            {
                                result.NeedsAudioReencode = true;
                                result.TargetAudioBitrate = targetAudioBitrate;
                            }
                        }
                    }
                }
            }
        }
示例#5
0
        private async Task <string> EncodeMediaInfoAsync(MediaAnalyzeInfo mediaInfo)
        {
            if (mediaInfo == null)
            {
                txtMediaInfo.Clear();
                return("");
            }

            // Sanity check
            if (!mediaInfo.NeedsVideoReencode && !mediaInfo.NeedsAudioReencode)
            {
                return("");
            }

            // Log
            EncodeLog($"Start Encoding: {mediaInfo.Filename}");

            txtMediaInfo.Text = GetMediaInfoAnalysis(mediaInfo);

            string videoOutputFileName = "";

            // Encode video
            if (mediaInfo.NeedsVideoReencode)
            {
                EncodeLog($"Video Encoding: {mediaInfo.Filename}...");

                X264VideoEncoderService videoEncoderService = ServiceFactory.GetService <X264VideoEncoderService>();

                int res = await Task.Run(() => videoEncoderService.Encode(
                                             mediaInfo,
                                             @"E:\Programs\MeGUI\tools\x264\x264.exe",
                                             new Action <string>((log) => EncodeLog(log)),
                                             new Action <string>((progress) => EncodeProgress(progress)),
                                             out videoOutputFileName)
                                         );

                if (res != 0)
                {
                    EncodeLog($"Video Encoder failed for {mediaInfo.Filename}! Exit code : {res}");
                    return("");
                }
            }

            string audioOutputFileName = "";

            // Encode Audio
            if (mediaInfo.NeedsAudioReencode)
            {
                EncodeLog($"Audio Encoding: {mediaInfo.Filename}...");

                AudioEncoderService audioEncoderService = ServiceFactory.GetService <AudioEncoderService>();

                NeroAacAudioEncoder audioEncoder = new NeroAacAudioEncoder(@"E:\Programs\MeGUI\tools\eac3to\neroAacEnc.exe");

                DefaultAudioEncoderSettings audioEncoderSettings = new DefaultAudioEncoderSettings(-1, "m4a");

                int res = await Task.Run(() => audioEncoderService.Encode(
                                             mediaInfo,
                                             audioEncoder,
                                             audioEncoderSettings,
                                             new Action <string>((log) => EncodeLog(log)),
                                             new Action <string>((progress) => EncodeProgress(progress)),
                                             out audioOutputFileName)
                                         );

                if (res != 0)
                {
                    EncodeLog($"Audio Encoder failed for {mediaInfo.Filename}! Exit code : {res}");
                    return("");
                }
            }

            // Mux final video!
            EncodeLog($"Muxing: {mediaInfo.Filename}...");

            DefaultMuxerSettings muxerSettings = new DefaultMuxerSettings(
                videoOutputFileName,
                mediaInfo.NeedsAudioReencode ? audioOutputFileName : mediaInfo.Filename,
                "mkv"
                );

            MkvMergeMuxer muxer = new MkvMergeMuxer(@"C:\Program Files\MKVToolNix\mkvmerge.exe");

            MkvMergeMuxerService mkvMergeMuxerService = new MkvMergeMuxerService();

            string muxedFilename = "";
            await Task.Run(() => mkvMergeMuxerService.Mux(
                               muxer,
                               muxerSettings,
                               new Action <string>((log) => EncodeLog(log)),
                               new Action <string>((progress) => EncodeProgress(progress)),
                               out muxedFilename)
                           );

            return(muxedFilename);
        }
        public string CreateAviSynthVideoScript(MediaAnalyzeInfo mediaInfo, bool overWriteScriptFile = true)
        {
            if (mediaInfo == null)
            {
                throw new ArgumentNullException(nameof(mediaInfo));
            }
            if (string.IsNullOrWhiteSpace(mediaInfo.Filename))
            {
                throw new ArgumentException("No filename was provided!", nameof(mediaInfo.Filename));
            }

            // Get the AVS script filename
            string avsScriptFilename = $"{mediaInfo.Filename}.video.avs";

            // Check if we need to create a new script file
            if (!overWriteScriptFile)
            {
                avsScriptFilename = avsScriptFilename.GetNewFileName();
            }

            StringBuilder avsScriptBuilder = new StringBuilder();

            // Decide on the Source filter
            //=============================
            string fileContainerFormat = mediaInfo.FileContainerFormat.Trim().ToLower();
            // Get the Source Service
            IAviSynthVideoSourceService sourceService = _aviSynthSourceFactory.GetAviSynthSourceService(fileContainerFormat);

            avsScriptBuilder.AppendLine(sourceService.GetAviSynthVideoSource(mediaInfo.Filename, false));

            // Decide what to do with VFR
            // Note: Windows Media files may report CFR frame rate mode, but in reality they are VFR inside
            //=============================
            if (fileContainerFormat.Equals("windows media") || mediaInfo.VideoInfo.FrameRateMode == VideoFrameRateMode.VFR)
            {
                // Create timecodes file
                var timecodesFileName = _timeCodesProviderService.GetTimecodesFileName(mediaInfo.Filename);

                // Read timecodes file
                var videoFrameList = _timeCodesParserService.ParseTimeCodes(timecodesFileName);

                // Create the VFR to CFR AviSynth script
                var timeCodesAviSynthScript = _aviSynthVfrToCfrConversionService.GetConvertVfrToCfrScript(videoFrameList, new List <VideoFrameSection>());

                // Append it to the main script
                avsScriptBuilder.AppendLine(timeCodesAviSynthScript);
            }

            // Decide if we need resize
            //=============================
            if (mediaInfo.TargetVideoWidth != mediaInfo.VideoInfo.Width ||
                mediaInfo.TargetVideoHeight != mediaInfo.VideoInfo.Height)
            {
                avsScriptBuilder.AppendLine($"Lanczos4resize({mediaInfo.TargetVideoWidth}, {mediaInfo.TargetVideoHeight})");
            }

            // Decide if we need rotation
            //=============================
            if (!string.IsNullOrWhiteSpace(mediaInfo.VideoInfo.Rotation))
            {
                // check if it's a valid rotation number (degrees)
                if (mediaInfo.VideoInfo.Rotation.PrepareStringForNumericParse().TryParseDecimal(out decimal rotation))
                {
                    // Check if the degrees are valid
                    if (rotation % 90 == 0)
                    {
                        // Check if it's a negative number
                        bool isNegative = mediaInfo.VideoInfo.Rotation.Contains("-");

                        // Determine turn command
                        string turnCommand = isNegative ? "turnLeft()" : "turnRight()";

                        // Get number of 90 degrees rotations
                        int numberOfRotations = Convert.ToInt32(rotation / 90.0m);

                        for (int i = 0; i < numberOfRotations; i++)
                        {
                            avsScriptBuilder.AppendLine(turnCommand);
                        }
                    }
                }
            }

            // Decide if we need colorspace conversion
            //=============================
            if (!mediaInfo.VideoInfo.ColorSpace.Trim().ToLower().Equals("yv12"))
            {
                avsScriptBuilder.AppendLine("ConvertToYV12()");
            }

            // Write the file
            using (StreamWriter sw = new StreamWriter(avsScriptFilename, false, Encoding.GetEncoding(1253)))
            {
                sw.Write(avsScriptBuilder.ToString());
            }

            return(avsScriptFilename);
        }
        public string CreateAviSynthAudioScript(MediaAnalyzeInfo mediaInfo, bool overWriteScriptFile = true, IAviSynthAudioSourceService audioSourceService = null)
        {
            if (mediaInfo == null)
            {
                throw new ArgumentNullException(nameof(mediaInfo));
            }
            if (string.IsNullOrWhiteSpace(mediaInfo.Filename))
            {
                throw new ArgumentException("No filename was provided!", nameof(mediaInfo.Filename));
            }

            // Get the AVS script filename
            string avsScriptFilename = $"{mediaInfo.Filename}.audio.avs";

            // Check if we need to create a new script file
            if (!overWriteScriptFile)
            {
                avsScriptFilename = avsScriptFilename.GetNewFileName();
            }

            StringBuilder avsScriptBuilder = new StringBuilder();

            IAviSynthAudioSourceService sourceService;

            // Check if no audio source service was provided
            if (audioSourceService == null)
            {
                // Use AviSynthLWLibavAudioSourceService Source filter to get the audio
                //=============================
                // Currently it has a bug in the latest versions that do not produce clips with correct audio length
                // Last good working version: r920-20161216
                // Use FFMS2 till then which seems to produce correct results
                // UPDATE: LSMASH 20200322 fixes the audio issues, switch to AviSynthLWLibavAudioSourceService
                // UPDATE: LSMASH still seems to have problems with some Windows Meadia Audio, so use FFMS2 for those files
                // UPDATE: FFMS2 also struggles with those particular audio formats, switch to DirectShowSource
                string fileContainerFormat = mediaInfo.FileContainerFormat.Trim().ToLower();
                if (fileContainerFormat.Equals("windows media"))
                {
                    //sourceService = ServiceFactory.GetService<AviSynthFfms2AudioSourceService>();
                    sourceService = ServiceFactory.GetService <AviSynthDirectShowAudioSourceService>();
                }
                else
                {
                    sourceService = ServiceFactory.GetService <AviSynthLWLibavAudioSourceService>();
                }
            }
            else
            {
                sourceService = audioSourceService;
            }

            avsScriptBuilder.AppendLine(sourceService.GetAviSynthAudioSource(mediaInfo.Filename, -1, false));

            // Write the file
            using (StreamWriter sw = new StreamWriter(avsScriptFilename, false, Encoding.GetEncoding(1253)))
            {
                sw.Write(avsScriptBuilder.ToString());
            }

            return(avsScriptFilename);
        }