Example #1
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;
            }
        }
Example #2
0
        public bool Trim(string sourceVideo, string workingPath, int startTrim, int endTrim)
        {
            _jobLog.WriteEntry(this, "Start Trim : " + startTrim.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            _jobLog.WriteEntry(this, "Stop Trim : " + endTrim.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            _jobLog.WriteEntry(this, "Video File : " + sourceVideo, Log.LogEntryType.Debug);

            if ((startTrim == 0) && (endTrim == 0))
            {
                _trimmedVideo = sourceVideo; // It's the same file
                return(true);                // nothing to do here
            }

            string tempFile = Path.Combine(workingPath, Path.GetFileNameWithoutExtension(sourceVideo) + "-temp" + Util.FilePaths.CleanExt(sourceVideo));

            // Get the length of the video, needed to calculate end point
            float Duration;

            Duration = VideoParams.VideoDuration(sourceVideo);
            FFmpegMediaInfo ffmpegStreamInfo = new FFmpegMediaInfo(sourceVideo, _jobStatus, _jobLog);

            if (!ffmpegStreamInfo.Success || ffmpegStreamInfo.ParseError)
            {
                _jobLog.WriteEntry(this, "Cannot read video info", Log.LogEntryType.Error);
                return(false);
            }

            if (Duration <= 0)
            {
                // Converted file should contain only 1 audio stream
                Duration = ffmpegStreamInfo.MediaInfo.VideoInfo.Duration;
                _jobLog.WriteEntry(this, "Video duration : " + Duration.ToString(System.Globalization.CultureInfo.InvariantCulture), Log.LogEntryType.Information);

                if (Duration == 0)
                {
                    _jobLog.WriteEntry(this, "Video duration 0", Log.LogEntryType.Error);
                    return(false);
                }
            }

            // dont' use threads here since we are copying video to improve stability
            string ffmpegParams = "-y";

            ffmpegParams += " -i " + Util.FilePaths.FixSpaces(sourceVideo);

            // While setting the start trim before the input file (FAST seek) can speed up seeking and (http://ffmpeg.org/trac/ffmpeg/wiki/Seeking%20with%20FFmpeg) FAST seek (since accurate seek cuts on a NON KeyFrame which causes Audio Sync Issues)
            // Due to ffmpeg ticket #3252 we need to use ACCURATE seek (trim after input file) to avoid PTS<DTS error
            if (startTrim != 0)
            {
                if (startTrim < Duration)
                {
                    ffmpegParams += " -ss " + startTrim.ToString(System.Globalization.CultureInfo.InvariantCulture);
                }
                else
                {
                    _jobLog.WriteEntry(this, "Start trim (" + startTrim.ToString() + ") greater than file duration (" + Duration.ToString(System.Globalization.CultureInfo.InvariantCulture) + "). Skipping start trimming.", Log.LogEntryType.Warning);
                    startTrim = 0; // Skip it
                }
            }

            // Set the end trim (calculate from reducing from video length)
            if (endTrim != 0)
            {
                // FFMPEG can specify duration of encoding, i.e. encoding_duration = stopTime - startTime
                // startTime = startTrim, stopTime = video_duration - endTrim
                int encDuration = (((int)Duration) - endTrim) - (startTrim); // by default _startTrim is 0
                if (encDuration > 0)
                {
                    ffmpegParams += " -t " + encDuration.ToString(System.Globalization.CultureInfo.InvariantCulture);
                }
                else
                {
                    _jobLog.WriteEntry(this, "End trim (" + endTrim.ToString() + ") + Start trim (" + startTrim.ToString() + ") greater than file duration (" + Duration.ToString(System.Globalization.CultureInfo.InvariantCulture) + "). Skipping end trimming.", Log.LogEntryType.Warning);
                    endTrim = 0;
                }
            }

            // Sanity check once more
            if ((startTrim == 0) && (endTrim == 0))
            {
                _jobLog.WriteEntry(this, "Start trim and end trim skipped. Skipping trimming.", Log.LogEntryType.Warning);
                _trimmedVideo = sourceVideo; // It's the same file
                return(true);                // nothing to do here
            }

            // Check for audio channels
            if (ffmpegStreamInfo.AudioTracks > 0)
            {
                ffmpegParams += " -map 0:a -acodec copy";
            }
            else
            {
                ffmpegParams += " -an";
            }

            // Check for video stream
            if (ffmpegStreamInfo.MediaInfo.VideoInfo.Stream != -1)
            {
                ffmpegParams += " -map 0:" + ffmpegStreamInfo.MediaInfo.VideoInfo.Stream.ToString() + " -vcodec copy"; // Fix for FFMPEG WTV MJPEG ticket #2227
            }
            else
            {
                ffmpegParams += " -vn";
            }

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

            if (!FFmpeg.FFMpegExecuteAndHandleErrors(ffmpegParams, _jobStatus, _jobLog, Util.FilePaths.FixSpaces(tempFile))) //check of file is created, outputhandler reports success (Percentage not requires since Success is more accurate)
            {
                _jobLog.WriteEntry("Failed to trim video at " + _jobStatus.PercentageComplete.ToString(System.Globalization.CultureInfo.InvariantCulture) + "%", Log.LogEntryType.Error);
                return(false);
            }

            // Replace the file
            if (File.Exists(tempFile))
            {
                _jobLog.WriteEntry(this, "TrimVideo trying to replace file Source : " + sourceVideo + " Temp : " + tempFile, Log.LogEntryType.Debug);

                // If the original uncut file is in the working temp directory, then just replace it
                if (Path.GetDirectoryName(sourceVideo).ToLower() == workingPath.ToLower())
                {
                    Util.FileIO.TryFileReplace(sourceVideo, tempFile);
                }
                else // If the original uncut video is not in the working temp directory, then just rename the tempFile with the original name and keep in the temp working directory (don't mangle original video file)
                {
                    FileIO.TryFileDelete(Path.Combine(workingPath, Path.GetFileName(sourceVideo))); // Just in case it exists
                    FileIO.MoveAndInheritPermissions(tempFile, Path.Combine(workingPath, Path.GetFileName(sourceVideo)));
                }

                _trimmedVideo = Path.Combine(workingPath, Path.GetFileName(sourceVideo)); // The final cut video always lies in the working directory
            }
            else
            {
                _jobLog.WriteEntry(this, "TrimVideo cannot find temp file " + tempFile, Log.LogEntryType.Error);
                _jobStatus.PercentageComplete = 0;
                return(false);
            }

            return(true); // All good here
        }
Example #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;

            _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);

            if (_detectInterlacing) // Get Interlacing from FFMPEG more reliable then MediaInfo - 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);
                }

                if (_scanType == ScanType.Unknown) // If we couldn't get it from FFMPEG lets try MediaInfo as a backup
                {
                    _scanType = VideoParams.VideoScanType(SourceVideo);
                    jobLog.WriteEntry(this, " MediaInfo Video Scan Type : " + _scanType.ToString(), Log.LogEntryType.Debug);
                }
            }

            // 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);
            }
        }
Example #4
0
        /// <summary>
        /// Extract closed captions from the source file into the temp path with same filename and SRT format
        /// </summary>
        /// <param name="sourceFile">Original file</param>
        /// <param name="workingPath">Temp folder</param>
        /// <param name="ccOptions">CC extraction options</param>
        /// <param name="startTrim">Trim initial</param>
        /// <param name="endTrim">Trim end</param>
        /// <param name="ccOffset">Offset initial</param>
        /// <returns>True if successful</returns>
        public bool ExtractCC(string sourceFile, string workingPath, string ccOptions, int startTrim, int endTrim, double ccOffset)
        {
            // TODO: Do we need process WTV files separately with -wtvmpeg2 option and also Teletext and DVB with -teletext and -codec dvb options?

            _jobLog.WriteEntry(this, ("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(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            _jobLog.WriteEntry(this, "Stop Trim : " + endTrim.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            _jobLog.WriteEntry(this, "Offset : " + ccOffset.ToString(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
            }
            // NOTE: delay does not work reliably do not use
            // Adjust for any offset required during extraction (opposite direction, so -ve)
            if (ccOffset != 0)
            {
                ccExtractorParams += " -delay " + (-ccOffset * 1000).ToString(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, ("Video duration") + " : " + Duration.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Information);

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

            // Set the start trim time (it only accepts +ve)
            if (startTrim > 0)
            {
                ccExtractorParams += " -startat " + TimeSpan.FromSeconds((double)startTrim).ToString();
            }
            else if (startTrim < 0)
            {
                _jobLog.WriteEntry(this, "Skipping start trim since it's negative", Log.LogEntryType.Warning);
            }

            // 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();
            }
            else if (endTrim < 0)
            {
                _jobLog.WriteEntry(this, "Skipping end trim since it's negative", Log.LogEntryType.Warning);
            }

            // 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(("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, ("No valid SRT file found"), Log.LogEntryType.Warning);
                return(true);                              // no error
            }

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

            return(true);
        }
Example #5
0
        /// <summary>
        /// Extracts subtitles from a video file into a SRT format (with same name) and cleans it up.
        /// It will overwrite any existing SRT files
        /// If there are multiple subtitles it extracts them into multiple files with incremental names.
        /// </summary>
        /// <param name="sourceFile">Path to video file</param>
        /// <param name="offset">Offset of the subtitles during extraction</param>
        /// <param name="overWrite">True to overwrite existing SRT files, false to create new ones with incremental names</param>
        /// <param name="languageExtractList">List of 3 digit language codes to extract (blank to extract all, unnamed languages will always be extracted)</param>
        /// <returns>True if successful</returns>
        public bool ExtractSubtitles(string sourceFile, string workingPath, int startTrim, int endTrim, double offset, bool overWrite, List <string> languageExtractList)
        {
            _jobLog.WriteEntry(this, ("Extracting Subtitles from " + sourceFile + " into 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, "Start Trim : " + startTrim.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            _jobLog.WriteEntry(this, "Stop Trim : " + endTrim.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
            _jobLog.WriteEntry(this, "Offset : " + offset.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Debug);

            if (String.IsNullOrEmpty(sourceFile))
            {
                return(true); // nothing to do
            }
            if (!File.Exists(sourceFile))
            {
                _jobLog.WriteEntry(this, ("File does not exist " + sourceFile), Log.LogEntryType.Warning);
                return(true); //nothing to process
            }

            FFmpegMediaInfo mediaInfo = new FFmpegMediaInfo(sourceFile, _jobStatus, _jobLog);

            if (!mediaInfo.Success || mediaInfo.ParseError)
            {
                _jobLog.WriteEntry(this, ("Error reading subtitle info from file"), Log.LogEntryType.Error);
                return(false);
            }

            _jobLog.WriteEntry(this, "Found " + mediaInfo.SubtitleTracks.ToString() + " Subtitle tracks, extract only the first matching track", Log.LogEntryType.Debug);

            bool extractedSubtitle = false;

            for (int i = 0; i < mediaInfo.SubtitleTracks; i++)
            {
                if (extractedSubtitle) // Only extract and use one subtitle (sometimes chapter tracks are misidentified as subtitle tracks)
                {
                    continue;
                }

                // Build the command line
                string parameters    = "";
                string outputSRTFile = ""; // Using Serviio subtitle filename format (filename.srt or filename_language.srt or filename_uniquenumber.srt)

                // Check for language comparison if required
                if (languageExtractList != null)
                {
                    if (languageExtractList.Count > 0) // If list is empty, we extract all
                    {
                        _jobLog.WriteEntry(this, "Subtitle language extraction list -> " + String.Join(",", languageExtractList.ToArray()), Log.LogEntryType.Debug);

                        if (!String.IsNullOrWhiteSpace(mediaInfo.MediaInfo.SubtitleInfo[i].Language))        // check if we have a language defined for this track
                        {
                            if (!languageExtractList.Contains(mediaInfo.MediaInfo.SubtitleInfo[i].Language)) // This language is not in the list of extraction
                            {
                                _jobLog.WriteEntry(this, "Skipping subtitle extraction since subtitle language >" + mediaInfo.MediaInfo.SubtitleInfo[i].Language + "< is NOT in the subtitle language list", Log.LogEntryType.Warning);
                                continue; // Skip this subtitle track
                            }
                        }
                        else
                        {
                            _jobLog.WriteEntry(this, "Extracting subtitle since there is no language defined for track", Log.LogEntryType.Debug);
                        }
                    }
                }

                // Check for existing SRT files
                if (overWrite)
                {
                    outputSRTFile = Path.Combine(workingPath, Path.GetFileNameWithoutExtension(sourceFile)) + (i > 0 ? (String.IsNullOrWhiteSpace(mediaInfo.MediaInfo.SubtitleInfo[i].Language) ? "_" + i.ToString() : "_" + mediaInfo.MediaInfo.SubtitleInfo[i].Language) : "") + ".srt"; // First file user default name, then try to name with language first, if not give a unique number
                    parameters   += " -y";
                }
                else // Create a unique SRT file name
                {
                    int existingSRTCount = 0;
                    outputSRTFile = Path.Combine(workingPath, Path.GetFileNameWithoutExtension(sourceFile)) + ".srt"; // Try default name
                    while (File.Exists(outputSRTFile))
                    {
                        _jobLog.WriteEntry(this, "Subtitle file " + outputSRTFile + " exists, creating a new unique SRT filename", Log.LogEntryType.Debug);
                        outputSRTFile = Path.Combine(workingPath, Path.GetFileNameWithoutExtension(sourceFile)) + (String.IsNullOrWhiteSpace(mediaInfo.MediaInfo.SubtitleInfo[i].Language) ? "_" + existingSRTCount.ToString() : "_" + mediaInfo.MediaInfo.SubtitleInfo[i].Language + (existingSRTCount > 0 ? existingSRTCount.ToString() : "")) + ".srt"; // Create a unique SRT filename, try with language first, if not give a unique identifier, avoid a loop
                        existingSRTCount++;
                    }
                }

                // Build ffmpeg command line
                parameters += " -i " + FilePaths.FixSpaces(sourceFile);
                parameters += " -an -vn";
                parameters += " -map 0:" + mediaInfo.MediaInfo.SubtitleInfo[i].Stream.ToString(); // Subtitle steam no
                parameters += " -scodec copy -copyinkf -f srt " + FilePaths.FixSpaces(outputSRTFile);

                // Now extract it
                _jobLog.WriteEntry(this, "Extracting Subtitle " + (i + 1).ToString() + " with language >" + mediaInfo.MediaInfo.SubtitleInfo[i].Language + "< to " + outputSRTFile, Log.LogEntryType.Debug);
                FFmpeg ffmpeg = new FFmpeg(parameters, _jobStatus, _jobLog);
                ffmpeg.Run();
                if (!ffmpeg.Success)
                {
                    FileIO.TryFileDelete(outputSRTFile); // Delete partial file

                    // Backup, try using MP4Box instead to extract it
                    _jobLog.WriteEntry(this, ("FFMPEG failed to extract subtitles into SRT file, retrying using MP4Box"), Log.LogEntryType.Warning);
                    parameters = "-srt " + (mediaInfo.MediaInfo.SubtitleInfo[i].Stream + 1).ToString() + " " + FilePaths.FixSpaces(sourceFile);
                    // MP4Box create an output file called <input>_<track>_text.srt
                    // Check if the output srt exists and then rename it if it does
                    string tempSrtOutput = FilePaths.GetFullPathWithoutExtension(sourceFile) + "_" + (mediaInfo.MediaInfo.SubtitleInfo[i].Stream + 1).ToString() + "_text.srt";
                    bool   tempSrtExists = false;
                    if (File.Exists(tempSrtOutput)) // Save the output srt filename if it exists
                    {
                        try
                        {
                            FileIO.MoveAndInheritPermissions(tempSrtOutput, tempSrtOutput + ".tmp");
                        }
                        catch (Exception e)
                        {
                            _jobLog.WriteEntry(this, ("Error extracting subtitles into SRT file.\r\n" + e.ToString()), Log.LogEntryType.Error);
                            return(false);
                        }
                        tempSrtExists = true;
                    }

                    // Extract the subtitle
                    MP4Box mp4Box = new MP4Box(parameters, _jobStatus, _jobLog);
                    mp4Box.Run();
                    if (!mp4Box.Success)
                    {
                        _jobLog.WriteEntry(this, ("Error extracting subtitles into SRT file"), Log.LogEntryType.Error);
                        FileIO.TryFileDelete(tempSrtOutput); // Delete partial file
                        if (tempSrtExists)
                        {
                            RestoreSavedSrt(tempSrtOutput);
                        }
                        return(false);
                    }

                    if (FileIO.FileSize(tempSrtOutput) <= 0) // MP4Box always return success even if nothing is extracted, so check if has been extracted
                    {
                        _jobLog.WriteEntry(this, "No or empty Subtitle file " + tempSrtOutput + " extracted by MP4Box, skipping", Log.LogEntryType.Debug);
                        FileIO.TryFileDelete(tempSrtOutput); // Delete empty file
                    }
                    else
                    {
                        // Rename the temp output SRT to the expected name
                        try
                        {
                            FileIO.MoveAndInheritPermissions(tempSrtOutput, outputSRTFile);
                        }
                        catch (Exception e)
                        {
                            _jobLog.WriteEntry(this, ("Error extracting subtitles into SRT file.\r\n" + e.ToString()), Log.LogEntryType.Error);
                            FileIO.TryFileDelete(tempSrtOutput); // Delete partial file
                            if (tempSrtExists)
                            {
                                RestoreSavedSrt(tempSrtOutput);
                            }
                            return(false);
                        }
                    }

                    // Restore temp SRT file if it exists
                    if (tempSrtExists)
                    {
                        RestoreSavedSrt(tempSrtOutput);
                    }
                }

                if (FileIO.FileSize(outputSRTFile) <= 0) // Check for empty files
                {
                    _jobLog.WriteEntry(this, "Empty Subtitle file " + outputSRTFile + " extracted, deleting it", Log.LogEntryType.Warning);
                    FileIO.TryFileDelete(outputSRTFile); // Delete empty file
                }
                else
                {
                    // Trim the SRT file if required
                    if (startTrim > 0 || endTrim > 0)
                    {
                        // 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, ("Video duration") + " : " + Duration.ToString(CultureInfo.InvariantCulture), Log.LogEntryType.Information);

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

                        // Trim the subtitle
                        if (!TrimSubtitle(outputSRTFile, workingPath, startTrim, endTrim, Duration, 0))
                        {
                            _jobLog.WriteEntry(this, ("Error trimming SRT file"), Log.LogEntryType.Error);
                            return(false);
                        }
                    }

                    // Clean it up and offset the SRT if required
                    if (!SRTValidateAndClean(outputSRTFile, offset))
                    {
                        _jobLog.WriteEntry(this, ("Cannot clean and set offset for SRT file"), Log.LogEntryType.Error);
                        return(false);
                    }

                    // Check for empty file
                    if (Util.FileIO.FileSize(outputSRTFile) <= 0)
                    {
                        FileIO.TryFileDelete(outputSRTFile); // Delete the empty file
                        _jobLog.WriteEntry(this, ("No valid SRT file found"), Log.LogEntryType.Warning);
                        continue;                            // check for the next subtitle track
                    }

                    _extractedSRTFile = outputSRTFile; // Save it
                    _jobLog.WriteEntry(this, "Extracted Subtitle file " + outputSRTFile + ", size [KB] " + (FileIO.FileSize(outputSRTFile) / 1024).ToString("N", CultureInfo.InvariantCulture), Log.LogEntryType.Debug);
                    extractedSubtitle = true;          // We have success
                }
            }

            return(true);
        }
Example #6
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());
        }