2011-05-14 213 views
1

我試圖用C#2010中的MP3文件轉換爲通過ffmpeg.exe和NeroAACenc.exe音頻書M4A格式的程序。 爲此,我使用Diagnostics.Process類中的內部版本將ffmpeg的stdout重定向到我的應用程序中的Nero編碼器的標準輸出。試圖ffmpeg的二進制標準輸出重定向到NeroAacEnc標準輸入

一切似乎按預期工作,但由於某種原因,標準輸出.BaseStream 停止接收數據在某個時間。該過程不會退出並且ErrorDataReceived事件也不會引發。 生成的輸出m4a文件的長度總是大約2分鐘。如果我只是在不提供Nero的情況下將mp3文件編碼爲臨時wav文件,情況也是如此。

我試過相同的命令行,這工作沒有任何問題。

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"); 
    } 

} 

回答

2

我遇到了類似的問題發送二進制數據到FFmpeg的StandardInput。當您將RedirectStandardError設置爲true時,您必須以某種方式處理它。或者,您可以將其設置爲false

否則,該過程將暫停,等待您讀取可能導致問題的StandardError/StandardOutput

1

這個問題解決了嗎?幾周前我剛剛注意到這個問題(2014年11月),我可能有一個解決方案。代碼模式如下所示,旨在對二進制stdin和二進制stdout數據進行操作。

有很多事情,我不得不考慮到得到這個在我的計劃工作。

  1. stdin,stdout可能代表大量的數據。因此StandardInput和StandardOutput由外部來源提供。

  2. 贏形式不處理,而無需複雜的代碼開發異步事件。我通過使用計時器簡化了問題。 (我相信.NET 4.5 async並等待簡化異步複雜性,但我還沒有使用它們。)

  3. Process.Exited事件似乎不可靠,所以我使用了一種替代方法來確定「Finished」 。我定義了方法OnFinish()來廣播「Finished」事件。有兩件事情需要確定「完成」:1)Ps必須運行,直到退出。使用WaitForExit()。 2)必須從Ps.StandardOutput中讀取所有數據。計時器檢查Ps已經退出並且Ps.StandardOutput沒有更多數據要讀取。如果兩個條件都成立,它會調用OnFinish()。在其他論壇上有跡象表明,在繼續處理StandardOutput數據之前,您必須同時使用WaitForExit()和讀取所有的StandardOutput。確保timer.Enabled爲true。我也用它的默認timer.interval = 100.

  4. StandardError(因爲它不是二進制的,可能不是那麼大)是由它自己的事件處理程序處理的。 Commander提供了自己的StandardError,並將消息(如果有的話)提供給ErrorMessage字符串。

  5. (如明顯觀察到的)的方法是盒裝入指揮官對象。所以我不直接使用Process。我使用Commander並讓Commander以更友好的方式使用Process。

    公共類指揮官{

    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()); 
        } 
    } 
    

    }

0

也許這個殼會幫助你

ffmpeg -i tmp/jay001.mp3 -f wav pipe:1 | neroAacEnc -cbr 24000 -hev2 -ignorelength -if - -of tmp/jay001-24.m4a 
相關問題