public void Probe() { string args = $"\"{FullPath}\" -of xml -show_streams -show_format"; using (FFprobeProcess prober = new FFprobeProcess(args)) { StreamInfo = prober.Probe(); using (StringReader stringReader = new StringReader(StreamInfo)) { XPathDocument doc = new XPathDocument(stringReader); XPathNavigator format = doc.CreateNavigator().SelectSingleNode("//ffprobe/format"); if (format != null) { // format Format = format.GetAttribute("format_long_name", ""); // file size double.TryParse(format.GetAttribute("size", ""), out double fileSize); FileSize = fileSize; // duration double.TryParse(format.GetAttribute("duration", ""), out double duration); Duration = TimeSpan.FromSeconds(duration); // bit rate double.TryParse(format.GetAttribute("bit_rate", ""), out double bitRate); BitRate = Convert.ToInt32(Math.Round(bitRate / 1000.0, 0)); Tags = new FileTags(); if (!format.IsEmptyElement) { XPathNodeIterator formatTags = format.Select(".//tag"); foreach (XPathNavigator formatTag in formatTags) { string tagKey = formatTag.GetAttribute("key", ""); string tagValue = formatTag.GetAttribute("value", ""); byte[] valueBytes = Encoding.Default.GetBytes(tagValue); if (tagKey == "title") { Tags.Title = Encoding.UTF8.GetString(valueBytes); } if (tagKey == "artist") { Tags.Author = Encoding.UTF8.GetString(valueBytes); } if (tagKey == "copyright") { Tags.Copyright = Encoding.UTF8.GetString(valueBytes); } if (tagKey == "comment") { Tags.Comment = Encoding.UTF8.GetString(valueBytes); } if (tagKey == "creation_time") { Tags.CreationTime = Encoding.UTF8.GetString(valueBytes); } } } } else { throw new InvalidDataException("Не удалось определить формат видео."); // Can't determine video format } VideoStreams = new List <VideoStream>(); AudioStreams = new List <AudioStream>(); XPathNodeIterator streams = doc.CreateNavigator().Select("//ffprobe/streams/stream"); foreach (XPathNavigator stream in streams) { int.TryParse(stream.GetAttribute("index", ""), out int streamIndex); string codecType = stream.GetAttribute("codec_type", ""); if (codecType == "audio") { AudioStream audioStream = new AudioStream { Index = streamIndex, CodecName = stream.GetAttribute("codec_name", ""), CodecLongName = stream.GetAttribute("codec_long_name", ""), SampleRate = stream.GetAttribute("sample_rate", "") }; // bit rate double.TryParse(stream.GetAttribute("bit_rate", ""), out double streamBitRate); audioStream.BitRate = Convert.ToInt32(Math.Round(streamBitRate / 1000.0, 0)); // channels int.TryParse(stream.GetAttribute("channels", ""), out int channels); audioStream.Channels = channels; audioStream.ChannelLayout = stream.GetAttribute("channel_layout", ""); if (!stream.IsEmptyElement) { // language stream.MoveTo(stream.SelectSingleNode(".//tag[@key='language']")); audioStream.Language = stream.GetAttribute("value", ""); } AudioStreams.Add(audioStream); } else if (codecType == "video") { string frameRate = stream.GetAttribute("r_frame_rate", ""); // skip image cover if (frameRate == "90000/1") { continue; } // original width and height int.TryParse(stream.GetAttribute("width", ""), out int width); int.TryParse(stream.GetAttribute("height", ""), out int height); // wrong size if (width == 0 || height == 0) { continue; } VideoStream videoStream = new VideoStream { Index = streamIndex, CodecName = stream.GetAttribute("codec_name", ""), CodecLongName = stream.GetAttribute("codec_long_name", ""), OriginalSize = new PictureSize(), PictureSize = new PictureSize(), }; videoStream.OriginalSize.Width = width; videoStream.OriginalSize.Height = height; // frame rate string avgFrameRate = stream.GetAttribute("avg_frame_rate", ""); string[] frameRateParts = !string.IsNullOrWhiteSpace(avgFrameRate) ? avgFrameRate.Split('/') : frameRate.Split('/'); videoStream.FrameRate = frameRateParts.Length != 2 ? 0.0 : Math.Round(double.Parse(frameRateParts[0]) / double.Parse(frameRateParts[1]), 3); // frame count int.TryParse(stream.GetAttribute("nb_frames", ""), out int frameCount); if (frameCount == 0) { frameCount = (int)Math.Round(Duration.TotalMilliseconds * videoStream.FrameRate / 1000.0, 0); } videoStream.FrameCount = frameCount; // field order string fieldOrder = stream.GetAttribute("field_order", ""); if (string.IsNullOrWhiteSpace(fieldOrder)) { fieldOrder = "progressive"; } videoStream.FieldOrder = fieldOrder; // picture width and height string[] sarParts = stream.GetAttribute("sample_aspect_ratio", "").Split(':'); if (sarParts.Length == 2 && sarParts[0] != "0" && sarParts[1] != "0" && (sarParts[0] != "1" || sarParts[1] != "1")) { string[] darParts = stream.GetAttribute("display_aspect_ratio", "").Split(':'); double sarX = double.Parse(sarParts[0]); double sarY = double.Parse(sarParts[1]); double darX = double.Parse(darParts[0]); double darY = double.Parse(darParts[1]); if (darX < darY) { height = (int)(Convert.ToDouble(height) / (sarX / sarY)); } else { width = (int)(Convert.ToDouble(width) * (sarX / sarY)); } videoStream.UsingDAR = true; } videoStream.PictureSize.Width = width; videoStream.PictureSize.Height = height; if (!stream.IsEmptyElement) { XPathNodeIterator videoStreamTags = stream.Select(".//tag"); foreach (XPathNavigator videoStreamTag in videoStreamTags) { string tagKey = videoStreamTag.GetAttribute("key", ""); string tagValue = videoStreamTag.GetAttribute("value", ""); // language if (tagKey == "language") { videoStream.Language = tagValue; } // correct picture size if video is rotated if (tagKey == "rotate" && (tagValue == "90" || tagValue == "270")) { videoStream.PictureSize.Width = height; videoStream.PictureSize.Height = width; int ow = videoStream.OriginalSize.Width; int oh = videoStream.OriginalSize.Height; videoStream.OriginalSize.Width = oh; videoStream.OriginalSize.Height = ow; } } } VideoStreams.Add(videoStream); } } if (VideoStreams.Count == 0) { throw new InvalidDataException("Не удалось найти видеопоток в файле."); // Can't find any video stream } } } }
private void ConvertVideo() { if (inputFile == null) { throw new Exception("Видео-файл не определен!"); } string input = Path.GetFullPath(inputFile.FullPath); string output = !string.IsNullOrWhiteSpace(textBoxOut.Text) ? Path.GetFullPath(textBoxOut.Text) : ""; ValidateOutputFile(output); if (input.Equals(output, StringComparison.OrdinalIgnoreCase)) { throw new Exception("Пути не должны совпадать!"); } VideoStream vStream = inputFile.VideoStreams[0]; string outputDir = Path.GetDirectoryName(output); // try to create out folder if (!string.IsNullOrWhiteSpace(outputDir) && !Directory.Exists(outputDir)) { try { Directory.CreateDirectory(outputDir); } catch (Exception ex) { throw new Exception("Ошибка создания выходной папки!" + Environment.NewLine + ex.Message); } } if (File.Exists(output)) { string question = Path.GetFileName(output) + " уже существует." + Environment.NewLine + "Хотите заменить его?"; if (MessageBox.Show(question, "Подтвердить перезапись", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No) { return; } } string subtitlesPath = textBoxSubtitlesPath.Text; if (!string.IsNullOrWhiteSpace(subtitlesPath) && !File.Exists(subtitlesPath)) { throw new Exception("Файл субтитров не найден!"); } int audioStreamIndex = ((ComboBoxIntItem)comboBoxAudioStreams.SelectedItem).Value; bool twoPass = !VideoConfig.UseCRF; List <string> videoArgs = new List <string>(); List <string> audioArgs = new List <string>(); List <string> specialCrfArgs = new List <string>(); List <string> specialArgsPass1 = new List <string>(); List <string> specialArgsPass2 = new List <string>(); // Video args videoArgs.Add($"-map 0:{inputFile.VideoStreams[0].Index}"); string x264Params = "sar=1/1"; string x265Params = "sar=1:range=limited"; if (checkBoxConvertVideo.Checked) { if (VideoConfig.UseCRF) { videoArgs.Add($"-c:v {VideoConfig.Encoder} -crf {VideoConfig.CRF}"); } else { videoArgs.Add($"-c:v {VideoConfig.Encoder} -b:v {VideoConfig.Bitrate}k"); } // https://trac.ffmpeg.org/wiki/Encode/H.264 if (VideoConfig.Encoder == "libx264") { // must be configurable string videoProfile = "high"; string videoLevel = ""; videoArgs.Add("-aq-mode autovariance-biased -fast-pskip 0 -mbtree 0 -pix_fmt yuv420p"); if (twoPass) { x264Params += ":no-dct-decimate=1"; } double finalFrameRate = CalcFinalFrameRate(); if (finalFrameRate == 0) { finalFrameRate = vStream.FrameRate; } // force level 4.1, max bitrate for high 4.1 - 62500, use max avg bitrate 50000 if (VideoConfig.UseCRF || VideoConfig.Bitrate <= 50000) { if (PictureConfig.OutputSize.Width <= 1920 && PictureConfig.OutputSize.Height <= 1080 && finalFrameRate <= 30.0) { videoLevel = " -level 4.1"; } } videoArgs.Add($"-preset:v {VideoConfig.Preset} -profile:v {videoProfile}{videoLevel}"); } // https://trac.ffmpeg.org/wiki/Encode/H.265 if (VideoConfig.Encoder == "libx265") { videoArgs.Add("-pix_fmt yuv420p"); // slow,slower,veryslow,placebo if (checkBoxCustomX265.Checked) { x265Params += ":rect=0:amp=0:limit-modes=0:rskip=1:tu-intra-depth=1:tu-inter-depth=1:limit-tu=0:strong-intra-smoothing=0:sao=0"; //x265Params += ":colorprim=1:transfer=1:colormatrix"; if (VideoConfig.Preset == "slower" || VideoConfig.Preset == "veryslow" || VideoConfig.Preset == "placebo") { x265Params += ":max-merge=3:limit-refs=3"; } } videoArgs.Add($"-preset:v {VideoConfig.Preset}"); } // Frame rate if (!string.IsNullOrWhiteSpace(VideoConfig.FrameRate)) { videoArgs.Add($"-r {VideoConfig.FrameRate}"); } // Video filters string vFilters = GetVideoFilters(subtitlesPath); // Add filters to video args if (vFilters.Length > 0) { videoArgs.Add("-vf \"" + vFilters + "\""); } } else { twoPass = false; // !!! videoArgs.Add($"-c:v copy"); } // Audio args if (audioStreamIndex == -1) { audioArgs.Add("-an"); } else { audioArgs.Add($"-map 0:{audioStreamIndex}"); if (checkBoxConvertAudio.Checked) { audioArgs.Add($"-c:a {AudioConfig.Encoder} -b:a {AudioConfig.Bitrate}k {AudioConfig.AdditionalArguments}"); // resampling if (AudioConfig.SampleRate > 0) { audioArgs.Add($"-ar {AudioConfig.SampleRate}"); } // channels if (AudioConfig.Channels > 0) { audioArgs.Add($"-ac {AudioConfig.Channels}"); } } else { audioArgs.Add($"-c:a copy"); } } // Meta List <string> metadataArgs = new List <string>(); metadataArgs.Add("-map_chapters -1 -map_metadata -1"); if (!string.IsNullOrWhiteSpace(TagsConfig.Title)) { metadataArgs.Add($"-metadata title=\"{TagsConfig.Title.Replace("\"", "\\\"")}\""); } if (!string.IsNullOrWhiteSpace(TagsConfig.Author)) { metadataArgs.Add($"-metadata artist=\"{TagsConfig.Author.Replace("\"", "\\\"")}\""); } if (!string.IsNullOrWhiteSpace(TagsConfig.Copyright)) { metadataArgs.Add($"-metadata copyright=\"{TagsConfig.Copyright.Replace("\"", "\\\"")}\""); } if (!string.IsNullOrWhiteSpace(TagsConfig.Comment)) { metadataArgs.Add($"-metadata comment=\"{TagsConfig.Comment.Replace("\"", "\\\"")}\""); } if (!string.IsNullOrWhiteSpace(TagsConfig.CreationTime)) { metadataArgs.Add($"-metadata creation_time=\"{TagsConfig.CreationTime.Replace("\"", "\\\"")}\""); } else { metadataArgs.Add($"-metadata creation_time=\"{DateTime.Now.ToString("o")}\""); } // mp4 if (checkBoxWebOptimized.Checked && (fileType == FormatMP4 || fileType == FormatMOV)) { specialArgsPass2.Add("-movflags +faststart"); specialCrfArgs.Add("-movflags +faststart"); } // Convert string[] arguments; //string videoParams = $"-x264-params \"{x264Params}\""; //string videoParams = $"-x264-params \"{x265Params}\""; //slow-firstpass string ffmpegFormat; if (fileType == FormatMOV) { ffmpegFormat = "mov"; } else if (fileType == FormatTS) { ffmpegFormat = "mpegts"; } else if (fileType == FormatMKV) { ffmpegFormat = "matroska"; } else { ffmpegFormat = "mp4"; } if (twoPass) { string passLogFile = GetTempLogFile(); string twoPassTpl; string x26xParams1; string x26xParams2; if (VideoConfig.Encoder == "libx265") { twoPassTpl = "-passlogfile \"{6}\" {0} -x265-params \"pass={5}:{7}\" {1} {2} {3} -f {4}"; x26xParams1 = x265Params + ":slow-firstpass=0"; x26xParams2 = x265Params; } else { twoPassTpl = "-pass {5} -passlogfile \"{6}\" {0} -x264-params \"{7}\" {1} {2} {3} -f {4}"; x26xParams1 = x264Params; x26xParams2 = x264Params; } arguments = new string[2]; arguments[0] = string.Format( twoPassTpl, string.Join(" ", videoArgs), "-an", string.Join(" ", specialArgsPass1), "", ffmpegFormat, 1, passLogFile, x26xParams1 ); arguments[1] = string.Format( twoPassTpl, string.Join(" ", videoArgs), string.Join(" ", audioArgs), string.Join(" ", specialArgsPass2), string.Join(" ", metadataArgs), ffmpegFormat, 2, passLogFile, x26xParams2 ); } else { string onePassTpl; string x26xParams; if (VideoConfig.Encoder == "libx265") { onePassTpl = "{0} -x265-params \"{5}\" {1} {2} {3} -f {4}"; x26xParams = x265Params; } else { onePassTpl = "{0} -x264-params \"{5}\" {1} {2} {3} -f {4}"; x26xParams = x264Params; } arguments = new string[1]; arguments[0] = string.Format( onePassTpl, string.Join(" ", videoArgs), string.Join(" ", audioArgs), string.Join(" ", specialCrfArgs), string.Join(" ", metadataArgs), ffmpegFormat, x26xParams ); } new ConverterForm(input, output, arguments, inputFile.Duration.TotalSeconds).ShowDialog(this); }
private void SetOutputInfo() { if (inputFile == null) { labelOutputInfoTitle.Visible = false; labelOutputInfo.Text = ""; return; } StringBuilder info = new StringBuilder(); // Video if (checkBoxConvertVideo.Checked) { if (VideoConfig.CodecList.ContainsKey(VideoConfig.Codec)) { info.Append($"{VideoConfig.CodecList[VideoConfig.Codec]}"); } else { info.Append($"{VideoConfig.Codec}"); } /*if (PictureConfig.Padding.X > 0 || PictureConfig.Padding.Y > 0) * { * PictureSize fullSize = new PictureSize(); * fullSize.Width = PictureConfig.OutputSize.Width + PictureConfig.Padding.X + PictureConfig.Padding.X; * fullSize.Height = PictureConfig.OutputSize.Height + PictureConfig.Padding.Y + PictureConfig.Padding.Y; * info.Append($", {fullSize.ToString()} ({PictureConfig.OutputSize.ToString()})"); * } * else * { * info.Append($", {PictureConfig.OutputSize.ToString()}"); * }*/ if (PictureConfig.Rotate == 90 || PictureConfig.Rotate == 270) { PictureSize size = new PictureSize { Width = PictureConfig.OutputSize.Height, Height = PictureConfig.OutputSize.Width }; info.Append($", {size.ToString()}"); } else { info.Append($", {PictureConfig.OutputSize.ToString()}"); } if (PictureConfig.Deinterlace) { info.Append(", деинт."); } if (PictureConfig.Rotate > 0) { if (PictureConfig.RotateList.ContainsKey(PictureConfig.Rotate)) { info.Append($", {PictureConfig.RotateList[PictureConfig.Rotate]}"); } else { info.Append($", {PictureConfig.Rotate}°"); } } if (PictureConfig.Flip) { info.Append($", отразить"); } if (PictureConfig.ColorFilter != "none") { info.Append($", {PictureConfig.ColorFilterList[PictureConfig.ColorFilter].ToLower()}"); } if (radioButtonCRF.Checked) { info.Append($", CRF {labelCRF.Text}"); } else { info.Append($", {numericUpDownBitrate.Value} кбит/с"); } double finalFrameRate = CalcFinalFrameRate(); if (finalFrameRate > 0) { info.Append($", {finalFrameRate} fps"); } } else { VideoStream vStream = inputFile.VideoStreams[0]; if (VideoConfig.CodecList.ContainsKey(vStream.CodecName)) { info.Append($"{VideoConfig.CodecList[vStream.CodecName]}"); } else { info.Append($"{vStream.CodecName}"); } info.Append($", без изменения"); } // Audio info.Append($"{Environment.NewLine}"); if (comboBoxAudioStreams.SelectedIndex < 1) { info.Append("нет"); } else { int index = ((ComboBoxIntItem)comboBoxAudioStreams.SelectedItem).Value; if (AudioConfig.CodecList.ContainsKey(AudioConfig.Codec)) { info.Append($"{AudioConfig.CodecList[AudioConfig.Codec]}"); } else { info.Append($"{AudioConfig.Codec}"); } int idx = 1; foreach (AudioStream aStream in inputFile.AudioStreams) { if (aStream.Index == index) { info.Append($", #{idx}"); break; } idx++; } if (!checkBoxConvertAudio.Checked) { info.Append($", без изменения"); } else { info.Append($", {AudioConfig.Bitrate} кбит/с"); if (AudioConfig.SampleRate > 0) { info.Append($", {AudioConfig.SampleRate} Гц"); } if (AudioConfig.Channels > 0) { if (AudioConfig.ChannelsList.ContainsKey(AudioConfig.Channels)) { info.Append($", каналы: {AudioConfig.ChannelsList[AudioConfig.Channels]}"); } } } } // Show labelOutputInfoTitle.Visible = true; labelOutputInfo.Text = info.ToString(); }
private void SetInputFile(string path) { try { ValidateInputFile(path); inputFile = new InputFile(path); inputFile.Probe(); } catch (Exception ex) { inputFile = null; textBoxIn.Text = "Файл не выбран"; textBoxOut.Text = ""; buttonGo.Enabled = false; buttonPreview.Enabled = false; buttonShowInfo.Enabled = false; buttonOpenInputFile.Enabled = false; ToggleTabs(); // reset PictureConfig.Reset(); UpdateCropSizeInfo(); CalcFileSize(); SetOutputInfo(); ClearTags(); ResetCrop(); sizeChanged = false; textBoxSubtitlesPath.Text = ""; Text = formTitle; MessageBox.Show(ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Hand); return; } #if DEBUG Console.WriteLine(inputFile.StreamInfo); #endif textBoxIn.Text = path; Properties.Settings.Default.InPath = Path.GetDirectoryName(path); ToggleTabs(); Text = Path.GetFileName(path) + " - " + formTitle; VideoStream vStream = inputFile.VideoStreams[0]; PictureConfig.Reset(); PictureConfig.InputOriginalSize = vStream.OriginalSize; PictureConfig.InputDisplaySize = vStream.PictureSize; numericUpDownHeight.Value = Math.Max(PictureConfig.CropSize.Height, PictureConfig.MinHeight); numericUpDownWidth.Value = Math.Max(PictureConfig.CropSize.Width, PictureConfig.MinWidth); // triggered UpdateHeight() if keeping ar numericCropTop.Maximum = PictureConfig.InputOriginalSize.Height; numericCropBottom.Maximum = PictureConfig.InputOriginalSize.Height; numericCropLeft.Maximum = PictureConfig.InputOriginalSize.Width; numericCropRight.Maximum = PictureConfig.InputOriginalSize.Width; ResetCrop(); // set original aspect ratio FillComboBoxAspectRatio(true); sizeChanged = false; // reset after wet WxH in numericUpDown // if need deinterlace PictureConfig.Deinterlace = checkBoxDeinterlace.Checked = vStream.FieldOrder != "progressive"; comboBoxFieldOrder.SelectedIndex = 0; // TODO: get from Picture ManageCheckPanel(checkBoxDeinterlace, panelDeinterlace); ResetRotateAndFlip(); comboBoxFrameRate.SelectedIndex = 0; CheckVideoMustConvert(); // fill audio streams FillAudioStreams(); // show info SetInputInfo(); CalcFileSize(); buttonGo.Enabled = true; buttonPreview.Enabled = true; buttonShowInfo.Enabled = true; buttonOpenInputFile.Enabled = true; UpdateCropSizeInfo(); SetOutputInfo(); SetTagsFromInputFile(); textBoxSubtitlesPath.Text = ""; try { string outDir = ""; string withoutExtension = Path.GetFileNameWithoutExtension(path); if (checkBoxKeepOutPath.Checked) { if (!string.IsNullOrWhiteSpace(textBoxOut.Text)) { outDir = Path.GetDirectoryName(textBoxOut.Text); } else if (!string.IsNullOrWhiteSpace(Properties.Settings.Default.OutPath)) { outDir = Properties.Settings.Default.OutPath; } } if (string.IsNullOrWhiteSpace(outDir) || !Directory.Exists(outDir)) { outDir = Path.GetDirectoryName(path); } Properties.Settings.Default.OutPath = outDir; string outPath = Path.Combine(outDir, withoutExtension + "." + fileType); int num = 2; while (File.Exists(outPath)) { outPath = Path.Combine(outDir, withoutExtension + " (" + num.ToString() + ")." + fileType); num++; // if so many duplicates set name manually if (num > 100) { break; } } textBoxOut.Text = outPath; } catch (Exception) { } }