多屏渲染就是一个解码线程对应多个渲染界面,通过addrender这种方式添加多个绘制窗体,我们经常可以在展会或者卖电视机的地方可以看到很多电视播放的同一个画面,原理应该类似,一个地方负责打开解码播放,将画面同步传输到多个显示的地方,完全保证了画面的一致性。这样相当于复用了解码,极大的降低了资源的占用,重复利用解码资源。
如果是ffmpeg内核或者其他内核转换成了image信号发出来的,可以直接用信号关联到对应窗体即可,如果是mdk内核或者qtav内核,他们提供的都是addrender这种方式,将继承自qopenglwidget或者qglwidget的窗体,添加到需要渲染的队列中就行,要多少个就添加多少,通过opengl绘制视频数据还是非常好的,每增加一个窗体,只是增加部分GPU占用,几乎不会增加CPU占用,要的就是这种效果。
#include "frmplayplus.h"
#include "frmmain.h"
#include "ui_frmplayplus.h"
#include "qthelper.h"
#include "videoutil.h"
#include "videohelper.h"
#include "videobox.h"
#include "videowidgetx.h"
#ifdef mdkx
#include "mdkplayer.h"
#include "mdkwidget.h"
#include "mdkhelper.h"
#endif
#ifdef qtavx
#include "AVPlayer.h"
#include "VideoOutput.h"
#include "qtavhelper.h"
using namespace QtAV;
#endif
frmPlayPlus::frmPlayPlus(QWidget *parent) : QWidget(parent), ui(new Ui::frmPlayPlus)
{
ui->setupUi(this);
this->initForm();
this->initUrl();
this->initConfig();
this->initWidget(0);
}
frmPlayPlus::~frmPlayPlus()
{
if (ui->btnOpen->text() == "关闭") {
this->clearWidget(0);
}
delete ui;
}
bool frmPlayPlus::eventFilter(QObject *watched, QEvent *event)
{
int type = event->type();
if (type == QEvent::MouseButtonPress) {
pressedTime = QDateTime::currentDateTime();
if (qApp->mouseButtons() == Qt::RightButton) {
videoMenu->exec(QCursor::pos());
}
} else if (type == QEvent::MouseButtonRelease) {
#ifdef Q_OS_ANDROID
//长按弹出菜单
int offset = pressedTime.msecsTo(QDateTime::currentDateTime());
if (offset >= 1000) {
videoMenu->exec(QCursor::pos());
}
#endif
}
return QWidget::eventFilter(watched, event);
}
void frmPlayPlus::initForm()
{
#ifdef appui
ui->verticalLayout->insertWidget(1, ui->cboxVideoUrl);
ui->cboxVideoCore->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
ui->cboxScaleMode->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
ui->cboxHardware->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
#endif
player = new QObject;
//右键菜单
videoMenu = new QMenu(this);
this->installEventFilter(this);
//实例化通道布局类
videoBox = new VideoBox(this);
connect(videoBox, SIGNAL(changeVideo(int, QString, bool)), this, SLOT(changeVideo(int, QString, bool)));
videoBox->setLayout(ui->gridLayout);
videoBox->setVideoType(AppConfig::Plus_VideoType);
//添加自定义行列数的布局
//videoBox->appendType(1, 8, 3);
videoBox->initMenu(videoMenu);
}
void frmPlayPlus::initUrl()
{
frmMain::initUrl(ui->cboxVideoUrl, AppConfig::Plus_VideoUrl);
}
void frmPlayPlus::initConfig()
{
VideoUtil::loadVideoUrl(ui->cboxVideoUrl, AppConfig::Plus_VideoUrl, -1);
connect(ui->cboxVideoUrl->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(saveConfig()));
VideoUtil::loadVideoCore(ui->cboxVideoCore, AppConfig::Plus_VideoCore, true);
connect(ui->cboxVideoCore, SIGNAL(currentIndexChanged(int)), this, SLOT(saveConfig()));
VideoUtil::loadScaleMode(ui->cboxScaleMode);
ui->cboxScaleMode->setCurrentIndex(AppConfig::Plus_ScaleMode);
connect(ui->cboxScaleMode, SIGNAL(currentIndexChanged(int)), this, SLOT(saveConfig()));
VideoUtil::loadHardware(ui->cboxHardware, (VideoCore)AppConfig::Plus_VideoCore, AppConfig::Plus_Hardware);
connect(ui->cboxHardware, SIGNAL(currentIndexChanged(int)), this, SLOT(saveConfig()));
}
void frmPlayPlus::saveConfig()
{
//内核变化后做出对应的处理/比如切换硬解码名称
int videoCore = ui->cboxVideoCore->itemData(ui->cboxVideoCore->currentIndex()).toInt();
if (AppConfig::Plus_VideoCore != videoCore) {
AppConfig::Plus_VideoCore = videoCore;
VideoCore core = (VideoCore)AppConfig::Plus_VideoCore;
VideoUtil::loadHardware(ui->cboxHardware, core, AppConfig::Plus_Hardware);
}
AppConfig::Plus_ScaleMode = ui->cboxScaleMode->currentIndex();
AppConfig::Plus_VideoUrl = ui->cboxVideoUrl->currentText().trimmed();
AppConfig::Plus_Hardware = ui->cboxHardware->currentText();
AppConfig::writeConfig();
}
void frmPlayPlus::initPlayer(int type)
{
player->deleteLater();
player = NULL;
if (type == 5) {
#ifdef mdkx
player = new MdkPlayer;
player->setObjectName("MdkPlayer");
#endif
} else if (type == 6) {
#ifdef qtavx
player = new AVPlayer;
player->setObjectName("AVPlayer");
#endif
}
if (!player) {
player = new QObject;
}
}
void frmPlayPlus::initWidget(int type)
{
this->clearWidget(type);
this->initPlayer(type);
int scale = AppConfig::Plus_ScaleMode;
QString hardware = AppConfig::Plus_Hardware;
//取出当前画面数
int count = 4;
QString videoType = videoBox->getVideoType();
QStringList list = videoType.split("_");
if (videoType.contains("x")) {
list = list.last().split("x");
count = (list.at(0).toInt() * list.at(1).toInt());
} else if (list.count() == 2) {
count = (list.at(1).toInt() - list.at(0).toInt() + 1);
}
//按照当前画面数量生成需要同屏渲染的画面
for (int i = 0; i < count; ++i) {
QWidget *widget;
if (type == 5) {
#ifdef mdkx
MdkPlayer *p = (MdkPlayer *)player;
widget = new MdkWidget(p);
p->setAspect((scale != 2 ? mdk::KeepAspectRatio : mdk::IgnoreAspectRatio), widget);
#endif
} else if (type == 6) {
#ifdef qtavx
VideoOutput *v = new VideoOutput;
v->setOutAspectRatioMode((scale != 2 ? VideoOutput::VideoAspectRatio : VideoOutput::RendererAspectRatio));
widget = v->widget();
renders << v;
#endif
} else {
VideoWidget *w = new VideoWidget;
connect(w, SIGNAL(sig_receivePlayFinsh()), w, SLOT(stop()));
WidgetPara widgetPara = w->getWidgetPara();
widgetPara.sharedData = true;
widgetPara.scaleMode = (ScaleMode)scale;
w->setWidgetPara(widgetPara);
VideoPara videoPara = w->getVideoPara();
videoPara.videoCore = VideoCore_FFmpeg;
videoPara.hardware = hardware;
videoPara.playRepeat = true;
videoPara.connectTimeout = 0;
w->setVideoPara(videoPara);
widget = w;
}
//widget->installEventFilter(this);
widgets << widget;
}
//剩余的用空窗体填充
for (int i = 0; i < (64 - count); ++i) {
VideoWidget *widget = new VideoWidget;
widget->installEventFilter(this);
widgets << widget;
}
videoBox->setWidgets(widgets);
videoBox->show_video_all();
this->playWidget(type);
}
void frmPlayPlus::clearWidget(int type)
{
if (type == 0 && player) {
QString objName = player->objectName();
int core = AppConfig::Plus_VideoCore;
if (core == 2) {
foreach (QWidget *widget, widgets) {
VideoWidget *w = (VideoWidget *)widget;
w->stop();
}
} else if (core == 5) {
#ifdef mdkx
if (objName == "MdkPlayer") {
MdkPlayer *p = (MdkPlayer *)player;
p->stop();
}
#endif
} else if (core == 6) {
#ifdef qtavx
if (objName == "AVPlayer") {
AVPlayer *p = (AVPlayer *)player;
p->stop();
qDeleteAll(renders);
renders.clear();
}
#endif
}
}
qDeleteAll(widgets);
widgets.clear();
}
void frmPlayPlus::playWidget(int type)
{
QString url = AppConfig::Plus_VideoUrl;
VideoType videoType = VideoHelper::getVideoType(url);
url = VideoHelper::getRightUrl(videoType, url);
QString hardware = AppConfig::Plus_Hardware;
if (type == 2) {
foreach (QWidget *widget, widgets) {
VideoWidget *w = (VideoWidget *)widget;
w->open(url);
}
} else if (type == 5) {
#ifdef mdkx
MdkPlayer *p = (MdkPlayer *)player;
p->setDecoders(MdkHelper::getHardware(hardware));
p->setMedia(url);
p->setLoop(-1);
p->play();
#endif
} else if (type == 6) {
#ifdef qtavx
AVPlayer *p = (AVPlayer *)player;
foreach (VideoOutput *v, renders) {
p->addVideoRenderer(v);
}
p->setVideoDecoderPriority(QtavHelper::getHardware(hardware));
p->setRepeat(-1);
p->play(url);
#endif
}
}
void frmPlayPlus::changeVideo(int type, const QString &videoType, bool videoMax)
{
AppConfig::Plus_VideoType = videoType;
AppConfig::writeConfig();
}
void frmPlayPlus::on_btnOpen_clicked()
{
if (ui->btnOpen->text() == "打开") {
this->initWidget(AppConfig::Plus_VideoCore);
ui->btnOpen->setText("关闭");
ui->cboxVideoCore->setEnabled(false);
} else {
this->initWidget(0);
ui->btnOpen->setText("打开");
ui->cboxVideoCore->setEnabled(true);
}
}