예제 #1
0
        public void RunGraph()
        {
            DateTime lastTick = DateTime.Now;
            int      hangPeriod = MCEBuddyConf.GlobalMCEConfig.GeneralOptions.hangTimeout;
            long     lastVideoSize = 0, lastAudioSize = 0, lastSubtitleSize = 0;
            long     totalPartsSize = 0, sourceSize = 0;
            bool     AbortError = false;
            int      hr         = 0;

            IMediaControl mediaControl = (IMediaControl)_fg;
            IMediaEvent   mediaEvent   = (IMediaEvent)_fg;

            hr = mediaControl.Run();
            checkHR(hr);

            // Change the priority temporarily (need to reset it back after Dumping Streams)
            ProcessPriorityClass lastPriority = GlobalDefs.Priority;         // Set it up

            Process.GetCurrentProcess().PriorityClass = GlobalDefs.Priority; // Set the CPU Priority
            IOPriority.SetPriority(GlobalDefs.IOPriority);                   // First set the CPU priority

            // Get filesize of source file
            sourceSize = Util.FileIO.FileSize(_SourceFile); // Sanity checking
            if (sourceSize <= 0)
            {
                _jobLog.WriteEntry(this, "Unable to get source file size, disabling infinite loop checking.", Log.LogEntryType.Warning);
                hangPeriod = 0;
            }

            bool stop = false, isSuspended = false;

            while (!stop)
            {
                System.Threading.Thread.Sleep(100);
                if (_jobStatus.Cancelled)
                {
                    // Received a shutdown command external to the filter extraction
                    _jobLog.WriteEntry(this, "Stream extraction cancelled, aborting graph.", Log.LogEntryType.Warning);
                    stop       = true;
                    AbortError = true;
                    mediaControl.Stop();
                    break;
                }

                if (isSuspended)
                {
                    lastTick = DateTime.Now;          // Since during suspension there will be no output it shouldn't terminate the process
                }
                if (!isSuspended && GlobalDefs.Pause) // Check if process has to be suspended (if not already)
                {
                    _jobLog.WriteEntry(this, "Stream extraction paused", Log.LogEntryType.Information);
                    mediaControl.Pause();
                    isSuspended = true;
                }

                if (isSuspended && !GlobalDefs.Pause) // Check if we need to resume the process
                {
                    _jobLog.WriteEntry(this, "Stream extraction resumed", Log.LogEntryType.Information);
                    isSuspended = false;
                    mediaControl.Run();
                }

                if (lastPriority != GlobalDefs.Priority) // Check if the priority was changed and if so update it
                {
                    _jobLog.WriteEntry(this, "Stream extraction priority changed", Log.LogEntryType.Information);
                    lastPriority = GlobalDefs.Priority;
                    Process.GetCurrentProcess().PriorityClass = GlobalDefs.Priority; // Set the CPU Priority
                    IOPriority.SetPriority(GlobalDefs.IOPriority);                   // First set the CPU priority
                }

                EventCode ev;
                IntPtr    p1, p2;
                if (mediaEvent.GetEvent(out ev, out p1, out p2, 0) == 0)
                {
                    if (ev == EventCode.Complete)
                    {
                        mediaControl.Stop();
                        stop = true;
                    }
                    else if (ev == EventCode.ErrorAbort || ev == EventCode.UserAbort || ev == EventCode.StErrStopped || ev == EventCode.ErrorAbortEx)
                    {
                        mediaControl.Stop();
                        stop = true;
                        //AbortError = true; - some partial/corrupted files are errored out, we'll handle extraction errors later
                    }
                    mediaEvent.FreeEventParams(ev, p1, p2);
                }

                // Sanity checking to prevent infinite loop for extraction (sometimes steams extracts infinitely)
                // Check if the filesize exceed the initial file size and if so abort the operation
                if (sourceSize > 0)
                {
                    totalPartsSize = 0;

                    // Video file check
                    if ((_extractMediaType & ExtractMediaType.Video) != 0)
                    {
                        long videoSize = Util.FileIO.FileSize(_VideoPart);
                        if (videoSize < 0)
                        {
                            _jobLog.WriteEntry(this, "Unable to get extracted video stream file size for infinite loop detection.", Log.LogEntryType.Warning);
                        }
                        else if (videoSize > (sourceSize * INFINITE_LOOP_CHECK_THRESHOLD))
                        {
                            _jobLog.WriteEntry(this, "Extracted video stream is greater than " + INFINITE_LOOP_CHECK_THRESHOLD.ToString(CultureInfo.InvariantCulture) + " times the source file size " + (sourceSize / 1024).ToString("N", CultureInfo.InvariantCulture) + " [KB].\r\nExtraction likely hung, terminating streams extraction.", Log.LogEntryType.Error);
                            stop       = true;
                            AbortError = true;
                            mediaControl.Stop();
                            break;
                        }

                        if (hangPeriod > 0)
                        {
                            if (videoSize > lastVideoSize) // If we have progress
                            {
                                lastTick = DateTime.Now;
                            }
                        }

                        totalPartsSize += videoSize;
                        lastVideoSize   = videoSize;
                    }

                    // Audio file check
                    if ((_extractMediaType & ExtractMediaType.Audio) != 0)
                    {
                        foreach (string audioPart in _AudioParts)
                        {
                            long audioSize = Util.FileIO.FileSize(audioPart);
                            if (audioSize < 0)
                            {
                                _jobLog.WriteEntry(this, "Unable to get extracted audio stream file size for infinite loop detection.", Log.LogEntryType.Warning);
                            }
                            else if (audioSize > (sourceSize * INFINITE_LOOP_CHECK_THRESHOLD))
                            {
                                _jobLog.WriteEntry(this, "Extracted audio stream is greater than " + INFINITE_LOOP_CHECK_THRESHOLD.ToString(CultureInfo.InvariantCulture) + " times the source file size " + (sourceSize / 1024).ToString("N", CultureInfo.InvariantCulture) + " [KB].\r\nExtraction likely hung, terminating streams extraction.", Log.LogEntryType.Error);
                                stop       = true;
                                AbortError = true;
                                mediaControl.Stop();
                                break;
                            }

                            if (hangPeriod > 0)
                            {
                                if (audioSize > lastAudioSize) // If we have progress
                                {
                                    lastTick = DateTime.Now;
                                }
                            }

                            totalPartsSize += audioSize;
                            lastAudioSize   = audioSize;
                        }
                    }

                    // Subtitle file check
                    if ((_extractMediaType & ExtractMediaType.Subtitle) != 0)
                    {
                        foreach (string subtitlePart in _SubtitleParts)
                        {
                            long subtitleSize = Util.FileIO.FileSize(subtitlePart);
                            if (subtitleSize < 0)
                            {
                                _jobLog.WriteEntry(this, "Unable to get extracted subtitle stream file size for infinite loop detection.", Log.LogEntryType.Warning);
                            }
                            else if (subtitleSize > (sourceSize * INFINITE_LOOP_CHECK_THRESHOLD))
                            {
                                _jobLog.WriteEntry(this, "Extracted subtitle stream is greater than " + INFINITE_LOOP_CHECK_THRESHOLD.ToString(CultureInfo.InvariantCulture) + " times the source file size " + (sourceSize / 1024).ToString("N", CultureInfo.InvariantCulture) + " [KB].\r\nExtraction likely hung, terminating streams extraction.", Log.LogEntryType.Error);
                                stop       = true;
                                AbortError = true;
                                mediaControl.Stop();
                                break;
                            }

                            if (hangPeriod > 0)
                            {
                                if (subtitleSize > lastSubtitleSize) // If we have progress
                                {
                                    lastTick = DateTime.Now;
                                }
                            }

                            totalPartsSize  += subtitleSize;
                            lastSubtitleSize = subtitleSize;
                        }
                    }

                    if (totalPartsSize < 0)
                    {
                        totalPartsSize = 0;                                                                                                                                           // Incase we get -ve numbers
                    }
                    _jobStatus.PercentageComplete = (((float)totalPartsSize / (float)sourceSize) > 1 ? 100 : ((float)totalPartsSize / (float)sourceSize) * 100);                      // Calculate % complete from size estimation (since no recoding is happening) and cap at 100%
                    _jobLog.WriteEntry(this, "Percentage complete : " + _jobStatus.PercentageComplete.ToString("0.00", CultureInfo.InvariantCulture) + " %", Log.LogEntryType.Debug); // Write to file
                }

                // Check if we have reached the end of the file or runs out of disk space, sometime windows just loops endlessly without any incremental output
                // TODO: Should we treat this as an error or normal processing
                if ((hangPeriod > 0) && (DateTime.Now > lastTick.AddSeconds(hangPeriod)))
                {
                    _jobLog.WriteEntry("No response from stream extraction for " + hangPeriod + " seconds, process likely finished, continuing.", Log.LogEntryType.Warning); // Don't treat as an error for now
                    stop = true;
                    // AbortError = true;  // Don't treat as an error for now
                    mediaControl.Stop();
                    break;
                }
            }

            // Reset Priority to Normal
            IOPriority.SetPriority(GlobalDefs.EngineIOPriority);                   // Set CPU priority after restoring the scheduling priority
            Process.GetCurrentProcess().PriorityClass = GlobalDefs.EnginePriority; // Set the CPU Priority back to Above Normal (engine always runs above normal)

            List <string> parts = _AudioParts.Concat(_SubtitleParts).ToList();     // Create a list of all subtitle, audio parts

            if (!String.IsNullOrWhiteSpace(_VideoPart))
            {
                parts.Add(_VideoPart);                                                                                                                                         // add video part
            }
            _jobLog.WriteEntry(this, "Source " + _SourceFile + " filesize [KB] : " + (sourceSize / 1024).ToString("N", CultureInfo.InvariantCulture), Log.LogEntryType.Debug); // Write to file
            foreach (string part in parts)
            {
                _jobLog.WriteEntry(this, part + " extracted filesize [KB] : " + (FileIO.FileSize(part) / 1024).ToString("N", CultureInfo.InvariantCulture), Log.LogEntryType.Debug); // Write to file
            }
            _jobLog.WriteEntry(this, "Total extracted parts size [KB] : " + (totalPartsSize / 1024).ToString("N", CultureInfo.InvariantCulture), Log.LogEntryType.Debug);            // Write to file

            if (!AbortError)
            {
                _SuccessfulExtraction = true;
            }
        }
예제 #2
0
        public virtual void Run()
        {
            //_success = false; //initial state to be defined by each inheriting class depending on it's handlers, we only update error conditions in this routine
            Process  Proc = null;
            DateTime ExecutionStartTime;

            // Last thing to check, for custom commands, this will log an error and exit
            if (!File.Exists(_ApplicationPath))
            {
                _jobStatus.ErrorMsg = "Application File Not Found";
                _jobLog.WriteEntry(this, _jobStatus.ErrorMsg + " : " + _ApplicationPath, Log.LogEntryType.Error);
                _jobStatus.PercentageComplete = 0;
                _success = false;
                return;
            }

            if (_Parameters.Length > 8192)
            {
                _jobLog.WriteEntry(this, Localise.GetPhrase("Warning - command parameters exceeding 8192 characters.  This may fail on some systems such as Windows XP"), Log.LogEntryType.Warning);
            }

            // Reset any progress counters
            _jobStatus.PercentageComplete = 0;
            _jobStatus.ETA = Localise.GetPhrase("Working") + "..."; //some processes like ReMuxSupp don't update perc, put a default message

            // Check if job has been cancelled, needed for some long running functions called Base
            if (_jobStatus.Cancelled)
            {
                _jobStatus.ErrorMsg = "Job cancelled, killing process";
                _jobLog.WriteEntry(this, _jobStatus.ErrorMsg, Log.LogEntryType.Error);
                _success = false; //process has been terminated
                return;
            }

            try
            {
                if (_HangPeriod > 0)
                {
                    _LastTick = DateTime.Now;
                }

                _jobLog.WriteEntry(this, "Launching process " + _ApplicationPath, Log.LogEntryType.Debug);
                _jobLog.WriteEntry(this, "Process arguments " + _Parameters, Log.LogEntryType.Debug);

                if (GlobalDefs.IsEngineRunningAsService) // these params only make sense when running as a service
                {
                    _jobLog.WriteEntry(this, "UI Session Admin Process : " + _uiAdminSessionProcess.ToString(), Log.LogEntryType.Debug);
                }

                //Start the process
                ExecutionStartTime = DateTime.Now;

                // Check if we need to run a UISession process - It can only be run from a service, if we are running from a non service console, we don't need this
                // Apps like handbrake with hardware accelleration cannot run from a non UI session in windows (even in Windows 8 there are limitations with ffmpeg and OpenCL from Session 0) due to unavailability of hardware (OpenCL) API's in NonUI sessions mode
                // These apps needs to be started in a UI session separately to run
                // Check if we are running as a Service (Session 0 non UI Interactive) and if the user forcing use of UI Session 1
                bool checkUICompliance = (GlobalDefs.IsEngineRunningAsService && _uiAdminSessionProcess);
                if (checkUICompliance)
                {
                    _jobLog.WriteEntry(this, "Starting process as a UISession process with Admin privileges. This requires atleast 1 user to be logged into the system (remote desktop or locally)", Log.LogEntryType.Debug);
                    uint procId = AppProcess.StartAppWithAdminPrivilegesFromNonUISession(_ApplicationPath, _Parameters, true, OutputHandler, _showWindow, _jobLog);
                    if (procId == 0)
                    {
                        _jobLog.WriteEntry(this, "Unable to create UI Session process with Admin Privileges from NonUI Session. Is any user logged on?", Log.LogEntryType.Warning);
                        _jobLog.WriteEntry(this, "Retrying process creation as a NonUI Session process with Admin privileges", Log.LogEntryType.Warning);
                        _jobLog.WriteEntry(this, "Some functions like hardware encoding may not work in this mode", Log.LogEntryType.Warning);
                        Proc = null;
                    }
                    else
                    {
                        Proc = Process.GetProcessById((int)procId); // Get the process identifier
                    }
                }

                // Create the process if it hasn't already been created
                if (Proc == null)
                {
                    //Set up the process
                    Proc = new Process();
                    Proc.StartInfo.FileName  = _ApplicationPath;
                    Proc.StartInfo.Arguments = _Parameters;
                    if (_showWindow)
                    {
                        Proc.StartInfo.CreateNoWindow = false; // for custom apps we create a window
                        Proc.StartInfo.WindowStyle    = ProcessWindowStyle.Normal;
                    }
                    else
                    {
                        Proc.StartInfo.CreateNoWindow = true;
                        Proc.StartInfo.WindowStyle    = ProcessWindowStyle.Hidden;
                    }
                    Proc.StartInfo.WorkingDirectory       = Path.GetDirectoryName(_ApplicationPath);
                    Proc.StartInfo.RedirectStandardOutput = true;
                    Proc.StartInfo.RedirectStandardError  = true;
                    Proc.StartInfo.UseShellExecute        = false; // always false else error handlers break
                    Proc.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
                    Proc.ErrorDataReceived  += new DataReceivedEventHandler(OutputHandler);

                    Proc.Start(); // First start the process else we get an exception
                    Proc.BeginOutputReadLine();
                    Proc.BeginErrorReadLine();
                }
            }
            catch (Exception Ex)
            {
                _jobStatus.ErrorMsg = "Unable to start process";
                _jobLog.WriteEntry(this, _jobStatus.ErrorMsg + "\r\n" + Ex.Message, Log.LogEntryType.Error);
                _success = false; //process did not start
                return;           // Exit now or we get an exception
            }

            // NOTE: Do all this after starting the process outside the catch block. Sometimes the process exists quickly (e.g. when process priority is IDLE) before this code runs and hence this code throws an exception
            try
            {
                _jobLog.WriteEntry(this, "Setting process priority to " + GlobalDefs.Priority.ToString(), Log.LogEntryType.Debug);
                Proc.PriorityClass = GlobalDefs.Priority;                   // Set the CPU Priority
                IOPriority.SetPriority(Proc.Handle, GlobalDefs.IOPriority); // Set the IO Priority

                if (MCEBuddyConf.GlobalMCEConfig.GeneralOptions.CPUAffinity != (IntPtr)0)
                {
                    _jobLog.WriteEntry(this, "Setting CPU affinity to -> " + MCEBuddyConf.GlobalMCEConfig.GeneralOptions.CPUAffinity.ToString("d"), Log.LogEntryType.Debug);
                    Proc.ProcessorAffinity = MCEBuddyConf.GlobalMCEConfig.GeneralOptions.CPUAffinity;
                }
            }
            catch (Exception e)
            {
                _jobLog.WriteEntry(this, "Error trying process priority or setting CPU affinity to -> " + MCEBuddyConf.GlobalMCEConfig.GeneralOptions.CPUAffinity.ToString("d") + "\n" + e.ToString(), Log.LogEntryType.Warning);
            }

            //Wait for an end
            while ((!Proc.HasExited))
            {
                try
                {
                    if (!_ignoreSuspend)                      // for some processes like ffmpegMediaInfo initiated by UI, we cannot block since it will hang the application
                    {
                        if (!isSuspended && GlobalDefs.Pause) // Check if process has to be suspended (if not already)
                        {
                            _jobLog.WriteEntry(this, "Suspending process", Log.LogEntryType.Information);
                            IOPriority.SuspendProcess(Proc.Id);
                            _jobLog.Flush(); // Flush the pending writes
                            isSuspended = true;
                        }

                        if (isSuspended && !GlobalDefs.Pause) // Check if we need to resume the process
                        {
                            _jobLog.WriteEntry(this, "Resuming process", Log.LogEntryType.Information);
                            isSuspended = false;
                            _percentageHistory.Clear(); // Lose the history and start from here
                            IOPriority.ResumeProcess(Proc.Id);
                        }
                    }

                    if (Proc.PriorityClass != GlobalDefs.Priority) // Check if the priority was changed and if so update it
                    {
                        _jobLog.WriteEntry(this, "Process priority changed to " + GlobalDefs.Priority.ToString(), Log.LogEntryType.Information);
                        Proc.PriorityClass = GlobalDefs.Priority;
                        IOPriority.SetPriority(Proc.Handle, GlobalDefs.IOPriority); // First set the CPU priority
                    }
                }
                catch { } //incase process exits in the background - not an issue, just avoid a crash

                if (_jobStatus.Cancelled) // if job has been cancelled kill the process
                {
                    try
                    {
                        Proc.Kill();
                        _jobStatus.ErrorMsg = "Job cancelled, killing process";
                        _jobLog.WriteEntry(_jobStatus.ErrorMsg, Log.LogEntryType.Error);
                        _success = false; //process has been terminated
                    }
                    catch (Exception Ex)
                    {
                        _jobStatus.ErrorMsg = "Job cancelled, unable to kill process";
                        _jobLog.WriteEntry(_jobStatus.ErrorMsg, Log.LogEntryType.Error);
                        _jobLog.WriteEntry(Ex.Message, Log.LogEntryType.Warning);
                        _success = false; //process has been terminated
                    }
                    break;
                }

                if (_unrecoverableError)
                {
                    _jobLog.WriteEntry("Unrecoverable error encountered. Process likely hung, killing it", Log.LogEntryType.Error);
                    _success = false;
                    break;
                }

                if (isSuspended)
                {
                    _LastTick = DateTime.Now; // Since during suspension there will be no output it shouldn't terminate the process
                }
                if ((_HangPeriod > 0) && (DateTime.Now > _LastTick.AddSeconds(_HangPeriod)))
                {
                    _jobLog.WriteEntry("No response from process for " + _HangPeriod + " seconds, process likely hung - killing it", Log.LogEntryType.Error);
                    _success = false;
                    break;
                }

                System.Threading.Thread.Sleep(100); // sleep and check again
            }

            if (!_jobStatus.Cancelled)
            {
                System.Threading.Thread.Sleep(2000); //Allow the last set of messages to be written
            }
            if (!Proc.HasExited)                     // If we broke out of the loop, somethign bad happened
            {
                try
                {
                    Proc.Kill();
                    _jobStatus.ErrorMsg = "Process hung, killing process";
                    _jobLog.WriteEntry(_jobStatus.ErrorMsg, Log.LogEntryType.Error);
                    _success = false; //process has been terminated
                }
                catch (Exception Ex)
                {
                    _jobStatus.ErrorMsg = "Unable to terminate process";
                    _jobLog.WriteEntry(_jobStatus.ErrorMsg + "\r\n" + Ex.Message, Log.LogEntryType.Error);
                    _success = false; //process has been terminated
                }
            }
            else
            {
                // Sometimes for some reason the process exits without the output redirect async handlers completing causing failures
                // MSDN http://msdn.microsoft.com/en-us/library/fb4aw7b8(v=vs.110).aspx recommends using WaitForExit(x) followed by WaitForExit() to flush all output async handlers
                // TODO: Aysnc output is broken in .NET even with WaitForExit(), see http://alabaxblog.info/2013/06/redirectstandardoutput-beginoutputreadline-pattern-broken/ and http://stackoverflow.com/questions/9533070/how-to-read-to-end-process-output-asynchronously-in-c
                try
                {
                    while (!Proc.WaitForExit(10))
                    {
                        ;               // Wait for it to return True
                    }
                    Proc.WaitForExit(); // Now wait for it to flush the buffers
                }
                catch { }

                _exitCode = Proc.ExitCode; // Store the exit code
                _jobLog.WriteEntry("Process exited with code " + _exitCode.ToString(), Log.LogEntryType.Debug);
            }


            _ExecutionTime = DateTime.Now - ExecutionStartTime;

            //Close out
            try
            {
                Proc.Close();
                Proc.Dispose();
            }
            catch (Exception Ex)
            {
                _jobLog.WriteEntry("Unable to cleanly close process" + "\r\n" + Ex.Message, Log.LogEntryType.Error);
            }

            Proc = null;

            if (!_jobStatus.Cancelled)
            {
                System.Threading.Thread.Sleep(100);    //Allow for the process to be flush any pending messages
            }
        }