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
}
}