using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text; using System.Threading; namespace m3uTool { /// /// A generic encoder. Creates an external process, sends it argument options, /// and acts as a wrapper for streaming reads and writes /// public abstract class Encoder : IDisposable { protected ProcessArguments mEncOpt; protected Process mEncoderProcess; protected const int mBufferLength = 16384; protected StreamWriter mOutputStreamWriter = null; protected IProgressCallback mProgressCallback = new DoNothingProgressCallback(); protected const byte mEndOfStream = 26; /// /// Gets or sets the progress callback. /// /// The progress callback. public IProgressCallback ProgressCallback { get { return mProgressCallback; } set { mProgressCallback = value; } } /// /// Gets the process executable filename, such as "LAME.EXE", "FAAC.EXE", etc. /// /// The process executable filename. protected abstract string ProcessExecutableFilename { get; } /// /// /// /// public Encoder(ProcessArguments encOpt) : this (encOpt, null) { } /// /// Initializes a new instance of the class. /// /// The enc opt. /// Output to this stream public Encoder(ProcessArguments encOpt, Stream outputStream) { //if (!outputStream.CanWrite) // throw new ArgumentException("Can't write to output stream"); //if (!string.IsNullOrEmpty(encOpt.OutputFilename) && encOpt.OutputFilename.Trim() != "-") // throw new ArgumentException("Trying to output to both a stream and a file " + encOpt.OutputFilename); //encOpt.OutputFilename = "-"; mEncOpt = encOpt; mEncoderProcess = CreateEncoderProcess(mEncOpt, ProcessExecutableFilename); if (outputStream != null) { mOutputStreamWriter = new StreamWriter(outputStream, Encoding.Default); mEncoderProcess.StartInfo.RedirectStandardOutput = true; } mEncoderProcess.StartInfo.RedirectStandardError = true; mEncoderProcess.Start(); if (mOutputStreamWriter != null) { Thread thread = new Thread(new ThreadStart(GetStandardOutput)); thread.Start(); } Thread standardErrorThread = new Thread(new ThreadStart(GetStandardError)); standardErrorThread.Start(); } private Process CreateEncoderProcess(ProcessArguments encOpt, string encoderFilename) { Process encoderProcess = new Process(); encoderProcess.StartInfo.RedirectStandardInput = true; encoderProcess.StartInfo.UseShellExecute = false; encoderProcess.StartInfo.FileName = encoderFilename; encoderProcess.StartInfo.Arguments = encOpt.GetCommandLineArguments(); encoderProcess.StartInfo.WorkingDirectory = Environment.CurrentDirectory; encoderProcess.StartInfo.CreateNoWindow = true; encoderProcess.EnableRaisingEvents = true; encoderProcess.Exited += new EventHandler(encoderProcess_Exited); return encoderProcess; } void encoderProcess_Exited(object sender, EventArgs e) { mProgressCallback.SetText("encoderProcess_Exited"); } /// /// Sends the data to the underlying process through its input stream. /// /// The data to encode /// How many characters to write /// The index to begin writing from public void Encode(char[] buffer, int startIndex, int count) { // Debug.WriteLine("-"); mEncoderProcess.StandardInput.Write(buffer, startIndex, count); if (count != mBufferLength) mProgressCallback.SetText("Encode : " + count); } public void WriteEndOfStream() { if (!mEncoderProcess.HasExited) { mEncoderProcess.StandardInput.BaseStream.Write(new byte[] { mEndOfStream }, 0, 1); mEncoderProcess.StandardInput.BaseStream.Flush(); } } private static int GetPercentDone(long previousLeft, long currentLeft, long currentDone, int lastDone) { // if currentLeft > previousLeft - lastDone, // addedLeft = previousLeft - lastDone - currentLeft // totalLength = previousLeft + currentDone + addedLeft long totalLength = currentLeft; if (currentLeft != previousLeft) { long leftDelta = previousLeft - lastDone - currentLeft; totalLength = previousLeft + currentDone + leftDelta; } try { return Convert.ToInt32((currentDone / (double)totalLength) * 100); } catch(Exception ex) { Debug.WriteLine(ex.ToString()); } return 0; } /// /// Encodes all of the data in the input stream /// /// public void Encode(Stream inputStream) { if (!inputStream.CanRead) throw new ArgumentException("Can't read from inputStream"); mProgressCallback.SetText("Encode: StreamReader sr"); long inputCount = inputStream.Length; long previousInputCount = inputStream.Length; long processCount = 0; int percentageComplete = 0; using (StreamReader sr = new StreamReader(inputStream, Encoding.Default)) { mProgressCallback.Begin(0, 100); mProgressCallback.SetText(mEncOpt.GetCommandLineArguments()); char[] buffer = new char[mBufferLength]; while (!sr.EndOfStream) { int readLength = sr.Read(buffer, 0, mBufferLength); if (buffer[readLength - 1] == mEndOfStream) readLength--; Encode(buffer, 0, readLength); // update progress processCount += readLength; previousInputCount = inputCount; inputCount = inputStream.Length; int currentPercentageComplete = GetPercentDone(previousInputCount, inputCount, processCount, readLength); if (currentPercentageComplete != percentageComplete) { percentageComplete = currentPercentageComplete; mProgressCallback.StepTo(percentageComplete); } } } mProgressCallback.SetText("Encode: Done!"); } /// /// Open this file and encode it. /// /// public void Encode(string filename) { using (FileStream inputStream = File.OpenRead(filename)) { Encode(inputStream); } } private void GetStandardOutput() { char[] writeBuffer = new char[mBufferLength]; try { while (!mEncoderProcess.StandardOutput.EndOfStream) { int readLength = mEncoderProcess.StandardOutput.Read(writeBuffer, 0, writeBuffer.Length); mOutputStreamWriter.Write(writeBuffer, 0, readLength); //Debug.WriteLine("GetStandardOutput() " + "mOutputStreamWriter.Write : " + readLength); } } catch(Exception ex) { mProgressCallback.SetText(ex.ToString()); } mProgressCallback.SetText("GetStandardOutput() " + "Done!"); } private void GetStandardError() { // char[] writeBuffer = new char[10]; try { while (!mEncoderProcess.StandardError.EndOfStream) { string line = mEncoderProcess.StandardError.ReadLine(); //int readLength = mEncoderProcess.StandardError.Read(writeBuffer, 0, writeBuffer.Length); Debug.WriteLine("GetStandardError() " + line); } } catch (Exception ex) { mProgressCallback.SetText(ex.ToString()); } mProgressCallback.SetText("GetStandardError() " + "Done!"); } #region IDisposable Members /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { int count = 0; mProgressCallback.SetText("Dispose()"); while (mEncoderProcess != null && !mEncoderProcess.HasExited) { //WriteEndOfStream(); //Debug.Write("."); if (count++ > 50) mEncoderProcess.Kill(); Thread.Sleep(10); } mEncoderProcess.WaitForExit(); if (mOutputStreamWriter != null) mOutputStreamWriter.Flush(); //if (mEncoderProcess.ExitCode != 0) // throw new ApplicationException("Exit code " + mEncoderProcess.ExitCode); mProgressCallback.SetText("Dispose() with exit code " + mEncoderProcess.ExitCode); //if (mEncoderProcess != null && mEncoderProcess.Responding) // mEncoderProcess.Close(); } #endregion } }