Skip to content
  • 0 Votes
    3 Posts
    366 Views
    J

    @Bonnie Thank you for the reply.

    Since my last post, I made some significant progress by debugging the Windows Qt multimedia source. I swapped QBuffer for a QRingBuffer. As it turns out, the performance issues were not related to the QBuffer in my previous implementation, however I prefer to use a lighter weight object as the Single Producer Single Consumer (SPSC) buffer between the microphone (the default input device) and the speaker (the default output device).

    The remaing problem that I need help with is how to schedule a restart after I encounter a buffer underrun or Eof condition (where there are no bytes available in the SPSC buffer).

    I swapped QBuffer for Qt's private QRingBuffer class which is used in other multimedia QIODevice derived objects. This class is not really well documented but it is relatively straight forward to understand - this link shows the impmentation). The ring buffer is basically made up from a list of RingChunks - each of which is effectively a wrapper around a QByteArray (with supporting head and tail offsets).

    The problem now is that I once the pull mode AudioSink encounters an error in its timer callback method - in this case the pullSource method (see below for Qt's windows AudioSink implementation) (QWindowsAudioSink::pullSource), the audio output changes its state to QAudio::IdleState with QAudio::IOError or QAudio::UnderrunError, and stops the pull timer. The pull timer callback is resposible for requesting the next raw chunk of audio from the QRingBuffer via the m_pullSource->read(readLen)) and writing it to the speaker ourput device. Meanwhile the capture slot in my worker thread keeps appending microphone data to this shared mpSinkDevice - so the buffer keeps growing (which would prevent the buffer underrun/eof condition) but I have no idea how to restart the audio output.

    void RtpWorker::handleAudioAvailable(const QAudioBuffer& rAudioBuffer) const { // append captured audio to mpSinkDevice's QRingBuffer mpSinkDevice->write(rAudioBuffer.constData< const char>(), rAudioBuffer.byteCount()); }

    The m_pullSource field in the code below is a pointer to the QIODevice containing the QRingBuffer which was opened for Read/Write (write is required by the audio capture slot to copy the raw audio from the microphone to the QRingBuffer).

    void QWindowsAudioSink::pullSource() { qCDebug(qLcAudioOutput) << "Pull source"; if (!m_pullSource) return; auto bytesAvailable = m_pullSource->isOpen() ? qsizetype(m_pullSource->bytesAvailable()) : 0; auto readLen = qMin(bytesFree(), bytesAvailable); if (readLen > 0) { QByteArray samples = m_pullSource->read(readLen); if (samples.size() == 0) { deviceStateChange(QAudio::IdleState, QAudio::IOError); return; } else { write(samples.data(), samples.size()); } } auto playTimeUs = remainingPlayTimeUs(); if (playTimeUs == 0) { deviceStateChange(QAudio::IdleState, m_pullSource->atEnd() ? QAudio::NoError : QAudio::UnderrunError); } else { deviceStateChange(QAudio::ActiveState, QAudio::NoError); m_timer->start(playTimeUs / 2000); } }

    Here is my SinkDevice

    //! Modeled after the Generator class example from QT 6.x audio output example. class SinkDevice : public QIODevice { Q_OBJECT public: /** * Explicit constructor * * @param parent [in] parent. */ explicit SinkDevice(QObject* parent = nullptr) : QIODevice(parent) , mBuffer{} {} /** * Explicit constructor * * @param rByteArray [in] array of multi-channel audio * samples. * @param parent [in] parent. */ explicit SinkDevice(const QByteArray& rByteArray, QObject* parent = nullptr) : QIODevice(parent) , mBuffer{} { mBuffer.append(rByteArray); } ~SinkDevice() override = default; /** * Start the IO device - open in read/write mode * so it can act like a ring buffer. */ void start(); /** * Close IO device. */ void stop(); //! Audio device should give sequential access [[nodiscard]] bool isSequential() const override { return true; } [[nodiscard]] qint64 bytesAvailable() const override { return mBuffer.size() + QIODevice::bytesAvailable(); } // Our size [[nodiscard]] qint64 size() const override { return mBuffer.size(); } protected: [[nodiscard]] qint64 readData(char* data, qint64 maxlen) override; [[nodiscard]] qint64 writeData(const char* data, qint64 maxlen) override; private: // disable copy & move semantics on QObject subclasses // https://www.cleanqt.io/blog/why-qobject-subclasses-are-not-copyable Q_DISABLE_COPY_MOVE(SinkDevice) void generateData(const QAudioFormat& format, qint64 durationUs, int sampleRate); QRingBuffer mBuffer; };
  • 0 Votes
    7 Posts
    2k Views
    S

    @SGaist Yes in case of Qt 6.5.0, but this also happens with Qt 6.3.1, although the error I can read from QMediaPlayer is different:

    Qt 6.5.0: QMediaPlayer::ResourceError Qt 6.3.1: QMediaPlayer::FormatError

    Edit:
    The root cause of this issue was connected to file types and the way my url was constructed. I opted to use the native darwin backend with Qt 6.5 instead of the new ffmpeg, at least until 6.5.1 is out and I can do further testing. Thank you for the valuable input (as always!).

  • 0 Votes
    6 Posts
    1k Views
    kbarniK

    @SGaist Thank you! That should be probably it!
    I will test it on 6.5.1 when it will be released and report back.

  • 0 Votes
    10 Posts
    1k Views
    VagabondV

    @Pablo-J-Rogina thanks. Just looked into it. As the docs for QNetworkConfiguration state:

    QNetworkConfiguration encapsulates a single access point or service network. In most cases a single access point configuration can be mapped to one network interface.

    I deduce from that, that any call to QMediaPlayer::setNetworkConfigurations(QList<QNetworkConfiguration>) will not fix my problem. In fact, I gather from the information, that QNetworkConfiguration deals more with hardware-side networking & routing configurations than it does with specifics for transmission protocols.

    This notion is strengthened by the fact, that there is no accessible public members for QNetworkConfiguration and the only available constructors are copy and default. Therefore, the only way accessible way to provide valid values to setNetworkConfigurations is by filtering QNetworkConfigurationManager::allConfigurations()). Then again, this method produces a list dependent on the host system, which indicates hardware specific settings.

    Still, I got curious while reading the desciption for QNetworkConfiguration::ServiceSpecificPurpose, which states:

    The configuration can be used for operator specific services (e.g. receiving MMS messages or content **streaming**).

    For experimental purposes I filtered all available configurations of that type, which again didn't change anything for me.

    (Also, be sure to check out my latest edit up top, if you care for more info)

  • 0 Votes
    18 Posts
    6k Views
    SGaistS

    Why are they not ?

  • 0 Votes
    12 Posts
    6k Views
    kshegunovK

    @QtExchange said in Segmentation fault or overflow when streaming from a QSerialPort:

    The trick is one QByteArray receives the data (on signal slot basis)

    QByteArray can receive nothing, it's an array of bytes - a container, nothing more, nothing less.

    where the other container (which is just a copy of the receiver-QByteArray) is parsing the received Data, as the symbols come in a random order.

    ??!
    How can you parse something that doesn't have any order? And where is this other container?

    I could think of having an extra signal-slot that exchanges the data between both containers whenever it is demanded, but it sounds like I have to double check if I'm actually dealing with the most current data.

    I don't follow. Why would be this even needed ...?

    There's an extra mutex for the device access, and another one for the shared message container.

    Qt imposes thread affinity on QObject instances, you don't need a mutex for device access, and you shouldn't share the message container.

    and because I'm dealing with QElapsedTimer, that needs the thread.

    In what universe QElapsedTimer needs a thread ...?! It's like saying that an integer needs a thread ... I don't get it.

  • 0 Votes
    1 Posts
    787 Views
    No one has replied
  • 1 Votes
    16 Posts
    10k Views
    M

    @hariny , @onek24 Since this thread was not resolved;
    the buffer is not open !!!
    your are missing two lines of code:

    buffer->open(QIODevice::ReadOnly); buffer->seek(0);
  • 0 Votes
    4 Posts
    2k Views
    SGaistS

    Then I'd recommend using a dedicated library to do that. Qt already uses GStreamer for it's backend on Linux so that might be a possibility. You also have the QtGstreamer module that can help with that.

  • 0 Votes
    10 Posts
    8k Views
    A

    Is anybody here who have worked with libVLC without any problem?

    is it a convenience library or a lib with a lot of problem?

    please help me?

  • 0 Votes
    9 Posts
    5k Views
    S

    So is there any magic involved in getting a decent fps? I am aware that this is more related to QtGstreamer and RPi2 forums, but I've got nothing to lose trying here too as i assume others have been here before me.

    The code I've used just to verify that it works is from https://github.com/shadeslayer/qtgstreamer/tree/master/examples/qmlplayer. I removed the OpenGL part as it slowed the fps down to 1-2, and i removed the buttons to make it simpler.

    A tl;dr of the code is:

    QtGstreamer 0.10 Gstreamer 0.10 QGst::Ui::GraphicsVideoSurface QML linked to surface via sink playbin2 pipeline stream directly from RPi camera on /dev/video0

    Ive been having a hard time finding out what kind of element name would work when generating a pipeline, especially since I'm using Gstreamer 0.10, so I'm thinking the cause of the low FPS may be related a mix of the element name, the pipeline properties, and the (Qt)GStreamer version?

    Also, does QtGstreamer 0.10 support black/white colors?

  • 0 Votes
    3 Posts
    2k Views
    D

    @TioRoy Obrigado pela resposta. Acabei fazendo com a biblioteca ffmpeg, pois consegui buscar os dados e jogar pra fora em multicast mas fica travando, estou errando em algum lugar.

  • 0 Votes
    1 Posts
    1k Views
    No one has replied
  • 0 Votes
    2 Posts
    3k Views
    H

    You can do this as well in QML as in Widgets.

    Create the MediaPlayer object and set the source to your URL.

    For a axis-WEB-Cam with rtsp the url is:

    rtsp://username: password@camera_ip:554/axis-media/media.amp?videocodec=h264&streamprofile=low