Exemplo n.º 1
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
                    File.Move(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
        }
Exemplo n.º 2
0
        private bool FixAudioDelay()
        {
            string encoderParams;

            _jobStatus.PercentageComplete = 100; //all good to start with
            _jobStatus.ETA = "";

            if (_videoFile.AudioDelaySet || _toolAudioDelay == 0)
                return true; //It's already been done (probably by mencoder) or been requested to skip

            // Check if the converted file has Audio AND Video streams (if one is missing, then skip this step)
            FFmpegMediaInfo ffmpegInfo = new FFmpegMediaInfo(_convertedFile, _jobStatus, _jobLog);
            if (!ffmpegInfo.Success || ffmpegInfo.ParseError)
            {
                _jobStatus.PercentageComplete = 0; // if the file wasn't completely converted the percentage will be low so no worries
                _jobStatus.ErrorMsg = "Fix AudioSync getting mediainfo Failed for " + _convertedFile;
                _jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                return false;
            }

            if ((ffmpegInfo.MediaInfo.VideoInfo.Stream == -1) || (ffmpegInfo.AudioTracks < 1))
            {
                _jobLog.WriteEntry(this, "Fix audiosync, No video or no audio track detected - skipping audio sync", Log.LogEntryType.Warning);
                return true;
            }

            double audioDelay = _toolAudioDelay;

            if (audioDelay != 0)
            {
                _jobLog.WriteEntry(this, ("Fixing Audio Delay, Detected :") + " " + _videoFile.AudioDelay.ToString(System.Globalization.CultureInfo.InvariantCulture) + ", Manual Adj : " + _toolAudioDelay.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Information);

                string ext = FilePaths.CleanExt(_convertedFile);
                string fixedFile = Path.Combine(_workingPath, Path.GetFileNameWithoutExtension(_convertedFile) + "_AVFIX" + FilePaths.CleanExt(_convertedFile));
                FileIO.TryFileDelete(fixedFile);

                _jobStatus.CurrentAction = Localise.GetPhrase("Correcting audio delay");

                switch (ext)
                {
                    case ".wmv":
                        _jobLog.WriteEntry(this, ("Using ASFBin to correct audio sync for extension ") + ext, Log.LogEntryType.Debug);

                        encoderParams = " -i " + Util.FilePaths.FixSpaces(_convertedFile) + " -adelay " + audioDelay.ToString(System.Globalization.CultureInfo.InvariantCulture) + " -o " + Util.FilePaths.FixSpaces(fixedFile) + " -y";
                        ASFBin asfBin = new ASFBin(encoderParams, _jobStatus, _jobLog);
                        asfBin.Run();
                        if (!asfBin.Success || (FileIO.FileSize(fixedFile) <= 0))
                        {
                            _jobStatus.ErrorMsg = "Fixing Audio Delay for WMV failed";
                            _jobLog.WriteEntry(this, ("Fixing Audio Delay for WMV failed"), Log.LogEntryType.Error);
                            _jobStatus.PercentageComplete = 0;
                            return false;
                        }

                        break;

                    case ".avi":
                        _jobLog.WriteEntry(this, ("Using Mencoder to correct audio sync for extension ") + ext, Log.LogEntryType.Debug);

                        encoderParams = Util.FilePaths.FixSpaces(_convertedFile) + " -oac copy -ovc copy -ni -delay " + (-1 * audioDelay).ToString(System.Globalization.CultureInfo.InvariantCulture) + " -o " + Util.FilePaths.FixSpaces(fixedFile.ToString(System.Globalization.CultureInfo.InvariantCulture)); // avoid using threads since we are copying to increase stability

                        _jobLog.WriteEntry(this, "Fixing Audio Delay using MEncoder with Parameters: " + encoderParams, Log.LogEntryType.Debug);
                        Mencoder mencoderAVI = new Mencoder(encoderParams, _jobStatus, _jobLog, false);
                        mencoderAVI.Run();
                        if (!mencoderAVI.Success) // something failed or was incomplete, do not check for % completion as Mencoder looks fro success criteria
                        {
                            _jobStatus.PercentageComplete = 0;
                            _jobStatus.ErrorMsg = ("Fix AudioSync failed for") + " " + ext;
                            _jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                            return false;
                        }

                        break;

                    default:
                        _jobLog.WriteEntry(this, ("Using FFMPEG to correct audio sync for extension ") + ext, Log.LogEntryType.Debug);

                        if (audioDelay > 0) // Map same file as 2 inputs, shift and take the audio in one and take the video from the other
                        {
                            encoderParams = "-y -i " + Util.FilePaths.FixSpaces(_convertedFile) +
                                            " -ss " + audioDelay.ToString(System.Globalization.CultureInfo.InvariantCulture) +" -i " + Util.FilePaths.FixSpaces(_convertedFile) +
                                            " -map 1:v -map 0:a -acodec copy -vcodec copy";

                            _jobLog.WriteEntry(this, "Fixing +ve Audio Delay using FFMPEG", Log.LogEntryType.Debug);

                        } // if audio is behind the video skip seconds from the 2nd input file and remap to ouput (keeping the audio shift positive)
                        else
                        {
                            encoderParams = "-y -ss " + (audioDelay * -1).ToString(System.Globalization.CultureInfo.InvariantCulture) + " -i " + Util.FilePaths.FixSpaces(_convertedFile) +
                                            " -i " + Util.FilePaths.FixSpaces(_convertedFile) +
                                            " -map 1:v -map 0:a -acodec copy -vcodec copy";

                            _jobLog.WriteEntry(this, "Fixing -ve Audio Delay using FFMPEG", Log.LogEntryType.Debug);
                        }

                        encoderParams += " " + Util.FilePaths.FixSpaces(fixedFile);

                        if (!FFmpeg.FFMpegExecuteAndHandleErrors(encoderParams, _jobStatus, _jobLog, Util.FilePaths.FixSpaces(fixedFile))) // Do not check for % completion since FFMPEG doesn't always report a % for this routine for some reason
                        {
                            _jobStatus.PercentageComplete = 0; // if the file wasn't completely converted the percentage will be low so no worries
                            _jobStatus.ErrorMsg = "Fix AudioSync Failed for " + ext;
                            _jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                            return false;
                        }

                        break;
                }
                
                try
                {
                    _jobLog.WriteEntry(this, ("Fix Audio Delay trying to move fixed file"), Log.LogEntryType.Information);
                    FileIO.TryFileDelete(_convertedFile);
                    File.Move(fixedFile, _convertedFile);
                }
                catch (Exception e)
                {
                    _jobLog.WriteEntry(this, ("Unable to move audio sync corrected file") + " " + fixedFile + "\r\nError : " + e.ToString(), Log.LogEntryType.Error);
                    _jobStatus.ErrorMsg = "Unable to move audio sync file";
                    _jobStatus.PercentageComplete = 0;
                    return false;
                }
                _jobLog.WriteEntry(this, ("Finished Audio Delay Correction, file size [KB]") + " " + (FileIO.FileSize(_convertedFile) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            }
            else
                _jobLog.WriteEntry(this, ("Fix Audio Delay, net correction 0, skipping correction"), Log.LogEntryType.Information);

            return true;
        }
Exemplo n.º 3
0
        /// <summary>
        /// Updates the Video file properties structure, check for crop, audio and video information
        /// </summary>
        /// <param name="skipCropDetect">True to skip detecting cropping parameters</param>
        /// <param name="detectInterlace">Extracts the video intelacing type by analyzing it in depth</param>
        /// <param name="videoFileName">Path to Original Source Video</param>
        /// <param name="remuxedFileName">Path to Remuxed video, else null or empty string</param>
        /// <param name="edlFile">Path to EDL file else null or empty string</param>
        /// <param name="audioLanguage">Audio Language</param>
        /// <param name="jobStatus">JobStatus</param>
        /// <param name="jobLog">JobLog</param>
        public void UpdateVideoInfo(bool skipCropDetect, bool detectInterlace, string videoFileName, string remuxedFileName, string edlFile, string audioLanguage, JobStatus jobStatus, Log jobLog)
        {
            ResetParameters(); // Reset VideoInfo parameters

            _jobStatus = jobStatus;
            _jobLog = jobLog;
            _EDLFile = edlFile;
            _requestedAudioLanguage = audioLanguage;
            _originalFileName = videoFileName;
            _remuxedFileName = remuxedFileName;

            Ini ini = new Ini(GlobalDefs.ProfileFile);

            _skipCropDetect = skipCropDetect;
            _detectInterlacing = detectInterlace;

            _jobLog.WriteEntry(this, "Reading MediaInfo from " + SourceVideo, Log.LogEntryType.Information);

            _videoCodec = VideoParams.VideoFormat(SourceVideo);
            jobLog.WriteEntry(this, "Video Codec : " + _videoCodec, Log.LogEntryType.Debug);
            if (String.IsNullOrWhiteSpace(_videoCodec))
                _error = true;

            _audioCodec = VideoParams.AudioFormat(SourceVideo);
            jobLog.WriteEntry(this, "Audio Codec : " + _audioCodec, Log.LogEntryType.Debug);
            if (String.IsNullOrWhiteSpace(_audioCodec))
                _error = true;

            _fps = VideoParams.FPS(SourceVideo);
            jobLog.WriteEntry(this, "Video FPS : " + _fps.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            if (_fps <= 0)
                _error = true;

            _width = VideoParams.VideoWidth(SourceVideo);
            jobLog.WriteEntry(this, "Video Width : " + _width.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            if (_width <= 0)
                _error = true;

            _height = VideoParams.VideoHeight(SourceVideo);
            jobLog.WriteEntry(this, "Video Height : " + _height.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            if (_height <= 0)
                _error = true;

            _duration = VideoParams.VideoDuration(SourceVideo);
            jobLog.WriteEntry(this, "Video Duration : " + _duration.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            if (_duration <= 0)
                _error = true;

            _audioDelay = VideoParams.AudioDelay(SourceVideo);
            jobLog.WriteEntry(this, "Audio Delay : " + _audioDelay.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);

            _scanType = VideoParams.VideoScanType(SourceVideo);
            jobLog.WriteEntry(this, "Video Scan Type : " + _scanType.ToString(), Log.LogEntryType.Debug);

            if (_scanType == ScanType.Unknown && _detectInterlacing) // If we couldn't find it lets try ffmpeg - avoid unnecessary cycles if not required
            {
                jobLog.WriteEntry(this, "Scan type unknown, trying with FFMPEGMediaInfo", Log.LogEntryType.Debug);

                _ffmpegStreamInfo = new FFmpegMediaInfo(SourceVideo, _jobStatus, _jobLog, 0, 0, _ignoreSuspend); // Run interlace detection with defaults
                if (_ffmpegStreamInfo.Success && !_ffmpegStreamInfo.ParseError)
                {
                    // Now calcuate whether it's Interlaced or Progressive based on the Multi Frame Interlaced Detection Results
                    long totalInterlaced = _ffmpegStreamInfo.MFInterlaceDetectionResults.BFF + _ffmpegStreamInfo.MFInterlaceDetectionResults.TFF;
                    long totalProgressive = _ffmpegStreamInfo.MFInterlaceDetectionResults.Progressive;
                    long totalUndetermined = _ffmpegStreamInfo.MFInterlaceDetectionResults.Undetermined; // TODO: What to do with this?

                    if (totalInterlaced == 0 && totalProgressive == 0) // Boundary conditions
                        _scanType = ScanType.Unknown;
                    else if (totalInterlaced == 0) // Avoid divide by zero exception
                        _scanType = ScanType.Progressive;
                    else
                    {
                        double PtoIRatio = totalProgressive / totalInterlaced; // See below comment

                        // Refer to this, how to tell if the video is interlaced or telecine
                        // http://forum.videohelp.com/threads/295007-Interlaced-or-telecined-how-to-tell?p=1797771&viewfull=1#post1797771
                        // It is a statistical ratio, telecine has approx split of 3 progressive and 2 interlaced (i.e. ratio of about 1.5 progressive to interlaced)
                        // TODO: We need to revisit the logic below for telecine, interlaced or progressive detection (check idet filter for updates ffmpeg ticket #3073)
                        if ((totalProgressive == 0) && (totalProgressive == 0)) // Unknown - could not find
                            _scanType = ScanType.Unknown;
                        else if ((PtoIRatio > TELECINE_LOW_P_TO_I_RATIO) && (PtoIRatio < TELECINE_HIGH_P_TO_I_RATIO)) // Let us keep a band to measure telecine ratio, see comment above
                            _scanType = ScanType.Telecine;
                        else if (PtoIRatio <= TELECINE_LOW_P_TO_I_RATIO) // We play safe, more interlaced than progressive
                            _scanType = ScanType.Interlaced;
                        else if (PtoIRatio >= TELECINE_HIGH_P_TO_I_RATIO) // Progressive has the clear lead
                            _scanType = ScanType.Progressive;
                        else
                            _scanType = ScanType.Unknown; // No idea where we are
                    }

                    jobLog.WriteEntry(this, "FFMPEG Video Scan Type : " + _scanType.ToString(), Log.LogEntryType.Debug);
                }
                else
                    jobLog.WriteEntry(this, "Error reading scan type from FFMPEGMediaInfo", Log.LogEntryType.Warning);
            }

            // We don't get AudioChannel information here as it interfers with FFMPEG
            /*mi.Option("Inform", "Audio; %Channels%");
            int.TryParse(mi.Inform(), out _audioChannels);
            jobLog.WriteEntry(this, "Audio Channels : " + _audioChannels.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);*/

            // Supplement with extracting Video and Audio information (sometimes MediaInfo fails) using FFMPEG and selected the Audio Language specified
            _jobLog.WriteEntry(this, "Supplementing Media information using FFMPEG", Log.LogEntryType.Information);
            _ffmpegStreamInfo = new FFmpegMediaInfo(SourceVideo, _jobStatus, _jobLog, _ignoreSuspend); // this may be called from the UI request
            if (_ffmpegStreamInfo.Success && !_ffmpegStreamInfo.ParseError)
            {
                // Store the video information (there's only 1 video per file)
                _width = _ffmpegStreamInfo.MediaInfo.VideoInfo.Width;
                _height = _ffmpegStreamInfo.MediaInfo.VideoInfo.Height;
                if ((_fps <= 0) || ((_fps > _ffmpegStreamInfo.MediaInfo.VideoInfo.FPS) && (_ffmpegStreamInfo.MediaInfo.VideoInfo.FPS > 0))) // Check _fps, sometimes MediaInfo get it below 0 or too high (most times it's reliable)
                    _fps = _ffmpegStreamInfo.MediaInfo.VideoInfo.FPS;
                else
                    _ffmpegStreamInfo.MediaInfo.VideoInfo.FPS = _fps; // Store the value from MediaInfo, more reliable
                _duration = _ffmpegStreamInfo.MediaInfo.VideoInfo.Duration;
                _videoCodec = _ffmpegStreamInfo.MediaInfo.VideoInfo.VideoCodec;
                _videoStream = _ffmpegStreamInfo.MediaInfo.VideoInfo.Stream;
                _videoPID = _ffmpegStreamInfo.MediaInfo.VideoInfo.PID; // video PID
                
                // Default Check if all audio streams have same codec, if so populate the field for later use during reading profiles
                for (int i = 0; i < _ffmpegStreamInfo.AudioTracks; i++)
                {
                    if (i == 0)
                        _audioCodec = _ffmpegStreamInfo.MediaInfo.AudioInfo[i].AudioCodec; // baseline the codec name
                    else if (_audioCodec != _ffmpegStreamInfo.MediaInfo.AudioInfo[i].AudioCodec)
                    {
                        _audioCodec = ""; // All codecs are not the same, reset it and let the encoder figure it out
                        break; // we're done here
                    }
                }

                // Default check if all audio streams have same channels, if so populate the field for later use during reading profiles
                for (int i = 0; i < _ffmpegStreamInfo.AudioTracks; i++)
                {
                    if (i == 0)
                        _audioChannels = _ffmpegStreamInfo.MediaInfo.AudioInfo[i].Channels; // baseline the channels
                    else if (_audioChannels != _ffmpegStreamInfo.MediaInfo.AudioInfo[i].Channels)
                    {
                        _audioChannels = 0; // All channels are not the same, reset it and let the encoder figure it out
                        break; // we're done here
                    }
                }

                // Audio parameters - find the best Audio channel for the selected language or best audio track if there are imparired tracks otherwise by default the encoder will select the best audio channel (encoders do not do a good job of ignoring imparired tracks)
                bool selectedTrack = false;
                if ((!String.IsNullOrEmpty(_requestedAudioLanguage) || (_ffmpegStreamInfo.ImpariedAudioTrackCount > 0)) && (_ffmpegStreamInfo.AudioTracks > 1)) // More than 1 audio track to choose from and either we have a language match request or a presence of an imparied channel (likely no audio)
                {
                    for (int i = 0; i < _ffmpegStreamInfo.AudioTracks; i++)
                    {
                        bool processTrack = false; // By default we don't need to process

                        // Language selection check, if the user has picked a specific language code, look for it
                        // If we find a match, we look the one with the highest number of channels in it
                        if (!String.IsNullOrEmpty(_requestedAudioLanguage))
                        {
                            if ((_ffmpegStreamInfo.MediaInfo.AudioInfo[i].Language.ToLower() == _requestedAudioLanguage) && (_ffmpegStreamInfo.MediaInfo.AudioInfo[i].Channels > 0))
                            {
                                if (selectedTrack)
                                {
                                    if (!( // take into account impaired tracks (since impaired tracks typically have no audio)
                                        ((_ffmpegStreamInfo.MediaInfo.AudioInfo[i].Channels > _audioChannels) && !_ffmpegStreamInfo.MediaInfo.AudioInfo[i].Impaired) || // PREFERENCE to non-imparied Audio tracks with the most channels
                                        ((_ffmpegStreamInfo.MediaInfo.AudioInfo[i].Channels > _audioChannels) && _selectedAudioImpaired) || // PREFERENCE to Audio tracks with most channels if currently selected track is impaired
                                        (!_ffmpegStreamInfo.MediaInfo.AudioInfo[i].Impaired && _selectedAudioImpaired) // PREFER non impaired audio over currently selected impaired
                                        ))
                                        continue; // we have found a lang match, now we are looking for more channels only now
                                }
                             
                                processTrack = true; // All conditions met, we need to process this track
                            }
                        }
                        else if (_ffmpegStreamInfo.MediaInfo.AudioInfo[i].Channels > 0)// we have a imparied audio track, select the non impaired track with the highest number of tracks or bitrate or frequency
                        {
                            if (selectedTrack)
                            {
                                if (!( // take into account impaired tracks (since impaired tracks typically have no audio)
                                    ((_ffmpegStreamInfo.MediaInfo.AudioInfo[i].Channels > _audioChannels) && !_ffmpegStreamInfo.MediaInfo.AudioInfo[i].Impaired) || // PREFERENCE to non-imparied Audio tracks with the most channels
                                    ((_ffmpegStreamInfo.MediaInfo.AudioInfo[i].Channels > _audioChannels) && _selectedAudioImpaired) || // PREFERENCE to Audio tracks with most channels if currently selected track is impaired
                                    (!_ffmpegStreamInfo.MediaInfo.AudioInfo[i].Impaired && _selectedAudioImpaired) // PREFER non impaired audio over currently selected impaired
                                    ))
                                    continue; // we have found a lang match, now we are looking for more channels only now
                            }

                            processTrack = true; // All conditions met, we need to process this track
                        }

                        if (processTrack) // We need to process this track
                        {
                            _audioChannels = _ffmpegStreamInfo.MediaInfo.AudioInfo[i].Channels;
                            _audioStream = _ffmpegStreamInfo.MediaInfo.AudioInfo[i].Stream; // store the stream number for the selected audio channel
                            _audioCodec = _ffmpegStreamInfo.MediaInfo.AudioInfo[i].AudioCodec;
                            _audioPID = _ffmpegStreamInfo.MediaInfo.AudioInfo[i].PID; // Audio PID
                            _audioTrack = i; // Store the audio track number we selected
                            _audioLanguage = _ffmpegStreamInfo.MediaInfo.AudioInfo[i].Language.ToLower(); // this is what we selected
                            _selectedAudioImpaired = _ffmpegStreamInfo.MediaInfo.AudioInfo[i].Impaired; // Is this an imparied audio track?
                            selectedTrack = true; // We found a suitable track

                            if (!String.IsNullOrEmpty(_requestedAudioLanguage))
                                _jobLog.WriteEntry(this, "Found Audio Language match for language" + " " + _requestedAudioLanguage.ToUpper() + ", " + "Audio Stream" + " " + _audioStream.ToString(System.Globalization.CultureInfo.InvariantCulture) + ", " + ("Audio Track") + " " + _audioTrack.ToString(System.Globalization.CultureInfo.InvariantCulture) + ", " + ("Channels") + " " + _audioChannels.ToString(System.Globalization.CultureInfo.InvariantCulture) + ", " + ("Codec") + " " + _audioCodec + " Impaired " + _selectedAudioImpaired.ToString(), Log.LogEntryType.Debug);
                            else
                                _jobLog.WriteEntry(this, "Compensating for audio impaired tracks, selected track with language" + " " + _requestedAudioLanguage.ToUpper() + ", " + "Audio Stream" + " " + _audioStream.ToString(System.Globalization.CultureInfo.InvariantCulture) + ", " + ("Audio Track") + " " + _audioTrack.ToString(System.Globalization.CultureInfo.InvariantCulture) + ", " + ("Channels") + " " + _audioChannels.ToString(System.Globalization.CultureInfo.InvariantCulture) + ", " + ("Codec") + " " + _audioCodec + " Impaired " + _selectedAudioImpaired.ToString(), Log.LogEntryType.Debug);
                        }
                    }

                    if (!selectedTrack)
                        _jobLog.WriteEntry(this, ("Could not find a match for selected Audio Language Code") + " " + _requestedAudioLanguage + ", letting encoder choose best audio language", Log.LogEntryType.Warning);
                    else
                        _jobLog.WriteEntry(this, ("Selected Audio Stream") + " " + _audioStream.ToString(System.Globalization.CultureInfo.InvariantCulture) + ", " + ("Audio Track") + " " + _audioTrack.ToString(System.Globalization.CultureInfo.InvariantCulture) + ", " + ("Channels") + " " + _audioChannels.ToString(System.Globalization.CultureInfo.InvariantCulture) + ", " + ("Codec") + " " + _audioCodec + ", Impaired " + _selectedAudioImpaired.ToString(), Log.LogEntryType.Debug);
                }
                else if (_ffmpegStreamInfo.AudioTracks == 1) // We have just one audio track, then populate the information otherwise the encoding operations will have a hard time determining audio information
                {
                    if (_ffmpegStreamInfo.MediaInfo.AudioInfo[0].Channels > 0)
                    {
                        _audioChannels = _ffmpegStreamInfo.MediaInfo.AudioInfo[0].Channels;
                        _audioStream = _ffmpegStreamInfo.MediaInfo.AudioInfo[0].Stream; // store the stream number for the selected audio channel
                        _audioCodec = _ffmpegStreamInfo.MediaInfo.AudioInfo[0].AudioCodec;
                        _audioPID = _ffmpegStreamInfo.MediaInfo.AudioInfo[0].PID; // Audio PID
                        _audioTrack = 0; // Store the audio track number we selected
                        _audioLanguage = _ffmpegStreamInfo.MediaInfo.AudioInfo[0].Language.ToLower(); // this is what we selected
                        _selectedAudioImpaired = _ffmpegStreamInfo.MediaInfo.AudioInfo[0].Impaired; // Is this an imparied audio track?

                        _jobLog.WriteEntry(this, "Only one audio track present, " + ("Audio Stream") + " " + _audioStream.ToString(System.Globalization.CultureInfo.InvariantCulture) + ", " + ("Audio Track") + " " + _audioTrack.ToString(System.Globalization.CultureInfo.InvariantCulture) + ", " + ("Channels") + " " + _audioChannels.ToString(System.Globalization.CultureInfo.InvariantCulture) + ", " + ("Codec") + " " + _audioCodec + " Impaired " + _selectedAudioImpaired.ToString(), Log.LogEntryType.Debug);
                    }
                }
                else
                {
                    _jobLog.WriteEntry(this, ("Audio Stream") + " " + _audioStream.ToString(System.Globalization.CultureInfo.InvariantCulture) + ", " + ("Audio Track") + " " + _audioTrack.ToString(System.Globalization.CultureInfo.InvariantCulture) + ", " + ("Channels") + " " + _audioChannels.ToString(System.Globalization.CultureInfo.InvariantCulture) + ", " + ("Codec") + " " + _audioCodec + ", Impaired " + _selectedAudioImpaired.ToString(), Log.LogEntryType.Debug);
                    _jobLog.WriteEntry(this, "No audio language selected, letting encoder choose best audio language", Log.LogEntryType.Warning);
                }

                _error = false; // all good now
            }
            else
                _error = true;
            
            if (_error)
            {
                _jobLog.WriteEntry(this, ("Unable to read media information using FFMPEG or MediaInfo"), Log.LogEntryType.Error);
                return;
            }

            // Get the video properties for the original video
            _jobLog.WriteEntry(this, "Reading Original File Media information", Log.LogEntryType.Information);
            _originalFileFFmpegStreamInfo = new FFmpegMediaInfo(_originalFileName, _jobStatus, _jobLog, _ignoreSuspend);
            if (!_originalFileFFmpegStreamInfo.Success || _originalFileFFmpegStreamInfo.ParseError)
                _jobLog.WriteEntry(this, ("Unable to read media information using FFMPEG"), Log.LogEntryType.Warning);

            if (_skipCropDetect)
                _jobLog.WriteEntry(this, "Skipping crop information", Log.LogEntryType.Information);
            else
                UpdateCropInfo(jobLog);
        }
Exemplo n.º 4
0
        public bool Extract(string sourceFile, string workingPath, string ccOptions, int startTrim, int endTrim, double ccOffset)
        {
            _jobLog.WriteEntry(this, Localise.GetPhrase("Extracting Closed Captions as SRT file"), Log.LogEntryType.Information);
            _jobLog.WriteEntry(this, "Source File : " + sourceFile, Log.LogEntryType.Debug);
            _jobLog.WriteEntry(this, "Working Path " + workingPath, Log.LogEntryType.Debug);
            _jobLog.WriteEntry(this, "CC Options : " + ccOptions, Log.LogEntryType.Debug);
            _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, "Offset : " + ccOffset.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);

            if (String.IsNullOrWhiteSpace(ccOptions))
                return true; // nothing to do, accidentally called

            // Output SRT file has to be working directory, will be copied to output afterwards
            string tmpExtractedSRTFile = Path.Combine(workingPath, Path.GetFileNameWithoutExtension(sourceFile)) + ".srt";
            string ccExtractorParams = "";
            string field = "";
            string channel = "";

            if (ccOptions.ToLower() != "default") // let ccextrator choose defaults if specified
            {
                // CCOptions are encoded as field,channel
                field = ccOptions.Split(',')[0];
                channel = ccOptions.Split(',')[1];
            }

            if (field == "1" || field == "2")
                ccExtractorParams += " -" + field; // Field is -1 or -2 (we don't support -12 since it creates 2 SRT files and there's no way to handle that)

            if (channel == "2")
                ccExtractorParams += " -cc2"; // By default is Channel 1, there is no parameter for it

            // Adjust for any offset required during extraction (opposite direction, so -ve)
            if (ccOffset != 0)
                ccExtractorParams += " -delay " + (-ccOffset * 1000).ToString(System.Globalization.CultureInfo.InvariantCulture); // ccOffset is in seconds while -delay required milliseconds

            // Get the length of the video, needed to calculate end point
            float Duration = 0;
            Duration = VideoParams.VideoDuration(sourceFile);
            if (Duration <= 0)
            {
                FFmpegMediaInfo ffmpegStreamInfo = new FFmpegMediaInfo(sourceFile, _jobStatus, _jobLog);
                if (ffmpegStreamInfo.Success && !ffmpegStreamInfo.ParseError)
                {
                    // Converted file should contain only 1 audio stream
                    Duration = ffmpegStreamInfo.MediaInfo.VideoInfo.Duration;
                    _jobLog.WriteEntry(this, Localise.GetPhrase("Video duration") + " : " + Duration.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Information);

                    if (Duration == 0)
                    {
                        _jobLog.WriteEntry(this, Localise.GetPhrase("Video duration 0"), Log.LogEntryType.Error);
                        return false;
                    }
                }
                else
                {
                    _jobLog.WriteEntry(this, Localise.GetPhrase("Cannot read video duration"), Log.LogEntryType.Error);
                    return false;
                }
            }

            // Set the start trim time
            if (startTrim != 0)
                ccExtractorParams += " -startat " + TimeSpan.FromSeconds((double)startTrim).ToString();

            // Set the end trim time
            if (endTrim != 0)
            {
                // startTime = startTrim, stopTime = video_duration - endTrim
                int encDuration = (((int)Duration) - endTrim) - (startTrim); // by default _startTrim is 0

                ccExtractorParams += " -endat " + TimeSpan.FromSeconds((double)encDuration).ToString();
            }

            // Set the input file
            ccExtractorParams += " " + Util.FilePaths.FixSpaces(sourceFile);

            // set output file
            ccExtractorParams += " -o " + Util.FilePaths.FixSpaces(tmpExtractedSRTFile);

            // Run the command
            CCExtractor ccExtractor = new CCExtractor(ccExtractorParams, _jobStatus, _jobLog);
            ccExtractor.Run();

            if (!ccExtractor.Success) // check for termination/success
            {
                _jobLog.WriteEntry("CCExtractor failed. Disabling detection , retrying using TS format", Log.LogEntryType.Warning);

                // TODO: Right format to pick (TS/ES/PS etc) - doing TS for now
                // Sometimes it doesn't detect the format correctly so try to force it (TS)
                ccExtractorParams += " -ts";
                ccExtractor = new CCExtractor(ccExtractorParams, _jobStatus, _jobLog);
                ccExtractor.Run();

                if (!ccExtractor.Success) // check for termination/success
                {
                    _jobLog.WriteEntry("CCExtractor failed to extract closed captions", Log.LogEntryType.Error);
                    return false;
                }
            }

            // Check if we have a format identification error (sometimes ccextractor misidentifies files)
            if ((Util.FileIO.FileSize(tmpExtractedSRTFile) <= 0) && ccExtractor.FormatReadingError)
            {
                FileIO.TryFileDelete(tmpExtractedSRTFile); // Delete the empty file
                _jobLog.WriteEntry(this, "No SRT file found and format error detected. CCExtractor may not have identified the format correctly, forcing TS format and retrying extraction.", Log.LogEntryType.Warning);
                ccExtractorParams += " -in=ts"; // force TS format and retry
                ccExtractor = new CCExtractor(ccExtractorParams, _jobStatus, _jobLog);
                ccExtractor.Run();

                if (!ccExtractor.Success) // check for termination/success
                {
                    _jobLog.WriteEntry(Localise.GetPhrase("Retrying: CCExtractor failed to extract closed captions"), Log.LogEntryType.Error);
                    return false;
                }
            }

            // Check for empty file
            if (Util.FileIO.FileSize(tmpExtractedSRTFile) <= 0)
            {
                FileIO.TryFileDelete(tmpExtractedSRTFile); // Delete the empty file
                _jobLog.WriteEntry(this, Localise.GetPhrase("No valid SRT file found"), Log.LogEntryType.Warning);
                return true; // no error
            }

            _extractedSRTFile = tmpExtractedSRTFile; // We got the file
            _jobLog.WriteEntry(this, Localise.GetPhrase("Extracted closed captions to") + " " + _extractedSRTFile, Log.LogEntryType.Information);

            return true;
        }
Exemplo n.º 5
0
        /// <summary>
        /// Remux with adjustments the extracted raw stream parts individually using ffmpeg
        /// </summary>
        /// <param name="originalFile">Path to original source video</param>
        /// <param name="remuxedFile">Path to output remuxed file</param>
        /// <param name="extractGraphResults">Extract graph object which has extracts the stream into raw parts</param>
        /// <returns>Success or Failure</returns>
        public static bool RemuxRawPartsFFmpeg(string originalFile, string remuxedFile, ExtractWithGraph extractGraphResults, JobStatus jobStatus, Log jobLog)
        {
            float audioDelay = 0, fps = 0;
            string vcodec = "";

            if (extractGraphResults == null || String.IsNullOrWhiteSpace(originalFile) || String.IsNullOrWhiteSpace(remuxedFile))
                return false;

            // Atleast 1 audio or video stream
            if (String.IsNullOrWhiteSpace(extractGraphResults.VideoPart) && (extractGraphResults.AudioParts.Count < 1))
                return false;

            Util.FileIO.TryFileDelete(remuxedFile); // Start clean

            // Audio Delay on the original file
            if (extractGraphResults.AudioParts.Count > 0)
            {
                audioDelay = VideoParams.AudioDelay(originalFile);
                jobLog.WriteEntry("Audio Delay : " + audioDelay.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            }

            // FPS and codec of the video
            if (!String.IsNullOrWhiteSpace(extractGraphResults.VideoPart))
            {
                fps = VideoParams.FPS(extractGraphResults.VideoPart);
                vcodec = VideoParams.VideoFormat(extractGraphResults.VideoPart).ToLower();
                FFmpegMediaInfo ffmpegStreamInfo = new FFmpegMediaInfo(extractGraphResults.VideoPart, jobStatus, jobLog);
                if (String.IsNullOrWhiteSpace(vcodec) || (fps <= 0) || ((fps > ffmpegStreamInfo.MediaInfo.VideoInfo.FPS) && (ffmpegStreamInfo.MediaInfo.VideoInfo.FPS > 0)))
                {
                    jobLog.WriteEntry("MediaInfo reading FPS/Vcodec failed, Reading FFMPEG info from " + extractGraphResults.VideoPart, Log.LogEntryType.Debug);

                    if (ffmpegStreamInfo.Success) // Don't check for parse error since this is raw video stream it will always give an parse error for N/A duration
                    {
                        if ((fps <= 0) || ((fps > ffmpegStreamInfo.MediaInfo.VideoInfo.FPS) && (ffmpegStreamInfo.MediaInfo.VideoInfo.FPS > 0))) // MediaInfo did not succeed or got wrong value
                            fps = ffmpegStreamInfo.MediaInfo.VideoInfo.FPS; // Get the FPS

                        if (String.IsNullOrWhiteSpace(vcodec))
                            vcodec = ffmpegStreamInfo.MediaInfo.VideoInfo.VideoCodec; // Get video codecc
                    }

                    if (fps <= 0)
                        jobLog.WriteEntry("Unable to read FPS from " + originalFile + ", assuming no video stream", Log.LogEntryType.Warning);

                    if (String.IsNullOrWhiteSpace(vcodec))
                        jobLog.WriteEntry("No Video Codec detected in raw stream, assuming no video stream", Log.LogEntryType.Warning);
                }

                jobLog.WriteEntry("Video Codec detected : " + vcodec + ", FPS : " + fps.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);

                // TODO: Check what vcodec is returned is by MediaInfo.dll and do we really need to do this? This is last recourse
                /*if ((_mpeg2Codecs.Any(s => s.Contains(vcodec.ToLower()))) && (!_allowH264CopyRemuxing)) // Accept H264 direct remuxing only if allowed
                {
                    _jobLog.WriteEntry("FFMpegParts incompatible settings, non mpeg2video video detected and H264CopyRemuxing is not enabled", Log.LogEntryType.Error);
                    return false;
                }*/
            }

            string FFmpegParams = "";
            string ffmpegMapParams = ""; // Keep track of the tracks added since we need to map them
            int ffmpegMapCount = 0;

            // Generate the timestamps, framerate and specify input format since it's raw video and ffmpeg won't recognize it
            FFmpegParams += "-fflags +genpts -y";
            if (fps > 0) // Check if we have a video stream
                FFmpegParams += " -r " + fps.ToString(CultureInfo.InvariantCulture);

            // Check for valid video stream otherwise just audio
            if (!String.IsNullOrWhiteSpace(vcodec) && fps > 0)
            {
                if (audioDelay == 0)
                    jobLog.WriteEntry("Skipping Audio Delay correction, cannnot read", Log.LogEntryType.Warning);

                if (audioDelay > 0) // positive is used on video track
                    FFmpegParams += " -itsoffset " + audioDelay.ToString(CultureInfo.InvariantCulture);

                // Define input video stream type
                if ((vcodec == "avc") || (vcodec == "h264"))
                    FFmpegParams += " -f h264";
                else
                    FFmpegParams += " -f mpegvideo";

                FFmpegParams += " -i " + Util.FilePaths.FixSpaces(extractGraphResults.VideoPart); // Video
                ffmpegMapParams += " -map " + ffmpegMapCount.ToString() + ":v";
                ffmpegMapCount++;

            }

            // Insert the audio streams
            foreach (string audioPart in extractGraphResults.AudioParts)
            {
                if (!String.IsNullOrWhiteSpace(vcodec) && fps > 0)
                    if (audioDelay < 0) // Negative is used on audio tracks
                        FFmpegParams += " -itsoffset " + (-1 * audioDelay).ToString(CultureInfo.InvariantCulture);

                FFmpegParams += " -i " + Util.FilePaths.FixSpaces(audioPart); // Audio streams
                ffmpegMapParams += " -map " + ffmpegMapCount.ToString() + ":a";
                ffmpegMapCount++;
            }

            // Copy all Streams to output file
            FFmpegParams += ffmpegMapParams;
            
            // Audio
            if (extractGraphResults.AudioParts.Count > 0)
                FFmpegParams += " -acodec copy";
            else
                FFmpegParams += " -an";

            // Video
            if (!String.IsNullOrWhiteSpace(vcodec) && fps > 0)
                FFmpegParams += " -vcodec copy";
            else
                FFmpegParams += " -vn";

            // Output file
            FFmpegParams += " -f mpegts " + Util.FilePaths.FixSpaces(remuxedFile);

            if (!FFmpeg.FFMpegExecuteAndHandleErrors(FFmpegParams, jobStatus, jobLog, Util.FilePaths.FixSpaces(remuxedFile))) // process was terminated or failed
            {
                jobLog.WriteEntry("FFmpeg Remux Parts failed", Log.LogEntryType.Error);
                Util.FileIO.TryFileDelete(remuxedFile);
                return false;
            }

            return (FileIO.FileSize(remuxedFile) <= 0 ? false : true); // check if the file exists
        }
Exemplo n.º 6
0
        /// <summary>
        /// Put the extracting raw streams back together in TS format using TsMuxer
        /// </summary>
        /// <param name="originalFile">Path to original source video</param>
        /// <param name="remuxedFile">Path to output remuxed file</param>
        /// <param name="extractGraphResults">Extract graph object which has extracts the stream into raw parts</param>
        /// <returns>True if successful</returns>
        public static bool RemuxRawTSMuxer(string originalFile, string remuxedFile, ExtractWithGraph extractGraphResults, JobStatus jobStatus, Log jobLog)
        {
            float audioDelay = 0, fps = 0;
            string vcodec = "";

            if (extractGraphResults == null || String.IsNullOrWhiteSpace(originalFile) || String.IsNullOrWhiteSpace(remuxedFile))
                return false;
            
            // Atleast 1 audio or video stream
            if (String.IsNullOrWhiteSpace(extractGraphResults.VideoPart) && (extractGraphResults.AudioParts.Count < 1))
                return false;

            Util.FileIO.TryFileDelete(remuxedFile); // Start clean

            // Audio Delay on the original file
            if (extractGraphResults.AudioParts.Count > 0)
            {
                audioDelay = VideoParams.AudioDelay(originalFile);
                jobLog.WriteEntry("Audio Delay : " + audioDelay.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            }

            // fps and codec of video
            if (!String.IsNullOrWhiteSpace(extractGraphResults.VideoPart))
            {
                fps = VideoParams.FPS(extractGraphResults.VideoPart);
                vcodec = VideoParams.VideoFormat(extractGraphResults.VideoPart).ToLower();
                FFmpegMediaInfo ffmpegStreamInfo = new FFmpegMediaInfo(extractGraphResults.VideoPart, jobStatus, jobLog);
                if (String.IsNullOrWhiteSpace(vcodec) || (fps <= 0) || ((fps > ffmpegStreamInfo.MediaInfo.VideoInfo.FPS) && (ffmpegStreamInfo.MediaInfo.VideoInfo.FPS > 0)))
                {
                    jobLog.WriteEntry("MediaInfo reading FPS/Vcodec failed, Reading FFMPEG info from " + extractGraphResults.VideoPart, Log.LogEntryType.Debug);

                    if (ffmpegStreamInfo.Success) // Don't check for parse error since this is raw video stream it will always give an parse error for N/A duration
                    {
                        if ((fps <= 0) || ((fps > ffmpegStreamInfo.MediaInfo.VideoInfo.FPS) && (ffmpegStreamInfo.MediaInfo.VideoInfo.FPS > 0))) // MediaInfo did not succeed or got wrong value
                            fps = ffmpegStreamInfo.MediaInfo.VideoInfo.FPS; // Get the FPS

                        if (String.IsNullOrWhiteSpace(vcodec))
                            vcodec = ffmpegStreamInfo.MediaInfo.VideoInfo.VideoCodec; // Get video codecc
                    }

                    if (fps <= 0)
                        jobLog.WriteEntry("Unable to read FPS from " + originalFile + ", assuming no video stream", Log.LogEntryType.Warning);

                    if (String.IsNullOrWhiteSpace(vcodec))
                        jobLog.WriteEntry("No Video Codec detected in raw stream, assuming no video stream", Log.LogEntryType.Warning);
                }

                jobLog.WriteEntry("Video Codec detected : " + vcodec + ", FPS : " + fps.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            }

            // Setup the TSMuxer - http://forum.doom9.org/archive/index.php/t-142559.html
            string MetaFile = MCEBuddy.Util.FilePaths.GetFullPathWithoutExtension(remuxedFile) + ".meta";
            string MetaFileContents = "MUXOPT --no-pcr-on-video-pid --new-audio-pes --vbr --vbv-len=500\r\n";

            if (String.IsNullOrWhiteSpace(vcodec) || fps <= 0)
                jobLog.WriteEntry("No video stream detected, skipping video" + fps.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug); // Nothing to do, no video stream
            else if ((vcodec == "avc") || (vcodec == "h264"))
            {
                /*if (!_allowH264CopyRemuxing)
                {
                    _jobStatus.ErrorMsg = "TsMuxer incompatible settings, H264 video detected";
                    _jobLog.WriteEntry(_jobStatus.ErrorMsg, Log.LogEntryType.Error);
                    return false;
                }*/
                // There is no other recourse so mux it

                MetaFileContents += "V_MPEG4/ISO/AVC, " + Util.FilePaths.FixSpaces(extractGraphResults.VideoPart) + ", fps=" + fps.ToString(CultureInfo.InvariantCulture) + ", insertSEI, contSPS\r\n";
            }
            else
                MetaFileContents += "V_MPEG-2, " + Util.FilePaths.FixSpaces(extractGraphResults.VideoPart) + ", fps=" + fps.ToString(CultureInfo.InvariantCulture) + "\r\n";

            // Setup the Audio streams
            foreach (string AudioPart in extractGraphResults.AudioParts)
            {
                string acodec = VideoParams.AudioFormat(AudioPart).ToLower();
                if (String.IsNullOrWhiteSpace(acodec))
                {
                    jobLog.WriteEntry("No Audio Codec detected in raw stream", Log.LogEntryType.Error);
                    return false;
                }

                jobLog.WriteEntry("Audio Stream Codec detected : " + acodec, Log.LogEntryType.Debug);

                if (acodec.Contains("ac3") || acodec.Contains("ac-3"))
                    MetaFileContents += "A_AC3";
                else if (acodec.Contains("aac"))
                    MetaFileContents += "A_AAC";
                else if (acodec.Contains("dts"))
                    MetaFileContents += "A_DTS";
                else
                    MetaFileContents += "A_MP3";

                MetaFileContents += ", " + Util.FilePaths.FixSpaces(AudioPart);

                if (audioDelay == 0)
                    jobLog.WriteEntry("Skipping Audio Delay correction, cannnot read", Log.LogEntryType.Warning);
                else
                    MetaFileContents += ", timeshift=" + (-1 * audioDelay).ToString(CultureInfo.InvariantCulture) + "s";

                MetaFileContents += "\r\n";
            }

            try
            {
                jobLog.WriteEntry("Writing TsMuxer Meta commands : \r\n" + MetaFileContents + "\r\nTo : " + MetaFile, Log.LogEntryType.Debug);
                System.IO.File.WriteAllText(MetaFile, MetaFileContents);
            }
            catch (Exception e)
            {
                jobLog.WriteEntry(Localise.GetPhrase("ReMuxTsMuxer failed to write Metafile...") + "\r\nError : " + e.ToString(), Log.LogEntryType.Error);
                return false;
            }

            AppWrapper.TSMuxer tsmuxer = new TSMuxer(Util.FilePaths.FixSpaces(MetaFile) + " " + Util.FilePaths.FixSpaces(remuxedFile), jobStatus, jobLog);
            tsmuxer.Run();

            Util.FileIO.TryFileDelete(MetaFile);

            if (!tsmuxer.Success) // process was terminated or failed
            {
                jobLog.WriteEntry("ReMuxTsMuxer failed", Log.LogEntryType.Error);
                Util.FileIO.TryFileDelete(remuxedFile);
                return false;
            }

            return (FileIO.FileSize(remuxedFile) <= 0 ? false : true); // check if the file exists
        }
Exemplo n.º 7
0
        /// <summary>
        /// Checks if the specified file has any zero channel audio tracks or no audio tracks (except original file)
        /// </summary>
        /// <param name="file">Full path to file to check</param>
        /// <returns>True if there are any zero channel audio tracks or No Audio Tracks (except Original file) or failure to read the audio streams</returns>
        private bool CheckForNoneOrZeroChannelAudioTrack(string file, JobStatus jobStatus, Log jobLog)
        {
            jobLog.WriteEntry("Checking for 0 Channel Audio Tracks in " + file, Log.LogEntryType.Debug);

            // Check for 0 channel audio stream in Remuxed file
            FFmpegMediaInfo ffmpegStreamInfo = new FFmpegMediaInfo(file, jobStatus, jobLog);
            if (ffmpegStreamInfo.Success && !ffmpegStreamInfo.ParseError)
            {
                if (ffmpegStreamInfo.ZeroChannelAudioTrackCount > 0)
                {
                    jobLog.WriteEntry("Found 0 channel audio track in file " + file, Log.LogEntryType.Warning);
                    return true;
                }

                if (String.Compare(file, _RecordingFile, true) != 0) // We don't check original file for no audio tracks (it is allowed to have no audio tracks)
                {
                    if (ffmpegStreamInfo.AudioTracks == 0 && _RecordingFileMediaInfo.AudioTracks != 0 && (_RecordingFileMediaInfo.AudioTracks > _RecordingFileMediaInfo.ImpariedAudioTrackCount)) // The original file itself may have no audio tracks or should have atleast 1 non imparired audio track
                    {
                        jobLog.WriteEntry("Found No audio tracks in remuxed file", Log.LogEntryType.Warning);
                        return true;
                    }
                }
            }
            else
            {
                jobLog.WriteEntry("Unable to read FFMPEG MediaInfo to verify audio streams in file " + file, Log.LogEntryType.Error);
                return true;
            }

            return false; // good
        }
Exemplo n.º 8
0
        public RemuxMCERecording(ConversionJobOptions cjo, JobStatus jobStatus, Log jobLog)
        {
            _jobStatus = jobStatus;
            _jobLog = jobLog;
            _RecordingFile = cjo.sourceVideo;
            _destinationPath = cjo.workingPath;
            _requestedAudioLanguage = cjo.audioLanguage;
            _tivoMAKKey = cjo.tivoMAKKey;

            if (Util.FilePaths.CleanExt(_RecordingFile) == ".ts") // Handle TS files difference since they will have the same namess
                _RemuxedFile = Path.Combine(_destinationPath, Path.GetFileNameWithoutExtension(_RecordingFile) + "-REMUXED.ts");
            else
                _RemuxedFile = Path.Combine(_destinationPath, Path.GetFileNameWithoutExtension(_RecordingFile) + ".ts");

            // Read various profile parameters
            Ini configProfileIni = new Ini(GlobalDefs.ProfileFile);
            _useRemuxsupp = configProfileIni.ReadBoolean(cjo.profile, "UseWTVRemuxsupp", false); // Some videos fail with FFMPEG remuxing and Mencoder encoding (use remuxsupp for remuxing there)
            _jobLog.WriteEntry(this, "Force Remuxsupp (UseWTVRemuxsupp) : " + _useRemuxsupp.ToString(), Log.LogEntryType.Debug);

            _forceWTVStreamsRemuxing = configProfileIni.ReadBoolean(cjo.profile, "ForceWTVStreamsRemuxing", false); // Use Streams remuxing for DVRMS and WTV files
            _jobLog.WriteEntry(this, "Force Streams Remuxing (ForceWTVStreamsRemuxing) : " + _forceWTVStreamsRemuxing.ToString(), Log.LogEntryType.Debug);

            _allowH264CopyRemuxing = configProfileIni.ReadBoolean(cjo.profile, "AllowH264CopyRemuxing", true); // Allow H.264 files to be remuxed into TS without recoding to MPEG2
            _jobLog.WriteEntry(this, "Allow H264 Copy Remuxing (AllowH264CopyRemuxing) (default: true) : " + _allowH264CopyRemuxing.ToString(), Log.LogEntryType.Debug);

            _allowAllCopyRemuxing = configProfileIni.ReadBoolean(cjo.profile, "AllowAllCopyRemuxing", false); // Allow any video codec to be remuxed into TS without recoding to MPEG2
            _jobLog.WriteEntry(this, "Allow All Video codec formats Copy Remuxing (AllowAllCopyRemuxing) (default: false) : " + _allowAllCopyRemuxing.ToString(), Log.LogEntryType.Debug);

            // Get the media info for the recording file once for the entire operation to reuse
            _jobLog.WriteEntry(this, "Reading Recording file " + _RecordingFile + " media information", Log.LogEntryType.Debug);
            _RecordingFileMediaInfo = new FFmpegMediaInfo(_RecordingFile, _jobStatus, _jobLog);

            // Check for donator version of Comskip
            Comskip checkComskip = new Comskip(MCEBuddyConf.GlobalMCEConfig.GeneralOptions.comskipPath, _jobLog);

            // Check if we are using a mpeg4 video and allowing h264 video codec for commercial skipping purposes
            if (_allowH264CopyRemuxing)
            {
                if (_mpeg4Codecs.Any(s => s.Contains(_RecordingFileMediaInfo.MediaInfo.VideoInfo.VideoCodec.ToLower())))
                {
                    if (cjo.commercialRemoval == CommercialRemovalOptions.Comskip)
                    {
                        if (checkComskip.IsDonator)
                            _jobLog.WriteEntry(this, "AllowH264CopyRemuxing will run fast for commercial detection, using donator version of Comskip", Log.LogEntryType.Information);
                        else
                            _jobLog.WriteEntry(this, "AllowH264CopyRemuxing is SLOW with the bundled Comskip. Use ShowAnalyzer or Comskip Donator version (http://www.comskip.org) to speed up commercial detection. Codec detected -> " + _RecordingFileMediaInfo.MediaInfo.VideoInfo.VideoCodec, Log.LogEntryType.Warning);
                    }
                }
            }

            // Check if we are using an unsupported codec and copying to TS format
            if (_allowAllCopyRemuxing)
                if (!_supportedCodecs.Any(s => s.Contains(_RecordingFileMediaInfo.MediaInfo.VideoInfo.VideoCodec.ToLower()))) // Check if we using any of the default supported codecs
                        _jobLog.WriteEntry(this, "AllowAllCopyRemuxing is enabled and an unsupported codec in the source video is detected. Some underlying programs may not work with this codec. Codec detected -> " + _RecordingFileMediaInfo.MediaInfo.VideoInfo.VideoCodec, Log.LogEntryType.Warning);
        }
Exemplo n.º 9
0
        /// <summary>
        /// Main conversion routine
        /// </summary>
        public void Convert()
        {
            _jobStatus.ErrorMsg = ""; //start clean
            _jobStatus.SuccessfulConversion = false; //no successful conversion yet
            _jobStatus.Completed = false;

            Log jobLog = CreateLog(_conversionOptions.sourceVideo);

            //Debug, dump all the conversion parameter before starting to help with debugging
            jobLog.WriteEntry("Starting conversion - DEBUG MESSAGES", Log.LogEntryType.Debug);
            jobLog.WriteEntry("Windows OS Version -> " + Util.OSVersion.TrueOSVersion.ToString() + " (" + Util.OSVersion.GetOSVersion().ToString() + ", " + Util.OSVersion.GetOSProductType() + ")", Log.LogEntryType.Information);
            jobLog.WriteEntry("Windows Platform -> " + (Environment.Is64BitOperatingSystem ? "64 Bit" : "32 Bit"), Log.LogEntryType.Information);
            jobLog.WriteEntry("MCEBuddy Build Platform -> " + ((IntPtr.Size == 4) ? "32 Bit" : "64 Bit"), Log.LogEntryType.Information);
            string currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
            jobLog.WriteEntry("MCEBuddy Build Version : " + currentVersion, Log.LogEntryType.Information);
            jobLog.WriteEntry("MCEBuddy Build Date : " + File.GetLastWriteTime(System.Reflection.Assembly.GetExecutingAssembly().Location).ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Information);
            jobLog.WriteEntry("MCEBuddy Running as Service : " + GlobalDefs.IsEngineRunningAsService.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Information);
            jobLog.WriteEntry(MCEBuddyConf.GlobalMCEConfig.GeneralOptions.ToString(), Log.LogEntryType.Debug);
            jobLog.WriteEntry(_conversionOptions.ToString(), Log.LogEntryType.Debug);
            jobLog.WriteEntry("Max Concurrent Jobs -> " + _maxConcurrentJobs.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            jobLog.WriteEntry("Commercial Skip Cut (profile + task) -> " + _commercialSkipCut.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            jobLog.WriteEntry("Auto DeInterlacing (profile + task) -> " + _autoDeinterlace.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            jobLog.WriteEntry("Pre-Conversion Commercial Remover -> " + _preConversionCommercialRemover.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            jobLog.WriteEntry("Copy LOG File -> " + _copyLOGFile.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            jobLog.WriteEntry("Free Space Check -> " + _spaceCheck.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            jobLog.WriteEntry("Subtitle Cut Segment Incremental Offset -> " + _subtitleSegmentOffset.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            jobLog.WriteEntry("Locale Language -> " + Localise.ThreeLetterISO().ToUpper(), Log.LogEntryType.Debug);
            
            try
            {
                RegistryKey installed_versions = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP");
                string[] version_names = installed_versions.GetSubKeyNames();
                //version names start with 'v', eg, 'v3.5' which needs to be trimmed off before conversion
                string Framework = version_names[version_names.Length - 1].Remove(0, 1);
                string SP = (string)installed_versions.OpenSubKey(version_names[version_names.Length - 1]).GetValue("SP", 0).ToString();
                jobLog.WriteEntry(".NET Framework Version -> " + Framework + ", Service Pack -> " + SP, Log.LogEntryType.Debug);
            }
            catch
            {
                jobLog.WriteEntry("Cannot get .NET Framework version from Registry", Log.LogEntryType.Warning);
            }

            try // the entire conversion process, incase of an abort signal
            {
                jobLog.WriteEntry(this, "Current System language is " + CultureInfo.CurrentCulture.DisplayName + " (" + CultureInfo.CurrentCulture.ThreeLetterISOLanguageName.ToString() + ")", Log.LogEntryType.Information);
                jobLog.WriteEntry(this, "Converting file -> " + _conversionOptions.sourceVideo, Log.LogEntryType.Information);

                // Create and clear the contents of the working folder
                Util.FilePaths.CreateDir(_conversionOptions.workingPath);
                Util.FileIO.ClearFolder(_conversionOptions.workingPath);
                if (!Directory.Exists(_conversionOptions.workingPath)) // Check if it exists, possible the folder isn't accesible or user put a bogus value here
                {
                    jobLog.WriteEntry(this, "Unable to create temp directory " + _conversionOptions.workingPath + ". Conversion will fail.", Log.LogEntryType.Error);
                    _jobStatus.ErrorMsg = "Unable to create temp directory";
                    Cleanup(jobLog);
                    return; // serious problem
                }

                // Print the vesion of FFMPEG or FFProbe being used (since FFProbe runs in silent mode) and dump file information
                if (!_jobStatus.Cancelled)
                    FFmpegMediaInfo.DumpFileInformation(OriginalFileName, _jobStatus, jobLog); // Dump the media information for debugging purposes

                // Run the pre metadata custom command from the user if configured
                if (!_jobStatus.Cancelled)
                {
                    LogStatus(Localise.GetPhrase("Running custom commands"), jobLog);

                    string tmpSrt, tmpEdl;
                    tmpEdl = (File.Exists(Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".edl") ? Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".edl" : "");
                    tmpSrt = (File.Exists(Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".srt") ? Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".srt" : "");
                    CustomCommand customCommand = new CustomCommand("PreMetaCustomCommand", _conversionOptions.profile, _conversionOptions.taskName, _conversionOptions.workingPath, "", _convertedFile, OriginalFileName, _remuxedVideo, tmpEdl, tmpSrt, null, _jobStatus, jobLog); // EDl and SRT file may lie with the source video
                    if (!customCommand.Run())
                    {
                        jobLog.WriteEntry(this, ("Pre metadata Custom command failed to run, critical failure"), Log.LogEntryType.Error);
                        _jobStatus.ErrorMsg = ("Pre metadata Custom command failed to run, critical failure");
                        Cleanup(jobLog);
                        return; // serious problem
                    }

                    // Check if the source file has been tampered with
                    if (!File.Exists(_conversionOptions.sourceVideo))
                    {
                        jobLog.WriteEntry(this, ("PreMeta Source file has been renamed or deleted by custom command") + " -> " + _conversionOptions.sourceVideo, Log.LogEntryType.Error);
                        _jobStatus.ErrorMsg = ("PreMeta Source file has been renamed or deleted by custom command");
                        Cleanup(jobLog);
                        return; // serious problem
                    }
                    else
                        jobLog.WriteEntry(this, ("Finished pre metadata custom command, source file size [KB]") + " " + (Util.FileIO.FileSize(_conversionOptions.sourceVideo) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                }

                // Get and process Metadata
                if (!_jobStatus.Cancelled)
                {
                    // Download/extract the video metadata first
                    LogStatus(Localise.GetPhrase("Getting show information and banner from Internet sources"), jobLog);
                    _metaData = new VideoMetaData(_conversionOptions, _jobStatus, jobLog);
                    _metaData.Extract();

                    // Check metadata for Copy Protection
                    if (_metaData.MetaData.CopyProtected && !_conversionOptions.renameOnly) // If it's copy protected fail the conversion here with the right message and status, if we are renaming only then skip the check
                    {
                        if (_conversionOptions.ignoreCopyProtection) // If we are asked to ignore copy protection, we will just continue
                            jobLog.WriteEntry(this, ("ERROR: VIDEO IS COPYPROTECTED. CONVERSION WILL FAIL, BUT TRYING ANYWAYS."), Log.LogEntryType.Warning);
                        else
                        {
                            jobLog.WriteEntry(this, ("ERROR: VIDEO IS COPYPROTECTED. CONVERSION WILL FAIL, STOPPING CONVERSION"), Log.LogEntryType.Error);
                            _jobStatus.ErrorMsg = ("ERROR: VIDEO IS COPYPROTECTED. CONVERSION WILL FAIL");
                            Cleanup(jobLog);
                            return;
                        }
                    }
                }

                // DO THIS HERE and NOT in in the queue manager because you need ALL METADATA before you can Generate Filename to compare with history file.
                // By now you have downloaded additional metadata which can be used to generate the filename
                // Now that we have metadata, figure out if we need to process the file
                // Check if the destination file exists (after calculating destination name and path) and check if are required to reprocess files that exist
                if (!_jobStatus.Cancelled)
                {
                    // Check if we need to check for destination file reprocessing
                    if (_conversionOptions.skipReprocessing)
                    {
                        jobLog.WriteEntry(this, "Checking for destination file skip reprocessing", Log.LogEntryType.Information);

                        string destinationFile = GetDestinationFilename(_conversionOptions, _metaData, OriginalFileName, jobLog);

                        // Check if we need to skip reprocessing this file
                        bool skipReprocessing = false;
                        if (File.Exists(destinationFile)) // Check if the destination file exists
                        {
                            jobLog.WriteEntry(this, "Destination file " + destinationFile + " EXISTS, skipping conversion - SUCCESSFUL processing", Log.LogEntryType.Warning);
                            skipReprocessing = true;
                        }

                        if (!skipReprocessing && _conversionOptions.checkReprocessingHistory) // If we aren't already skipping reprocessig (save disk IO/CPU), do we need to check the history file for reprocessing?
                        {
                            if (QueueManager.DoesConvertedFileExistCheckHistory(destinationFile))
                            {
                                jobLog.WriteEntry(this, "Destination file " + destinationFile + " found converted in HISTORY, skipping re-conversion - SUCCESSFUL processing", Log.LogEntryType.Warning);
                                skipReprocessing = true;
                            }
                        }
                        
                        // Check if the destination file exists
                        if (skipReprocessing)
                        {
                            // SPECIAL CONDITION, THIS IS A SUCCESSFUL EXIT - Let exit here since we don't need to process it
                            _convertedFile = destinationFile; // Update the link to the converted file as it will be required for post processing
                            _jobStatus.ErrorMsg = ""; // all done, no error here, skipping processing but successful
                            _jobStatus.SuccessfulConversion = _jobStatus.SuccessfulSkipConversion = true; // now the original file can be deleted if required
                            Cleanup(jobLog);
                            return;
                        }
                        else
                        {
                            jobLog.WriteEntry(this, "Destination file " + destinationFile + " does not exist, continuing with conversion", Log.LogEntryType.Information);
                        }
                    }
                }

                // Run the pre remuxing custom command from the user if configured
                if (!_jobStatus.Cancelled)
                {
                    LogStatus(Localise.GetPhrase("Running custom commands"), jobLog);

                    string tmpSrt, tmpEdl;
                    tmpEdl = (File.Exists(Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".edl") ? Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".edl" : "");
                    tmpSrt = (File.Exists(Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".srt") ? Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".srt" : "");
                    CustomCommand customCommand = new CustomCommand("PreCustomCommand", _conversionOptions.profile, _conversionOptions.taskName, _conversionOptions.workingPath, Path.GetDirectoryName(GetDestinationFilename(_conversionOptions, _metaData, OriginalFileName, jobLog)), _convertedFile, OriginalFileName, _remuxedVideo, tmpEdl, tmpSrt, _metaData.MetaData, _jobStatus, jobLog); // EDL and SRT file may lie with the source video
                    if (!customCommand.Run())
                    {
                        jobLog.WriteEntry(this, ("Pre remuxing Custom command failed to run, critical failure"), Log.LogEntryType.Error);
                        _jobStatus.ErrorMsg = ("Pre remuxing Custom command failed to run, critical failure");
                        Cleanup(jobLog);
                        return; // serious problem
                    }

                    // Check if the source file has been tampered with
                    if (!File.Exists(WorkingVideo))
                    {
                        jobLog.WriteEntry(this, ("Pre remuxing Source file has been renamed or deleted by custom command") + " -> " + _conversionOptions.sourceVideo, Log.LogEntryType.Error);
                        _jobStatus.ErrorMsg = ("Pre rexmuing Source file has been renamed or deleted by custom command");
                        Cleanup(jobLog);
                        return; // serious problem
                    }
                    else
                        jobLog.WriteEntry(this, ("Finished pre remuxing custom command, source file size [KB]") + " " + (Util.FileIO.FileSize(_conversionOptions.sourceVideo) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                }

                //Check for available disk space
                LogStatus(Localise.GetPhrase("Checking for disk space"), jobLog);
                if (!SufficientSpace(jobLog))
                {
                    jobLog.WriteEntry(this, ("Insufficient disk space"), Log.LogEntryType.Error);
                    _jobStatus.ErrorMsg = "Insufficient disk space";
                    Cleanup(jobLog); // close the joblog and clean up
                    return;
                }

                // Check for small test files);)
                if (Util.FileIO.FileSize(_conversionOptions.sourceVideo) < 100000000)
                {
                    jobLog.WriteEntry(this, ("This is a small video file less than 100MB. Some actions such as advertisement removal will not occur"), Log.LogEntryType.Warning);
                    _conversionOptions.commercialRemoval = CommercialRemovalOptions.None;
                }

                // Copy support files over
                if (!_jobStatus.Cancelled)
                {
                    // If the SRT File exists, copy it
                    jobLog.WriteEntry(this, ("Checking for SRT File"), Log.LogEntryType.Information);
                    {
                        string srtFile;
                        if (Text.ContainsUnicode(OriginalFileName) && !_conversionOptions.renameOnly) // Unicode test, not all underlying apps and functions currently support UNICODE filenames, so use a ASCII name for now
                            srtFile = GlobalDefs.UNICODE_TEMPNAME + ".srt";
                        else
                            srtFile = Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".srt"; // SRT file along with original source video

                        if (File.Exists(srtFile) && (Util.FileIO.FileSize(srtFile) > 0)) // Exist and non empty
                        {
                            string srtDest = Path.Combine(_conversionOptions.workingPath, Path.GetFileName(srtFile));
                            try
                            {
                                File.Copy(srtFile, srtDest);
                                _deleteSRTFile = true;
                                _srtFile = srtDest;
                                jobLog.WriteEntry(this, ("Found existing SRT file and saved it"), Log.LogEntryType.Information);
                            }
                            catch (Exception e)
                            {
                                jobLog.WriteEntry(this, ("Found existing SRT file but unable to save it") + "\r\nError : " + e.ToString(), Log.LogEntryType.Warning);
                            }
                        }
                    }

                    //If the EDL/EDLP file exists, copy it to working directory
                    jobLog.WriteEntry(this, Localise.GetPhrase("Checking for EDL File"), Log.LogEntryType.Information);
                    {
                        string edlFile;
                        if (Text.ContainsUnicode(OriginalFileName) && !_conversionOptions.renameOnly) // Unicode test, not all underlying apps and functions currently support UNICODE filenames, so use a ASCII name for now
                            edlFile = GlobalDefs.UNICODE_TEMPNAME + ".edl";
                        else
                            edlFile = Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".edl";

                        if (File.Exists(edlFile)) // EDL File
                        {
                            string edlDest = Path.Combine(_conversionOptions.workingPath, Path.GetFileName(edlFile));
                            try
                            {
                                File.Copy(edlFile, edlDest);
                                _saveEDLFile = true;
                                jobLog.WriteEntry(this, ("Found existing EDL file and saved it"), Log.LogEntryType.Information);
                            }
                            catch (Exception e)
                            {
                                jobLog.WriteEntry(this, ("Found existing EDL file but unable to save it") + "\r\nError : " + e.ToString(), Log.LogEntryType.Warning);
                            }
                        }
                    }

                    // Check for EDLP file
                    jobLog.WriteEntry(this, ("Checking for EDLP File"), Log.LogEntryType.Information);
                    {
                        string edlpFile;
                        if (Text.ContainsUnicode(OriginalFileName) && !_conversionOptions.renameOnly) // Unicode test, not all underlying apps and functions currently support UNICODE filenames, so use a ASCII name for now
                            edlpFile = GlobalDefs.UNICODE_TEMPNAME + ".edlp";
                        else
                            edlpFile = Util.FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".edlp";

                        if (File.Exists(edlpFile)) // EDLP file
                        {
                            string edlpDest = Path.Combine(_conversionOptions.workingPath, Path.GetFileName(edlpFile));
                            try
                            {
                                File.Copy(edlpFile, edlpDest);
                                _saveEDLFile = true;
                                jobLog.WriteEntry(this, ("Found existing EDLP file and saved it"), Log.LogEntryType.Information);
                            }
                            catch (Exception e)
                            {
                                jobLog.WriteEntry(this, ("Found existing EDLP file but unable to save it") + "\r\nError : " + e.ToString(), Log.LogEntryType.Warning);
                            }
                        }
                    }
                }

                if (!_jobStatus.Cancelled)
                {
                    // DO this last before starting the analysis/conversion process after copying SRT, EDL, NFO etc files
                    // If Commerical removal is set for TS files copy them to the temp directory (During Commercial removal, files (except TS) are remuxed later into their own temp working directories)
                    // We need exclusive access to each copy of the file in their respective temp working directories otherwise commercial skipping gets messed up when we have multiple simultaneous tasks for the same file (they all share/overwrite the same EDL file) which casuses a failure
                    // Check if the TS file (or any file if we are skipping remuxing) has any Zero Channel Audio tracks, in which case it needs to be remuxed to remove/compensate for them, remuxing will overwrite the original file if it's a TS file, so make a local copy
                    string sourceExt = FilePaths.CleanExt(_conversionOptions.sourceVideo);
                    if ((sourceExt == ".ts") || _conversionOptions.skipRemuxing)
                    {
                        FFmpegMediaInfo ffmpegStreamInfo = new FFmpegMediaInfo(_conversionOptions.sourceVideo, _jobStatus, jobLog);
                        if (ffmpegStreamInfo.Success && !ffmpegStreamInfo.ParseError)
                        {
                            if ((ffmpegStreamInfo.ZeroChannelAudioTrackCount > 0)) // TODO: Do we need to check for Imparied Audio tracks here when not Remuxing? (imparied audio tracks are sometimes empty - can they cause the encoders to fail?)
                                _zeroChannelAudio = true;
                        }
                        else
                        {
                            jobLog.WriteEntry(this, ("Unable to read FFMPEG MediaInfo to verify remuxsupp audio streams"), Log.LogEntryType.Warning);
                        }
                    }

                    // Make a local copy only if we are renaming, has zero channel audio or skipping remuxing for non-TS or is a TS file with some conditions met (like commercial removal, etc where the original file may be modified or locked and accessed simultaneously)
                    // If the sourcefile has a Unicode name, always copy it since underlying programs cannot handle unicode names and then rename the local copy
                    if ((Text.ContainsUnicode(_conversionOptions.sourceVideo) && !_conversionOptions.renameOnly) || (!_conversionOptions.skipCopyBackup && (_zeroChannelAudio || _conversionOptions.renameOnly || (((sourceExt == ".ts") || ((sourceExt != ".ts") && _conversionOptions.skipRemuxing)) && (_conversionOptions.commercialRemoval != CommercialRemovalOptions.None)))))
                    {
                        if (_conversionOptions.skipCopyBackup)
                            jobLog.WriteEntry(this, "DANGER: Skip copying original files (skip original backup) is enabled. This could lead to conversion failures or unpredictable outcomes. Please DISABLE this option unless absolutely necessary.", Log.LogEntryType.Warning);

                        string newSource;
                        if (Text.ContainsUnicode(_conversionOptions.sourceVideo) && !_conversionOptions.renameOnly) // Unicode test, not all underlying apps and functions currently support UNICODE filenames, so use a ASCII name for now
                            newSource = Path.Combine(_conversionOptions.workingPath, GlobalDefs.UNICODE_TEMPNAME + Util.FilePaths.CleanExt(_conversionOptions.sourceVideo));
                        else
                            newSource = Path.Combine(_conversionOptions.workingPath, Path.GetFileName(_conversionOptions.sourceVideo));

                        LogStatus(Localise.GetPhrase("Copying source file to working directory"), jobLog);
                        try
                        {
                            jobLog.WriteEntry(this, ("Copying source video to working directory") + " Source:" + _conversionOptions.sourceVideo + ", Target:" + newSource, Log.LogEntryType.Information);
                            File.Copy(_conversionOptions.sourceVideo, newSource, true); // Copy the file, overwrite if required
                            
                            _conversionOptions.sourceVideo = newSource; // replace the source file that we will work on going forward on success
                        }
                        catch (Exception e)
                        {
                            jobLog.WriteEntry(this, ("Unable to copy source video to working directory") + " Source:" + _conversionOptions.sourceVideo + ", Target:" + newSource + " Error : " + e.ToString(), Log.LogEntryType.Error);
                            _jobStatus.ErrorMsg = ("Unable to copy source video to working directory");
                            Cleanup(jobLog);
                            return;
                        }
                    }
                }

                // If we are ONLY renaming NOT using free Comskip AND WITHOUT extracting subtitles, we skip the video processing, removal etc (i.e. no Remuxing)
                // If detecting commercials while only renaming, Showanalyzer and donator comskip support all formats, so no remuxing required
                if (!(_conversionOptions.renameOnly && !(_conversionOptions.commercialRemoval == CommercialRemovalOptions.Comskip && !(new Comskip(MCEBuddyConf.GlobalMCEConfig.GeneralOptions.comskipPath, jobLog).IsDonator)) && String.IsNullOrEmpty(_conversionOptions.extractCC)))
                {
                    if (!_jobStatus.Cancelled)
                    {
                        if (_conversionOptions.skipRemuxing)
                            jobLog.WriteEntry(this, "SKIPPING REMUXING, this may lead to conversion failure since all underlying apps may not support all file formats.\r\nWTV commercial detection is only supported by donator version of Comskip (http://www.kaashoek.com/comskip/).", Log.LogEntryType.Warning);

                        // Remux media center recordings or any video if commercials are being removed to TS (except TS) and we are not asked to skip remuxing
                        // Unless TS tracks have zero channel audio in them, then remux them to remove the zero audio channel tracks, otherwise future functions fails (like trim) - NOTE while 0 TS channel audio is remuxed, it ends up replacing the original file
                        string sourceExt = FilePaths.CleanExt(_conversionOptions.sourceVideo);
                        if (((sourceExt != ".ts") && !_conversionOptions.skipRemuxing) || _zeroChannelAudio)
                        {
                            LogStatus(Localise.GetPhrase("Remuxing recording"), jobLog);
                            RemuxMediaCenter.RemuxMCERecording remux = new RemuxMCERecording(_conversionOptions, _jobStatus, jobLog);
                            bool res = remux.Remux();
                            jobLog.WriteEntry(this, ("Remuxing: Percentage Complete") + " " + _jobStatus.PercentageComplete.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                            if (!res)       
                            {
                                // Remux failed
                                _jobStatus.ErrorMsg = ("Remux failed");
                                jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                                Cleanup(jobLog);
                                return;
                            }
                            WorkingVideo = remux.RemuxedFile;
                            _workingVideoRecoded = remux.VideoStreamRecoded; // Was the original video recoded? We lost CC data in remued file
                            _initialEDLSkipSeconds = (_saveEDLFile ? remux.SkipInitialSeconds : 0); // If we are using a saved EDL File, we need to compensate for any seconds that were trimmed while remuxing to compensate for EDL later
                            jobLog.WriteEntry(this, "Was remuxed video recoded : " + _workingVideoRecoded.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                            jobLog.WriteEntry(this, "Remuxed video file : " + WorkingVideo, Log.LogEntryType.Debug);
                            jobLog.WriteEntry(this, Localise.GetPhrase("Finished Remuxing, file size [KB]") + " " + (Util.FileIO.FileSize(WorkingVideo) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                        }
                    }
                }

                // If we are ONLY renaming, we skip the video processing, trimming, removal etc
                if (!_conversionOptions.renameOnly)
                {
                    // If we are trimming the video, try to do it here, if it fails then try to do it during the conversion
                    if (!_jobStatus.Cancelled)
                    {
                        LogStatus(Localise.GetPhrase("Trimming video recording"), jobLog);
                        TrimVideo trimVideo = new TrimVideo(_conversionOptions.profile, _jobStatus, jobLog);
                        if (!trimVideo.Trim(WorkingVideo, _conversionOptions.workingPath, _conversionOptions.startTrim, _conversionOptions.endTrim))
                        {
                            // Trimming failed - just log the error here, we'll try to pick it up during conversion
                            jobLog.WriteEntry(this, "Trimming failed, will retry during conversion", Log.LogEntryType.Warning);
                        }
                        else
                        {
                            jobLog.WriteEntry(this, "Trimming successful, setting trim parameters to 0 to avoid retrimming", Log.LogEntryType.Debug);
                            _conversionOptions.startTrim = _conversionOptions.endTrim = 0; // Indicate that trimming is complete so we don't redo it later
                            WorkingVideo = trimVideo.TrimmedVideo; // Update the working video path with the new trimmed video
                        }
                    }
                }

                // Do commercial detection if there is not metadata copy protection since this can be run for rename only option also
                if (!_jobStatus.Cancelled && (!_metaData.MetaData.CopyProtected || _conversionOptions.ignoreCopyProtection))
                {
                    // Get the Video duration
                    float duration = VideoParams.VideoDuration(WorkingVideo);
                    if (duration <= 0)
                    {

                        FFmpegMediaInfo ffmpegStreamInfo = new FFmpegMediaInfo(WorkingVideo, _jobStatus, jobLog);
                        if (!ffmpegStreamInfo.Success || ffmpegStreamInfo.ParseError)
                        {
                            // Getting Video Duration failed
                            _jobStatus.ErrorMsg = Localise.GetPhrase("Unable to get Video Duration");
                            jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                            Cleanup(jobLog);
                            return;
                        }

                        duration = ffmpegStreamInfo.MediaInfo.VideoInfo.Duration;
                    }

                    // If we are using ShowAnalyzer, do it here
                    jobLog.WriteEntry(this, "Checking for ShowAnalyzer", Log.LogEntryType.Information);
                    if (_conversionOptions.commercialRemoval == CommercialRemovalOptions.ShowAnalyzer)
                    {
                        LogStatus(Localise.GetPhrase("ShowAnalyzer advertisement scan"), jobLog);
                        _commercialScan = new Scanner(_conversionOptions, WorkingVideo, true, duration, _jobStatus, jobLog);
                        if (!_commercialScan.Scan())
                        {
                            _jobStatus.ErrorMsg = Localise.GetPhrase("ShowAnalyzer failed");
                            jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                            Cleanup(jobLog);
                            return;
                        }
                    }

                    // If we are using comskip, do it here
                    jobLog.WriteEntry(this, "Checking for Comskip", Log.LogEntryType.Information);
                    if (_conversionOptions.commercialRemoval == CommercialRemovalOptions.Comskip)
                    {
                        LogStatus(Localise.GetPhrase("Comskip advertisement scan"), jobLog);
                        _commercialScan = new Scanner(_conversionOptions, WorkingVideo, false, duration, _jobStatus, jobLog);
                        if (!_commercialScan.Scan())
                        {
                            _jobStatus.ErrorMsg = "Comskip failed";
                            jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                            Cleanup(jobLog);
                            return;
                        }
                    }

                    // We extract the closed captions after the commerical scanning, trimming and video info is complete
                    // We check if the SRT file alread exists and needs to be adjusted for EDL commercials
                    // If not we check if commerical removal is enabled, if so we need to create a temp file which we will cut to compensate for EDL and THEN extract the CC (this helps keep the audio in sync with the CC due to cutting on non KeyFrames issues)
                    // If no commercial removal we just extract the CC from the remuxed file (TS)
                    if (!_jobStatus.Cancelled)
                    {
                        // CC Extractor only works on TS files or if WTV/DVRMS, we can extract the streams and remux into TS file
                        if (!String.IsNullOrEmpty(_conversionOptions.extractCC) && ((FilePaths.CleanExt(WorkingVideo) == ".ts") || (_conversionOptions.skipRemuxing && ((FilePaths.CleanExt(WorkingVideo) == ".wtv") || (FilePaths.CleanExt(WorkingVideo) == ".dvr-ms")))))
                        {
                            LogStatus(Localise.GetPhrase("Extracting closed captions"), jobLog);

                            // Setup closed captions for extraction
                            _cc = new ClosedCaptions(_conversionOptions.profile, _jobStatus, jobLog);

                            if (!(String.IsNullOrWhiteSpace(_srtFile))) // We already have a SRT file to work with
                            {
                                jobLog.WriteEntry(this, "Found saved SRT file -> " + _srtFile, Log.LogEntryType.Debug);

                                // We need to validate the clean up the SRT file 
                                LogStatus(Localise.GetPhrase("Validating closed captions"), jobLog);
                                if (!_cc.SRTValidateAndClean(_srtFile))
                                {
                                    // Validating CC failed
                                    _jobStatus.ErrorMsg = Localise.GetPhrase("Validating closed captions failed");
                                    jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                                    Cleanup(jobLog);
                                    return;
                                }

                                if ((_commercialScan != null) && (!_commercialSkipCut)) // Incase we asked not to cut the video, just create the EDL file, let us not cut the SRT files also
                                {
                                    if (_commercialScan.CommercialsFound) // We just adjust the SRT file with the EDL file
                                    {
                                        LogStatus(Localise.GetPhrase("Trimming closed captions"), jobLog);
                                        if (!_cc.EDLTrim(_commercialScan.EDLFile, _srtFile, _conversionOptions.ccOffset, _subtitleSegmentOffset))
                                        {
                                            // Trimming CC failed
                                            _jobStatus.ErrorMsg = Localise.GetPhrase("Trimming closed captions failed");
                                            jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                                            Cleanup(jobLog);
                                            return;
                                        }
                                    }
                                }
                            }
                            else // We need to create a SRT file
                            {
                                string tempCCFile = "";
                                tempCCFile = WorkingVideo; // If there are no commercials to remove, we can work directly on the remuxed file

                                // If we are skipping remuxing for wtv/dvrms files then we need to extract the streams data
                                // If the original video was recoded we have lost the CC data, try to extract the original video stream
                                if ((_workingVideoRecoded || _conversionOptions.skipRemuxing) && ((Util.FilePaths.CleanExt(_conversionOptions.sourceVideo) == ".wtv") || (Util.FilePaths.CleanExt(_conversionOptions.sourceVideo) == ".dvr-ms")))
                                {
                                    ExtractWithGraph extractVideo = null;

                                    try
                                    {
                                        LogStatus(Localise.GetPhrase("Extracting streams"), jobLog);
                                        jobLog.WriteEntry("Creating graph to extract streams", Log.LogEntryType.Debug);
                                        extractVideo = new ExtractWithGraph(_conversionOptions.sourceVideo, _conversionOptions.workingPath, ExtractWithGraph.ExtractMediaType.Video, _jobStatus, jobLog); // We only need video stream to extract CCData using ccExtractor
                                        jobLog.WriteEntry("Building graph", Log.LogEntryType.Debug);
                                        extractVideo.BuildGraph();
                                        jobLog.WriteEntry("Extracting streams", Log.LogEntryType.Debug);
                                        extractVideo.RunGraph();
                                        if ((!extractVideo.SuccessfulExtraction) || (String.IsNullOrWhiteSpace(extractVideo.VideoPart))) // Successful extraction and atleast one video stream
                                        {
                                            jobLog.WriteEntry(this, "Error extracting streams using Graph", Log.LogEntryType.Warning);
                                            throw new Exception("Graph extraction unsuccessful"); // It'll clean up automatically
                                        }

                                        // Dispose the Graph object
                                        try
                                        {
                                            jobLog.WriteEntry("Disposing Graph....", Log.LogEntryType.Debug);
                                            extractVideo.Dispose();
                                        }
                                        catch (Exception e)
                                        {
                                            jobLog.WriteEntry(this, "Error disposing graph.\r\nError : " + e.ToString(), Log.LogEntryType.Warning); // We can still continue
                                        }

                                        // We need to put the stream parts back together in a TS format so that ccExtractor can work on it
                                        // temp file name to be used by ccExtractor
                                        string ccStreamsFile = Path.Combine(_conversionOptions.workingPath, Path.GetFileNameWithoutExtension(WorkingVideo) + "-ccExtract.ts");

                                        // Put the streams back together in TS format
                                        jobLog.WriteEntry(this, Localise.GetPhrase("Muxing streams with TsMuxer"), Log.LogEntryType.Information);
                                        LogStatus(Localise.GetPhrase("Muxing streams"), jobLog);
                                        if (!RemuxMCERecording.RemuxRawTSMuxer(_conversionOptions.sourceVideo, ccStreamsFile, extractVideo, _jobStatus, jobLog))
                                        {
                                            jobLog.WriteEntry(this, "TsMuxer Streams Muxing failed", Log.LogEntryType.Error);

                                            // Otherwise try tsMuxer
                                            jobLog.WriteEntry(this, Localise.GetPhrase("Fallback muxing streams with FFMpegParts"), Log.LogEntryType.Information);
                                            LogStatus(Localise.GetPhrase("Fallback muxing streams"), jobLog);
                                            if (!RemuxMCERecording.RemuxRawPartsFFmpeg(_conversionOptions.sourceVideo, ccStreamsFile, extractVideo, _jobStatus, jobLog))
                                            {
                                                extractVideo.DeleteParts(); // Clean up
                                                FileIO.TryFileDelete(ccStreamsFile); // Clean up
                                                jobLog.WriteEntry(this, "Fallback Remux Streams FFMpegParts failed", Log.LogEntryType.Warning);
                                            }
                                        }

                                        extractVideo.DeleteParts(); // Clean up, we have the file now

                                        // All good we should have a video file now
                                        if (FileIO.FileSize(ccStreamsFile) > 0) // We have a good file
                                            tempCCFile = ccStreamsFile; // This is what we will work with now
                                        else
                                        {
                                            FileIO.TryFileDelete(ccStreamsFile); // Clean up
                                            jobLog.WriteEntry("Extracted video stream unsuccessful, continuing without closed captions", Log.LogEntryType.Warning);
                                        }
                                    }
                                    catch (Exception e)
                                    {
                                        try
                                        {
                                            jobLog.WriteEntry(this, ("Unable to extract video using DirectShow Graph to get closed captions.\r\nError : " + e.ToString()), Log.LogEntryType.Warning);
                                            if (extractVideo != null)
                                            {
                                                extractVideo.DeleteParts();
                                                extractVideo.Dispose();
                                            }
                                        }
                                        catch (Exception e1)
                                        {
                                            jobLog.WriteEntry(this, "Error disposing directshow graph.\r\nError : " + e1.ToString(), Log.LogEntryType.Warning);
                                        }

                                        // We couldn't extract the video - continue with Working Video - we can still use the remuxed TS file to extract CC possibly
                                    }
                                }

                                // DONT NEED TO ACTUALLY CUT THE VIDEO, RATHER JUST EXTRACT AND ADJUST, GIVES GREATER DEGREE OF CONTROL ON SYNC ISSUES WHEN THE ACTUAL VIDEO IS CUT
                                /*if ((_commercialScan != null) && (!commercialSkipCut)) // If we are not asked to skip cutting commercials (we don't need to adjust CC), for commerical cutting we need to create a special EDL adjusted file before extracting of CC
                                {
                                    jobLog.WriteEntry(this, "Checking if commercials were found", Log.LogEntryType.Information);
                                    if (_commercialScan.CommercialsFound) // If there are commericals we need to create a special EDL cut file to work with before extracting CC
                                    {
                                        // Copy the remuxed file to a temp file which we will then cut using the EDL file and then extract the closed captions
                                        // This helps keep the closed captions in sync with the cut video
                                        tempCCFile = Path.Combine(_conversionOptions.workingPath, Path.GetFileNameWithoutExtension(WorkingVideo) + "-CCTemp" + Path.GetExtension(SourceVideo));
                                        try
                                        {
                                            // Create a temp file for CC EDL cutting and extracting
                                            jobLog.WriteEntry(this, "Creating temporary remuxed file for extracting CC and adjusting for commercials", Log.LogEntryType.Debug);
                                            File.Copy(SourceVideo, tempCCFile);
                                        }
                                        catch (Exception e)
                                        {
                                            // Creating temp CC file failed
                                            _jobStatus.ErrorMsg = Localise.GetPhrase("Creating temporary file for CC extracting file failed");
                                            jobLog.WriteEntry(this, _jobStatus.ErrorMsg + "\r\nError : " + e.ToString(), Log.LogEntryType.Error);
                                            Cleanup(jobLog);
                                            return;
                                        }

                                        // Now adjust the file for commercial removal
                                        jobLog.WriteEntry(this, "Removing commercials from temp file before extracting CC", Log.LogEntryType.Debug);
                                        Remover edlCCAdjust = new Remover(_conversionOptions.profile, tempCCFile, _commercialScan.EDLFile, ref _videoFile, _jobStatus, jobLog); // Pass the original or remuxed video here
                                        edlCCAdjust.StripCommercials();
                                        _videoFile.AdsRemoved = false; // Reset it, since we didn't really remove the ad's, just on temp file for extracting CC's
                                        if (_jobStatus.PercentageComplete == 0) // for Commercial Stripping failure, this numbers is set to 0
                                        {
                                            // Adjusting EDL CC failed
                                            _jobStatus.ErrorMsg = Localise.GetPhrase("Removing commercials from temp file for CC extracting failed");
                                            jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                                            Cleanup(jobLog);
                                            return;
                                        }
                                    }
                                }*/

                                // Trimming is already complete so we just need to extract 
                                _deleteSRTFile = true; // We are extracting a SRT file, delete it incase it's along with the original file
                                LogStatus(Localise.GetPhrase("Extracting closed captions"), jobLog);
                                if (!_cc.Extract(tempCCFile, _conversionOptions.workingPath, _conversionOptions.extractCC, 0, 0, _conversionOptions.ccOffset))
                                {
                                    // Extracting CC failed
                                    _jobStatus.ErrorMsg = Localise.GetPhrase("Extracting closed captions failed");
                                    jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                                    Cleanup(jobLog);
                                    return;
                                }

                                // We need to validate the clean up the SRT file 
                                LogStatus(Localise.GetPhrase("Validating closed captions"), jobLog);
                                if (!_cc.SRTValidateAndClean(_cc.SRTFile))
                                {
                                    // Validating CC failed
                                    _jobStatus.ErrorMsg = Localise.GetPhrase("Validating closed captions failed");
                                    jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                                    Cleanup(jobLog);
                                    return;
                                }

                                if (!String.IsNullOrWhiteSpace(_cc.SRTFile)) // If we have a valid SRT file
                                {
                                    if (String.Compare(tempCCFile, WorkingVideo, true) != 0) // If we created a temp file, lets get rid of it now
                                        Util.FileIO.TryFileDelete(tempCCFile);

                                    _srtFile = _cc.SRTFile; // Save the SRT file location

                                    jobLog.WriteEntry(this, "SRT file -> " + _srtFile, Log.LogEntryType.Debug);
                                }
                            }
                        }
                    }
                }

                // If we are ONLY renaming, we skip the video processing, trimming, removal etc
                if (!_conversionOptions.renameOnly)
                {
                    // Get the video properties
                    if (!_jobStatus.Cancelled)
                    {
                        // Create the Video File object and get the container + stream information
                        LogStatus(Localise.GetPhrase("Analyzing video information"), jobLog);

                        _videoFile = new VideoInfo(true, false, _conversionOptions.sourceVideo, _remuxedVideo, "", _conversionOptions.audioLanguage, _jobStatus, jobLog); // Skip cropping information for now, we'll get that later before conversion
                        if (_videoFile.Error)
                        {
                            _jobStatus.ErrorMsg = "Analyzing video information failed";
                            jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                            Cleanup(jobLog);
                            return;
                        }
                    }

                    // Run the pre commercial removal custom command from the user if configured
                    if (!_jobStatus.Cancelled)
                    {
                        LogStatus(Localise.GetPhrase("Running custom commands"), jobLog);

                        CustomCommand customCommand = new CustomCommand("PreCommercialRemovalCustomCommand", _conversionOptions.profile, _conversionOptions.taskName, _conversionOptions.workingPath, Path.GetDirectoryName(GetDestinationFilename(_conversionOptions, _metaData, OriginalFileName, jobLog)), _convertedFile, OriginalFileName, _remuxedVideo, (_commercialScan == null ? "" : _commercialScan.EDLFile), _srtFile, _metaData.MetaData, _jobStatus, jobLog); // EDL and SRT file may lie with the source video
                        if (!customCommand.Run())
                        {
                            jobLog.WriteEntry(this, Localise.GetPhrase("Pre commercial removal Custom command failed to run, critical failure"), Log.LogEntryType.Error);
                            _jobStatus.ErrorMsg = Localise.GetPhrase("Pre commercial removal Custom command failed to run, critical failure");
                            Cleanup(jobLog);
                            return; // serious problem
                        }

                        // Check if the working file has been tampered with
                        if (!File.Exists(WorkingVideo))
                        {
                            jobLog.WriteEntry(this, Localise.GetPhrase("Pre commercial removal working file has been renamed or deleted by custom command") + " -> " + WorkingVideo, Log.LogEntryType.Error);
                            _jobStatus.ErrorMsg = Localise.GetPhrase("Pre commercial removal working file has been renamed or deleted by custom command");
                            Cleanup(jobLog);
                            return; // serious problem
                        }
                        else
                            jobLog.WriteEntry(this, Localise.GetPhrase("Finished pre commercial removal  custom command, source file size [KB]") + " " + (Util.FileIO.FileSize(_conversionOptions.sourceVideo) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                    }

                    // Remove the commercials incase they aren't supported format for after conversion for commercial removal
                    if (!_jobStatus.Cancelled)
                    {
                        if (_commercialScan != null)
                        {
                            // Check if the final conversion extension has a format that's supported by the commercial remover, else remove the commercials here itself during the TS file stage
                            // Check if the profile dicates to remove commercials before the actual conversion
                            if (_preConversionCommercialRemover || (!Remover.IsSupportedExtension(Transcode.Convert.GetConversionExtension(_conversionOptions), _conversionOptions.profile)))
                            {
                                jobLog.WriteEntry(this, "Final format is not a supported format for removing commercials, PRE-Removing commercials for Ext -> " + Transcode.Convert.GetConversionExtension(_conversionOptions), Log.LogEntryType.Information);
                                jobLog.WriteEntry(this, "Checking if commercials were found", Log.LogEntryType.Information);
                                if ((_commercialScan.CommercialsFound) && (!_videoFile.AdsRemoved)) //commercials might be stripped
                                {
                                    if (!_commercialSkipCut)
                                    {
                                        LogStatus(Localise.GetPhrase("Removing commercials"), jobLog);
                                        _commercialRemover = new Remover(_conversionOptions.profile, WorkingVideo, _conversionOptions.workingPath, _commercialScan.EDLFile, _initialEDLSkipSeconds, _videoFile, _jobStatus, jobLog);
                                        _commercialRemover.StripCommercials(true); // we need to select the language while stripping the TS file else we lose the language information

                                        //We dont' check for % completion here since some files are very short and % isn't reliable
                                        if (_videoFile.AdsRemoved) // for Commercial Stripping success, this is true
                                        {
                                            WorkingVideo = _commercialRemover.CommercialFreeVideo; // Since we have a new commercial free video, this will be our new source (remuxed) video
                                            jobLog.WriteEntry(this, Localise.GetPhrase("Finished removing commercials, file size [KB]") + " " + (Util.FileIO.FileSize(WorkingVideo) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                                            
                                            // Cut the SRT file also if it exists
                                            if (!String.IsNullOrWhiteSpace(_srtFile))
                                            {
                                                LogStatus(Localise.GetPhrase("Trimming closed captions"), jobLog);
                                                if (!_cc.EDLTrim(_commercialScan.EDLFile, _srtFile, 0, _subtitleSegmentOffset)) // Offset is already compensated for while extraction
                                                {
                                                    // Trimming CC failed
                                                    _jobStatus.ErrorMsg = Localise.GetPhrase("Trimming closed captions failed");
                                                    jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                                                    Cleanup(jobLog);
                                                    return;
                                                }
                                            }

                                            // After removing commercials it's possible that the Audio/Video streams properties have changed - this is due to MEncoder and cutting TS, it only keeps 1 audio stream, so rescan
                                            // Create the Video File object and get the container + stream information
                                            jobLog.WriteEntry(this, "ReAnalyzing video information post commercial removal before video conversion", Log.LogEntryType.Information);
                                            LogStatus(Localise.GetPhrase("Analyzing video information"), jobLog);

                                            // While updating we don't need to pass EDL file anymore since the ad's have been removed and no cropping information here
                                            _videoFile.UpdateVideoInfo(true, false, _conversionOptions.sourceVideo, _remuxedVideo, "", _conversionOptions.audioLanguage, _jobStatus, jobLog);
                                            if (_videoFile.Error)
                                            {
                                                _jobStatus.ErrorMsg = "Analyzing video information failed";
                                                jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                                                Cleanup(jobLog);
                                                return;
                                            }
                                        }
                                        else
                                            jobLog.WriteEntry(this, "Not able to remove commercials, will try again after conversion using unsupported format cutter", Log.LogEntryType.Warning);
                                    }
                                    else
                                        jobLog.WriteEntry(this, Localise.GetPhrase("Skipping commercial cutting, preserving EDL file"), Log.LogEntryType.Information);
                                }
                                else
                                    jobLog.WriteEntry(this, Localise.GetPhrase("Commercials not found or cutting already completed"), Log.LogEntryType.Information);
                            }
                        }
                    }

                    // Get the updated video information
                    if (!_jobStatus.Cancelled)
                    {
                        // Get information before/after removing commercials if required
                        // After removing commercials it's possible that the Audio/Video streams properties have changed - this is due to MEncoder and cutting TS, it only keeps 1 audio stream, so rescan
                        // Create the Video File object and get the container + stream information
                        LogStatus(Localise.GetPhrase("Analyzing video information"), jobLog);

                        // Get the updated video information, skip cropping for now (it will be handled during the conversion)
                        _videoFile.UpdateVideoInfo(true, _autoDeinterlace, _conversionOptions.sourceVideo, _remuxedVideo, (_videoFile.AdsRemoved ? "" : (_commercialScan != null ? _commercialScan.EDLFile : "")), _conversionOptions.audioLanguage, _jobStatus, jobLog); // Check if ad's have not been removed, if we have scanned for commercial pass along the EDL file to speed up the crop detect
                        if (_videoFile.Error)
                        {
                            _jobStatus.ErrorMsg = "Analyzing video information failed";
                            jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                            Cleanup(jobLog);
                            return;
                        }
                    }

                    // Convert the video
                    if (!_jobStatus.Cancelled)
                    {
                        // Convert the file
                        Transcode.Convert convertFile = new Transcode.Convert(_jobStatus, jobLog);
                        LogStatus(Localise.GetPhrase("Converting"), jobLog);
                        bool res = convertFile.Run(_conversionOptions, _videoFile, _commercialScan, _srtFile); // if we're using MEncoder, then we will complete the commercial stripping here itself
                        if (!res)
                        {
                            _jobStatus.ErrorMsg = Localise.GetPhrase("Conversion failed");
                            jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                            // Conversion failed
                            Cleanup(jobLog);
                            return;
                        }

                        // If we burned the subtitles into the file, then delete the SRT file since it's no longer required
                        if (convertFile.SubtitleBurned)
                        {
                            jobLog.WriteEntry(this, "Subtitles were burned into the video while converting, deleting the SRT file", Log.LogEntryType.Information);
                            FileIO.TryFileDelete(_srtFile); // Delete the SRT
                            _srtFile = ""; // Point to nothing
                        }

                        _convertedFile = convertFile.ConvertedFile;
                        jobLog.WriteEntry(this, "Converted File : " + _convertedFile, Log.LogEntryType.Information);
                        jobLog.WriteEntry(this, Localise.GetPhrase("Finished conversion, file size [KB]") + " " + (Util.FileIO.FileSize(_convertedFile) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                    }

                    // Remove the commercials incase they weren't removed earlier
                    if (!_jobStatus.Cancelled)
                    {
                        if (_commercialScan != null)
                        {
                            jobLog.WriteEntry(this, "Checking if commercials were found", Log.LogEntryType.Information);
                            if ((_commercialScan.CommercialsFound) && (!_videoFile.AdsRemoved)) //commercials might be stripped during conversion or before conversion
                            {
                                if (!_commercialSkipCut)
                                {
                                    LogStatus(Localise.GetPhrase("Removing commercials"), jobLog);
                                    _commercialRemover = new Remover(_conversionOptions.profile, _convertedFile, _conversionOptions.workingPath, _commercialScan.EDLFile, _initialEDLSkipSeconds, _videoFile, _jobStatus, jobLog);
                                    _commercialRemover.StripCommercials();

                                    //We dont' check for % completion here since some files are very short and % isn't reliable
                                    if (!_videoFile.AdsRemoved) // Commercial Stripping failure
                                    {
                                        _jobStatus.ErrorMsg = Localise.GetPhrase("Removing commercials failed");
                                        jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                                        Cleanup(jobLog);
                                        return;
                                    }
                                    
                                    _convertedFile = _commercialRemover.CommercialFreeVideo;
                                    jobLog.WriteEntry(this, Localise.GetPhrase("Finished removing commercials, file size [KB]") + " " + (Util.FileIO.FileSize(_convertedFile) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);

                                    // Cut the SRT file also if it exists (since commercials not were not cut earlier, the SRT wasn't cut earlier either
                                    if (!String.IsNullOrWhiteSpace(_srtFile))
                                    {
                                        LogStatus(Localise.GetPhrase("Trimming closed captions"), jobLog);
                                        if (!_cc.EDLTrim(_commercialScan.EDLFile, _srtFile, 0, _subtitleSegmentOffset)) // Offset is already compensated for while extraction
                                        {
                                            // Trimming CC failed
                                            _jobStatus.ErrorMsg = Localise.GetPhrase("Trimming closed captions failed");
                                            jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                                            Cleanup(jobLog);
                                            return;
                                        }
                                    }
                                }
                                else
                                    jobLog.WriteEntry(this, Localise.GetPhrase("Skipping commercial cutting, preserving EDL file"), Log.LogEntryType.Information);
                            }
                            else
                                jobLog.WriteEntry(this, Localise.GetPhrase("Commercials not found or cutting already completed"), Log.LogEntryType.Information);
                        }
                    }

                    // Add the subtitles and chapters to the container if asked
                    if (!_jobStatus.Cancelled)
                    {
                        if (_conversionOptions.embedSubtitlesChapters)
                        {
                            string chapFile = ""; // Nero Chapter file
                            string xmlChapFile = ""; // iTunes Chapter file

                            // Convert the EDL file to Chapters (EDLToChapter will adjust chapters based on whether the video is cut with the EDL file or not, i.e. CommercialSkipCut)
                            if (_commercialScan != null)
                            {
                                EDL edl = new EDL(_conversionOptions.profile, _convertedFile, _videoFile.Duration, _commercialScan.EDLFile, _initialEDLSkipSeconds, _jobStatus, jobLog);
                                if (edl.ConvertEDLToChapters(!_commercialSkipCut))
                                {
                                    chapFile = edl.CHAPFile; // Get the Nero chapter file
                                    xmlChapFile = edl.XMLCHAPFile; // Get the iTunes chapter file
                                }
                            }

                            LogStatus(Localise.GetPhrase("Adding subtitles and chapters to file"), jobLog);
                            if (!_metaData.AddSubtitlesAndChaptersToFile(_srtFile, chapFile, xmlChapFile, _convertedFile))
                            {
                                _jobStatus.ErrorMsg = Localise.GetPhrase("Adding subtitles and chapters failed");
                                jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                                Cleanup(jobLog);
                                return;
                            }
                            jobLog.WriteEntry(this, Localise.GetPhrase("Finished adding subtitles and chapters to file, file size [KB]") + " " + (Util.FileIO.FileSize(_convertedFile) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                        }
                    }

                    // This point onward there is not ETA or % so set ETA to working
                    _jobStatus.ETA = "Working...";

                    // Write the meta data
                    if (!_jobStatus.Cancelled)
                    {
                        if (_conversionOptions.writeMetadata)
                        {
                            LogStatus(Localise.GetPhrase("Writing show information"), jobLog);
                            _metaData.WriteTags(_convertedFile); // we can ignore failure of writing meta data, not critical
                            jobLog.WriteEntry(this, Localise.GetPhrase("Finished writing tags, file size [KB]") + " " + (Util.FileIO.FileSize(_convertedFile) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                        }
                    }
                }
                else // If we are ONLY RENAMING, then we are working direcly on the original file (ignore the remuxed video which was used to extract EDL, XML, NFO and SRT files)
                {
                    WorkingVideo = ""; // Disregard the remuxed file completely, we are working on the original video

                    // Get the video properties
                    if (!_jobStatus.Cancelled)
                    {
                        // Create the Video File object and get the container + stream information
                        LogStatus(Localise.GetPhrase("Analyzing video information"), jobLog);

                        _videoFile = new VideoInfo(true, false, _conversionOptions.sourceVideo, "", "", "", _jobStatus, jobLog); // No work done here just get basic properties

                        // Dont' need to worrk about errors/failues here since this is only used if we are extracting MC information
                    }

                    _jobStatus.ETA = "Working...";
                    _convertedFile = _conversionOptions.sourceVideo; // We are working directly on the source file here (copied)
                }

                // Create the XML file with Source video information for WTV and DVRMS file (XBMC compliant NFO file, http://wiki.xbmc.org/index.php?title=Import_-_Export_Library)
                if (!_jobStatus.Cancelled)
                {
                    if (_conversionOptions.extractXML)
                        _metaData.WriteXBMCXMLTags(OriginalFileName, WorkingVideo, _conversionOptions.workingPath, _videoFile);
                }

                // Processing complete, now rename the file based upon meta data
                string subDestinationPath = "";
                if (!_jobStatus.Cancelled)
                {
                    // Before renaming get the MediaInformation once to dump into the Log File for debugging purposes.
                    FFmpegMediaInfo.DumpFileInformation(_convertedFile, _jobStatus, jobLog);

                    LogStatus(Localise.GetPhrase("Renaming file using show information"), jobLog);
                    // Check if we are using a Unicode Temp file name and restore it before renaming it
                    if (Text.ContainsUnicode(OriginalFileName) && !_conversionOptions.renameOnly) // Unicode test, not all underlying apps and functions currently support UNICODE filenames, so use a ASCII name for now
                    {
                        try
                        {
                            string orgConvFile = Path.Combine(_conversionOptions.workingPath, Path.GetFileNameWithoutExtension(OriginalFileName) + FilePaths.CleanExt(_convertedFile));
                            jobLog.WriteEntry(this, "Restoring temporary Unicode file to original filename -> " + orgConvFile, Log.LogEntryType.Debug);
                            FileIO.TryFileDelete(orgConvFile);
                            File.Move(_convertedFile, orgConvFile); // Restore original filename
                            _convertedFile = orgConvFile; // Restore the name
                        }
                        catch (Exception e)
                        {
                            jobLog.WriteEntry(this, "Unable to rename temporary Unicode file to original filename.\r\nError -> " + e.ToString(), Log.LogEntryType.Warning);
                        }
                    }

                    RenameConvertedFile(out subDestinationPath, jobLog); // Rename the file
                }

                // Now run the post conversion custom command on the final file before it is moved
                if (!_jobStatus.Cancelled)
                {
                    LogStatus(Localise.GetPhrase("Running custom commands"), jobLog);

                    CustomCommand customCommand = new CustomCommand("CustomCommand", _conversionOptions.profile, _conversionOptions.taskName, _conversionOptions.workingPath, Path.GetDirectoryName(GetDestinationFilename(_conversionOptions, _metaData, OriginalFileName, jobLog)), _convertedFile, OriginalFileName, _remuxedVideo, (_commercialScan == null ? "" : _commercialScan.EDLFile), _srtFile, _metaData.MetaData, _jobStatus, jobLog);
                    if (!customCommand.Run())
                    {
                        jobLog.WriteEntry(this, Localise.GetPhrase("Custom command failed to run, critical failure"), Log.LogEntryType.Error);
                        _jobStatus.ErrorMsg = Localise.GetPhrase("Custom command failed to run, critical failure");
                        Cleanup(jobLog);
                        return; // serious problem
                    }

                    // Check if the converted file has been tampered with
                    if (!File.Exists(_convertedFile))
                    {
                        jobLog.WriteEntry(this, Localise.GetPhrase("Converted file has been renamed or deleted by custom command") + " -> " + _convertedFile, Log.LogEntryType.Error);
                        _jobStatus.ErrorMsg = Localise.GetPhrase("Converted file has been renamed or deleted by custom command");
                        Cleanup(jobLog);
                        return; // serious problem
                    }
                    else
                        jobLog.WriteEntry(this, Localise.GetPhrase("Finished custom command, file size [KB]") + " " + (Util.FileIO.FileSize(_convertedFile) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                }

                // Finally move the file
                if (!_jobStatus.Cancelled)
                {
                    LogStatus(Localise.GetPhrase("Moving converted file to destination"), jobLog);
                    if (!MoveConvertedFile(subDestinationPath, jobLog))
                    {
                        _jobStatus.ErrorMsg = Localise.GetPhrase("Moving converted file to destination failed"); // don't set this unless you want to indicate failure up the chain and kill the conversion process
                        jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                        Cleanup(jobLog);
                        return;
                    }
                }

                // After moving, now add the destination file to the iTunes/WMP library if required
                if (!_jobStatus.Cancelled)
                {
                    if (_conversionOptions.addToiTunes)
                    {
                        LogStatus(Localise.GetPhrase("Adding file to the iTunes library"), jobLog);
                        VideoMetaData.AddFileToiTunesLibrary(_convertedFile, jobLog);
                    }

                    if (_conversionOptions.addToWMP)
                    {
                        LogStatus(Localise.GetPhrase("Adding file to the WMP library"), jobLog);
                        VideoMetaData.AddFileToWMPLibrary(_convertedFile, jobLog);
                    }
                }

                // Finally - Move the remaining files file
                if (!_jobStatus.Cancelled)
                {
                    // XML FILE (generated by Comskip or any other program)
                    string xmlFile = Path.Combine(_conversionOptions.workingPath, (Path.GetFileNameWithoutExtension(WorkingVideo) + ".xml")); // XML file created by 3rd Party in temp working directory
                    try
                    {
                        if (File.Exists(xmlFile))
                        {
                            if (String.Compare(xmlFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".xml"), true) != 0) // Don't delete if they are the same file, e.g. TS to TS in same directory
                            {
                                jobLog.WriteEntry(this, Localise.GetPhrase("Found XML file, moving to destination") + " XML:" + xmlFile + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".xml"));
                                File.Move(xmlFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".xml")); //rename to match destination file
                            }
                        }
                    }
                    catch (Exception e) // Not critial to be unable to move SRT File
                    {
                        jobLog.WriteEntry(this, Localise.GetPhrase("Unable to move XML file to destination") + " XML:" + xmlFile + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                    }

                    // SRT FILE
                    try
                    {
                        if (File.Exists(_srtFile))
                        {
                            if (String.Compare(_srtFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".srt"), true) != 0) // Don't delete if they are the same file, e.g. TS to TS in same directory
                            {
                                jobLog.WriteEntry(this, Localise.GetPhrase("Found SRT file, moving to destination") + " SRT:" + _srtFile + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".srt"));
                                File.Move(_srtFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".srt")); //rename to match destination file
                            }
                        }
                    }
                    catch (Exception e) // Not critial to be unable to move SRT File
                    {
                        jobLog.WriteEntry(this, Localise.GetPhrase("Unable to move SRT file to destination") + " SRT:" + _srtFile + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                    }

                    // EDL FILE
                    if (_commercialScan != null)
                    {
                        try
                        {
                            if (File.Exists(_commercialScan.EDLFile) && _commercialSkipCut) // if we are asked to keep EDL file, we copy it out to output
                            {
                                if (String.Compare(_commercialScan.EDLFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edl"), true) != 0) // don't delete the same file, e.g. TS to TS in same directory
                                {
                                    jobLog.WriteEntry(this, Localise.GetPhrase("Found EDL file, request to move to destination") + " EDL:" + _commercialScan.EDLFile + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                    FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edl"));
                                    File.Move(_commercialScan.EDLFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edl"));
                                }
                            }
                        }
                        catch (Exception e)
                        {
                            jobLog.WriteEntry(this, Localise.GetPhrase("Unable to move EDL file to destination") + " EDL:" + _commercialScan.EDLFile + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                        }
                    }
                    else if (_saveEDLFile) // no commercial scan but we still found an EDL file with the source, we copy it to the output
                    {
                        // EDL File
                        string edlFile = Path.Combine(_conversionOptions.workingPath, (Path.GetFileNameWithoutExtension(WorkingVideo) + ".edl")); // Saved EDL file
                        try
                        {
                            if (File.Exists(edlFile))
                            {
                                if (String.Compare(edlFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edl"), true) != 0) // Don't delete if they are the same file, e.g. TS to TS in same directory
                                {
                                    jobLog.WriteEntry(this, Localise.GetPhrase("Found EDL file, moving to destination") + " EDL:" + edlFile + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                    FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edl"));
                                    File.Move(edlFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edl")); //rename to match destination file
                                }
                            }
                        }
                        catch (Exception e) // Not critial to be unable to move EDL File
                        {
                            jobLog.WriteEntry(this, Localise.GetPhrase("Unable to move EDL file to destination") + " EDL:" + edlFile + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                        }

                        //EDLP File
                        string edlpFile = Path.Combine(_conversionOptions.workingPath, (Path.GetFileNameWithoutExtension(WorkingVideo) + ".edlp")); // Saved EDLP file
                        try
                        {
                            if (File.Exists(edlpFile))
                            {
                                if (String.Compare(edlpFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edlp"), true) != 0) // Don't delete if they are the same file, e.g. TS to TS in same directory
                                {
                                    jobLog.WriteEntry(this, Localise.GetPhrase("Found EDLP file, moving to destination") + " EDLP:" + edlpFile + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                    FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edlp"));
                                    File.Move(edlpFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edlp")); //rename to match destination file
                                }
                            }
                        }
                        catch (Exception e) // Not critial to be unable to move EDL File
                        {
                            jobLog.WriteEntry(this, Localise.GetPhrase("Unable to move EDLP file to destination") + " EDLP:" + edlpFile + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                        }
                    }

                    // NFO FILE
                    string nfoFile = Path.Combine(_conversionOptions.workingPath, Path.GetFileNameWithoutExtension(WorkingVideo) + ".nfo"); // Path\FileName.nfo
                    try
                    {
                        if (File.Exists(nfoFile))
                        {
                            if (String.Compare(nfoFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".nfo"), true) != 0) // Don't delete if they are the same file, e.g. TS to TS in same directory
                            {
                                jobLog.WriteEntry(this, Localise.GetPhrase("Found NFO file, moving to destination") + " NFO:" + nfoFile + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".nfo"));
                                File.Move(nfoFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".nfo")); //rename to match destination file
                            }
                        }
                    }
                    catch (Exception e) // Not critial to be unable to move NFO File
                    {
                        jobLog.WriteEntry(this, Localise.GetPhrase("Unable to move NFO file to destination") + " NFO:" + nfoFile + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                    }

                    // COVER ART
                    if (_conversionOptions.extractXML) // Only if asked to save the information
                    {
                        string coverArt = _metaData.MetaData.BannerFile; // Path to cover art
                        try
                        {
                            if (File.Exists(coverArt))
                            {
                                if (String.Compare(coverArt, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + FilePaths.CleanExt(coverArt)), true) != 0) // Don't delete if they are the same file, e.g. TS to TS in same directory
                                {
                                    jobLog.WriteEntry(this, ("Found Cover Art file, copying to destination") + " CoverArt:" + coverArt + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                    FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + FilePaths.CleanExt(coverArt)));
                                    File.Copy(coverArt, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + FilePaths.CleanExt(coverArt))); // Copy (NOT move since it may be used by other conversions and we don't keep downloading cover art) and rename to match destination file
                                }
                            }
                        }
                        catch (Exception e) // Not critial to be unable to move Cover Art File
                        {
                            jobLog.WriteEntry(this, ("Unable to copy Cover Art file to destination") + " CoverArt:" + coverArt + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                        }

                    }

                    // LOG FILE
                    if (_copyLOGFile)
                    {
                        string logFile = Path.Combine(_conversionOptions.workingPath, Path.GetFileNameWithoutExtension(WorkingVideo) + ".log"); // Path\FileName.log
                        try
                        {
                            if (File.Exists(logFile))
                            {
                                if (String.Compare(logFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".log"), true) != 0) // Don't delete if they are the same file, e.g. TS to TS in same directory
                                {
                                    jobLog.WriteEntry(this, Localise.GetPhrase("Found LOG file, moving to destination") + " LOG:" + logFile + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                    FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".log"));
                                    File.Move(logFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".log")); //rename to match destination file
                                }
                            }
                        }
                        catch (Exception e) // Not critial to be unable to move LOG File
                        {
                            jobLog.WriteEntry(this, Localise.GetPhrase("Unable to move LOG file to destination") + " LOG:" + logFile + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                        }
                    }

                    // PROPERTIES FILE
                    if (_copyPropertiesFile)
                    {
                        string propertiesFile = FilePaths.GetFullPathWithoutExtension(OriginalFileName) + ".properties"; // The properties file lies with the original file
                        try
                        {
                            if (File.Exists(propertiesFile))
                            {
                                if (String.Compare(propertiesFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".properties"), true) != 0) // Don't delete if they are the same file, e.g. TS to TS in same directory
                                {
                                    jobLog.WriteEntry(this, Localise.GetPhrase("Found SageTV Properties file, moving to destination") + " PROPERTIES:" + propertiesFile + " Destination:" + Path.GetDirectoryName(_convertedFile), Log.LogEntryType.Information);
                                    FileIO.TryFileDelete((Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".properties"));
                                    File.Move(propertiesFile, (Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".properties")); //rename to match destination file
                                }
                            }
                        }
                        catch (Exception e) // Not critial to be unable to move LOG File
                        {
                            jobLog.WriteEntry(this, Localise.GetPhrase("Unable to move LOG file to destination") + " PROPERTIES:" + propertiesFile + " Destination:" + Path.GetDirectoryName(_convertedFile) + " Error -> " + e.ToString(), Log.LogEntryType.Warning);
                        }
                    }

                    // Last things - Run the end of conversion post rename and move custom command from the user if configured
                    if (!_jobStatus.Cancelled)
                    {
                        LogStatus(Localise.GetPhrase("Running custom commands"), jobLog);

                        string tmpSrt, tmpEdl;
                        tmpEdl = (File.Exists(Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edl") ? Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".edl" : "");
                        tmpSrt = (File.Exists(Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".srt") ? Util.FilePaths.GetFullPathWithoutExtension(_convertedFile) + ".srt" : "");
                        CustomCommand customCommand = new CustomCommand("PostCustomCommand", _conversionOptions.profile, _conversionOptions.taskName, _conversionOptions.workingPath, Path.GetDirectoryName(GetDestinationFilename(_conversionOptions, _metaData, OriginalFileName, jobLog)), _convertedFile, OriginalFileName, _remuxedVideo, tmpEdl, tmpSrt, _metaData.MetaData, _jobStatus, jobLog);
                        if (!customCommand.Run())
                        {
                            jobLog.WriteEntry(this, Localise.GetPhrase("End of Conversion Custom command failed to run, critical failure"), Log.LogEntryType.Error);
                            _jobStatus.ErrorMsg = Localise.GetPhrase("End of Conversion Custom command failed to run, critical failure");
                            Cleanup(jobLog);
                            return; // serious problem
                        }

                        // Check if the converted file has been tampered with
                        if (!File.Exists(_convertedFile))
                        {
                            // WE WILL NOTE THIS AS AN SERIOUS ERROR BUT IT ISN'T A DEALBREAKER SINCE WE ARE ALL DONE HERE AND THE FILE HAS BEEN MOVED.
                            jobLog.WriteEntry(this, Localise.GetPhrase("Destination file has been renamed or deleted by post custom command") + " -> " + _convertedFile, Log.LogEntryType.Error);
                        }
                        else
                            jobLog.WriteEntry(this, Localise.GetPhrase("Finished end of conversion custom command, file size [KB]") + " " + (Util.FileIO.FileSize(_convertedFile) / 1024).ToString("N", System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                    }

                    _jobStatus.ErrorMsg = ""; // all done, we are in the clear, success
                    _jobStatus.SuccessfulConversion = true; //now the original file can be deleted if required
                    LogStatus(Localise.GetPhrase("Success - All done!"), jobLog);
                }

                Cleanup(jobLog); // all done, clean it up, close jobLog and mark it inactive
            }
            catch(ThreadAbortException)
            {
                //incase the conversion thread is stopped due to the GUI stopping/cancelling, then release the logfile locks
                _jobStatus.ErrorMsg = "Conversion thread aborted, conversion cancelled";
                jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                Cleanup(jobLog);
            }
            catch (Exception e)
            {
                // Unhandled error, clean up and log error
                _jobStatus.ErrorMsg = "Unhanded error during conversion, conversion cancelled";
                jobLog.WriteEntry(this, _jobStatus.ErrorMsg + "\r\n" + e.ToString(), Log.LogEntryType.Error);
                Cleanup(jobLog);
            }
        }
Exemplo n.º 10
0
        private void ScanWithComskip()
        {
            string parameters = "";
            
            if(_convOptions.comskipIni != "") // Check for custom Ini file
            {
                if(File.Exists(_convOptions.comskipIni))
                    parameters += "--ini=" + Util.FilePaths.FixSpaces(_convOptions.comskipIni);
                else
                    _jobLog.WriteEntry(this, Localise.GetPhrase("Custom Comskip INI file does not exist, Skipping custom INI"), Log.LogEntryType.Warning);
            }

            parameters += " --output=" + Util.FilePaths.FixSpaces(_convOptions.workingPath); // Redirect all files to working folder (save issues with skipping copying original files and simulatenous scanning on same file)

            parameters += " " + Util.FilePaths.FixSpaces(_videoFileName);

            float Duration = 0;
            Duration = VideoParams.VideoDuration(_videoFileName);
            if (Duration <= 0)
            {
                FFmpegMediaInfo mediaInfo = new FFmpegMediaInfo(_videoFileName, _jobStatus, _jobLog);
                if (!mediaInfo.Success || mediaInfo.ParseError)
                    _jobLog.WriteEntry(this, Localise.GetPhrase("Cannot read video duration"), Log.LogEntryType.Error);

                if (mediaInfo.MediaInfo.VideoInfo.Duration == 0)
                    _jobLog.WriteEntry(this, Localise.GetPhrase("Video duration 0"), Log.LogEntryType.Warning);
                else
                    Duration = mediaInfo.MediaInfo.VideoInfo.Duration;
            }

            // Check for custom version of Comskip path
            Comskip comskip = new Comskip(_customComskipPath, parameters, Duration, _jobStatus, _jobLog); // Use custom comskip or fallback to default
            comskip.Run();
            if (!comskip.Success || !(File.Exists(WorkingEDLFilePath) || File.Exists(WorkingEDLPFilePath))) // Check if the EDL/EDLP file exists (here it is in working path), % does not always work
            {
                _jobLog.WriteEntry(this, "Comskip failed or no output EDL/EDLP file found", Log.LogEntryType.Warning);
                _jobStatus.PercentageComplete = 0;
            }
        }
Exemplo n.º 11
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();
        }
Exemplo n.º 12
0
        /// <summary>
        /// Removes commercials from the converted file using the EDL file and updates the RemoveAds flag if successful
        /// </summary>
        /// <param name="filterAudioLanguage">True if you want to keep only the identified audio language while cutting TS files (before conversion since VideoFile will be used to determine audio and video track properties). This is useful when commercial remove is done before video conversion since audio language information is lost.</param>
        public void StripCommercials(bool remuxedVideoFilterAudioLanguage = false)
        {
            _jobStatus.PercentageComplete = 100; //all good to start with 
            _jobStatus.ETA = "";

            if (!File.Exists(_uncutVideo))
            {
                _jobLog.WriteEntry(this, Localise.GetPhrase("File not found") + " " + _uncutVideo, Log.LogEntryType.Error);
                _jobStatus.PercentageComplete = 0;
                _jobStatus.ErrorMsg = "Strip Commercials file not found";
                return;
            }
            if (!File.Exists(EDLFile))
            {
                _jobLog.WriteEntry(this, Localise.GetPhrase("EDL file does not exist - no commercials found") + " " + EDLFile, Log.LogEntryType.Information);
                _jobStatus.ErrorMsg = "Strip commercial EDL file not found";
                _jobStatus.PercentageComplete = 0;
                return;
            }

            if (remuxedVideoFilterAudioLanguage) // If we are being called preconversion then the media info is the same as the remuxed file
                _uncutVideoMediaInfo = _remuxedVideoFileInfo.FFMPEGStreamInfo;
            else // otherwise lets get the media info for the conversted file
            {
                _uncutVideoMediaInfo = new FFmpegMediaInfo(_uncutVideo, _jobStatus, _jobLog);
                if (!_uncutVideoMediaInfo.Success || _uncutVideoMediaInfo.ParseError)
                {
                    _jobStatus.PercentageComplete = 0; // if the file wasn't completely converted the percentage will be low so no worries
                    _jobStatus.ErrorMsg = "Commerical remover, getting video mediainfo failed for " + _uncutVideo;
                    _jobLog.WriteEntry(this, (_jobStatus.ErrorMsg), Log.LogEntryType.Error);
                    return;
                }
            }

            _jobLog.WriteEntry(this, Localise.GetPhrase("Commercial Remove: Before removal file size [KB] ") + (Util.FileIO.FileSize(_uncutVideo)/1024).ToString("N", CultureInfo.InvariantCulture), Log.LogEntryType.Debug); ;

            if (_universalCommercialRemover)
            {
                _jobLog.WriteEntry(this, Localise.GetPhrase("Forcing use of Universal Commercial Remover for") + " " + _ext, Log.LogEntryType.Warning);
                CutFFMPEG(remuxedVideoFilterAudioLanguage);
            }
            else
            {
                switch (_ext)
                {
                    case ".ts":
                        _jobLog.WriteEntry(this, Localise.GetPhrase("Removing commercials using CutTS") + " ext -> " + _ext, Log.LogEntryType.Information);
                        CutTS(remuxedVideoFilterAudioLanguage);
                        // TODO: Mencoder based TS cutting is broken, generated unusable files and also EDL cutting does not work
                        /*if (0 == _jobStatus.PercentageComplete) // Try backup
                        {
                            _jobLog.WriteEntry(this, Localise.GetPhrase("CutTS failed, trying to remove commercials using CutMencoder") + " ext -> " + _ext, Log.LogEntryType.Information);
                            _jobStatus.PercentageComplete = 100; //Reset
                            _jobStatus.ErrorMsg = ""; //reset error message since we are trying again
                            CutMencoder(filterTSAudioLanguage);
                        }*/
                        break;

                    case ".wmv":
                        _jobLog.WriteEntry(this, Localise.GetPhrase("Removing commercials using CutWMV") + " ext -> " + _ext, Log.LogEntryType.Information);
                        CutWMV();
                        break;

                    case ".mpg":
                    case ".avi":
                        _jobLog.WriteEntry(this, Localise.GetPhrase("Removing commercials using CutMencoder") + " ext -> " + _ext, Log.LogEntryType.Information);
                        CutMencoder();
                        break;

                    case ".m4v":
                    case ".mp4":
                        if (_cutMP4Alternative)
                        {
                            _jobLog.WriteEntry(this, Localise.GetPhrase("Removing commercials using CutMP4Alternate") + " ext -> " + _ext, Log.LogEntryType.Information);
                            CutMP4Alternate();
                        }
                        else
                        {
                            _jobLog.WriteEntry(this, Localise.GetPhrase("Removing commercials using CutMP4") + " ext -> " + _ext, Log.LogEntryType.Information);
                            CutMP4();
                            if (0 == _jobStatus.PercentageComplete) // CutMP4 can fail for some .TS files that can't be read by MediaInfo, do a backup try
                            {
                                _jobStatus.ErrorMsg = ""; //reset error message since we are trying again
                                _jobLog.WriteEntry(this, Localise.GetPhrase("CutMP4 failed, trying backup CutMP4Alternate to remove commercials"), Log.LogEntryType.Warning);
                                _jobStatus.PercentageComplete = 100; //Reset
                                CutMP4Alternate();
                            }
                        }
                        break;

                    case ".mkv":
                        {
                            _jobLog.WriteEntry(this, Localise.GetPhrase("Removing commercials using CutMKV"), Log.LogEntryType.Information);
                            CutMKV();
                            break;
                        }

                    default:
                        {
                            _jobLog.WriteEntry(this, Localise.GetPhrase("Unsupported extension for removing commercials ") + " " + _ext + ", trying default FFMPEG cutter", Log.LogEntryType.Warning);
                            CutFFMPEG(remuxedVideoFilterAudioLanguage);
                            break;
                        }
                }

                // If all fails, try one last chance with CutFFMPEG
                if (0 == _jobStatus.PercentageComplete) // Try backup
                {
                    _jobLog.WriteEntry(this, Localise.GetPhrase("Cutting failed, trying to remove commercials using CutFFMPEG") + " ext -> " + _ext, Log.LogEntryType.Information);
                    _jobStatus.PercentageComplete = 100; //Reset
                    _jobStatus.ErrorMsg = ""; //reset error message since we are trying again
                    CutFFMPEG(remuxedVideoFilterAudioLanguage);
                }
            }

            if ((_jobStatus.PercentageComplete != 0) && !String.IsNullOrWhiteSpace(_cutVideo))
                _remuxedVideoFileInfo.AdsRemoved = true; // We have removed the Ad's
            else
                _remuxedVideoFileInfo.AdsRemoved = false;

            _jobLog.WriteEntry(this, Localise.GetPhrase("Commercial Remove: After removal  file size [KB] ") + (Util.FileIO.FileSize(_cutVideo) / 1024).ToString("N", CultureInfo.InvariantCulture), Log.LogEntryType.Debug); ;
            _jobLog.WriteEntry(this, Localise.GetPhrase("Commercial Remove: Percentage Complete") + " " + _jobStatus.PercentageComplete.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
        }
Exemplo n.º 13
0
        /// <summary>
        /// Backup remove commercials from MP4 using MEncoder, not the best, limited in many ways
        /// </summary>
        private void CutMP4Alternate()
        {
            string tempFile = CutFileName(_uncutVideo, 0, 0);

            // TODO: Need to add support for non AAC Audio Codecs
            // Refer to: http://forum.videohelp.com/threads/337215-Mencoder-not-copying-aac-to-mp4?p=2140000
            // and http://www.mplayerhq.hu/DOCS/codecs-status.html#ac
            // Do not use -hr-edl-seek as it doesn't work well with -ovc copy
            // lavdopts supports a max of 8 threads
            // avoid using threads, stablity - only copying here
            string mencoderParams = Util.FilePaths.FixSpaces(_uncutVideo) + " -of lavf -ovc copy -oac copy";

            //Set fafmttag based on the type of audio codec of converted file (currently supported aac, ac3, eac3)
            _jobLog.WriteEntry(this, Localise.GetPhrase("Trying to reading converted file Audio information"), Log.LogEntryType.Information);
            string audioCodec = "";
            FFmpegMediaInfo ffmpegStreamInfo = new FFmpegMediaInfo(_uncutVideo, _jobStatus, _jobLog);
            if (ffmpegStreamInfo.Success && !ffmpegStreamInfo.ParseError)
            {
                // Converted file should contain only 1 audio stream
                audioCodec = ffmpegStreamInfo.MediaInfo.AudioInfo[0].AudioCodec;
                _jobLog.WriteEntry(this, Localise.GetPhrase("Found AudioCodec") + " " + audioCodec, Log.LogEntryType.Information);

                if (String.IsNullOrEmpty(audioCodec))
                    _jobLog.WriteEntry(this, Localise.GetPhrase("Audio codec information is blank, will try to continue without it"), Log.LogEntryType.Warning);
            }
            else
                _jobLog.WriteEntry(this, Localise.GetPhrase("Cannot read Audio codec information, will try to continue without it"), Log.LogEntryType.Warning);

            switch (audioCodec)
            {
                case "aac":
                    _jobLog.WriteEntry(this, Localise.GetPhrase("CutMP4Alternate found AAC Audio Codec"), Log.LogEntryType.Information);
                    mencoderParams += @" -fafmttag 0x706D";
                    break;

                case "ac3":
                case "ac-3":
                    _jobLog.WriteEntry(this, Localise.GetPhrase("CutMP4Alternate found AC3 Audio Codec"), Log.LogEntryType.Information);
                    mencoderParams += @" -fafmttag 0x2000";
                    break;

                case "e-ac-3":
                    _jobLog.WriteEntry(this, Localise.GetPhrase("CutMP4Alternate found E-AC3 Audio Codec"), Log.LogEntryType.Information);
                    mencoderParams += @" -fafmttag 0x33434145";
                    break;

                case "mp3":
                    _jobLog.WriteEntry(this, Localise.GetPhrase("CutMP4Alternate found mp3 Audio Codec"), Log.LogEntryType.Information);
                    mencoderParams += @" -fafmttag 0x55";
                    break;

                case "flac":
                    _jobLog.WriteEntry(this, Localise.GetPhrase("CutMP4Alternate found flac Audio Codec"), Log.LogEntryType.Information);
                    mencoderParams += @" -fafmttag 0xF1AC";
                    break;

                case "mp1":
                case "mp2":
                    _jobLog.WriteEntry(this, Localise.GetPhrase("CutMP4Alternate found mp2 Audio Codec"), Log.LogEntryType.Information);
                    mencoderParams += @" -fafmttag 0x50";
                    break;

                default:
                    _jobLog.WriteEntry(this, Localise.GetPhrase("CutMP4Alternate could not identify Audio Codec, DEFAULTING TO AAC - please check video file and audio encoder type"), Log.LogEntryType.Warning);
                    mencoderParams += @" -fafmttag 0x706D";
                    break;
            }

            mencoderParams += " -edl " + Util.FilePaths.FixSpaces(EDLFile) + " -o " + Util.FilePaths.FixSpaces(tempFile);

            _jobStatus.CurrentAction = Localise.GetPhrase("Merging commercial free segments into new video");
            _jobLog.WriteEntry(this, Localise.GetPhrase("CutMP4Alternate: Merging commercial free segments into new video"), Log.LogEntryType.Information);

            Mencoder mencoder = new Mencoder(mencoderParams, _jobStatus, _jobLog, false);
            mencoder.Run();
            if (!mencoder.Success || (Util.FileIO.FileSize(tempFile) <= 0)) // do not check for % success here since sometimes it does not show complete number
            {
                _jobStatus.ErrorMsg = "CutMP4Alternate Commercial cutting failed";
                _jobLog.WriteEntry(this, Localise.GetPhrase("CutMP4Alternate Commercial cutting failed"), Log.LogEntryType.Error);
                _jobStatus.PercentageComplete = 0;
                return;
            }

            _jobLog.WriteEntry(this, Localise.GetPhrase("CutMP4Alternate trying to replace file") + " Output : " + _uncutVideo + " Temp : " + tempFile, Log.LogEntryType.Debug);
            RenameAndMoveFile(tempFile);
        }
Exemplo n.º 14
0
        /// <summary>
        /// Remove commercials from TS files using FFMPEG, AVIDemux and FFMPEG as backup
        /// </summary>
        /// <param name="remuxedVideoFilterAudioLanguage">True if you want to use the user selected language to filter out while cutting</param>
        private void CutTS(bool remuxedVideoFilterAudioLanguage)
        {
            bool selectedTSAudioSuccess = false; // have we successfully filtered the audio tracks
            List<KeyValuePair<float, float>> keepList = new List<KeyValuePair<float, float>>();
            long totalSegmentSize = 0;

            // Read the EDL file and convert into cut structure
            if (!ParseEDLFile(ref keepList))
                return;

            //Do the cuts and pick them up as we go
            int cutNumber = 0;
            List<string> filesFound = new List<string>();

            foreach (KeyValuePair<float, float> keep in keepList)
            {
                _jobStatus.CurrentAction = Localise.GetPhrase("Cutting commercials from video - segment") + " " + cutNumber.ToString(CultureInfo.InvariantCulture);
                _jobLog.WriteEntry(this, Localise.GetPhrase("CutTS: Cutting commercials from video - segment ") + cutNumber.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Information);

                // Create the splits - reset success each time
                string cutFileName = CutFileName(_uncutVideo, keep.Key, keep.Value);
                string parameters = " -y -threads 0";

                // http://ffmpeg.org/trac/ffmpeg/wiki/Seeking%20with%20FFmpeg - We use only FAST seek (since accurate seek cuts on a NON KeyFrame which causes Audio Sync Issues)
                parameters += " -ss " + keep.Key.ToString(CultureInfo.InvariantCulture);

                parameters += " -i " + Util.FilePaths.FixSpaces(_uncutVideo);

                // how much to Cut
                parameters += " -t " + (keep.Value - keep.Key).ToString(CultureInfo.InvariantCulture);

                // If we are requested to filter out the selected audio language, check for it's existance and filter it
                if (remuxedVideoFilterAudioLanguage && (_remuxedVideoFileInfo.FFMPEGStreamInfo.AudioTracks > 1))
                {
                    if (_remuxedVideoFileInfo.AudioStream == -1)
                    {
                        _jobLog.WriteEntry("Cannot get audio stream selection details, copying all audio streams", Log.LogEntryType.Warning);

                        // Check for video track
                        if (_uncutVideoMediaInfo.MediaInfo.VideoInfo.Stream != -1)
                            parameters += " -map 0:v -vcodec copy";
                        else
                            parameters += " -vn";

                        // Audio tracks
                        parameters += " -map 0:a -acodec copy";
                    }
                    else
                    {
                        _jobLog.WriteEntry("Selecting Audio Language " + _remuxedVideoFileInfo.AudioLanguage + " Audio Stream " + _remuxedVideoFileInfo.AudioStream.ToString(), Log.LogEntryType.Debug);

                        // Check for video track
                        if (_uncutVideoMediaInfo.MediaInfo.VideoInfo.Stream != -1)
                            parameters += " -map 0:v -vcodec copy";
                        else
                            parameters += " -vn";

                        // Audio track
                        parameters += " -map 0:" + _remuxedVideoFileInfo.AudioStream.ToString() + " -acodec copy"; // Select the Audiotrack we had isolated earlier
                        selectedTSAudioSuccess = true;
                    }
                }
                else
                {
                    // Check for video track
                    if (_uncutVideoMediaInfo.MediaInfo.VideoInfo.Stream != -1)
                        parameters += " -map 0:v -vcodec copy";
                    else
                        parameters += " -vn";

                    // Check for Audio tracks
                    if (_uncutVideoMediaInfo.AudioTracks > 0)
                        parameters += " -map 0:a -acodec copy";
                    else
                        parameters += " -an";
                }

                // Stream copy
                parameters += " " + Util.FilePaths.FixSpaces(cutFileName);

                if (!FFmpeg.FFMpegExecuteAndHandleErrors(parameters, _jobStatus, _jobLog, Util.FilePaths.FixSpaces(cutFileName), false)) // Don't check for % here since Comskip has a bug that gives EDL cut segments past the end of the file || _jobStatus.PercentageComplete < GlobObjects.ACCEPTABLE_COMPLETION) // each run resets this number or an error in the process
                {
                    filesFound.Add(cutFileName); // add the last file created (which may be partial)
                    CleanupCutFiles(filesFound); //Clean up cut files
                    _jobStatus.PercentageComplete = 0;
                    _jobStatus.ErrorMsg = "CutTS splitting video segments failed";
                    _jobLog.WriteEntry(this, Localise.GetPhrase("CutTS splitting video segments failed"), Log.LogEntryType.Error);
                    return;
                }

                filesFound.Add(cutFileName); // to be deleted later
                cutNumber++;

                totalSegmentSize += Util.FileIO.FileSize(cutFileName); // Calculate the total size of all segments for validation later
            }

            if (cutNumber < 1)
            {
                _jobLog.WriteEntry(this, Localise.GetPhrase("No commercials to remove from ") + EDLFile, Log.LogEntryType.Information);
                _jobStatus.PercentageComplete = 100; //Set to success since sometime mp4box doesn't set to 100 if there no/incomplete pieces to strip
                return;
            }

            // Build the AVIDemux Merge Params
            cutNumber = 0;
            string aviDemuxMergeParams = "";
            foreach (string file in filesFound)
            {
                if (cutNumber++ == 0)
                    aviDemuxMergeParams += Util.FilePaths.FixSpaces(file);
                else
                    aviDemuxMergeParams += " --append " + Util.FilePaths.FixSpaces(file);
            }

            // Save the file as a TS file
            string tempFile = CutFileName(_uncutVideo, 0, 0);
            aviDemuxMergeParams += " --video-codec copy --audio-codec copy --output-format ffts --save " + Util.FilePaths.FixSpaces(tempFile);

            // Build the FFMPEG Merge Params
            // Create a text file with the list of all the files which need to be concatenated
            // http://ffmpeg.org/trac/ffmpeg/wiki/How%20to%20concatenate%20(join,%20merge)%20media%20files
            string concatFileList = "";
            string concatFileName = Path.Combine(_workingPath, Path.GetFileNameWithoutExtension(_uncutVideo) + "_ConcatList.txt");

            foreach (string file in filesFound)
                concatFileList += "file " + "'" + file.Replace("'", @"'\''") + "'" + "\r\n"; // File paths are enclosed in single quotes, compensate for ' with '\'' in the filename

            System.IO.File.WriteAllText(concatFileName, concatFileList); // write the text file

            cutNumber = 0;

            // Merge the files using FFMPEG as a backup
            string ffmpegMergeParams = "-y -f concat -i " + Util.FilePaths.FixSpaces(concatFileName);

            // Check for video track
            if (_uncutVideoMediaInfo.MediaInfo.VideoInfo.Stream != -1)
                ffmpegMergeParams += " -map v -vcodec copy";
            else
                ffmpegMergeParams += " -vn";

            // Check for Audio tracks
            if (_uncutVideoMediaInfo.AudioTracks > 0)
                ffmpegMergeParams += " -map a -acodec copy";
            else
                ffmpegMergeParams += " -an";

            // Avoid negative timestamp issues https://trac.ffmpeg.org/wiki/Seeking%20with%20FFmpeg
            ffmpegMergeParams += " -avoid_negative_ts 1";

            ffmpegMergeParams += " " + Util.FilePaths.FixSpaces(tempFile); // Output file

            _jobStatus.CurrentAction = Localise.GetPhrase("Merging commercial free segments into new video");
            _jobLog.WriteEntry(this, "CutTS: Merging commercial free segments into new video", Log.LogEntryType.Information);

            // Check if the converted file has more than 1 audio track, AVIDemux does not support merging files with > 1 audio track (it drops them randomly)
            bool useFFMPEG = false;
            bool retVal = false;
            FFmpegMediaInfo ffmpegStreamInfo = new FFmpegMediaInfo(_uncutVideo, _jobStatus, _jobLog);
            if (!ffmpegStreamInfo.Success || ffmpegStreamInfo.ParseError)
            {
                CleanupCutFiles(filesFound); //Clean up cut files
                Util.FileIO.TryFileDelete(concatFileName);
                _jobStatus.PercentageComplete = 0;
                _jobStatus.ErrorMsg = "CutTS unable to get video info";
                _jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                return;
            }

            AVIDemux aviDemux = new AVIDemux(aviDemuxMergeParams, _jobStatus, _jobLog);

            // If we have more than one audio track we should use FFMPEG instead of AVIDemux
            if (_useFFMPEGMerge)
            {
                _jobLog.WriteEntry(this, "Forcing FFMPEG instead of AVIDemux to merge tracks. There may be issues with the video at the merged segments", Log.LogEntryType.Warning);
                useFFMPEG = true;
                retVal = FFmpeg.FFMpegExecuteAndHandleErrors(ffmpegMergeParams, _jobStatus, _jobLog, Util.FilePaths.FixSpaces(tempFile));
            }
            else if (_useAVIDemuxMerge)
            {
                _jobLog.WriteEntry(this, "Forcing AVIDemux to merge tracks. Sometimes merging may hang", Log.LogEntryType.Warning);
                useFFMPEG = false;
                aviDemux.Run();
                retVal = aviDemux.Success;
            }
            else if (ffmpegStreamInfo.AudioTracks <= 1 || selectedTSAudioSuccess) // If the source file has 1 audio track or if we have filtered the audio track while cutting the segements
            {
                _jobLog.WriteEntry(this, "Single audio track detected in converted file, using AVIDemux to merge tracks.", Log.LogEntryType.Debug);
                useFFMPEG = false;
                aviDemux.Run(); // Run only if we have 1 or less audio tracks
                retVal = aviDemux.Success;
            }
            else
            {
                _jobLog.WriteEntry(this, "More than 1 audio track detected in converted file, using FFMPEG instead of AVIDemux to merge tracks. There may be issues with the video at the merged segments", Log.LogEntryType.Warning);
                useFFMPEG = true;
                retVal = FFmpeg.FFMpegExecuteAndHandleErrors(ffmpegMergeParams, _jobStatus, _jobLog, Util.FilePaths.FixSpaces(tempFile));
            }

            _jobLog.WriteEntry(this, "CutTS : Merged Segements size [KB] " + (Util.FileIO.FileSize(tempFile) / 1024).ToString("N", CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            _jobLog.WriteEntry(this, "CutTS : Total Expected Segements size [KB] " + (totalSegmentSize / 1024).ToString("N", CultureInfo.InvariantCulture), Log.LogEntryType.Debug);

            // If we didn't succeed or filesize wasn't as expected then try backup merging
            if (!retVal || (Util.FileIO.FileSize(tempFile) < (GlobalDefs.MINIMUM_MERGED_FILE_THRESHOLD * totalSegmentSize)) || (totalSegmentSize <= 0)) // Check final merged file size to double verify, sometime AVIDemux skips merging segments if there is an error (like video width for segment is different due to commercials)
            {
                _jobLog.WriteEntry(this, "CutTS: Merging video segments failed, retrying with backup", Log.LogEntryType.Warning);

                _jobStatus.CurrentAction = Localise.GetPhrase("Merging commercial free segments into new video");
                _jobLog.WriteEntry(this, "CutTS: Merging commercial free segments into new video", Log.LogEntryType.Information);

                // If we used ffmpeg earlier, then use AVIDemux else use ffmpeg for the backup
                if (useFFMPEG)
                {
                    _jobLog.WriteEntry(this, "Backup merging, forcing AVIDemux to merge tracks. Sometimes merging may hang", Log.LogEntryType.Warning);
                    aviDemux.Run();
                    retVal = aviDemux.Success;
                }
                else // use ffmpeg since we used avidemux earlier
                {
                    _jobLog.WriteEntry(this, "Backup merging, forcing FFMPEG instead of AVIDemux to merge tracks. There may be issues with the video at the merged segments", Log.LogEntryType.Warning);
                    retVal = FFmpeg.FFMpegExecuteAndHandleErrors(ffmpegMergeParams, _jobStatus, _jobLog, Util.FilePaths.FixSpaces(tempFile));
                }

                _jobLog.WriteEntry(this, "CutTS : Backup merged Segements size [KB] " + (Util.FileIO.FileSize(tempFile) / 1024).ToString("N", CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                _jobLog.WriteEntry(this, "CutTS : Total Expected Segements size [KB] " + (totalSegmentSize / 1024).ToString("N", CultureInfo.InvariantCulture), Log.LogEntryType.Debug);

                if (!retVal || (Util.FileIO.FileSize(tempFile) < (GlobalDefs.MINIMUM_MERGED_FILE_THRESHOLD * totalSegmentSize)) || (totalSegmentSize <= 0)) // Check final merged file size to double verify, sometime AVIDemux skips merging segments if there is an error (like video width for segment is different due to commercials)
                {
                    CleanupCutFiles(filesFound); //Clean up cut files
                    Util.FileIO.TryFileDelete(concatFileName);
                    _jobStatus.PercentageComplete = 0;
                    _jobStatus.ErrorMsg = "CutTS merging video segments failed";
                    _jobLog.WriteEntry(this, "CutTS merging video segments failed", Log.LogEntryType.Error);
                    return;
                }
            }

            // Clean up
            Util.FileIO.TryFileDelete(concatFileName);

            // Move the files
            _jobLog.WriteEntry(this, Localise.GetPhrase("CutTS trying to replace file") + " Output : " + _uncutVideo + " Temp : " + tempFile, Log.LogEntryType.Debug);
            RenameAndMoveFile(tempFile);

            //Clean up cut files
            CleanupCutFiles(filesFound);
        }
Exemplo n.º 15
0
        public MediaInfo GetFileInfo(string videoFilePath)
        {
            if (String.IsNullOrWhiteSpace(videoFilePath))
            {
                Log.AppLog.WriteEntry(this, "Empty filepath passed to get file info", Log.LogEntryType.Error, true);
                return null;
            }

            // Get the properties of this source video
            JobStatus jobStatus = new JobStatus();

            // Get the FPS from MediaInfo, more reliable then FFMPEG but it doesn't always work
            float FPS = VideoParams.FPS(videoFilePath);

            FFmpegMediaInfo videoInfo = new FFmpegMediaInfo(videoFilePath, jobStatus, Log.AppLog, true); // cannot suspend during a UI request else it hangs
            if (videoInfo.Success && !videoInfo.ParseError)
            {
                if ((FPS > 0) && (FPS <= videoInfo.MediaInfo.VideoInfo.FPS)) // Check _fps, sometimes MediaInfo get it below 0 or too high (most times it's reliable)
                    videoInfo.MediaInfo.VideoInfo.FPS = FPS; // update it
            }
            else
            {
                Log.AppLog.WriteEntry("Error trying to get Audio Video information. Unable to Read Media File -> " + videoFilePath, Log.LogEntryType.Error, true);
            }

            return videoInfo.MediaInfo;
        }