protected override bool ConvertWithTool() { if (ParameterValue("-vcodec") == "libx264") { if (String.IsNullOrWhiteSpace(ParameterSubValue("-x264opts", "threads"))) // Auto threads { ParameterSubValueReplaceOrInsert("-x264opts", "threads", "=auto"); } } if (_2Pass == true) { // 1st Pass string baseParam = _cmdParams; // save the base command params { // Optimizing the parameters // The next set of parameters need to come before -pass n, so cut off everything from -pass n to end and attach it back after string endParams = _cmdParams.Substring(_cmdParams.IndexOf(" -pass n")); // save it _cmdParams = _cmdParams.Remove(_cmdParams.IndexOf(" -pass n")); // Remove the end // We replicate -Turbo option from Mencoder to speed up the 1st pass, i.e. reduce subq and framerefs to 1, partitions none // Other encoders use ffmpeg options which are replaced below, only libx264 uses it's own options // http://forum.doom9.org/archive/index.php/t-134225.html // http://forum.doom9.org/showthread.php?t=141202 // http://forum.doom9.org/archive/index.php/t-143668.html // Delete these values if they exist ParameterSubValueDelete("-x264opts", "subq"); // Will be replaced with generic afterwards ParameterSubValueDelete("-x264opts", "ref"); // Will be replaced with generic afterwards ParameterSubValueDelete("-x264opts", "trellis"); // Will be replaced with generic afterwards ParameterSubValueDelete("-x264opts", "b-adapt"); // Will be replaced with generic afterwards ParameterSubValueDelete("-x264opts", "me"); // Will be replaced with generic afterwards ParameterSubValueReplace("-x264opts", "partitions", "=none"); ParameterSubValueReplace("-x264opts", "mixed-refs", "=0"); ParameterSubValueReplace("-x264opts", "no-fast-pskip", "=0"); ParameterSubValueReplace("-x264opts", "weightb", "=0"); ParameterSubValueDelete("-x264opts", "8x8dct"); // Replace these generic values that will be passed to all encoders ParameterValueReplaceOrInsert("-subq", "1"); ParameterValueReplaceOrInsert("-refs", "1"); ParameterValueReplaceOrInsert("-trellis", "0"); ParameterValueReplaceOrInsert("-b_strategy", "1"); // b-adapt ParameterValueReplaceOrInsert("-me_method", "dia"); // Now add it back _cmdParams += endParams; } // Speed up the 1st pass, no file required //_cmdParams = _cmdParams.Replace(Util.FilePaths.FixSpaces(_convertedFile), "-f rawvideo NUL"); // DO NOT DO -f rawvideo NUL, SEE FFMPEG TICKET #3109, 1st PASS MUXER SHOULD MATCH 2nd PASS MUXER OTHERWISE FFMPEG WILL CRASH, PLUS THIS DOES NOT REALLY IMPROVE PERFORMANCEWe don't need file on 1st pass, will compensate for output file in next file with -f rawvideo NULL (empty and fast) _cmdParams = _cmdParams.Replace("-pass n", "-pass 1"); // This is 1st pass _jobStatus.CurrentAction = Localise.GetPhrase("Converting video file - Pass 1"); if (!FFmpeg.FFMpegExecuteAndHandleErrors(_cmdParams, _jobStatus, _jobLog, Util.FilePaths.FixSpaces(_convertedFile), false)) // Don't check output file size since 1st pass size may not be reliable { _jobLog.WriteEntry(this, Localise.GetPhrase("FFMpeg Pass 1 conversion failed"), Log.LogEntryType.Error); _jobStatus.ErrorMsg = "FFMpeg Pass 1 conversion failed"; return(false); } // Reset the command line for the 2nd pass _cmdParams = baseParam; _cmdParams = _cmdParams.Replace("-pass n", "-pass 2"); // This is 2nd pass _jobStatus.CurrentAction = Localise.GetPhrase("Converting video file - Pass 2"); if (!FFmpeg.FFMpegExecuteAndHandleErrors(_cmdParams, _jobStatus, _jobLog, Util.FilePaths.FixSpaces(_convertedFile))) { _jobLog.WriteEntry(this, Localise.GetPhrase("FFMpeg Pass 2 conversion failed"), Log.LogEntryType.Error); _jobStatus.ErrorMsg = "FFMpeg Pass 2 conversion failed"; return(false); } } else { _jobStatus.CurrentAction = Localise.GetPhrase("Converting video file"); if (!FFmpeg.FFMpegExecuteAndHandleErrors(_cmdParams, _jobStatus, _jobLog, Util.FilePaths.FixSpaces(_convertedFile))) { _jobLog.WriteEntry(this, Localise.GetPhrase("FFMpeg conversion failed"), Log.LogEntryType.Error); _jobStatus.ErrorMsg = "FFMpeg conversion failed"; return(false); } } return(true); }
public bool Trim(string sourceVideo, string workingPath, int startTrim, int endTrim) { _jobLog.WriteEntry(this, "Start Trim : " + startTrim.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug); _jobLog.WriteEntry(this, "Stop Trim : " + endTrim.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug); _jobLog.WriteEntry(this, "Video File : " + sourceVideo, Log.LogEntryType.Debug); if ((startTrim == 0) && (endTrim == 0)) { _trimmedVideo = sourceVideo; // It's the same file return(true); // nothing to do here } string tempFile = Path.Combine(workingPath, Path.GetFileNameWithoutExtension(sourceVideo) + "-temp" + Util.FilePaths.CleanExt(sourceVideo)); // Get the length of the video, needed to calculate end point float Duration; Duration = VideoParams.VideoDuration(sourceVideo); FFmpegMediaInfo ffmpegStreamInfo = new FFmpegMediaInfo(sourceVideo, _jobStatus, _jobLog); if (!ffmpegStreamInfo.Success || ffmpegStreamInfo.ParseError) { _jobLog.WriteEntry(this, "Cannot read video info", Log.LogEntryType.Error); return(false); } if (Duration <= 0) { // Converted file should contain only 1 audio stream Duration = ffmpegStreamInfo.MediaInfo.VideoInfo.Duration; _jobLog.WriteEntry(this, "Video duration : " + Duration.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Information); if (Duration == 0) { _jobLog.WriteEntry(this, "Video duration 0", Log.LogEntryType.Error); return(false); } } // dont' use threads here since we are copying video to improve stability string ffmpegParams = "-y"; ffmpegParams += " -i " + Util.FilePaths.FixSpaces(sourceVideo); // While setting the start trim before the input file (FAST seek) can speed up seeking and (http://ffmpeg.org/trac/ffmpeg/wiki/Seeking%20with%20FFmpeg) FAST seek (since accurate seek cuts on a NON KeyFrame which causes Audio Sync Issues) // Due to ffmpeg ticket #3252 we need to use ACCURATE seek (trim after input file) to avoid PTS<DTS error if (startTrim != 0) { if (startTrim < Duration) { ffmpegParams += " -ss " + startTrim.ToString(System.Globalization.CultureInfo.InvariantCulture); } else { _jobLog.WriteEntry(this, "Start trim (" + startTrim.ToString() + ") greater than file duration (" + Duration.ToString(System.Globalization.CultureInfo.InvariantCulture) + "). Skipping start trimming.", Log.LogEntryType.Warning); startTrim = 0; // Skip it } } // Set the end trim (calculate from reducing from video length) if (endTrim != 0) { // FFMPEG can specify duration of encoding, i.e. encoding_duration = stopTime - startTime // startTime = startTrim, stopTime = video_duration - endTrim int encDuration = (((int)Duration) - endTrim) - (startTrim); // by default _startTrim is 0 if (encDuration > 0) { ffmpegParams += " -t " + encDuration.ToString(System.Globalization.CultureInfo.InvariantCulture); } else { _jobLog.WriteEntry(this, "End trim (" + endTrim.ToString() + ") + Start trim (" + startTrim.ToString() + ") greater than file duration (" + Duration.ToString(System.Globalization.CultureInfo.InvariantCulture) + "). Skipping end trimming.", Log.LogEntryType.Warning); endTrim = 0; } } // Sanity check once more if ((startTrim == 0) && (endTrim == 0)) { _jobLog.WriteEntry(this, "Start trim and end trim skipped. Skipping trimming.", Log.LogEntryType.Warning); _trimmedVideo = sourceVideo; // It's the same file return(true); // nothing to do here } // Check for audio channels if (ffmpegStreamInfo.AudioTracks > 0) { ffmpegParams += " -map 0:a -acodec copy"; } else { ffmpegParams += " -an"; } // Check for video stream if (ffmpegStreamInfo.MediaInfo.VideoInfo.Stream != -1) { ffmpegParams += " -map 0:" + ffmpegStreamInfo.MediaInfo.VideoInfo.Stream.ToString() + " -vcodec copy"; // Fix for FFMPEG WTV MJPEG ticket #2227 } else { ffmpegParams += " -vn"; } //Output file ffmpegParams += " " + Util.FilePaths.FixSpaces(tempFile); if (!FFmpeg.FFMpegExecuteAndHandleErrors(ffmpegParams, _jobStatus, _jobLog, Util.FilePaths.FixSpaces(tempFile))) //check of file is created, outputhandler reports success (Percentage not requires since Success is more accurate) { _jobLog.WriteEntry("Failed to trim video at " + _jobStatus.PercentageComplete.ToString(System.Globalization.CultureInfo.InvariantCulture) + "%", Log.LogEntryType.Error); return(false); } // Replace the file if (File.Exists(tempFile)) { _jobLog.WriteEntry(this, "TrimVideo trying to replace file Source : " + sourceVideo + " Temp : " + tempFile, Log.LogEntryType.Debug); // If the original uncut file is in the working temp directory, then just replace it if (Path.GetDirectoryName(sourceVideo).ToLower() == workingPath.ToLower()) { Util.FileIO.TryFileReplace(sourceVideo, tempFile); } else // If the original uncut video is not in the working temp directory, then just rename the tempFile with the original name and keep in the temp working directory (don't mangle original video file) { FileIO.TryFileDelete(Path.Combine(workingPath, Path.GetFileName(sourceVideo))); // Just in case it exists FileIO.MoveAndInheritPermissions(tempFile, Path.Combine(workingPath, Path.GetFileName(sourceVideo))); } _trimmedVideo = Path.Combine(workingPath, Path.GetFileName(sourceVideo)); // The final cut video always lies in the working directory } else { _jobLog.WriteEntry(this, "TrimVideo cannot find temp file " + tempFile, Log.LogEntryType.Error); _jobStatus.PercentageComplete = 0; return(false); } return(true); // All good here }
/// <summary> /// Remuxes the converted file to the specified extension/format using FFMPEG. /// (Optionally) If a new Audio Stream file is specified, the audio from the new file is taken and video from the original file /// </summary> /// <param name="newAudioStream">(Optional) New Audio stream to use</param> /// <returns>True is successful</returns> public bool FfmpegRemux(string newAudioStream = "") { _jobStatus.ErrorMsg = ""; _jobStatus.PercentageComplete = 100; //all good to start with _jobStatus.ETA = ""; Util.FileIO.TryFileDelete(RemuxedTempFile); FFmpegMediaInfo ffmpegStreamInfo = new FFmpegMediaInfo(_originalFile, _jobStatus, _jobLog); if (!ffmpegStreamInfo.Success || ffmpegStreamInfo.ParseError) { _jobStatus.ErrorMsg = "Unable to read video information"; _jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error); _jobStatus.PercentageComplete = 0; return(false); } // Input original string ffmpegParams = "-y -i " + FilePaths.FixSpaces(_originalFile); // Add New Audio stream if (!String.IsNullOrEmpty(newAudioStream)) { // Take audio stream from new audio file ffmpegParams += " -i " + FilePaths.FixSpaces(newAudioStream) + " -map 1:a -acodec copy"; // Video from the original file if (ffmpegStreamInfo.MediaInfo.VideoInfo.Stream != -1) { ffmpegParams += " -map 0:" + ffmpegStreamInfo.MediaInfo.VideoInfo.Stream.ToString() + " -vcodec copy"; // Fix for FFMPEG WTV MJPEG ticket #2227 } else { ffmpegParams += " -vn"; } } else { // Check for audio tracks if (ffmpegStreamInfo.AudioTracks > 0) { ffmpegParams += " -map 0:a -acodec copy"; } else { ffmpegParams += " -an"; } // Check for video tracks if (ffmpegStreamInfo.MediaInfo.VideoInfo.Stream != -1) { ffmpegParams += " -map 0:" + ffmpegStreamInfo.MediaInfo.VideoInfo.Stream.ToString() + " -vcodec copy"; // Fix for FFMPEG WTV MJPEG ticket #2227 } else { ffmpegParams += " -vn"; } } ffmpegParams += " " + FilePaths.FixSpaces(RemuxedTempFile); if (!FFmpeg.FFMpegExecuteAndHandleErrors(ffmpegParams, _jobStatus, _jobLog, FilePaths.FixSpaces(RemuxedTempFile))) // Let this function handle error conditions since it's just a simple execute { _jobStatus.ErrorMsg = Localise.GetPhrase("ffmpeg remux failed"); _jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error); return(false); } _jobLog.WriteEntry(this, Localise.GetPhrase("FFMPEG ReMux moving file"), Log.LogEntryType.Information); return(ReplaceTempRemuxed()); }