275 lines
8.4 KiB
C#
275 lines
8.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Threading;
|
|
|
|
namespace m3uTool
|
|
{
|
|
/// <summary>
|
|
/// A generic encoder. Creates an external process, sends it argument options,
|
|
/// and acts as a wrapper for streaming reads and writes
|
|
/// </summary>
|
|
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;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the progress callback.
|
|
/// </summary>
|
|
/// <value>The progress callback.</value>
|
|
public IProgressCallback ProgressCallback
|
|
{
|
|
get { return mProgressCallback; }
|
|
set { mProgressCallback = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the process executable filename, such as "LAME.EXE", "FAAC.EXE", etc.
|
|
/// </summary>
|
|
/// <value>The process executable filename.</value>
|
|
protected abstract string ProcessExecutableFilename
|
|
{
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="encOpt"></param>
|
|
public Encoder(ProcessArguments encOpt) : this (encOpt, null)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Encoder"/> class.
|
|
/// </summary>
|
|
/// <param name="encOpt">The enc opt.</param>
|
|
/// <param name="outputStream">Output to this stream</param>
|
|
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");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sends the data to the underlying process through its input stream.
|
|
/// </summary>
|
|
/// <param name="buffer">The data to encode</param>
|
|
/// <param name="count">How many characters to write</param>
|
|
/// <param name="startIndex">The index to begin writing from</param>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Encodes all of the data in the input stream
|
|
/// </summary>
|
|
/// <param name="inputStream"></param>
|
|
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!");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Open this file and encode it.
|
|
/// </summary>
|
|
/// <param name="filename"></param>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
|
/// </summary>
|
|
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
|
|
}
|
|
}
|