qtvideorgbaqvideowidget

Displaying a video with an alpha channel using qt


I need to display RGBA encoded videos (that is, video with a transparent background) using qt. The idea is to merge videos in real time. I usually use libmpv but it does not seem to be allow rendering transparent background video. I am trying to use a QMediaPlayer with the following code:

 QMainWindow w;
 w.resize(1920,1080);
 QVideoWidget videoWidget(&w);
 videoWidget.move(0,100);
 videoWidget.resize(1920,1080);
 QMediaPlayer *player = new QMediaPlayer(&w);
 w.resize(w.size());
 player->setMedia( QUrl::fromLocalFile(PATH+"video2.mov") );
 player->setVideoOutput(&videoWidget);
 w.show();
 player->play();

This successfully loads the video (which is an RGBA mov video) but fills the video widget with a black background where it should be transparent thus covering any item behind the video player.

Is there any way to actually load a transparent video using QVideoPlayer/QVideoWidget? If not, is there an efficient alternative (I would rather not use a lower level solution such as opencv).

Thanks a lot,

Fred


Solution

  • here's the solution I finally found:

    Subclass a QAbstractVideoSurface:

    class alphaVideoDrawer : public QAbstractVideoSurface
    {
        Q_OBJECT
    public:
        alphaVideoDrawer(QLabel *displayLbl);
    private:
        QLabel *displayLbl;
    
    protected:
        bool present(const QVideoFrame &frame);
    
        QList<QVideoFrame::PixelFormat> supportedPixelFormats(
                QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const
        {
            Q_UNUSED(handleType);
            return QList<QVideoFrame::PixelFormat>() << QVideoFrame::Format_ARGB32;
        }
    };
    

    The surface will receive the frame convert and forward it to the qlabel which displays the video.

    alphaVideoDrawer::alphaVideoDrawer(QLabel *displayLbl):displayLbl(displayLbl)
    {
    }
    
    extern QImage qt_imageFromVideoFrame(const QVideoFrame &f);
    
    
    bool alphaVideoDrawer::present(const QVideoFrame &frame)
    {
        QImage image = qt_imageFromVideoFrame(frame);
        displayLbl->setPixmap(QPixmap::fromImage(image));
        return true;
    }
    

    Then we subclass a QLabel which will be our video output:

    class alphaVideo : public QLabel
    {
        Q_OBJECT
    public:
        alphaVideo(QLabel *parent = nullptr);
    
    private:
        alphaVideoDrawer *videoDrawer;
        QMediaPlayer *videoPlayer;
        QMediaPlaylist *playlist;
    };
    

    It loads the drawer and the player and start playing/rendering the video:

        alphaVideo::alphaVideo(QLabel *parent): QLabel(parent)
    {
        setStyleSheet("QLabel { background-color : transparent; }");
        videoDrawer = new alphaVideoDrawer(this);
        videoPlayer = new QMediaPlayer(this);
        playlist = new QmediaPlaylist();
        videoPlayer->setPlaylist(playlist);
        videoPlayer->setVideoOutput(videoDrawer);
        playlist->addMedia( Qurl::fromLocalFile(“your RGBA video file.mp4”) );
        videoPlayer->play();
    }