예제 #1
0
        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);
        }
예제 #2
0
        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
        }
예제 #3
0
        /// <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());
        }