QT 播放器之VideoWidget

我们首先需要找到视频播放的类, 不会找不要紧~直接打开翻译 视频 ->Video ,播放 ->Play

组合起来就是 QVideoPlay或者 QPlayVideo,搜索文档看看,很好!并没有想要的类

那么搜索一下QVideo呢,滚动一下看到一个QVideoWidget,或许这个就是我们想要的?

打开该文档找一下,找到以下栗子~

OK,这么说 我们需要的就是 QMediaPlayer 媒体播放类了。

根据栗子写一遍,不错啊感觉已经满足了我的需要。但是当我在VideoWidget上面添加控件时却发现。。。这就是个辣鸡

根本就不能在VideoWidget上面添加控件,添加后会看到不到。要它何用!
看一下媒体播放类的 setVideoOutput 。可以看到这个函数重载3次

打开QGraphicsVideoItem类文档看看,感觉只是说视频作为一个媒体对象在QGraphicsScene显示而已,感觉没啥帮助。看下一个类

嗯,一眼看到一个视频帧!。想想。获得帧后直接在Widget绘制,这样就可以在视频上添加控件了

子类化后应该重写 supportedPixelFormats函数给出接受的格式和present函数接收到的帧信息,然后利用信号返回图像即可

#ifndef VIDEOSURFACE_H
#define VIDEOSURFACE_H

#include <QAbstractVideoSurface>

class VideoSurface : public QAbstractVideoSurface
{
    Q_OBJECT
public:
    explicit VideoSurface(QObject *parent = nullptr);

signals:
    void showImage(QImage img);
public slots:

    // QAbstractVideoSurface interface
public:
    QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const;
    bool present(const QVideoFrame &frame);
};

#endif // VIDEOSURFACE_H
#include "videosurface.h"

VideoSurface::VideoSurface(QObject *parent) : QAbstractVideoSurface(parent)
{

}

QList<QVideoFrame::PixelFormat> VideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const
{
    Q_UNUSED(type);
    QList<QVideoFrame::PixelFormat> list;
    list<<QVideoFrame::Format_RGB32<<QVideoFrame::Format_RGB32;
    return  list;
}

bool VideoSurface::present(const QVideoFrame &frame)
{
    if(frame.isValid())
    {
        QVideoFrame cloneFrame(frame);
        cloneFrame.map(QAbstractVideoBuffer::ReadOnly);
        QImage img(cloneFrame.bits(),cloneFrame.width(),cloneFrame.height(),QVideoFrame::imageFormatFromPixelFormat(cloneFrame.pixelFormat()));
        cloneFrame.unmap();
        emit showImage(img);
        return true;
    }
    return  false;
}

 

让我们来测试一下这个类是否达到我们的要求

#include "mainwindow.h"
#include "videosurface.h"
#include <QApplication>
#include <QLabel>
#include <QPushButton>

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

    auto player = new QMediaPlayer;
    VideoSurface *video = new VideoSurface();
    player->setVideoOutput(video);

    QLabel label;
    label.resize(500,350);
    QPushButton *button = new QPushButton("23333",&label);
    label.show();

    QObject::connect(video,&VideoSurface::showImage,&label,[=,&label](QImage img){
        label.setPixmap(QPixmap::fromImage(img.scaled(label.width(),label.height())));
    });

    player->setMedia(QUrl::fromLocalFile("D:/fj/1.mp4"));
    player->play();

    return a.exec();
}

编译运行后。完美!~

 

有了视频帧后。接下来就是显示视频的Widget了,我希望该Widget有一下功能

  • 最重要的当然是接收Image然后绘制啦
  • 绘制的图像应该自适应WIdget
  • Widget上面应该有一个按钮,用来打开文件进行播放
  • 双击的时候应该可以把窗口全屏化
  • 当播放视频的时候,按钮肯定是要隐藏的啊
  • Widget没有图像的地方肯定不能空着啊比较难看,直接设置背景为黑色就行

 

自适应大小只需要使用QImage的缩放函数即可

img.scaled(width(),height(),Qt::KeepAspectRatio, Qt::SmoothTransformation);

使用QPainter 绘制图片,在设置帧的时候肯定需要update刷新一下Widget

    if(!img.isNull())
    {
        m_image = img.scaled(width(),height(),Qt::KeepAspectRatio, Qt::SmoothTransformation);
    }
    else
    {
        m_image = img;
    }
    update();

因为设置帧的时候已经进行缩放,所以绘制的时候只需要移动图片的位置到中间就行。

drawRect。底图是必须的,不然控件在上面移动会出现花屏现象

void VideoWidget::paintEvent(QPaintEvent *event)
{
    QPainter *painter = new QPainter();
    painter->begin(this);
    painter->setBrush(QBrush(QColor(0,0,0)));
    painter->drawRect(-1,-1,width()+1,height()+1);
    if(!m_image.isNull())
    {
        QRect target((width()-m_image.width())/2,(height()-m_image.height())/2,m_image.width(),m_image.height());
        painter->drawImage(target,m_image);
    }
    painter->end();
    QWidget::paintEvent(event);
}

按钮点击后当然是选择文件啦。此时就需要打开对话框了。查找一下文档, 文件对话框 ,直接就有QFileDialog

可以看到它有8个静态函数用来打开对话框,我现在需要的是打开一个文件然后进行播放。当然是getopenfile啦

然后QMediaPlayer setMedia 函数的参数要求是 QUrl  。所以我们需要的是 getopenfileurl

 

QUrl url = QFileDialog::getOpenFileUrl(nullptr,"select video file");

嗯。。测试了一下,打开后可以选择所有的类型的文件,但是我们不可能让用户选择所有文件。此时就需要使用该函数的第四个参数过滤一下文件类型

[
	{"media":["asf","wm","wmp","wmv","wma"]},
	{"real":["ram","rm","rmvb","rpm","scm","ra","rp","rt","smi","smil"]},
	{"dvd":["dat","evo","vob","ifo"]},
	{"quickTime":["mov","qt","aif","aiff","3g2","3gp","3gp2","3gpp","arm"]},
	{"otherVideo":["bjd","ghd","amv","avi","bik","csf","d2v","dsm","dsv","ivf","m1v","m2p","m2ts","m2v","m4p","m4p","m4k","mkv","mp4","mpe","mpeg","mpg","mts","ogm","pmp","pmp2","pss","pva","ratDVD","smk","tp","tpr","ts","vg2","vid","vp6","vp7","wv","asm","avsts","divx","webm","iso","vp9","hevc","265"]},
	{"otherAudio":["aac","ac3","acc","act","ape","au","cda","dsa","dss","dts","flac","m4a","mac","mid","midi","mp2","mp3","mp5","mpa","mpga","mod","ogg","ofr","rmi","tak","tta","wav","aifc"]},
	{"animation":["swf","flv","flic","fli","flc"]}
]

上面是从暴风影音抄下来关联类型。我们只需要读取一下json就好,至于类名称 当然是 QJson文档啦   QJsonDocument

通过该类读取Json即可

#ifndef FILEFILTER_H
#define FILEFILTER_H

#include <QString>
#include <QMap>

class FileFilter
{
public:
    FileFilter();
    void addFilter(const QString &filePath);
    QStringList getFilter(const QString &key) const;
    QString getFilterString(const QString &key)const;
    QStringList getFilterAll()const;
    QString getFilterString()const;

private:
    QMap<QString,QStringList> m_data;
};

#endif // FILEFILTER_H
#include "filefilter.h"

#include <QFile>
#include <QJsonParseError>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
#include <QDebug>
#include <QApplication>

FileFilter::FileFilter()
{

}

void FileFilter::addFilter(const QString &filePath)
{
    QFile file(filePath);
    if(!file.open(QIODevice::Text|QIODevice::ReadOnly))
    {
        qDebug()<<QStringLiteral("not open file!");
        return;
    }
    QByteArray json = file.readAll();
    file.close();

    QJsonParseError json_error;
    QJsonDocument jsonDoc(QJsonDocument::fromJson(json, &json_error));
    if(json_error.error!=QJsonParseError::NoError)
    {
        qDebug()<<json_error.errorString();
        return;
    }

    if(jsonDoc.isArray())
    {
        auto array = jsonDoc.array();
        for(auto i=array.begin();i!=array.end();i++)
        {
            if((*i).isObject())
            {
                auto obj = (*i).toObject();
                auto key = obj.value(obj.keys().first());
                if(key.isArray())
                {
                    auto values = key.toArray();
                    QStringList list;
                    for(auto j=values.begin();j!=values.end();j++)
                    {
                        list<< "*."+(*j).toString();
                    }
                    m_data[obj.keys().first()]=list;
                }
            }
        }
    }
}


QStringList FileFilter::getFilter(const QString &key)const
{
    return m_data[key];
}

QString FileFilter::getFilterString(const QString &key)const
{
    QString result;
    foreach (auto str, m_data[key]) {
        result.append(str+" ");
    }
    return result;
}

QStringList FileFilter::getFilterAll() const
{
    QStringList result;
    foreach (auto key, m_data.keys())
    {
        result<<m_data[key];
    }
    return result;
}

QString FileFilter::getFilterString()const
{
    QString result;
    foreach (auto key, m_data.keys())
    {
        result .append( getFilterString(key));
    }
    return result;
}

 

 

 

 

 

 

 

最后来看一下完整的代码

#ifndef VIDEOWIDGET_H
#define VIDEOWIDGET_H

#include <QImage>
#include <QWidget>

QT_BEGIN_NAMESPACE
class QPushButton;
class FileFilter;
QT_END_NAMESPACE

class VideoWidget : public QWidget
{
    Q_OBJECT
public:
    explicit VideoWidget(QWidget *parent = nullptr);
    void setFileFilter(FileFilter *filter);
    void showOpenButton();
    void hideOpenButton();

signals:
    void doubleClieck();
    void openFile(const QUrl &url);

public slots:
    void setImage(QImage image);

private slots:
    void openUrl();

    // QWidget interface
protected:
    void mouseDoubleClickEvent(QMouseEvent *event);
    void paintEvent(QPaintEvent *event);
    void resizeEvent(QResizeEvent *event);

private:
    QImage m_image;
    QPushButton *m_openButon;
    FileFilter *m_fileFilter=nullptr;
};

#endif // VIDEOWIDGET_H
#include "filefilter.h"
#include "videowidget.h"
#include <QEvent>
#include <QFileDialog>
#include <QPainter>
#include <QPushButton>
#include <QWidget>
#include <QDebug>

VideoWidget::VideoWidget(QWidget *parent) : QWidget(parent)
{
    m_openButon = new QPushButton(QStringLiteral("打开文件"),this);
    m_openButon->resize(100,50);
    connect(m_openButon,&QPushButton::clicked,this,&VideoWidget::openUrl);

    setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);

    QPalette p = palette();
    p.setColor(QPalette::Window, Qt::black);
    setPalette(p);

    setAttribute(Qt::WA_OpaquePaintEvent);
}

void VideoWidget::setFileFilter(FileFilter *filter)
{
    m_fileFilter = filter;
}

void VideoWidget::showOpenButton()
{
    m_openButon->show();
}

void VideoWidget::hideOpenButton()
{
    m_openButon->hide();
}

void VideoWidget::setImage(QImage img)
{
    if(!img.isNull())
    {
        m_image = img.scaled(width(),height(),Qt::KeepAspectRatio, Qt::SmoothTransformation);
    }
    else
    {
        m_image = img;
    }
    update();
}

void VideoWidget::openUrl()
{
    QString filter=m_fileFilter?m_fileFilter->getFilterString():QString();
    QUrl url = QFileDialog::getOpenFileUrl(nullptr,"select video file",QUrl(),filter);

    if(url.isLocalFile())
    {
        emit openFile(url);
    }
}

void VideoWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
    emit doubleClieck();
    QWidget::mouseDoubleClickEvent(event);
}

void VideoWidget::paintEvent(QPaintEvent *event)
{
    QPainter *painter = new QPainter();
    painter->begin(this);
    painter->setBrush(QBrush(QColor(0,0,0)));
    painter->drawRect(-1,-1,width()+1,height()+1);
    if(!m_image.isNull())
    {
        QRect target((width()-m_image.width())/2,(height()-m_image.height())/2,m_image.width(),m_image.height());
        painter->drawImage(target,m_image);
    }
    painter->end();
    QWidget::paintEvent(event);
}

void VideoWidget::resizeEvent(QResizeEvent *event)
{
    m_openButon->move((width()-m_openButon->width())/2, (height()-m_openButon->height())/2);
    QWidget::resizeEvent(event);
}

 

 

 

  • 4
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小鱼游戏开发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值