C# 尝试将ffmpeg的二进制标准输出重定向到NeroAacEnc标准输出
我正试图用C#2010编写一个程序,通过ffmpeg.exe和NeroAACenc.exe将mp3文件转换成m4a格式的有声图书。 为此,我使用内置Diagnostics.Process类将ffmpeg的stdout重定向到应用程序中Nero编码器的stdin 一切似乎都按预期工作,但出于某种原因,StandardOutput.BaseStream 的ffmpeg在某个时间停止接收数据。进程不会退出,并且也不会引发ErrorDataReceived事件。 生成的输出m4a文件的长度始终为~2分钟。如果我只是将mp3文件编码为一个临时wav文件而不输入Nero,同样适用 我通过命令行尝试了同样的方法,并且没有任何问题C# 尝试将ffmpeg的二进制标准输出重定向到NeroAacEnc标准输出,c#,ffmpeg,stdout,stdin,redirectstandardoutput,C#,Ffmpeg,Stdout,Stdin,Redirectstandardoutput,我正试图用C#2010编写一个程序,通过ffmpeg.exe和NeroAACenc.exe将mp3文件转换成m4a格式的有声图书。 为此,我使用内置Diagnostics.Process类将ffmpeg的stdout重定向到应用程序中Nero编码器的stdin 一切似乎都按预期工作,但出于某种原因,StandardOutput.BaseStream 的ffmpeg在某个时间停止接收数据。进程不会退出,并且也不会引发ErrorDataReceived事件。 生成的输出m4a文件的长度始终为~2分钟
ffmpeg -i test.mp3 -f wav - | neroAacEnc -ignorelength -if - -of test.m4a
谁能告诉我我做错了什么?
提前谢谢
class Encoder
{
private byte[] ReadBuffer = new byte[4096];
private Process ffMpegDecoder = new Process();
private Process NeroEncoder = new Process();
private BinaryWriter NeroInput;
//Create WAV temp file for testing
private Stream s = new FileStream("D:\\test\\test.wav", FileMode.Create);
private BinaryWriter outfile;
public void Encode()
{
ProcessStartInfo ffMpegPSI = new ProcessStartInfo("ffmpeg.exe", "-i D:\\test\\test.mp3 -f wav -");
ffMpegPSI.UseShellExecute = false;
ffMpegPSI.CreateNoWindow = true;
ffMpegPSI.RedirectStandardOutput = true;
ffMpegPSI.RedirectStandardError = true;
ffMpegDecoder.StartInfo = ffMpegPSI;
ProcessStartInfo NeroPSI = new ProcessStartInfo("neroAacEnc.exe", "-if - -ignorelength -of D:\\test\\test.m4a");
NeroPSI.UseShellExecute = false;
NeroPSI.CreateNoWindow = true;
NeroPSI.RedirectStandardInput = true;
NeroPSI.RedirectStandardError = true;
NeroEncoder.StartInfo = NeroPSI;
ffMpegDecoder.Exited += new EventHandler(ffMpegDecoder_Exited);
ffMpegDecoder.ErrorDataReceived += new DataReceivedEventHandler(ffMpegDecoder_ErrorDataReceived);
ffMpegDecoder.Start();
NeroEncoder.Start();
NeroInput = new BinaryWriter(NeroEncoder.StandardInput.BaseStream);
outfile = new BinaryWriter(s);
ffMpegDecoder.StandardOutput.BaseStream.BeginRead(ReadBuffer, 0, ReadBuffer.Length, new AsyncCallback(ReadCallBack), null);
}
private void ReadCallBack(IAsyncResult asyncResult)
{
int read = ffMpegDecoder.StandardOutput.BaseStream.EndRead(asyncResult);
if (read > 0)
{
NeroInput.Write(ReadBuffer);
NeroInput.Flush();
outfile.Write(ReadBuffer);
outfile.Flush();
ffMpegDecoder.StandardOutput.BaseStream.Flush();
ffMpegDecoder.StandardOutput.BaseStream.BeginRead(ReadBuffer, 0, ReadBuffer.Length, new AsyncCallback(ReadCallBack), null);
}
else
{
ffMpegDecoder.StandardOutput.BaseStream.Close();
outfile.Close();
}
}
private void ffMpegDecoder_Exited(object sender, System.EventArgs e)
{
Console.WriteLine("Exit");
}
private void ffMpegDecoder_ErrorDataReceived(object sender, DataReceivedEventArgs errLine)
{
Console.WriteLine("Error");
}
}
我在向ffmpeg的
StandardInput
发送二进制数据时遇到了类似的问题。当您将RedirectStandardError
设置为true
时,必须以某种方式进行处理。或者,您可以将其设置为false
否则,进程将暂停,等待您读取可能导致问题的
StandardError
/StandardOutput
。是否已解决此问题?我几周前(2014年11月)才注意到这个问题,我可能有一个解决方案。代码模式如下所示,用于对二进制stdin和二进制stdout数据进行操作
有很多事情要做,我必须考虑让它在我的程序中工作。
private bool DoneReadingStdOut { get; set; }
private bool DoneWaitingForPs { get; set; }
public string ErrorMessage { get; set; }
public bool IsFinished { get; set; }
private Process Ps { get; set; }
private ProcessPriorityClass m_PriorityClass = ProcessPriorityClass.Normal;
public ProcessPriorityClass PriorityClass {
get
{
return m_PriorityClass;
}
set
{
m_PriorityClass = value;
}
}
private MemoryStream m_StdIn = null;
public MemoryStream StandardInput
{
get { return m_StdIn; }
set
{
m_StdIn = value;
Ps.StartInfo.RedirectStandardInput = (value == null ? false : true);
}
}
private MemoryStream m_StdOut = null;
public MemoryStream StandardOutput
{
get { return m_StdOut; }
set
{
m_StdOut = value;
Ps.StartInfo.RedirectStandardOutput = (value == null ? false : true);
}
}
private Timer m_Timer; // To synchronize asynchronous activity
public Commander(string command, string options)
{
m_Timer = new Timer();
m_Timer.Enabled;
m_Timer.Tick += timer_Tick;
ErrorMessage = null;
IsFinished = false;
Ps = new Process();
Ps.ErrorDataReceived += Ps_ErrorDataReceived;
Ps.StartInfo.Arguments = options;
Ps.StartInfo.CreateNoWindow = true;
Ps.StartInfo.FileName = command;
Ps.StartInfo.RedirectStandardError = true;
Ps.StartInfo.WorkingDirectory = Path.GetDirectoryName(command); // optional
}
public event EventHandler<EventArgs> Finished;
private void OnFinish(EventArgs e)
{
if (IsFinished) return;
IsFinished = true;
if (Finished != null)
{
Finished(this, e);
}
}
public void Run()
{
Start();
}
public void Run(ref MemoryStream stdin, ref MemoryStream stdout)
{
StandardInput = stdin;
StandardOutput = stdout;
Start();
}
private void Start()
{
Ps.Start();
Ps.PriorityClass = m_PriorityClass;
Ps.BeginErrorReadLine();
if (StandardInput != null)
{
Inject();
}
AsyncExtract();
Ps.WaitForExit();
DoneWaitingForPs = true;
}
private void Inject()
{
StandardInput.Position = 0;
StandardInput.CopyTo(Ps.StandardInput.BaseStream);
Ps.StandardInput.BaseStream.Close();
}
private byte[] m_StreamData = null;
private int m_StreamDataLength = 8192;
private void AsyncExtract()
{
if (m_StreamData == null)
{
m_StreamData = new byte[m_StreamDataLength];
}
Ps.StandardOutput.BaseStream.BeginRead(
m_StreamData, 0, m_StreamDataLength,
new AsyncCallBack(StandardOutput_AsyncCallBack),
null
);
}
private void StandardOutput_AsyncCallBack(IAsyncResult asyncResult)
{
int stdoutreadlength = Ps.StandardOutput.BaseStream.EndRead(asyncResult);
if (stdoutreadlength == 0)
{
Ps.StandardOutput.BaseStream.Close();
DoneReadingStdOut = true;
}
else
{
StandardOutput.Write(m_StreamData, 0, stdoutreadlength);
AsyncExtract();
}
}
private void Ps_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data == null) return;
ErrorMessage += e.Data;
}
private timer_Tick(object sender, EventArgs e)
{
if (DoneWaitingForPs && DoneReadingStdOut)
{
m_Timer.Enabled = false;
OnFinish(new EventArgs());
}
}
private bool DoneReadingStdOut{get;set;}
私有bool DoneWaitingForPs{get;set;}
公共字符串错误消息{get;set;}
公共bool已完成{get;set;}
私有进程Ps{get;set;}
private ProcessPriorityClass m_PriorityClass=ProcessPriorityClass.Normal;
公共进程PriorityClass PriorityClass{
得到
{
返回m_PriorityClass;
}
设置
{
m_PriorityClass=值;
}
}
私有内存流m_StdIn=null;
公共内存流标准输入
{
获取{return m_StdIn;}
设置
{
m_StdIn=数值;
Ps.StartInfo.RedirectStandardInput=(值==null?false:true);
}
}
私有内存流m_StdOut=null;
公共内存流标准输出
{
获取{return m_StdOut;}
设置
{
m_StdOut=值;
Ps.StartInfo.RedirectStandardOutput=(值==null?false:true);
}
}
private Timer m_Timer;//同步异步活动
公共指挥官(字符串命令、字符串选项)
{
m_Timer=新定时器();
m_定时器已启用;
m_Timer.Tick+=定时器_Tick;
ErrorMessage=null;
IsFinished=false;
Ps=新工艺();
Ps.ErrorDataReceived+=Ps_ErrorDataReceived;
Ps.StartInfo.Arguments=选项;
Ps.StartInfo.CreateNoWindow=true;
Ps.StartInfo.FileName=命令;
Ps.StartInfo.RedirectStandardError=true;
Ps.StartInfo.WorkingDirectory=Path.GetDirectoryName(命令);//可选
}
公共事件事件处理程序已完成;
私有void OnFinish(事件参数e)
{
如果(完成)返回;
IsFinished=true;
如果(已完成!=null)
{
完成(本,e);
}
}
公开募捐
{
Start();
}
公共无效运行(ref MemoryStream stdin,ref MemoryStream stdout)
{
标准输入=标准输入;
标准输出=标准输出;
Start();
}
私有void Start()
{
Ps.Start();
Ps.PriorityClass=m_PriorityClass;
Ps.BeginErrorReadLine();
if(StandardInput!=null)
{
注入();
}
异步提取();
Ps.WaitForExit();
DoneWaitingForPs=真;
}
私有无效注入()
{
标准输入。位置=0;
StandardInput.CopyTo(Ps.StandardInput.BaseStream);
Ps.StandardInput.BaseStream.Close();
}
私有字节[]m_StreamData=null;
private int m_StreamDataLength=8192;
私有void AsyncExtract()
{
如果(m_StreamData==null)
{
m_StreamData=新字节[m_StreamDataLength];
}
Ps.StandardOutput.BaseStream.BeginRead(
m_StreamData,0,m_StreamDataLength,
新的AsyncCallBack(标准输出\u AsyncCallBack),
无效的
);
}
私有void StandardOutput\u AsyncCallBack(IAsyncResult asyncResult)
{
int stdouredLength=Ps.StandardOutput.BaseStream.EndRead(asyncResult);
如果(stdoureadlength==0)
{
Ps.StandardOutput.BaseStream.Close();
DoneReadingStdOut=true;
}
其他的
ffmpeg -i tmp/jay001.mp3 -f wav pipe:1 | neroAacEnc -cbr 24000 -hev2 -ignorelength -if - -of tmp/jay001-24.m4a