Initial commit — M3U playlist tool with MP3/AAC encoding
This commit is contained in:
+274
@@ -0,0 +1,274 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user