飞凌嵌入式
直播中

fsdzdzy

10年用户 260经验值
擅长:嵌入式技术 控制/MCU opencv
私信 关注
[技术]

【飞凌RK3568开发板试用体验】4-Qt开发一个视频播放器

上篇文章:【飞凌RK3568开发板试用体验】3-Qt开发一个音乐播放器,使用Qt制作了一个音乐播放器,并在OK3568开发板上进行了运行测试,实际测试效果还不错。

本篇继续来实现一个Qt视频播放器软件,可以实现视频列表的显示与选择播放等,先来看下最终的效果:

本篇的Qt代码从野火开发板的例程中移植修改而来,下面分析下程序的代码结构。

1 视频播放器开发总体结构

整个Qt视频播放器项目的代码结构如下:

  • 主代码中是视频播放器相关的代码,包括:
    • 视频播放器主界面
    • 视频名列表界面:在视频播放时可以查看视频列表并切换视频
    • Qt视频播放界面:实现单纯的视频播放
    • 操作按钮界面:实现播放、暂停、继续、上一个、下一个、进度调节,音量调节
    • 播放预览列表界面:在进入视频播放之前的视频预览列表界面
  • Ui代码中使用一些Qt的基本功能,包括:
    • 一个Qt界面基类
    • 滑条功能类
    • 图标按钮显示类
    • 列表功能类
    • 工具类
    • 视频帧解析
    • 页面列表类
  • Skin中是一些图片资源和字体/皮肤定义
  • 最后是编译的中间文件和编译结果存储的目录

下面分类介绍了程序的主要代码实现。

2 软件开发

首先是整体的主界面部分,进行各项功能的初始化显示,主要包括:

  • 播放列表初始化
  • 视频播放界面初始化
void VideoPlayer::InitWidget()
{
    QtWidgetTitleBar *m_widgetTitle = new QtWidgetTitleBar(this);
    m_widgetTitle->SetScalSize(Skin::m_nScreenWidth, 80);
    m_widgetTitle->SetBackground(Qt::transparent);
    m_widgetTitle->setFont(QFont(Skin::m_strAppFontNormal));
    m_widgetTitle->SetTitle(tr("视频播放器"), "#ffffff", 32);
    connect(m_widgetTitle, SIGNAL(signalBackHome()), this, SIGNAL(signalBackHome()));
​
    QVBoxLayout *verLayoutCentor = new QVBoxLayout(this);
    verLayoutCentor->setContentsMargins(0, 0, 0, 0);
    verLayoutCentor->setSpacing(0);
    verLayoutCentor->addWidget(m_widgetTitle, 1);
​
    // 播放列表
    m_videosList = new VideoListViewer(this);
    m_videosList->SetBackground(Qt::transparent);
    connect(m_videosList, SIGNAL(currentItemClicked(QtPageListWidgetItem *)), this, SLOT(SltItemClicked(QtPageListWidgetItem *)));
    verLayoutCentor->addWidget(m_videosList, 5);
​
    // 视频播放界面
    m_videoWidget = new QtVideoWidget(this);
    m_videoWidget->hide();
}

初始化各个界面后,还要连接对应的槽函数,如切换视频播放列表中的视频时的处理等。

2.1 视频播放

视频播放部分,主要有两部分功能:

  • 视频解码播放:使用Qt自带的媒体播放器组件进行音频播放
  • 播放时的按钮操作:实现播放、暂停、继续、上一个、下一个、进度调节,音量调节

2.1.1 QMediaPlayer播放视频

这里使用Qt自带的QMediaPlayer组件进行视频的播放,QMediaPlayer播放视频于播放音频的步骤类似。

本项目的视频播放初始化部分:

QtVideoWidget::QtVideoWidget(QWidget *parent) : QtWidgetBase(parent)
{
    m_urlMedia = QUrl();
    m_bToolBarShow = false;
​
    m_nDuration = 0;
    m_nPostion = 0;
​
    m_player = new QMediaPlayer(this);
    surface = new QtVideoWidgetSurface(this);
    m_player->setVideoOutput(surface);
​
    m_playList = new MediaPlayListWidget(this);
    m_playList->setVisible(false);
    m_player->setPlaylist(m_playList->palyList());
​
    connect(m_player, SIGNAL(durationChanged(qint64)), this, SLOT(SltDurationChanged(qint64)));
    connect(m_player, SIGNAL(positionChanged(qint64)), this, SLOT(SltPostionChanged(qint64)));
​
    m_titleBar = new PlayTitleBarWidget(this);
    connect(m_playList, SIGNAL(signalMediaChanged(QString)), m_titleBar, SLOT(SetText(QString)));
    connect(m_titleBar, SIGNAL(signalBack()), this, SLOT(SltBackClicked()));
​
    m_playBar = new PlayerBarWidget(this);
    connect(m_playBar, SIGNAL(signalPlay(bool)), this, SLOT(SltBtnPlayClicked(bool)));
    connect(m_playBar, SIGNAL(currentPostionChanged(int)), this, SLOT(SltChangePostion(int)));
    connect(m_playBar, SIGNAL(signalPrev()), m_playList->palyList(), SLOT(previous()));
    connect(m_playBar, SIGNAL(signalNext()), m_playList->palyList(), SLOT(next()));
    connect(m_playBar, SIGNAL(signalMuenList()), this, SLOT(SltShowMenuList()));
    connect(m_playBar, SIGNAL(signalVolume()), this, SLOT(SltChangeVolume()));
​
    m_timerShow = new QTimer(this);
    m_timerShow->setSingleShot(true);
    m_timerShow->setInterval(5000);
    connect(m_timerShow, SIGNAL(timeout()), this, SLOT(SltAutoCloseToolBar()));
​
    m_volumeSlider = new QtSliderBar(this);
    m_volumeSlider->SetHorizontal(false);
    m_volumeSlider->SetValue(100);
    m_volumeSlider->hide();
    connect(m_volumeSlider, SIGNAL(currentValueChanged(int)), m_player, SLOT(setVolume(int)));
}

2.1.2 播放操作按钮

视频播放时,需要用到播放、暂停、继续、上一个、下一个、进度调节、音量调节等操作按钮,这里的对应按钮使用图标显示为对应的按钮,通过加载QPixmap来实现:

void PlayerBarWidget::InitWidget()
{
    m_btns.insert(0, new QtPixmapButton(0, QRect(295, 40, 60, 60), QPixmap(":/images/video/ic_prev.png"), QPixmap(":/images/video/ic_prev_pre.png")));
    m_btns.insert(1, new QtPixmapButton(1, QRect(375, 40, 60, 60), QPixmap(":/images/video/ic_play.png"), QPixmap(":/images/video/ic_pause.png")));
    m_btns.insert(2, new QtPixmapButton(2, QRect(465, 40, 60, 60), QPixmap(":/images/video/ic_next.png"), QPixmap(":/images/video/ic_next_pre.png")));
    m_btns.insert(3, new QtPixmapButton(3, QRect(655, 40, 60, 60), QPixmap(":/images/video/ic_volume.png"), QPixmap(":/images/video/ic_volume_pre.png")));
    m_btns.insert(4, new QtPixmapButton(4, QRect(728, 40, 60, 60), QPixmap(":/images/video/ic_list.png"), QPixmap(":/images/video/ic_list_pre.png")));
    m_btns.value(1)->setCheckAble(true);
​
    // 进度条
    m_progressBar = new QtSliderBar(this);
    m_progressBar->SetHorizontal(true);
    m_progressBar->SetSliderSize(2, 40);
    connect(m_progressBar, SIGNAL(currentValueChanged(int)), this, SIGNAL(currentPostionChanged(int)));
}

2.2 播放列表

播放列表功能用来实现视频文件的显示,以及手动指定切换要播放的视频。

2.2.1 读取视频文件

通过读取指定目录下的文件,查找类型为mp4的视频文件,构造视频播放列表。

void MediaPlayListWidget::ScanDirMedias(const QString &path)
{
    QDir dir(path);
    if (!dir.exists())
        return;
    dir.setFilter(QDir::Dirs | QDir::Files);
    dir.setSorting(QDir::DirsFirst);
    QFileInfoList list = dir.entryInfoList();
    int index = m_mapItems.size();
    for (int i = 0; i < list.size(); i++)
    {
        QFileInfo fileInfo = list.at(i);
        if (fileInfo.fileName() == "." || fileInfo.fileName() == "..")
        {
            continue;
        }
​
        if (fileInfo.isDir())
        {
            ScanDirMedias(fileInfo.filePath());
        }
        else if (fileInfo.suffix() == "mp4")
        {
            QString strName = fileInfo.baseName().toLocal8Bit().constData();
            QString strPath = fileInfo.absoluteFilePath().toLocal8Bit().constData();
            m_playList->addMedia(QUrl::fromLocalFile(strPath));
            m_mapItems.insert(index, new QtListWidgetItem(index, strPath, strName, QPixmap(":/images/video/ic_video_preview.png")));
            index++;
        }
    }
}

2.2.2 视频列表界面

视频列表的具体界面实现,主要是在对应的矩形位置,显示各个视频文件的名称:

void VideoListViewer::drawItemInfo(QPainter *painter, QtPageListWidgetItem *item)
{
    painter->save();
    QFont font(Skin::m_strAppFontBold);
    font.setPixelSize(18);
    painter->setFont(font);
​
    QPixmap pixmap = item->m_pixmapIcon;
    int nXoffset = (item->m_rect.width() - pixmap.width()) / 2;
    int nYoffset = (item->m_rect.height() - pixmap.height() - painter->fontMetrics().height() - 10) / 2;
    QRect rectPixmap(nXoffset + item->m_rect.left(), item->m_rect.top() + nYoffset, pixmap.width(), pixmap.height());
    if (pixmap.isNull())
    {
        painter->setPen(QColor("#7effffff"));
        painter->drawRect(item->m_rect);
    }
    else
    {
        painter->drawPixmap(rectPixmap.topLeft(), pixmap);
    }
​
    painter->setPen(Qt::white);
    painter->drawText(item->m_rect.left(), rectPixmap.bottom() + 10, item->m_rect.width(), 30, Qt::AlignCenter, item->m_strBaseName);
    painter->restore();
}

2.3 视频预览列表

在进入正式的视频播放前,会先有一个视频预览列表界面,在这个界面会列出指定搜索目录下的所有视频,点击任意视频图标即可进入播放界面。

此部分的列表绘制代码逻辑如下:

void QtPageListWidget::drawItemInfo(QPainter *painter, QtPageListWidgetItem *item)
{
    painter->save();
​
    QRect rect = item->m_rect;
    int nXoffset = (rect.width() > ICON_SIZE) ? (rect.width() - ICON_SIZE) / 2 : 0;
    QRect rectPixmap(rect.left() + nXoffset + ICON_SIZE / 2, rect.top() + ICON_SIZE / 2, ICON_SIZE, ICON_SIZE);
    if (item->m_pixmapIcon.isNull())
    {
        painter->setPen(Qt::NoPen);
        painter->setBrush(QColor("#02a7f0"));
        painter->drawEllipse(rectPixmap.topLeft(), ICON_SIZE / 2, ICON_SIZE / 2);
    }
    else
    {
        painter->drawPixmap(rect.left() + nXoffset, rect.top(), item->m_pixmapIcon);
    }
​
    if (m_nCurrentIndex == item->m_nId)
    {
        painter->setPen(Qt::NoPen);
        painter->setBrush(QColor("#32000000"));
        painter->drawEllipse(rectPixmap.topLeft(), ICON_SIZE / 2, ICON_SIZE / 2);
    }
​
    QFont font = painter->font();
    font.setPixelSize(16);
    painter->setFont(font);
    painter->setPen("#ffffff");
    int nTextHeight = painter->fontMetrics().height();
    QRect rectText(rect.left(), rect.bottom() - nTextHeight, rect.width(), nTextHeight);
    painter->drawText(rectText, Qt::AlignCenter, item->m_strText);
    painter->restore();
}

3 交叉编译与测试

Qt程序编写好后,通过交叉编译,来测试视频播放器在OK3568-C板子上的播放效果。

使用编译音乐播放器时编写my3568build.sh的编译脚本编译即可,无需修改:

#! /bin/bash
​
mkdir -p build
cd build
​
export PATH=/home/xxpcb/myTest/OK3568/sourcecode/OK3568-linux-source/buildroot/output/OK3568/host/bin:$PATH
​
qmake .. && make

执行该脚本即可编译:

./my3568build.sh

将编译成功的可执行文件VIdeoPlayer复制到OK3568-C板子中,然后在其同目录下创建一个video文件夹,里面放入若干个mp4视频文件,然后就可以测试了,此外,代码中,还指定了/userdata/media这个搜索目录,这里有板子自带的一些视频:

文件复制到板子,我这里仍然使用的ADB无线传输的方式,比较方便,具体ADB操作演示,可参考之前这篇文章(【飞凌RK3568开发板试用体验】2-RK3568源码编译与交叉编译环境搭建)最后的Qt交叉编译与测试部分

需注意的是,板子里的这个Linux系统,不支持中文的显示,视频名不要有中文。

实测效果见文末视频,整体体验播放流畅,视频切换流程,进度条调整播放进度没有音乐播放器调整的顺畅,另外不知道怎么原因,视频播放的声音外放声音特别小,用耳机插孔听是正常的。

4 总结

本篇介绍了在OK3568-C开发板上实现一个视频乐播放器的测评过程,首先使用Qt编写视频播放器的代码,然后在Ubuntu中,使用搭建好的交叉编译环境进行代码编译,最后把编译出的可执行文件放到板子中进行实际测试。

该视频播放器实现的功能包括基础的播放功能、暂停与继续,音量调节,视频列表显示,下一个、下一个切换,进度条调节播放进度等。

附:之前的文章链接

【飞凌RK3568开发板试用体验】1-开箱与基础功能详细测评

【飞凌RK3568开发板试用体验】2-RK3568源码编译与交叉编译环境搭建

【飞凌RK3568开发板试用体验】3-Qt开发一个音乐播放器

演示视频:

RK3568-Qt做一个视频播放器

更多回帖

发帖
×
20
完善资料,
赚取积分