Как сохранить кадр с помощью QMediaPlayer?

Вопрос:Я хочу сохранить изображение кадра из QMediaPlayer. После прочтения документации я понял, что должен использовать QVideoProbe. Я использую следующий код: QMediaPlayer *player = new QMediaPlayer(); QVideoProbe *probe = new QVideoProbe; connect(probe, SIGNAL(videoFrameProbed(QVideoFrame)), this, SLOT(processFrame(QVideoFrame))); qDebug()setVideoOutput(myVideoSurface); player->setMedia(QUrl::fromLocalFile("observation.mp4")); player->play(); // Start receving frames as they get presented to myVideoSurface Но, к сожалению,

Вопрос:

Я хочу сохранить изображение кадра из QMediaPlayer. После прочтения документации я понял, что должен использовать QVideoProbe. Я использую следующий код:

QMediaPlayer *player = new QMediaPlayer(); QVideoProbe *probe = new QVideoProbe; connect(probe, SIGNAL(videoFrameProbed(QVideoFrame)), this, SLOT(processFrame(QVideoFrame))); qDebug()<<probe->setSource(player); // Returns true, hopefully. player->setVideoOutput(myVideoSurface); player->setMedia(QUrl::fromLocalFile(«observation.mp4»)); player->play(); // Start receving frames as they get presented to myVideoSurface

Но, к сожалению, probe->setSource(player) всегда возвращает false для меня, и поэтому мой слот processFrame не запускается.

Что я делаю неправильно? У кого-нибудь есть рабочий пример QVideoProbe?

Лучший ответ:

Ты не делаешь ничего плохого. Как отметил @DYangu, экземпляр медиа-объекта не поддерживает мониторинг видео. У меня была та же проблема (и то же самое для QAudioProbe, но это нас не интересует). Я нашел решение, посмотрев этот ответ и этот.

Основная идея заключается в подклассе QAbstractVideoSurface. После того, как вы это сделаете, он вызовет метод QAbstractVideoSurface::present(const QVideoFrame & frame) вашей реализации QAbstractVideoSurface, и вы сможете обрабатывать кадры вашего видео.

Как сказано здесь, обычно вам просто нужно переопределить два метода:

  • supportedPixelFormats, чтобы производитель мог выбрать соответствующий формат для QVideoFrame
  • present, который позволяет отображать фрейм

Но в то время я искал в исходном коде Qt и с радостью нашел этот фрагмент кода, который помог мне выполнить полную реализацию, Итак, – это полный код для использования “захвата видеофрагмента”.

VideoFrameGrabber.cpp:

#include «VideoFrameGrabber.h» #include <QtWidgets> #include <qabstractvideosurface.h> #include <qvideosurfaceformat.h> VideoFrameGrabber::VideoFrameGrabber(QWidget *widget, QObject *parent) : QAbstractVideoSurface(parent) , widget(widget) , imageFormat(QImage::Format_Invalid) { } QList<QVideoFrame::PixelFormat> VideoFrameGrabber::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const { Q_UNUSED(handleType); return QList<QVideoFrame::PixelFormat>() << QVideoFrame::Format_ARGB32 << QVideoFrame::Format_ARGB32_Premultiplied << QVideoFrame::Format_RGB32 << QVideoFrame::Format_RGB24 << QVideoFrame::Format_RGB565 << QVideoFrame::Format_RGB555 << QVideoFrame::Format_ARGB8565_Premultiplied << QVideoFrame::Format_BGRA32 << QVideoFrame::Format_BGRA32_Premultiplied << QVideoFrame::Format_BGR32 << QVideoFrame::Format_BGR24 << QVideoFrame::Format_BGR565 << QVideoFrame::Format_BGR555 << QVideoFrame::Format_BGRA5658_Premultiplied << QVideoFrame::Format_AYUV444 << QVideoFrame::Format_AYUV444_Premultiplied << QVideoFrame::Format_YUV444 << QVideoFrame::Format_YUV420P << QVideoFrame::Format_YV12 << QVideoFrame::Format_UYVY << QVideoFrame::Format_YUYV << QVideoFrame::Format_NV12 << QVideoFrame::Format_NV21 << QVideoFrame::Format_IMC1 << QVideoFrame::Format_IMC2 << QVideoFrame::Format_IMC3 << QVideoFrame::Format_IMC4 << QVideoFrame::Format_Y8 << QVideoFrame::Format_Y16 << QVideoFrame::Format_Jpeg << QVideoFrame::Format_CameraRaw << QVideoFrame::Format_AdobeDng; } bool VideoFrameGrabber::isFormatSupported(const QVideoSurfaceFormat &format) const { const QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat()); const QSize size = format.frameSize(); return imageFormat != QImage::Format_Invalid && !size.isEmpty() && format.handleType() == QAbstractVideoBuffer::NoHandle; } bool VideoFrameGrabber::start(const QVideoSurfaceFormat &format) { const QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat()); const QSize size = format.frameSize(); if (imageFormat != QImage::Format_Invalid && !size.isEmpty()) { this->imageFormat = imageFormat; imageSize = size; sourceRect = format.viewport(); QAbstractVideoSurface::start(format); widget->updateGeometry(); updateVideoRect(); return true; } else { return false; } } void VideoFrameGrabber::stop() { currentFrame = QVideoFrame(); targetRect = QRect(); QAbstractVideoSurface::stop(); widget->update(); } bool VideoFrameGrabber::present(const QVideoFrame &frame) { if (frame.isValid()) { QVideoFrame cloneFrame(frame); cloneFrame.map(QAbstractVideoBuffer::ReadOnly); const QImage image(cloneFrame.bits(), cloneFrame.width(), cloneFrame.height(), QVideoFrame::imageFormatFromPixelFormat(cloneFrame .pixelFormat())); emit frameAvailable(image); // this is very important cloneFrame.unmap(); } if (surfaceFormat().pixelFormat() != frame.pixelFormat() || surfaceFormat().frameSize() != frame.size()) { setError(IncorrectFormatError); stop(); return false; } else { currentFrame = frame; widget->repaint(targetRect); return true; } } void VideoFrameGrabber::updateVideoRect() { QSize size = surfaceFormat().sizeHint(); size.scale(widget->size().boundedTo(size), Qt::KeepAspectRatio); targetRect = QRect(QPoint(0, 0), size); targetRect.moveCenter(widget->rect().center()); } void VideoFrameGrabber::paint(QPainter *painter) { if (currentFrame.map(QAbstractVideoBuffer::ReadOnly)) { const QTransform oldTransform = painter->transform(); if (surfaceFormat().scanLineDirection() == QVideoSurfaceFormat::BottomToTop) { painter->scale(1, -1); painter->translate(0, -widget->height()); } QImage image( currentFrame.bits(), currentFrame.width(), currentFrame.height(), currentFrame.bytesPerLine(), imageFormat); painter->drawImage(targetRect, image, sourceRect); painter->setTransform(oldTransform); currentFrame.unmap(); } }

VideoFrameGrabber.h

#ifndef VIDEOFRAMEGRABBER_H #define VIDEOFRAMEGRABBER_H #include <QtWidgets> class VideoFrameGrabber : public QAbstractVideoSurface { Q_OBJECT public: VideoFrameGrabber(QWidget *widget, QObject *parent = 0); QList<QVideoFrame::PixelFormat> supportedPixelFormats( QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const; bool isFormatSupported(const QVideoSurfaceFormat &format) const; bool start(const QVideoSurfaceFormat &format); void stop(); bool present(const QVideoFrame &frame); QRect videoRect() const { return targetRect; } void updateVideoRect(); void paint(QPainter *painter); private: QWidget *widget; QImage::Format imageFormat; QRect targetRect; QSize imageSize; QRect sourceRect; QVideoFrame currentFrame; signals: void frameAvailable(QImage frame); }; #endif //VIDEOFRAMEGRABBER_H

Примечание: в .h вы увидите, что я добавил signal, взяв изображение как параметр. Это позволит вам обрабатывать фрейм в любом месте вашего кода. В то время этот сигнал принимал QImage как параметр, но вы можете, конечно, взять QVideoFrame, если хотите.

Теперь мы готовы использовать этот захват видеофрагмента:

QMediaPlayer* player = new QMediaPlayer(this); // no more QVideoProbe VideoFrameGrabber* grabber = new VideoFrameGrabber(this); player->setVideoOutput(grabber); connect(grabber, SIGNAL(frameAvailable(QImage)), this, SLOT(processFrame(QImage)));

Теперь вам просто нужно объявить слот с именем processFrame(QImage image), и вы получите QImage каждый раз, когда вы введете метод present вашего VideoFrameGrabber.

Я надеюсь, что это поможет вам!

Ответ №1

После Документация Qt QVideoProbe:

bool QVideoProbe::setSource(QMediaObject *mediaObject)

Запускает мониторинг данного объекта mediaObject.

Если нет медиа-объекта, связанного с mediaObject, или если он ноль, этот датчик будет деактивирован, и эта функция вернется правда.

Если экземпляр мультимедийного объекта не поддерживает мониторинг видео, этот функция вернет false.

Любые ранее контролируемые объекты больше не будут отслеживаться. Переходя в том же объекте будут проигнорированы, но мониторинг будет продолжаться.

Итак, кажется, что ваш экземпляр медиа-объекта не поддерживает мониторинг видео.

Оцените статью
Добавить комментарий