获取本地 qt_用QT实现视频/音乐播放组件
前言
我已經(jīng)有四五天沒有發(fā)布文章了,趁著這個(gè)周末有空,就又開始構(gòu)思我們自己的QT組件庫中的新組件,思考還有哪些有用、有趣、值得研究學(xué)習(xí)并構(gòu)建實(shí)現(xiàn)的組件,于是又有了兩個(gè)新的目標(biāo),即多媒體播放組件和地圖組件。之所以準(zhǔn)備實(shí)現(xiàn)這兩個(gè)小組件,是因?yàn)楝F(xiàn)在流行的很多軟件應(yīng)用中幾乎都能用得到音頻視頻播放和地圖導(dǎo)航或定位功能。所以我們在自己的軟件開發(fā)中如果也遇到類似的需求,能有一個(gè)使用方便、快捷靈活的多媒體或地圖組件,肯定能夠節(jié)省我們很多的工作量和精力,QT雖然有相關(guān)的類但是沒有相關(guān)的組件,如是我就開始了自己的實(shí)現(xiàn)。
功能展示
今天先跟大家分享一下我自己實(shí)現(xiàn)的多媒體播放組件,使用該組件可以很方便的用來播放音樂和視頻。而且所有功能完全是基于QT自帶的QMediaPlayer類和QVideoWidget類進(jìn)行實(shí)現(xiàn),沒有使用任何第三方庫,所以上手也比較容易。目前已實(shí)現(xiàn)的基本功能有:
- 音樂/視頻播放、暫停功能
- 時(shí)長和進(jìn)度顯示
- 音量調(diào)節(jié)和靜音功能
- 播放進(jìn)度調(diào)整,支持時(shí)間軸點(diǎn)選、拖拉和鍵盤控制
- 全屏
因?yàn)殇浿频膅if沒有聲音,所以我只展示視頻播放功能,具體實(shí)現(xiàn)效果如下方動圖所示:
播放、暫停功能,點(diǎn)擊按鈕或視頻窗口都能實(shí)現(xiàn)該功能:
進(jìn)度調(diào)節(jié)和音量調(diào)節(jié):
靜音:
全屏:
實(shí)現(xiàn)方法
1、怎么使用QMediaPlayer類播放音樂和視頻?
播放音樂只需要設(shè)置好源即可,源既可以是本地路徑也可以是網(wǎng)址,如果播放使用本地文件的話,需要使用
QUrl::fromLocalFile函數(shù),方法如下:
m_pPlayer = new QMediaPlayer;m_pPlayer->setMedia(QMediaContent(QUrl::fromLocalFile(strPath)));m_pPlayer->play();播放視頻的話還需要設(shè)置視頻輸出位置,所以QMediaPlayer通常需要連接一個(gè)QVideoWidget來進(jìn)行視頻的播放,方法如下:
m_pPlayer = new QMediaPlayer;m_pVideoWidget = new QVideoWidget;m_pPlayer->setMedia(QMediaContent(QUrl::fromLocalFile(strPath)));m_pPlayer->setVideoOutput(m_pVideoWidget);m_pVideoWidget->show();2、怎么顯示視頻總時(shí)長和當(dāng)前播放進(jìn)度?
總時(shí)長可以通過m_pPlayer->duration()獲取,當(dāng)前播放進(jìn)度可以通過m_pPlayer->position()獲取,這兩個(gè)函數(shù)返回的都是毫秒數(shù),我們只需要將該毫秒數(shù)轉(zhuǎn)換為時(shí)間并顯示到標(biāo)簽上即可,我為了這兩個(gè)值能夠與時(shí)間軸對應(yīng)起來,所以我都是先轉(zhuǎn)換成秒,然后再對秒進(jìn)行處理:
//將秒數(shù)轉(zhuǎn)換為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'));}為了能夠?qū)崟r(shí)更新當(dāng)前的播放進(jìn)度,我設(shè)置了一個(gè)定時(shí)器,每50ms檢查一次當(dāng)前秒數(shù)是否變化并對界面進(jìn)行刷新:
//定時(shí)器超時(shí)處理void MediaPlayer::onTimerOut(){ if (m_pSlider->sliderPosition() != m_nLastPosition) { //之所以會出現(xiàn)這種情況,是因?yàn)橛|發(fā)了鍵盤事件,比如用戶按下了方向鍵、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對視頻進(jìn)行進(jìn)行展示和控制?
展示功能是很好實(shí)現(xiàn)的,只需要設(shè)置好QSlider的范圍并在定時(shí)器中setValue即可,QSlider本身也支持拖動控制,但是當(dāng)我自定義了QSlider上滑塊的樣式后,發(fā)現(xiàn)拖動滑塊不是很靈敏且經(jīng)常無法選中滑塊,且QSlider本身并不支持點(diǎn)擊改變當(dāng)前值,因此我對QSlider添加了事件過濾器,并自己處理了它的鼠標(biāo)點(diǎn)擊事件和鼠標(biāo)移動事件。
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){ //進(jìn)度滑動條 if(watched == m_pSlider) { if (event->type() == QEvent::MouseButtonPress) { QMouseEvent *mouseEvent = static_cast(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(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);}如果在視頻播放過程中改變進(jìn)度的話,定時(shí)器還在運(yùn)行,可能也會同時(shí)修改視頻的進(jìn)度,所以在用m_pSlider改變播放進(jìn)度時(shí),需要先停止視頻播放和定時(shí)器,這個(gè)操作我們通過管理QSlider的鼠標(biāo)按下響應(yīng)、鼠標(biāo)移動響應(yīng)和鼠標(biāo)釋放響應(yīng)進(jìn)行處理。當(dāng)鼠標(biāo)按下時(shí)停止視頻播放和定時(shí)器,鼠標(biāo)移動時(shí)修改視頻進(jìn)度,鼠標(biāo)釋放時(shí)恢復(fù)視頻播放和重啟定時(shí)器。
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()));//滑動條鼠標(biāo)點(diǎn)擊響應(yīng)void MediaPlayer::onSliderPressed(){ m_bIsLastPlay = m_bIsPlay;}//滑動條滑動響應(yīng)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); }}//滑動條鼠標(biāo)釋放響應(yīng)void MediaPlayer::onSliderReleased(){ if (m_bIsLastPlay) play();}4、如何實(shí)現(xiàn)音量控制和音量控制窗口的自動顯示和隱藏?
音量修改也是使用滑動條進(jìn)行控制,實(shí)現(xiàn)方法和改變播放進(jìn)度類似,就不再細(xì)說。這里只說一下怎么控制音量窗口的自動出現(xiàn)和隱藏。當(dāng)鼠標(biāo)移動到音量按鈕上方時(shí),音量窗口自動顯示,當(dāng)鼠標(biāo)移動到音量窗口上方時(shí),音量窗口一直保持顯示狀態(tài),當(dāng)鼠標(biāo)從音量窗口移出1s以后,音量窗口自動隱藏。了解這個(gè)過程以后,其實(shí)不難發(fā)現(xiàn),還是需要對鼠標(biāo)事件進(jìn)行過濾和自定義處理。至于怎么使窗口延遲1s后隱藏,使用QTimer::singleShot進(jìn)行單次觸發(fā)最為方便合適。
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()); //計(jì)算位置,使其位于音量控制按鈕的上方 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、如何實(shí)現(xiàn)實(shí)現(xiàn)靜音和全屏顯示?
當(dāng)我們點(diǎn)擊音量按鈕的時(shí)候進(jìn)行打開靜音或關(guān)閉靜音操作,當(dāng)然靜音時(shí)手動的設(shè)置音量為0也無可厚非,但是QMediaPlayer中也提高了相關(guān)接口,使用方法如下:
//打開或關(guān)閉靜音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());全屏方法也是調(diào)用接口,直接上代碼:
//打開或關(guān)閉全屏顯示void MediaPlayer::openOrCloseFullScreen(){ if (this->isFullScreen()) { m_pFullScreenButton->setIcon(QIcon(":/Images/MediaPlayer_Icon/showMax.png")); m_pFullScreenButton->setToolTip("進(jìn)入全屏"); this->showNormal(); } else { m_pFullScreenButton->setIcon(QIcon(":/Images/MediaPlayer_Icon/showNormal.png")); m_pFullScreenButton->setToolTip("退出全屏"); this->showFullScreen(); }}總結(jié)
其實(shí)整個(gè)實(shí)現(xiàn)過程也很簡單,主要是QSlider的使用和對鼠標(biāo)移動事件的處理,該組件的下方控制窗口也可以實(shí)現(xiàn)自動隱藏,實(shí)現(xiàn)方法和音量窗口類似。還可以集成之前我們自己做的彈幕組件,從而支持發(fā)送彈幕功能,由于時(shí)間關(guān)系,我就沒有實(shí)現(xiàn)。該組件的使用方法也非常方便:
MediaPlayer *pMediaPlayer = new MediaPlayer;pMediaPlayer->show();//pMediaPlayer->playMusic("D:/CloudMusic/1.mp3"); //播放音樂pMediaPlayer->playVideo("C:/Users/金/Desktop/2.mp4"); //播放視頻源碼大概有500行左右,這里就不再貼出來了,否則篇幅太長,主要功能實(shí)現(xiàn),上面也都放了源碼,如果有需要也可以評論留言,后期也會統(tǒng)一上傳github。
總結(jié)
以上是生活随笔為你收集整理的获取本地 qt_用QT实现视频/音乐播放组件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 武林外史java游戏,武林外史_网络游戏
- 下一篇: 【C++深度剖析教程27】多态的概念与意