百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

用QT实现视频/音乐播放组件 御龙在天qt怎么放音乐

liebian365 2024-10-20 09:54 24 浏览 0 评论

前言

我已经有四五天没有发布文章了,趁着这个周末有空,就又开始构思我们自己的QT组件库中的新组件,思考还有哪些有用、有趣、值得研究学习并构建实现的组件,于是又有了两个新的目标,即多媒体播放组件和地图组件。之所以准备实现这两个小组件,是因为现在流行的很多软件应用中几乎都能用得到音频视频播放和地图导航或定位功能。所以我们在自己的软件开发中如果也遇到类似的需求,能有一个使用方便、快捷灵活的多媒体或地图组件,肯定能够节省我们很多的工作量和精力,QT虽然有相关的类但是没有相关的组件,如是我就开始了自己的实现。

功能展示

今天先跟大家分享一下我自己实现的多媒体播放组件,使用该组件可以很方便的用来播放音乐和视频。而且所有功能完全是基于QT自带的QMediaPlayer类和QVideoWidget类进行实现,没有使用任何第三方库,所以上手也比较容易。目前已实现的基本功能有:

  • 音乐/视频播放、暂停功能
  • 时长和进度显示
  • 音量调节和静音功能
  • 播放进度调整,支持时间轴点选、拖拉和键盘控制
  • 全屏

因为录制的gif没有声音,所以我只展示视频播放功能,具体实现效果如下方动图所示:

播放、暂停功能,点击按钮或视频窗口都能实现该功能:

进度调节和音量调节:

静音:

全屏:

实现方法

1、怎么使用QMediaPlayer类播放音乐和视频?

播放音乐只需要设置好源即可,源既可以是本地路径也可以是网址,如果播放使用本地文件的话,需要使用

QUrl::fromLocalFile函数,方法如下:

m_pPlayer = new QMediaPlayer;
m_pPlayer->setMedia(QMediaContent(QUrl::fromLocalFile(strPath)));
m_pPlayer->play();

播放视频的话还需要设置视频输出位置,所以QMediaPlayer通常需要连接一个QVideoWidget来进行视频的播放,方法如下:

m_pPlayer = new QMediaPlayer;
m_pVideoWidget = new QVideoWidget;
m_pPlayer->setMedia(QMediaContent(QUrl::fromLocalFile(strPath)));
m_pPlayer->setVideoOutput(m_pVideoWidget);
m_pVideoWidget->show();

2、怎么显示视频总时长和当前播放进度?

总时长可以通过m_pPlayer->duration()获取,当前播放进度可以通过m_pPlayer->position()获取,这两个函数返回的都是毫秒数,我们只需要将该毫秒数转换为时间并显示到标签上即可,我为了这两个值能够与时间轴对应起来,所以我都是先转换成秒,然后再对秒进行处理:

//将秒数转换为HH:mm::ss格式
QString MediaPlayer::changeSecondsToStr(int nMesc)
{
    m_nHour = nSeconds / 3600;
    nSeconds %= 3600;
    m_nMinute = nSeconds / 60;
    nSeconds %= 60;
    m_nSecond = nSeconds;

    return QString("%1:%2:%3").arg(m_nHour, 2, 10, QLatin1Char('0')).arg(m_nMinute, 2, 10, QLatin1Char('0')).arg(m_nSecond, 2, 10, QLatin1Char('0'));
}

为了能够实时更新当前的播放进度,我设置了一个定时器,每50ms检查一次当前秒数是否变化并对界面进行刷新:

//定时器超时处理
void MediaPlayer::onTimerOut()
{   
    if (m_pSlider->sliderPosition() != m_nLastPosition)
    {
        //之所以会出现这种情况,是因为触发了键盘事件,比如用户按下了方向键、PgUp、PgDn、Home、End
        m_nLastPosition = m_pSlider->sliderPosition();
        m_pSlider->setValue(m_nLastPosition);
        m_pPlayer->setPosition(m_nLastPosition * 1000);
        setPlayTime(m_nLastPosition);
        return;
    }

    int nCurPosition = m_pPlayer->position() / 1000;
    if (m_nLastPosition != nCurPosition)
    {
        m_nLastPosition = nCurPosition;
        //检查是否播放完毕
        if (nCurPosition >= m_pPlayer->duration() / 1000)
            play();

        m_pSlider->setValue(nCurPosition);
        setPlayTime(nCurPosition);
    }
}

3、如何使用QSlider对视频进行进行展示和控制?

展示功能是很好实现的,只需要设置好QSlider的范围并在定时器中setValue即可,QSlider本身也支持拖动控制,但是当我自定义了QSlider上滑块的样式后,发现拖动滑块不是很灵敏且经常无法选中滑块,且QSlider本身并不支持点击改变当前值,因此我对QSlider添加了事件过滤器,并自己处理了它的鼠标点击事件和鼠标移动事件。

m_pSlider = new QSlider(Qt::Horizontal, this);
    m_pSlider->setObjectName("m_pSlider");
    m_pSlider->setCursor(Qt::PointingHandCursor);
    m_pSlider->setSingleStep(1);
    m_pSlider->setPageStep(10);
    m_pSlider->installEventFilter(this);
    m_pSlider->setEnabled(false);
bool MediaPlayer::eventFilter(QObject *watched, QEvent *event)
{
    //进度滑动条
    if(watched == m_pSlider)
    {
        if (event->type() == QEvent::MouseButtonPress)
        {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
            if (mouseEvent->button() == Qt::LeftButton)	//判断左键
            {
                QSlider* pSlider = (QSlider*)watched;
                int dur = pSlider->maximum() - pSlider->minimum();
                int pos = pSlider->minimum() + dur * ((double)mouseEvent->x() / pSlider->width());
                if (pos != pSlider->sliderPosition())
                {
                    //if (pSlider == m_pSlider)
                    {
                        if (m_pSlider->isEnabled())
                        {
                            m_pSlider->setValue(pos);
                            onSliderPressed();
                            onSliderMoved(pos);
                            onSliderReleased();
                            return true;
                        }
                    }
                }
            }
        }
        else if (event->type() == QEvent::MouseMove)
        {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
            //if (mouseEvent->button() == Qt::LeftButton)	//判断左键
            {
                QSlider* pSlider = (QSlider*)watched;
                int dur = pSlider->maximum() - pSlider->minimum();
                int pos = pSlider->minimum() + dur * ((double)mouseEvent->x() / pSlider->width());

                //防止超出范围
                if (pos > pSlider->maximum())
                    pos = pSlider->maximum();
                if (pos < pSlider->minimum())
                    pos = pSlider->minimum();

                if (pos != pSlider->sliderPosition())
                {
                    //if (pSlider == m_pSlider)
                    {
                        if (m_pSlider->isEnabled())
                        {
                            m_pSlider->setValue(pos);
                            onSliderPressed();
                            onSliderMoved(pos);
                            onSliderReleased();
                            return true;
                        }
                    }
                }
            }
        }
    }
    return QObject::eventFilter(watched, event);
}
  

如果在视频播放过程中改变进度的话,定时器还在运行,可能也会同时修改视频的进度,所以在用m_pSlider改变播放进度时,需要先停止视频播放和定时器,这个操作我们通过管理QSlider的鼠标按下响应、鼠标移动响应和鼠标释放响应进行处理。当鼠标按下时停止视频播放和定时器,鼠标移动时修改视频进度,鼠标释放时恢复视频播放和重启定时器。

connect(m_pSlider, SIGNAL(sliderPressed()), this, SLOT(onSliderPressed()));
connect(m_pSlider, SIGNAL(sliderMoved(int)), this, SLOT(onSliderMoved(int)));
connect(m_pSlider, SIGNAL(sliderReleased()), this, SLOT(onSliderReleased()));
//滑动条鼠标点击响应
void MediaPlayer::onSliderPressed()
{
    m_bIsLastPlay = m_bIsPlay;
}

//滑动条滑动响应
void MediaPlayer::onSliderMoved(int nPosition)
{
    Q_UNUSED(nPosition);
    //在拖动过程中暂停播放
    if (m_bIsPlay)
        play();

    if (m_nLastPosition != nPosition)
    {
        m_nLastPosition = nPosition;
        m_pSlider->setValue(m_nLastPosition);
        m_pPlayer->setPosition(m_nLastPosition * 1000);
        setPlayTime(m_nLastPosition);
    }
}

//滑动条鼠标释放响应
void MediaPlayer::onSliderReleased()
{
    if (m_bIsLastPlay)
        play();
}

4、如何实现音量控制和音量控制窗口的自动显示和隐藏?

音量修改也是使用滑动条进行控制,实现方法和改变播放进度类似,就不再细说。这里只说一下怎么控制音量窗口的自动出现和隐藏。当鼠标移动到音量按钮上方时,音量窗口自动显示,当鼠标移动到音量窗口上方时,音量窗口一直保持显示状态,当鼠标从音量窗口移出1s以后,音量窗口自动隐藏。了解这个过程以后,其实不难发现,还是需要对鼠标事件进行过滤和自定义处理。至于怎么使窗口延迟1s后隐藏,使用QTimer::singleShot进行单次触发最为方便合适。

bool MediaPlayer::eventFilter(QObject *watched, QEvent *event)
{
    if (watched == m_pVolumeButton) //音量键
    {
        if (event->type() == QEvent::Enter)
        {
            changeVolume(m_pPlayer->volume());
            m_pVolumeSlider->setValue(m_pPlayer->volume());
            //计算位置,使其位于音量控制按钮的上方
            m_pVolumeWidget->setGeometry(QRect(m_pVolumeButton->pos().rx()+0.5*m_pVolumeButton->width()-m_pVolumeWidget->width()/2,
                                               m_pControlWidget->y()-110 , m_pVolumeWidget->width(), 100));
            m_pVolumeWidget->show();
            m_bVolume = true;
        }
        if (event->type() == QEvent::Leave)
        {
            m_bVolume = false;
            QTimer::singleShot(1000, this, SLOT(hideSlider()));
        }
    }
    else if (watched == m_pVolumeWidget) //音量窗口
    {
        if (event->type() == QEvent::Enter)
            m_bVolume = true;
        else if(event->type() == QEvent::Leave)
        {
            m_bVolume = false;
            QTimer::singleShot(1000, this, SLOT(hideSlider()));
        }
    }

    return QObject::eventFilter(watched, event);
}

5、如何实现实现静音和全屏显示?

当我们点击音量按钮的时候进行打开静音或关闭静音操作,当然静音时手动的设置音量为0也无可厚非,但是QMediaPlayer中也提高了相关接口,使用方法如下:

//打开或关闭静音
void MediaPlayer::openOrCloseMute()
{
    if (m_pPlayer->isMuted())
        m_pVolumeButton->setIcon(QIcon(":/Images/MediaPlayer_Icon/sound.png"));
    else
        m_pVolumeButton->setIcon(QIcon(":/Images/MediaPlayer_Icon/mute.png"));

    m_pPlayer->setMuted(!m_pPlayer->isMuted());

全屏方法也是调用接口,直接上代码:

//打开或关闭全屏显示
void MediaPlayer::openOrCloseFullScreen()
{
    if (this->isFullScreen())
    {
        m_pFullScreenButton->setIcon(QIcon(":/Images/MediaPlayer_Icon/showMax.png"));
        m_pFullScreenButton->setToolTip("进入全屏");
        this->showNormal();
    }
    else
    {
        m_pFullScreenButton->setIcon(QIcon(":/Images/MediaPlayer_Icon/showNormal.png"));
        m_pFullScreenButton->setToolTip("退出全屏");
        this->showFullScreen();
    }
}

总结

其实整个实现过程也很简单,主要是QSlider的使用和对鼠标移动事件的处理,该组件的下方控制窗口也可以实现自动隐藏,实现方法和音量窗口类似。还可以集成之前我们自己做的弹幕组件,从而支持发送弹幕功能,由于时间关系,我就没有实现。该组件的使用方法也非常方便:

MediaPlayer *pMediaPlayer = new MediaPlayer;
pMediaPlayer->show();
//pMediaPlayer->playMusic("D:/CloudMusic/1.mp3"); //播放音乐
pMediaPlayer->playVideo("C:/Users/金/Desktop/2.mp4");  //播放视频

源码大概有500行左右,这里就不再贴出来了,否则篇幅太长,主要功能实现,上面也都放了源码,如果有需要也可以评论留言,后期也会统一上传github。

相关推荐

看黑客是如何获取你电脑最高权限的,一定要看

在渗透过程中,通过各种方式获取到一枚cmdshell,但是这个shell的权限比较低,无法让我们做我们想要做的一些操作,比如说获取系统密码,获取数据库信息,又或者比如说拿到服务器中的另一个站点的权限,...

是50个常用的Visual Basic代码示例:

以下是50个常用的VisualBasic代码示例:1.声明变量```vb...

电脑系统型号怎么看版本(如何看电脑系统型号)

有时候我们会需要进行查看电脑上安装的windows系统版本及系统版本号,但对于不懂电脑知识的小白来说要怎么查看电脑系统版本信息呢?别着急,有小编在接下来,就将查看电脑系统版本的教程来分享给你们,希望对...

dos命令systeminfo,查看系统启动时间。电脑卡慢,win10怎么了?

最近一段时间,有几个反应电脑卡慢的,都是windows10的系统。询问得知每天电脑有关机,打开任务管理器,内存使用量达到百分之九十多,而程序只打开微信、wps、360浏览器。cmd窗口运行命令syst...

systeminfo命令:全面解析系统信息!

你是否曾想过,仅凭一条简单的命令,就能深入了解计算机的"内心世界"?是不是有点不可思议?那么,让我们一起探寻这个神奇的命令,揭开它背后的奥秘吧!它能提供的信息超乎你的想象,从操作系统到硬件配置,再到驱...

电脑序列号怎么查询?只需两行命令一键查询

当我们的电脑出问题需要保修的时候,需要查询到电脑的型号和序列号才更便于进行下一步的操作,有包装盒的朋友还可以在包装盒上查询,笔记本用户可以在电脑底部标签上查询,没有包装盒和标签破损的用户就无从下手了。...

快速显示系统信息:Systeminfo命令详解

Systeminfo命令是windows系统中显示系统信息的命令,此命令可以显示出计算机的操作系统的详细配置信息,包括操作系统配置、安全信息、产品ID和硬件属性(如RAM、磁盘空间和网卡)。使用...

dos命令systeminfo图文教程,显示操作系统配置信息msinfo32

大家好,我是老盖,首先感谢观看本文,本篇文章做的有视频,视频讲述的比较详细,也可以看我发布的视频。今天我们学习systeminfo命令,该工具显示本地或远程机器(包括服务包级别)的操作系统配置的信息,...

基于uniapp+vue3跨端仿制chatgpt实例uniapp-chatgpt

#夏日生活打卡季#...

原创新作uniapp+vue3+pinia2高仿微信App聊天

前段时间有给大家分享一个flutter3.x桌面端os系统。今天再分享一款最新原创之作uniapp-vue3-wechat聊天实例。uni-vue3-wechat采用...

UniApp开发的设备适配(uniapp服务器配置)

UniApp是一个跨平台开发框架,支持多端应用(如H5、小程序、iOS、Android等)。由于不同设备的屏幕尺寸、分辨率、操作系统等存在差异,设备适配是开发过程中需要重点关注的问题。以下是Uni...

如何用服务器搭建自己的个人网站(自己服务器怎么做网站)

这篇教程主要是告诉大家如何利用TCP和HTTP协议来完成网站的搭建。首先你需要有C/C++语言基础,且有服务器、客户端概念,如果你了解TCP或者HTTP协议的话,那么将会帮助你更快的学会如何搭建个人网...

大话C语言:字符数组(c语言字符数组教学视频)

1字符数组概述C语言中没有字符串这种数据类型,可以通过char的数组来替代。数字0(和字符'\0'等价)结尾的char数组就是一个字符串,字符串是一种特殊的char的数组。...

源码分享:在pdf上加盖电子签章(pdf怎么加电子签章)

在pdf上加盖电子签章,并不是只是加个印章图片,。而是要使用一对密钥中的私钥对文件进行签字。为啥要用私钥呢?很简单,因为公钥是公开的,其他人才可以用公钥为你证明,这个文件是你签的。这就是我们常说的:私...

C语言wcstombs函数详解:宽字符字符串到多字节的「翻译官」

核心定位wcstombs是C语言中用于将宽字符字符串转换为多字节字符串的「翻译官」,它能将宽字符(wchar_t)转换为多字节字符(如UTF-8编码的中文)。就像一位翻译官,它能将一种语言(宽字符...

取消回复欢迎 发表评论: