Попытка перенаправить двоичный стандартный вывод ffmpeg в стандартный вывод NeroAacEnc - PullRequest
1 голос
/ 14 мая 2011

Я пытаюсь написать программу на C # 2010, которая конвертирует mp3-файлы в аудиокнигу в формате m4a через ffmpeg.exe и NeroAACenc.exe.Для этого я перенаправляю стандартный вывод ffmpeg на стандартный кодировщик Nero в моем приложении, используя встроенный класс Diagnostics.Process.

Кажется, что все работает, как и ожидалось, но по какой-то причине StandardOutput.BaseStream из ffmpeg перестает получать данные через некоторое время.Процесс не завершается, и событие ErrorDataReceived также не вызывается.Полученный выходной файл m4a всегда имеет длину ~ 2 минуты.То же самое относится и к тому, что я просто кодирую mp3-файл в временный wav-файл без подачи Nero.

Я попробовал то же самое через командную строку, и это работает без проблем.

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

}

Ответы [ 3 ]

2 голосов
/ 14 июня 2012

Я столкнулся с аналогичной проблемой отправки двоичных данных в StandardInput ffmpeg.Когда вы устанавливаете RedirectStandardError на true, вы должны каким-то образом справиться с этим.Кроме того, вы можете установить его на false.

В противном случае процесс приостанавливается, ожидая, что вы прочитаете StandardError / StandardOutput, что может вызвать проблемы.

1 голос
/ 19 декабря 2014

Это было решено? Я только что заметил этот вопрос несколько недель назад (ноябрь 2014), и у меня может быть решение. Шаблон кода представлен ниже и предназначен для работы с двоичными данными стандартного ввода и двоичными данными.

Есть много вещей, которые я должен был рассмотреть, чтобы заставить это работать в моей программе.

  1. stdin, stdout может представлять большой объем данных. Поэтому StandardInput и StandardOutput предоставляются внешними источниками.

  2. Win Forms не обрабатывает асинхронные события без сложной разработки кода. Я упростил проблему с помощью таймера. (Я считаю, что .NET 4.5 асинхронен и ожидаю упрощения асинхронных сложностей, но я их пока не использую.)

  3. Событие Process.Exited не показалось надежным, поэтому я использовал альтернативный метод для определения «Завершено». Я определяю метод OnFinish () для трансляции события «Закончено». Для определения «Готово» необходимы две вещи: 1) Ps должны бежать до выхода. WaitForExit () используется. 2) Все данные должны быть прочитаны из Ps.StandardOutput. Таймер проверяет, что Ps вышел и у Ps.StandardOutput больше нет данных для чтения. Если оба условия выполняются, он вызывает OnFinish (). На других форумах есть указание, что вы должны и WaitForExit (), и прочитать все StandardOutput, прежде чем сможете продолжить обработку данных StandardOutput. Убедитесь, что timer.Enabled имеет значение true. Я также использовал значение по умолчанию timer.Interval = 100.

  4. Ошибка StandardError (поскольку она не двоичная и, вероятно, не такая большая) была обработана собственным обработчиком событий. А Commander предоставил свой собственный StandardError и передал сообщения (если они есть) в строку ErrorMessage.

  5. (как очевидно, наблюдается) Процесс заключен в объект Commander. Поэтому я не использую 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 голосов
/ 02 мая 2017

Может быть, эта оболочка поможет вам

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