Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QAudioOutput playing a QProcess standard output: end of data undetected
Forum Updated to NodeBB v4.3 + New Features

QAudioOutput playing a QProcess standard output: end of data undetected

Scheduled Pinned Locked Moved General and Desktop
2 Posts 1 Posters 2.0k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • T Offline
    T Offline
    thiel
    wrote on last edited by
    #1

    QAudioOutput playing a QProcess standard output: end of data undetected

    Hello,

    I have an issue regarding the detection of the end of data while reading the standard output of a QProcess, especially when using it as audio source for a QAudioOutput instance.

    I use Qt 5.1.0 on Windows 7.

    I have isolated the issue in the sample source code below. I create a QProcess instance which runs the ffmpeg executable as a simple example. The ffmpeg command extracts the first audio stream of a video file and converts it into simple 16-bit PCM samples. The output file is the process standard output.

    I close the standard input and standard error channels of the QProcess. I explicitly sets standard output of the QProcess as its "read channel". Then, I start a QAudioOutput on the QProcess (which is a subclass of QIODevice). The audio is played correctly until the end of the process output.

    But the QAudioOutput never enters the IdleState after the end of data. The problem is: How can I be notified that the QAudioOutput has completed the audio stream?

    There are several tracks that I explored without luck.

    I am notified of the process completion. But most of the time there are still many audio samples in the output pipe. If I stop the QAudioOutput when I am notified of the process completion, the last part of the audio is not played. For small files, the process completion is notified even before we can hear any sample.

    For testing, I manually ran the same ffmpeg command and saved the output in a file. In a modified version of the test, I use a QFile on this file as QAudioOutput source. The end of the file is detected by QAudioOutput which enters IdleState. Program output:

    @
    Audio state changed to ActiveState
    Audio state changed to IdleState
    @

    On the other hand, when QAudioOutput uses the process output as source, IdleState is never entered. Program output:

    @
    Audio state changed to ActiveState
    Process finished, exit code 0 exit status 0
    @

    As another test, I removed the QAudioOutput and tried to read myself the ffmpeg process output, just like QAudioOutput does. I connect to the signal QIODevice::readyRead() and get notified of all process output data. There is something weird here. In the corresponding slot, I read data and check QIODevice::atEnd(). In all invocations of the slot, atEnd() returns true, even the first one. At the end of the process output, I simply no longer get invoked. How can I detect that I reached the end of data?

    Note that the atEnd() documentation is pretty vague on the reliability of the information it returns: "For some devices, atEnd() can return true even though there is more data to read. This special case only applies to devices that generate data in direct response to you calling read() (e.g., /dev or /proc files on Unix and Mac OS X, or console input / stdin on all platforms)".

    So, it seems that there are two distinct issues here:

    A QProcess issue: How can we be reliably notified of the end of data while reading the process output?

    A QAudioOutput issue: When the QAudioOuput source runs out of samples, it should notify somehow. When the audio source is a QFile, the IdleState is entered. Why not with a QProcess source? Probably because, unlike QFile, the end of the file is not reliably detected. After end of data, the QAudioOutput should report a QAudio::UnderrunError. But there seems to be no signal to notify an error, QAudioOutput::error() must be call synchronously.

    Any idea on how to reliably stop a QAudioOutput when its source is a QProcess?
    Thanks.

    PS: I will post the sample code in a reply. Attempting to include it here resulted in "Your post is too large. The maximum number of allowed characters is 6000".

    1 Reply Last reply
    0
    • T Offline
      T Offline
      thiel
      wrote on last edited by
      #2

      Sample code to reproduce the problem.

      Project file:
      @
      TEMPLATE = app
      CONFIG += qt thread
      QT += core multimedia
      TARGET = testaudio
      SOURCES += testaudio.cpp main.cpp
      HEADERS += testaudio.h
      @

      Main program main.cpp:
      @
      #include "testaudio.h"

      int main(int argc, char *argv[])
      {
      QCoreApplication a(argc, argv);
      // Customize with your path to ffmep executable and test file
      TestAudio test("C:\Program Files\FFmpeg\bin\ffmpeg.exe", "test.mpg");
      return a.exec();
      }
      @

      TestAudio header file testaudio.h:
      @
      #include <QtCore>
      #include <QtDebug>
      #include <QtMultimedia>

      class TestAudio: public QObject
      {
      Q_OBJECT
      public:
      TestAudio(const QString& ffmpeg, const QString& fileName, QObject* parent = 0);
      private slots:
      void audioStateChanged(QAudio::State audioState);
      void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
      void processError(QProcess::ProcessError error);
      private:
      QProcess* process;
      QAudioOutput* audio;
      };
      @

      TestAudio implementation file testaudio.cpp:
      @
      #include "testaudio.h"

      TestAudio::TestAudio(const QString& ffmpeg, const QString& fileName, QObject* parent) :
      QObject(parent),
      process(0),
      audio(0)
      {
      // FFmpeg argument list.
      QStringList args;
      args << "-i" << fileName
      << "-vn" // Suppress video streams.
      << "-map" << "0:a:0" // Select first audio stream.
      << "-codec:a" << "pcm_s16le" // Audio format: PCM 16 bits little endian.
      << "-ar" << "44100" // Resample to 44.1 kHz.
      << "-ac" << "1" // Remix to one channel (mono).
      << "-f" << "s16le" // Output file format is raw PCM.
      << "-"; // Output file is standard output.

      // Start FFmpeg process. Keep only standard output channel.
      process = new QProcess(this);
      connect(process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus)));
      connect(process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
      process->setProcessChannelMode(QProcess::SeparateChannels);
      process->setReadChannel(QProcess::StandardOutput);
      process->start(ffmpeg, args);
      process->closeWriteChannel();
      process->closeReadChannel(QProcess::StandardError);
      
      // Same audio format for QAudioOutput.
      QAudioFormat audioFormat;
      audioFormat.setCodec("audio/pcm");
      audioFormat.setSampleRate(44100);
      audioFormat.setChannelCount(1);
      audioFormat.setSampleSize(16);
      audioFormat.setByteOrder(QAudioFormat::LittleEndian);
      audioFormat.setSampleType(QAudioFormat::SignedInt);
      
      // Start audio output.
      audio = new QAudioOutput(audioFormat, this);
      audio->setVolume(1.0);
      connect(audio, SIGNAL(stateChanged(QAudio::State)), this, SLOT(audioStateChanged(QAudio::State)));
      audio->start(process);
      

      }

      void TestAudio::audioStateChanged(QAudio::State audioState)
      {
      qDebug() << "Audio state changed to " << audioState;
      }

      void TestAudio::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
      {
      qDebug() << "Process finished, exit code " << exitCode << "exit status" << exitStatus;
      }

      void TestAudio::processError(QProcess::ProcessError error)
      {
      qDebug() << "Process error " << error;
      }
      @

      1 Reply Last reply
      0

      • Login

      • Login or register to search.
      • First post
        Last post
      0
      • Categories
      • Recent
      • Tags
      • Popular
      • Users
      • Groups
      • Search
      • Get Qt Extensions
      • Unsolved