2016-06-09 7 views
1

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

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?

+0

У меня был такая же проблема, и мне удалось найти обходной путь, я отправлю ответ в ближайшее время (это немного долго, хотя) – IAmInPLS

+0

ОК, спасибо большое, я awating свой реплей И в моей предыдущей пост о QMediaPlayer [ссылка] (http://stackoverflow.com/questions/37680515/qmediaplayer-duration-error), я вам обратился к вам, пожалуйста, посмотрите – user3627553

ответ

3

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

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

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

  1. supportedPixelFormats так, что производитель может выбрать подходящий формат для QVideoFrame
  2. present, который позволяет отобразить рамку

Но в то время я искал в исходном коде Qt и с радостью нашел this piece of code, что помогло мне выполнить полную реализацию. Итак, вот полный код за использование «видеографа-граббера».

VideoFrameGrabber.каст:

#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.

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

+0

вопрос о stuipid, прежде чем я использовал QWIdget для показа видео 'player-> setVideoOutput (ui-> videoWidget);' теперь я должен написать следующее: 'player = новый QMediaPlayer (это); probe = new VideoFrameGrabber (ui-> videoWidget, this); player-> setVideoOutput (probe); ' – user3627553

+0

Только это:' player = new QMediaPlayer (this); зонд = новый VideoFrameGrabber (это); плеер-> комплектVideoOutput (зонд); ' – IAmInPLS

1

После Qt QVideoProbe documentation:

bool QVideoProbe::setSource(QMediaObject *mediaObject) 

начинает контролировать данную MediaObject.

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

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

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

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

+0

Взгляните на мой код - я не использую mediaRecorder Я использую QMediaPlayer. Между тем воспроизведение видео - без проблем – user3627553

Смежные вопросы