使 QGraphicsVideoItem 填充 QWidget

Make QGraphicsVideoItem Fill QWidget

我的目标是创建一个简单的视频播放器 QWidget,允许一些重叠的图形(考虑字幕或类似内容)。

我从天真的方法开始,即使用 QVideoWidget 和另一组 QWidget 在顶部(我的叠加层)。不幸的是,这不起作用,因为 Qt 不允许具有透明背景的小部件呈现在视频之上。背景显示为黑色而不是实际视频。

下一个想法是使用 QGraphicsScene 等。这应该允许这种合成,所以我创建了一个像这样的简单设置:

// The widget we will use as viewport
auto viewport= new QWidget();
//Set an easily recognizable bg color for debugging
palette.setColor(QPalette::Window, Qt::green);
viewport->setPalette(palette);
viewport->setAutoFillBackground(true);

// Our scene
auto mScene=new QGraphicsScene(this);

// The video
auto mVideoItem = new QGraphicsVideoItem();
mVideoItem->setPos(0,0);
myVideoSource.setVideoOutput(mVideoItem); // ... not shown: setting up of the video source
mScene->addItem(mVideoItem);

// Yellow line starting at 0,0 for debugging
auto line=new QGraphicsLineItem (0,0,100,100);
line->setPos(0,0);
line->setPen(QPen(Qt::yellow, 2));
mScene->addItem(line);

// A Text string
auto text=new QGraphicsTextItem("Its Wednesday my dudes", mVideoItem);
text->setPos(10, 10);

// Our view
mView=new QGraphicsView;
mView->setScene(mScene);
mView->setViewport(viewport);

viewport->show()

现在这看起来很有希望,因为我可以看到合成作品;线条和文字完美地呈现在视频之上。但是,视频位于小部件中看似随机的位置。 (见截图)

至此我已经尝试了所有可以想到的和不可思议的组合

mVideoItem->setSize();
mVideoItem->setOffset();
mScene->setSceneRect();
mView->fitInView();
mView->ensureVisible();
mView->centerOn()

尝试用视频项目填充视口小部件,但似乎完全不符合逻辑。它没有将内容居中,而是以违反逻辑的方式在屏幕上飞来飞去,我已经放弃了。我将我的代码放在视口小部件的 resizeEvent 中,并使用视口小部件的 size() 作为基础。

所以我的问题是; 如何在调整大小时用视频项目填充视口小部件?

我认为 QGraphicsVideoItem 不适合这项任务。

您可以实施 QAbstractVideoSurface 来接收 QVideoFrame 并将它们提供给 QWidget,然后将它们转换为 QImage,然后在 paintEvent 中缩放和绘制它们.由于您控制 paintEvent,您可以在视频上绘制任何内容,并免费获得“填充视口”功能。

要点:


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    Surface surface;
    Widget widget(surface);
    widget.show();

    QMediaPlayer player;
    player.setVideoOutput(&surface);
    player.setMedia(QMediaContent(QUrl("path/to/media")));
    player.play();

    return a.exec();
}

bool Surface::present(const QVideoFrame &frame)
{
    mFrame = frame;
    emit frameReceived();
    return true;
}

Widget::Widget(Surface &surface, QWidget *parent)
    : QWidget{parent}, mSurface(surface)
{
    connect(&mSurface,SIGNAL(frameReceived()),this,SLOT(update()));
}

void Widget::paintEvent(QPaintEvent *event)
{
    QVideoFrame frame = mSurface.frame();

    if (frame.map(QAbstractVideoBuffer::ReadOnly)) {

        QPainter painter(this);

        int imageWidth = mSurface.imageSize().width();
        int imageHeight = mSurface.imageSize().height();

        auto image = QImage(frame.bits(),
                            imageWidth,
                            imageHeight,
                            mSurface.imageFormat());

        double scale1 = (double) width() / imageWidth;
        double scale2 = (double) height() / imageHeight;
        double scale = std::min(scale1, scale2);

        QTransform transform;
        transform.translate(width() / 2.0, height() / 2.0);
        transform.scale(scale, scale);
        transform.translate(-imageWidth / 2.0, -imageHeight / 2.0);

        painter.setTransform(transform);

        painter.drawImage(QPoint(0,0), image);

        painter.setTransform(QTransform());

        painter.setFont(QFont("Arial", 20));
        int fontHeight = painter.fontMetrics().height();

        int ypos = height() - (height() - imageHeight * scale) / 2 - fontHeight;
        QRectF textRect(QPoint(0, ypos), QSize(width(), fontHeight));
        QTextOption opt(Qt::AlignCenter);
        painter.setPen(Qt::blue);
        painter.drawText(textRect, "Subtitles sample", opt);

        frame.unmap();
    }

}

完整来源:draw-over-video

基于 Qt 的 customvideosurface example