一、前言說明
音視頻開發除了應用在安防監控、視頻網站、各種流媒體app開發之外,還有一個小眾的市場,那就是多媒體展廳場景,這個場景目前處于壟斷地位的軟件是HirenderS3,做的非常早而且非常全面,都是通用的需求,這個場景需求,在播放這塊,有幾個明顯的需求是,播放的視頻文件分辨率特別大,一般是4K/8K甚至到16K,展廳的屏幕很大,分辨率小了的話,不夠清晰,所以至少會上4K分辨率,一般超過2K的分辨率,一定要上硬解碼,不然CPU很可能撐不住,這里問題就來了,在底層(不清楚是操作系統層面還是硬件層面),264只能支持4K硬解,265支持到8K硬解,再往上的分辨率都不支持的,而現在不少的多媒體文件是8K的264格式,12K/16K的265,那怎么辦,不開啟硬解的話,資源極其緊張,幾乎是快占滿的節奏,所以要從邏輯層面去優化,一個辦法是外接多個顯卡對應顯示器,文件切割成多個4K或者8K,每個文件指定一個顯卡去硬解,顯示在指定的顯示器上。一個辦法是用多個顯示窗體拼接,每個播放窗體都可以硬解。
還有個重要需求點是同步問題,如果是本地多個畫面拼接,則需要幀同步,不然開起來多畫面是不夠銜接的,大于1幀的誤差能夠肉眼可見,多個電腦之間不同播放器也需要幀同步,所以就產生了兩種幀同步需求,一個是本地幀同步,一個是網絡幀同步,本地幀同步主要是控制同時解碼同一幀后同時刷新顯示,網絡幀同步比較麻煩,因為必須通過網絡數據通知當前都是播放第一幀,這個網絡通信都是有延遲的,所以還需要考慮延遲的時間。
二、效果圖
三、相關代碼
#include "synclocal.h"
#include "qthelper.h"
#include "frmplay.h"SINGLETON_IMPL(SyncLocal)
QDateTime SyncLocal::SyncTime = QDateTime::currentDateTime().addDays(-1);
SyncLocal::SyncLocal(QObject *parent) : QThread(parent)
{isStop = false;this->reset();syncInterval = 5;syncOffset = 15;syncSleep = 500;updateInterval = 10;
}SyncLocal::~SyncLocal()
{this->stop();
}void SyncLocal::run()
{while (!isStop) {this->checkPosition();this->checkSync();this->checkPause();this->updateWidget();count++;msleep(updateInterval);//qDebug() << TIMEMS << "111" << updateInterval << count;}isStop = false;this->reset();
}void SyncLocal::checkPosition()
{//同步間隔0表示不啟用/至少要2個窗體才需要同步int size = frmPlay::widgets.size();if (size < 2 || isSync || isPasue) {count = 0;return;}//永遠同步到到第一個窗體/處于非播放狀態/或者暫停狀態不用繼續frmPlay *widget = frmPlay::widgets.first();if (!widget->isPlaying() || widget->isPaused()) {return;}//優先執行手動同步指令/-1則同步到第一個窗體/>=0則同步到對應位置if (syncPosition >= -1) {position = (syncPosition == -1 ? widget->position() : syncPosition);count = 0;isSync = true;qDebug() << TIMEMS << "hand" << position;return;}//同步間隔0表示不啟用if (syncInterval == 0) {count = 0;return;}//計算同步間隔需要循環多少次int maxCount = syncInterval * 1000 / updateInterval;//到了需要同步的時候執行同步if (count < maxCount) {return;}count = 0; //剛開始或者快結束先不同步position = widget->position();if (position < 2000 || qAbs(widget->duration() - position) < 2000) {return;}for (int i = 1; i < size; ++i) {offset = position - frmPlay::widgets.at(i)->position();qDebug() << TIMEMS << "posi" << position << "\t" << offset;if (qAbs(offset) >= syncOffset) {isSync = true;break;}}
}void SyncLocal::checkSync()
{//同步標志位為真則先同步if (isSync) {count = 0;isSync = false;isPasue = true;SyncTime = QDateTime::currentDateTime();qDebug() << TIMEMS << "seek" << position;//先暫停再執行設置進度foreach (frmPlay *widget, frmPlay::widgets) {widget->pause();widget->seek(position);}}
}void SyncLocal::checkPause()
{//暫停階段說明剛才執行過同步/等待一段時間重新播放if (isPasue) {qint64 time = SyncTime.msecsTo(QDateTime::currentDateTime());if (time >= syncSleep) {foreach (frmPlay *widget, frmPlay::widgets) {widget->next();}count = 0;isPasue = false;syncPosition = -2;emit receiveSync(offset);qDebug() << TIMEMS << "play" << position;}}
}void SyncLocal::updateWidget()
{//刷新界面用來觸發繪制foreach (frmPlay *widget, frmPlay::widgets) {widget->updateVideo();}
}void SyncLocal::setSyncInterval(int syncInterval)
{this->reset();this->syncInterval = syncInterval;
}void SyncLocal::setSyncOffset(int syncOffset)
{this->syncOffset = syncOffset;
}void SyncLocal::setSyncSleep(int syncSleep)
{this->syncSleep = syncSleep;
}void SyncLocal::setUpdateInterval(int updateInterval)
{this->updateInterval = updateInterval;
}void SyncLocal::stop()
{if (this->isRunning()) {this->isStop = true;this->wait();}
}void SyncLocal::reset()
{this->count = 0;this->isSync = false;this->isPasue = false;this->syncPosition = -2;
}//-1則同步到第一個窗體/>=0則同步到對應位置
void SyncLocal::sync(qint64 position)
{//至少要兩個窗體才能同步/處于暫停階段說明上一個同步還沒執行完成if (frmPlay::widgets.size() >= 2 && !isPasue && syncPosition == -2) {this->syncPosition = position;}
}
四、相關地址
- 國內站點:https://gitee.com/feiyangqingyun
- 國際站點:https://github.com/feiyangqingyun
- 個人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
- 文件地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取碼:01jf 文件名:bin_video_sync。
五、功能特點
- 實時幀同步,本地無縫拼接多個視頻。
- 支持網絡同步,可選主控端和被控端,主控端將本地播放的進度實時同步到被控端。
- 網絡同步支持組播、廣播、單播三種模式,默認組播,既可以跨網段,也可以避免廣播數據風暴。
- 默認開啟自動同步,也可以手動同步和復位同步,手動同步是立即執行一次同步,將第一個視頻的進度同步到其他視頻文件,復位同步是將所有視頻播放進度切換到最開始0的位置。
- 支持各種視音頻文件,包括但不限于mp4/mov/mkv/rmvb/avi等格式。
- 硬解碼和GPU繪制,最大化利用硬件資源,支持qsv/cuda/dxva2/d3d11va/vaapi等硬解碼。
- 極低的CPU占用,8K30fps只占不到1%的CPU,解碼和繪制全部交給GPU。
- 提供示例按照行列生成多個視頻播放窗口,每個窗口可以選擇不同的視頻文件,在手動同步模式下,可以切換任意一個視頻播放進度,會將所有的視頻按照這個進度同步。
- 自動循環播放視頻文件,無縫切換循環播放,看起來非常絲滑。
- 支持Qt4/Qt5/Qt6所有版本,支持各種操作系統包括國產OS和嵌入式OS。