Вы не делаете ничего плохого. Как отметил @DYangu, экземпляр медиа-объекта не поддерживает мониторинг видео. У меня была та же проблема (и то же самое для QAudioProbe
, но это нас не интересует). Я нашел решение, посмотрев на this ответ и this one.
Основная идея заключается в том, что подкласс QAbstractVideoSurface. Как только вы это сделаете, он вызовет метод QAbstractVideoSurface::present(const QVideoFrame & frame)
вашей реализации QAbstractVideoSurface
, и вы сможете обрабатывать кадры вашего видео.
Как сказано here, как правило, вам просто нужно переопределять два метода:
- supportedPixelFormats так, что производитель может выбрать подходящий формат для
QVideoFrame
- 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
.
Я надеюсь, что это вам поможет!
У меня был такая же проблема, и мне удалось найти обходной путь, я отправлю ответ в ближайшее время (это немного долго, хотя) – IAmInPLS
ОК, спасибо большое, я awating свой реплей И в моей предыдущей пост о QMediaPlayer [ссылка] (http://stackoverflow.com/questions/37680515/qmediaplayer-duration-error), я вам обратился к вам, пожалуйста, посмотрите – user3627553