public void AnalyzeVideoFile( MediaAnalyzeFileRequest request, CurveFittingSettings curveFittingSettings, 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)}!"); } // Get the Data for calculating the CurveFittingFunction var data = curveFittingSettings.Data.ToDictionary( k => (double)k.Width * k.Height, v => (double)v.Bitrate / (double)(v.Width * v.Height) ); // Get the CurveFitting Function var targetFunction = new CurveFittingFactory() .GetCurveFittingService(curveFittingSettings.CurveFittingType) .GetCurveFittingFunction(data); // Call the internal recursive method AnalyzeVideoFileInternal(request, targetFunction, actions); }
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; } } } } } }