我已经开发了基于Qt和QtGstreamer的视频播放器。它用于播放实时流(RTSP)。我必须增加用户在播放实时流时拍摄快照而不干扰视频播放的可能性。

这是我制作的管道图:

                                -->queue-->autovideosink
uridecodebin-->videoflip-->tee--|
            |                   -->queue->videoconvert-->pngenc-->filesink
            |
            |->audioconvert-->autoaudiosink


我使用来自pad-addeduridecodebin信号来添加我的元素并将其动态链接到管道,即接收到的上限的功能。

void Player::onPadAdded(const QGst::PadPtr &pad)
{
    QGst::CapsPtr caps = pad->currentCaps();
    if (caps->toString().startsWith("video/x-raw")) {
        qDebug("Received 'video/x-raw' caps");
        handleNewVideoPad(pad);
    }
    else if (caps->toString().startsWith("audio/x-raw")) {
        qDebug("Received 'audio/x-raw' caps");
        if (!m_audioEnabled) {
            qDebug("Audio is disabled in the player. Ignoring...");
            return;
        }
        handleNewAudioPad(pad);
    }
    else {
        qWarning("Unsuported caps, arborting ...!");
        return;
    }
}

[...]

void Player::handleNewVideoPad(QGst::PadPtr pad)
{
    m_player->videoTeeVideoSrcPad = m_player->videoTee->getRequestPad("src_%u");

    // Add video elements
    m_player->pipeline->add(m_player->videoFlip);
    m_player->pipeline->add(m_player->videoTee);
    m_player->pipeline->add(m_player->videoQueue);
    m_player->pipeline->add(m_player->videoSink);

    // Add snap elements
    m_player->pipeline->add(m_player->snapQueue);
    m_player->pipeline->add(m_player->snapConverter);
    m_player->pipeline->add(m_player->snapEncoder);
    m_player->pipeline->add(m_player->snapSink);

    // Link video elements
    m_player->videoFlip->link(m_player->videoTee);
    m_player->videoQueue->link(m_player->videoSink);

    // Link snap elements
    m_player->snapQueue->link(m_player->snapConverter);
    m_player->snapConverter->link(m_player->snapEncoder);
    m_player->snapEncoder->link(m_player->snapSink);

    // Lock snap elements
    m_player->snapQueue->setStateLocked(true);
    m_player->snapConverter->setStateLocked(true);
    m_player->snapEncoder->setStateLocked(true);
    m_player->snapSink->setStateLocked(true);

    m_player->videoFlip->setState(QGst::StatePlaying);
    m_player->videoTee->setState(QGst::StatePlaying);
    m_player->videoQueue->setState(QGst::StatePlaying);
    m_player->videoSink->setState(QGst::StatePlaying);

    // Link pads
    m_player->videoTeeVideoSrcPad->link(m_player->videoQueue->getStaticPad("sink"));
    pad->link(m_player->videoSinkPad);

    m_player->videoLinked = true;
}


拍摄快照的方法:

void Player::takeSnapshot()
{
    QDateTime dateTime = QDateTime::currentDateTime();
    QString snapLocation = QString("/%1/snap_%2.png").arg(m_snapDir).arg(dateTime.toString(Qt::ISODate));

    m_player->inSnapshotCaputre = true;

    if (m_player->videoTeeSnapSrcPad) {
        m_player->videoTee->releaseRequestPad(m_player->videoTeeSnapSrcPad);
        m_player->videoTeeSnapSrcPad.clear();
    }
    m_player->videoTeeSnapSrcPad = m_player->videoTee->getRequestPad("src_%u");

    // Stop the snapshot branch
    m_player->snapQueue->setState(QGst::StateNull);
    m_player->snapConverter->setState(QGst::StateNull);
    m_player->snapEncoder->setState(QGst::StateNull);
    m_player->snapSink->setState(QGst::StateNull);

    // Link Tee src pad to snap queue sink pad
    m_player->videoTeeSnapSrcPad->link(m_player->snapQueue->getStaticPad("sink"));

    // Set the snapshot location property
    m_player->snapSink->setProperty("location", snapLocation);

    // Unlock snapshot branch
    m_player->snapQueue->setStateLocked(false);
    m_player->snapConverter->setStateLocked(false);
    m_player->snapEncoder->setStateLocked(false);
    m_player->snapSink->setStateLocked(false);
    m_player->videoTeeSnapSrcPad->setActive(true);

    // Synch snapshot branch state with parent
    m_player->snapQueue->syncStateWithParent();
    m_player->snapConverter->syncStateWithParent();
    m_player->snapEncoder->syncStateWithParent();
    m_player->snapSink->syncStateWithParent();
}


总线消息回调:

void Player::onBusMessage(const QGst::MessagePtr & message)
{
    QGst::ElementPtr source = message->source().staticCast<QGst::Element>();
    switch (message->type()) {
    case QGst::MessageEos: { //End of stream. We reached the end of the file.
        qDebug("Message End Off Stream");
        if (m_player->inSnapshotCaputre) {
            blockSignals(true);
            pause();
            play();
            blockSignals(false);
            m_player->inSnapshotCaputre = false;
        }
        else {
            m_eos = true;
            stop();
        }
        break;
    }
    [...]
}


问题是:


当我将snapshot元素的true属性设置为pngenc时,会收到EOS事件,该事件会停止我的管道,因此我需要重新启动它,这会使视频播放冻结大约半秒钟,在我的情况下不可接受。
当我将snapshot元素的false属性设置为pngenc时,我没有管道扰动,但是我的png文件一直在增长,直到再次调用Player::takeSnapshot()方法。


我哪里错了?有更好的方法吗?
我尝试为我的QGst::Bin分支创建snapshot元素失败。那垫式探头呢?

预先感谢

最佳答案

您可以在任何接收器上使用last-sample属性,例如您的视频接收器。它包含一个GstSample,它具有一个缓冲区,其中包含最新的视频帧。您可以将其作为快照,例如使用gst_video_convert_sample()或它的异步变体,将其转换为PNG / JPG /其他格式。

请参见https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-libs/html/GstBaseSink.html#GstBaseSink--last-samplehttps://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-base-libs/html/gst-plugins-base-libs-gstvideo.html#gst-video-convert-sample

或者,您必须在第一帧之后关闭filesink快照管道。例如,通过让Pad探针知道第一帧何时发生,然后注入EOS事件来防止将其他PNG帧附加到同一文件。

关于qt - Qt + GStreamer:如何在播放实时视频流时拍摄快照,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/35983701/

10-13 07:05