一.前言
在浏览器中实现播放RTSP实时视频流,⼤体上有如下⼏个⽅案:
⽅案一:浏览器插件⽅案 ActiveX、NPAPI、PPAPI
ActiveX插件适用于IE浏览器,NPAPI与PPAPI插件适用于谷歌浏览器,不过这些插件都已经不被浏览器所支持。
⽅案二:先转码再转流⽅案
⼯作原理是架设一个视频流转码服务器,将RTSP视频流转换为flv后用Web Socket或WebRTC推送到前端,前端收到后再转换为Video所⽀持的MP4后播放。这过程中需要经过2次转码才播放,画⾯延迟时间⼤幅增加。如果有多路视频流时,服务器端转码和转流对CPU、内存、⽹络带宽的压⼒⼤幅度增加,长期使⽤综合成本很⾼,对⾼分辨率的视频流播放经常出现花屏、卡顿现象。此⽅案要求浏览器⽀持流媒体扩展特性(MSE),且⽆法利⽤本机硬件加速实现解码和渲染播放。优点是可兼容移动端⽹页播放。此⽅案在国内有TSINGSEE的免插件EasyPlayer RTSP播放器。
⽅案三:先转码再转流⽅案
⼯作原理是架设⼀个Web Socket的视频流转发服务器,前端连接到此服务器后,服务端不断把RTSP视频流通过Web Socket不断转发给前端的JS处理库,JS处理库再把视频流转换为Video所⽀持的MP4后播放。此⽅案不⽀持IE浏览器,最⼤的问题是画⾯延迟达数秒,⾸屏内容显⽰慢,也⽆法利⽤本机硬件加速实现解码和渲染播放,CPU占⽤⾼,播放时花屏、卡顿现象,体验⽐较差。此⽅案的典型代表是Streamedian公司的免插件播放器Html5 RTSP Player。
⽅案四:Wasm⽅案
工作原理是通过Emscripten将音视频解码库编译成Js(WebAssembly,简称wasm)运行于浏览器之中,RTSP视频流通过ffmpeg的Wasm版软解码成Video所⽀持的MP4后播放。此方案由于Wasm不⽀持硬件解码,对多路同时播放来说,终端电脑的CPU和内存占⽤会⽐较⾼,性能也堪忧。此方案有Jessibuca,Jessibuca项目地址:https://gitee.com/InternetJava/jessibuca
⽅案五:网页调用VLC插件方式播放
其原理是底层调用VLC的ActiveX控件可实现在网页中内嵌播放多路RTSP的实时视频流。
⽅案六:浏览器内嵌C++播放器
基本原理在浏览器⽹页中的指定位置和⼤⼩,实现⼀个内嵌到⽹页中显⽰的播放窗⼝,前端还必须可对这个内嵌播放窗⼝进⾏控制,⽽且播放窗⼝必须跟随浏览器窗⼝的移动和缩放、⽹页滚动、标签页切换、关闭等操作进⾏⾃动联动。播放器可以通过QT或MFC进行实现,可以充分利⽤终端电脑的硬件加速特性。这个播放窗⼝同时提供Web Socket的服务端和JSON打包命令的解析执⾏模块,前端就可以通过Web Socket连接后发送JSON打包的控制命令实现控制播放窗⼝。通过这种方案实现的有大华的视频插件。
二.浏览器内嵌C++播放器的实现
2.1 播放器功能介绍
该播放器仿照大华视频插件,支持软硬解码,支持录像截图,支持音频播放,支持多路播放,支持右键菜单栏操作,支持多路分页显示,支持全屏显示等功能,如下图:
2.2 播放器部分代码分享
网页部分:
index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" />
<link rel="stylesheet" type="text/css" href="css/index.css">
<link rel="stylesheet" type="text/css" href="css/slider/jquery-ui-slider-pips.min.css">
<title>My VideoPlayer For Web</title>
<script src="js/jquery.min.js"></script>
<script src="js/myplayer.js"></script>
<script src="js/index.js"></script>
<script src="jquery/slider/jquery-plus-ui.min.js"></script>
<script src="jquery/slider/jquery-ui-slider-pips.js"></script>
<script src="jquery/slider/slider.js"></script>
</head>
<body style="background-color: white; margin-left: 10px">
<div align="center" class="pageContent">
<div id="pageVideo" style="float:left" class="pageVideo">
<div id="myPlayer"></div>
</div>
<div id="pageConfig" class="pageConfig">
<div id="video_box">
<table border="0">
<h1 class="h_font" style="margin-top: 30px;">
播放器设置:
</h1>
<tr>
<td>
<label>当前窗口: </label>
<input class="input_style" style="width: 300px" type="number" name="Index" id="windowIndex" value="0"/>
</td>
</tr>
<tr>
<td>
<label> 设备ID: </label>
<input class="input_style" style="width: 300px" type="text" name="DevID" id="devid" value="0"/>
</td>
</tr>
<tr>
<td class="decode-type">
<label>软硬解码: </label>
<input type="radio" value="0" name="radioCode"/>
<label for="0">软解</label>
<input type="radio" value="1" name="radioCode" checked style="margin-left:30px;"/>
<label for="1">硬解</label>
</td>
</tr>
<tr class="real">
<td class="video-connect">
<label>连接方式: </label>
<input type="radio" value="1" name="radioConnect" checked />
<label for="1">TCP</label>
<input type="radio" value="0" name="radioConnect" style="margin-left:30px;"/>
<label for="0">UDP</label>
</td>
</tr>
<tr class="real">
<td>
<label>码流地址: </label>
<input class="input_style" style="width: 300px" type="text" name="RTSP" id="realInput" placeholder=" 请输入码流地址" />
</td>
</tr>
<tr class="real">
<td>
<input type="button" class="btn_style" onclick="PlayRealStream()" value="播放" />
<input type="button" class="btn_style" onclick="StopPlay()" value="停止" />
<input type="button" class="btn_style" onclick="StopAllPlay()" value="全部停止" />
</td>
</tr>
<!--视频操作-->
<th colspan="2" class="table_th opetate">
视频操作:
</th>
<tr class="operate">
<td>
<input type="button" class="btn_style" onclick="StartRecord()" value="录像" />
<input type="button" class="btn_style" onclick="StopRecord()" value="停止录像" />
<input type="button" class="btn_style" onclick="Snapshot()" value="截图" />
<input type="button" class="btn_style" onclick="OpenAudio()" value="开启音频" />
<input type="button" class="btn_style" onclick="CloseAudio()" value="关闭音频" />
</td>
</tr>
<tr class="operate">
<td>
<div class="tabs-content" style="margin-bottom:30px;">
<label style="margin-top:10px;">音量大小: </label>
<div class="content active" style="margin-left:80px;margin-top:-10px;">
<div id="audioSlier" style="width:190px;"> </div>
</div>
</div>
</td>
</tr>
<!--窗口操作-->
<th colspan="2" class="table_th opetate">
窗口操作:
</th>
<tr class="operate">
<td>
<!--
<input type="button" class="btn_style" onclick="initPlayer()" value="窗口创建" />
<input type="button" class="btn_style" onclick="destroyPlayer()" value="窗口销毁" />-->
<input type="button" class="btn_style" onclick="showVideoPlayer()" value="窗口显示" />
<input type="button" class="btn_style" onclick="hideVideoPlayer()" value="窗口隐藏" />
<input type="button" class="btn_style" onclick="setFullScreen()" value="窗口全屏" />
</td>
</tr>
</table>
</div>
</div>
</div>
</body>
</html>
index.js
/*
* Filename: index.js
* Description: 界面功能实现
* Version: 1.0
* *******************************************************/
//全局变量
var g_videoPlayer = null
var g_currentIndex = 0
var g_decodeType = 1
var g_protocolType = 1
//初始化
$(function () {
initPlayer()
initUI()
})
//初始化视频窗口
function initPlayer() {
if(g_videoPlayer) {
destroyPlayer()
}
g_currentIndex = 0
$('#windowIndex').val(0)
g_videoPlayer = new VideoPlayer({
videoId: 'myPlayer',
num: 4, //初始化创建窗口个数
windowType: 3,
connectSuccess: () => {
console.log('连接成功')
},
createSuccess: (e) => {
console.log('窗口创建成功')
},
clickWindow: (wndIndex) => { //获取当前点击的窗口
g_currentIndex = wndIndex
$('#windowIndex').val(wndIndex)
console.log("当前点击了第${wndIndex}个窗口")
}
})
}
//初始化UI组件
function initUI() {
$('.decode-type :radio').click(function () {
var type = parseInt($(this).val())
g_decodeType = type
})
$('.video-connect :radio').click(function () {
var type = parseInt($(this).val())
g_protocolType = type
})
$("#select_record_file").on("change", "input[id='record_file']", function () {
document.getElementById("record_file_path").value = $(this).val()
})
}
//显示视频窗口
function showVideoPlayer() {
g_videoPlayer.show()
}
//隐藏视频窗口
function hideVideoPlayer() {
g_videoPlayer.hide()
}
//设置全屏
function setFullScreen() {
g_videoPlayer.setFullScreen()
}
//销毁视频窗口
function destroyPlayer() {
if (!g_videoPlayer) {
alert('请先创建视频窗口')
}
g_videoPlayer.destroy()
g_videoPlayer = null
}
实时预览
//播放
function PlayRealStream() {
if(!g_videoPlayer) {
alert('请先创建视频窗口')
}
var sUrl = $('#realInput').val()
if (!sUrl) {
alert("实时地址不能为空")
return false
}
var windowIndex = parseInt($('#windowIndex').val());
if(windowIndex < 0 || windowIndex > 64) {
alert("当前窗口号需要设置(>=0 && <64)的数字")
return false
}
var devid = $('#devid').val();
if(!devid) {
alert("设备ID不能为空")
return false
}
g_videoPlayer.playReal({
devId: devid,
winIndex: windowIndex,
url: sUrl,
decodeType: g_decodeType,
connectType: g_protocolType
})
}
//停止
function StopPlay() {
if(!g_videoPlayer) {
alert('请先创建视频窗口')
}
var windowIndex = parseInt($('#windowIndex').val());
if(windowIndex < 0 || windowIndex > 64) {
alert("当前窗口号需要设置(>=0 && <64)的数字")
return false
}
g_videoPlayer.stopVideo(windowIndex)
}
//全部停止
function StopAllPlay() {
if(!g_videoPlayer) {
alert('请先创建视频窗口')
}
g_videoPlayer.stopVideo('')
}
//视频操作
//开始录像
function StartRecord() {
if(!g_videoPlayer) {
alert('请先创建视频窗口')
}
var windowIndex = parseInt($('#windowIndex').val());
if(windowIndex < 0 || windowIndex > 64) {
alert("当前窗口号需要设置(>=0 && <64)的数字")
return false
}
g_videoPlayer.enableRecord({
winIndex: windowIndex,
isEnable: true
})
}
//结束录像
function StopRecord() {
if(!g_videoPlayer) {
alert('请先创建视频窗口')
}
var windowIndex = parseInt($('#windowIndex').val());
if(windowIndex < 0 || windowIndex > 64) {
alert("当前窗口号需要设置(>=0 && <64)的数字")
return false
}
g_videoPlayer.enableRecord({
winIndex: windowIndex,
isEnable: false
})
}
//截图
function Snapshot() {
if(!g_videoPlayer) {
alert('请先创建视频窗口')
}
var windowIndex = parseInt($('#windowIndex').val());
if(windowIndex < 0 || windowIndex > 64) {
alert("当前窗口号需要设置(>=0 && <64)的数字")
return false
}
g_videoPlayer.snapshot(windowIndex)
}
//开启音频
function OpenAudio() {
if(!g_videoPlayer) {
alert('请先创建视频窗口')
}
var windowIndex = parseInt($('#windowIndex').val());
if(windowIndex < 0 || windowIndex > 64) {
alert("当前窗口号需要设置(>=0 && <64)的数字")
return false
}
g_videoPlayer.enableAudio({
winIndex: windowIndex,
isEnable: true
})
}
//关闭音频
function CloseAudio() {
if(!g_videoPlayer) {
alert('请先创建视频窗口')
}
var windowIndex = parseInt($('#windowIndex').val());
if(windowIndex < 0 || windowIndex > 64) {
alert("当前窗口号需要设置(>=0 && <64)的数字")
return false
}
g_videoPlayer.enableAudio({
winIndex: windowIndex,
isEnable: false
})
}
//设置音量
function SetAudioVolume() {
var audioVolumn = parseInt($("#audioSlier").slider('value'));
if(g_videoPlayer) {
g_videoPlayer.setAudioVolumn({
volumn: audioVolumn
})
}
}
C++部分:
MainWindow.cpp
#include <QMessageBox>
#include <QFileDialog>
#include <QMetaType>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "ctaudioplayer.h"
#include <QDesktopServices>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDesktopServices>
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
#include <QScreen>
#else
#include <QDesktopWidget>
#endif
#pragma execution_character_set("utf-8")
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//初始化多路播放器
InitMul();
//初始化websocket
InitWeb();
//窗口置顶
this->setWindowFlags(Qt::Widget | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
}
MainWindow::~MainWindow()
{
if(m_pWebSocketServer)
m_pWebSocketServer->close();
delete ui;
}
void MainWindow::InitMul()
{
qRegisterMetaType<MEDIA_DEV_INFO_T>("MEDIA_DEV_INFO_T");
connect(this, SIGNAL(sig_setScreenType(int)),
ui->widget_mulvideo, SLOT(slot_setScreenType(int)));
connect(this, SIGNAL(sig_playOne(MEDIA_DEV_INFO_T)),
ui->widget_mulvideo, SLOT(slot_playOne(MEDIA_DEV_INFO_T)));
connect(this, SIGNAL(sig_stopOne(int)),
ui->widget_mulvideo, SLOT(slot_stopOne(int)));
connect(this, SIGNAL(sig_snapshot(int)),
ui->widget_mulvideo, SLOT(slot_snapshot(int)));
connect(this, SIGNAL(sig_enableRecord(bool,int)),
ui->widget_mulvideo, SLOT(slot_enableRecord(bool,int)));
connect(this, SIGNAL(sig_stopAll()),
ui->widget_mulvideo, SLOT(slot_stopAll()));
connect(this, SIGNAL(sig_nextPage()),
ui->widget_mulvideo, SLOT(slot_NextPage()));
connect(this, SIGNAL(sig_prevPage()),
ui->widget_mulvideo, SLOT(slot_PrevPage()));
connect(ui->widget_mulvideo, SIGNAL(sig_pageInfo(QString)),
this, SLOT(slot_setPageInfo(QString)));
connect(ui->widget_mulvideo, SIGNAL(sig_curWinIndex(int)),
this, SLOT(slot_curWinIndex(int)));
connect(ui->widget_mulvideo, SIGNAL(sig_playFailTip(QString)),
this, SLOT(slot_playFailTip(QString)));
connect(this, SIGNAL(sig_fullscreen(bool)),
ui->widget_mulvideo, SLOT(slot_fullscreen(bool)));
connect(ui->widget_mulvideo, SIGNAL(sig_fullscreen(bool)),
this, SLOT(slot_fullscreen(bool)));
ui->comboBox_ChangeVideo->setCurrentIndex(1);
emit sig_setScreenType(1);
}
void MainWindow::InitWeb()
{
//web param
m_stWebParam.sInfo = SOFTWARE_VERSION;
m_stWebParam.sVer = SOFTWARE_VERSION;
m_stWebParam.nCode = 0;
m_stWebParam.nHwnd = 0;
//窗口置顶
this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
//设置背景
QColor color("#f0faff");
QPalette pal(this->palette());
pal.setColor(QPalette::Background, color);
this->setAutoFillBackground(true);
this->setPalette(pal);
//websocket
m_pWebSocketServer = new QWebSocketServer("myServer", QWebSocketServer::NonSecureMode);
connect(m_pWebSocketServer, SIGNAL(newConnection()), this, SLOT(on_newConnection()));
m_pWebSocketServer->listen(QHostAddress::Any, WEB_LISTEN_PORT);
InitMethodFun();
}
void MainWindow::InitMethodFun()
{
METHOD_FUN_T stMethodFun[] =
{
{"window.version", MainWindow::GetPlayerVer},
{"window.create", MainWindow::windowCreate},
{"window.change", MainWindow::windowChange},
{"window.show", MainWindow::windowShow},
{"media.playReal", MainWindow::PlayReal},
{"media.stop", MainWindow::StopMedia},
{"media.snapshot", MainWindow::Snapshot},
{"media.enableRecord", MainWindow::enableRecord},
{"media.enableAudio", MainWindow::enableAudio},
{"media.setAudioVolumn", MainWindow::setAudioVolumn},
{"media.fullscreen", MainWindow::fullScreen},
{"player.test", NULL},
};
for(int i=0; stMethodFun[i].methodFun != NULL; i++)
{
m_hashFun.insert(stMethodFun[i].sMethod, stMethodFun[i].methodFun);
}
}
void MainWindow::SendJsonData(QJsonObject Json)
{
//构建 Json 文档
QJsonDocument document;
document.setObject(Json);
QByteArray byteArray = document.toJson(QJsonDocument::Compact);
QString strJson(byteArray);
for (int i=0;i<m_clientsList.size();i++)
{
m_clientsList.at(i)->sendTextMessage(strJson);
}
}
void MainWindow::GetPlayerVer(void* pObject, QJsonObject* pJson)
{
MainWindow* pWin = (MainWindow*)pObject;
if(pJson->contains("id"))
{
QJsonValue id = pJson->value("id");
if(id.isDouble())
{
pWin->m_stWebParam.nID = id.toVariant().toInt();
MY_DEBUG << "yibin test m_stWebParam.nID:" << pWin->m_stWebParam.nID;
QJsonObject dataObject;
dataObject.insert("info", pWin->m_stWebParam.sInfo);
dataObject.insert("ver", pWin->m_stWebParam.sVer);
QJsonObject json;
json.insert("code", pWin->m_stWebParam.nCode);
json.insert("data", QJsonValue(dataObject));
json.insert("id", pWin->m_stWebParam.nID);
json.insert("session", pWin->m_stWebParam.nSession);
json.insert("success", "true");
pWin->SendJsonData(json);
}
}
}
void MainWindow::windowCreate(void *pObject, QJsonObject *pJson)
{
MainWindow* pWin = (MainWindow*)pObject;
if(pJson->contains("id"))
{
QJsonValue id = pJson->value("id");
if(id.isDouble())
{
pWin->m_stWebParam.nID = id.toVariant().toInt();
}
}
if(pJson->contains("info"))
{
QJsonValue value = pJson->value("info");
if(value.isObject())
{
QJsonObject obj = value.toObject();
if(obj.contains("clientAreaWidth"))
{
QJsonValue value = obj.value("clientAreaWidth");
if(value.isDouble())
{
pWin->m_stWebParam.nClientAreaWidth = value.toVariant().toInt();
}
}
if(obj.contains("clientAreaHeight"))
{
QJsonValue value = obj.value("clientAreaHeight");
if(value.isDouble())
{
pWin->m_stWebParam.nClientAreaHeight = value.toVariant().toInt();
}
}
if(obj.contains("width"))
{
QJsonValue value = obj.value("width");
if(value.isDouble())
{
pWin->m_stWebParam.nWidth = value.toVariant().toInt();
}
}
if(obj.contains("height"))
{
QJsonValue value = obj.value("height");
if(value.isDouble())
{
pWin->m_stWebParam.nHeight = value.toVariant().toInt();
}
}
if(obj.contains("left"))
{
QJsonValue value = obj.value("left");
if(value.isDouble())
{
pWin->m_stWebParam.nLeft = value.toVariant().toInt();
}
}
if(obj.contains("top"))
{
QJsonValue value = obj.value("top");
if(value.isDouble())
{
pWin->m_stWebParam.nTop = value.toVariant().toInt();
}
}
if(obj.contains("num"))
{
QJsonValue value = obj.value("num");
if(value.isDouble())
{
pWin->m_stWebParam.nNum = value.toVariant().toInt();
}
}
}
pWin->move(pWin->m_stWebParam.nScreenX+pWin->m_stWebParam.nLeft, pWin->m_stWebParam.nScreenY+pWin->m_stWebParam.nTop+140);
}
QJsonObject dataObject;
dataObject.insert("bRtsps", pWin->m_stWebParam.bRtsp);
dataObject.insert("hwnd", pWin->m_stWebParam.nHwnd);
QJsonObject json;
json.insert("code", pWin->m_stWebParam.nCode);
json.insert("data", QJsonValue(dataObject));
json.insert("id", pWin->m_stWebParam.nID);
json.insert("session", pWin->m_stWebParam.nSession);
json.insert("success", "true");
pWin->SendJsonData(json);
}
void MainWindow::windowChange(void *pObject, QJsonObject *pJson)
{
MainWindow* pWin = (MainWindow*)pObject;
if(pJson->contains("id"))
{
QJsonValue id = pJson->value("id");
if(id.isDouble())
{
pWin->m_stWebParam.nID = id.toVariant().toInt();
}
}
if(pJson->contains("info"))
{
QJsonValue value = pJson->value("info");
if(value.isObject())
{
QJsonObject obj = value.toObject();
if(obj.contains("clientAreaWidth"))
{
QJsonValue value = obj.value("clientAreaWidth");
if(value.isDouble())
{
pWin->m_stWebParam.nClientAreaWidth = value.toVariant().toInt();
}
}
if(obj.contains("clientAreaHeight"))
{
QJsonValue value = obj.value("clientAreaHeight");
if(value.isDouble())
{
pWin->m_stWebParam.nClientAreaHeight = value.toVariant().toInt();
}
}
if(obj.contains("left"))
{
QJsonValue value = obj.value("left");
if(value.isDouble())
{
pWin->m_stWebParam.nLeft = value.toVariant().toInt();
}
}
if(obj.contains("top"))
{
QJsonValue value = obj.value("top");
if(value.isDouble())
{
pWin->m_stWebParam.nTop = value.toVariant().toInt();
}
}
if(obj.contains("screenX"))
{
QJsonValue value = obj.value("screenX");
if(value.isDouble())
{
pWin->m_stWebParam.nScreenX = value.toVariant().toInt();
}
}
if(obj.contains("screenY"))
{
QJsonValue value = obj.value("screenY");
if(value.isDouble())
{
pWin->m_stWebParam.nScreenY = value.toVariant().toInt();
}
}
}
}
pWin->move(pWin->m_stWebParam.nScreenX+pWin->m_stWebParam.nLeft, pWin->m_stWebParam.nScreenY+pWin->m_stWebParam.nTop+140);
}
void MainWindow::windowShow(void *pObject, QJsonObject *pJson)
{
MainWindow* pWin = (MainWindow*)pObject;
if(pJson->contains("id"))
{
QJsonValue id = pJson->value("id");
if(id.isDouble())
{
pWin->m_stWebParam.nID = id.toVariant().toInt();
}
}
if(pJson->contains("info"))
{
QJsonValue value = pJson->value("info");
if(value.isObject())
{
QJsonObject obj = value.toObject();
if(obj.contains("show"))
{
QJsonValue value = obj.value("show");
if(value.isBool())
{
pWin->m_stWebParam.bShow = value.toVariant().toBool();
}
}
if(obj.contains("hwnd"))
{
QJsonValue value = obj.value("hwnd");
if(value.isDouble())
{
pWin->m_stWebParam.nHwnd = value.toVariant().toInt();
}
}
if(obj.contains("browserType"))
{
QJsonValue value = obj.value("browserType");
if(value.isDouble())
{
pWin->m_stWebParam.nBrowserType = value.toVariant().toInt();
}
}
}
}
pWin->setVisible(pWin->m_stWebParam.bShow);
}
void MainWindow::PlayReal(void *pObject, QJsonObject *pJson)
{
MainWindow* pWin = (MainWindow*)pObject;
if(pJson->contains("id"))
{
QJsonValue id = pJson->value("id");
if(id.isDouble())
{
pWin->m_stWebParam.nID = id.toVariant().toInt();
}
}
if(pJson->contains("info"))
{
QJsonValue value = pJson->value("info");
if(value.isObject())
{
QJsonObject obj = value.toObject();
if(obj.contains("devId"))
{
QJsonValue value = obj.value("devId");
if(value.isString())
{
pWin->m_stWebParam.sDevId = value.toVariant().toString();
}
}
if(obj.contains("winIndex"))
{
QJsonValue value = obj.value("winIndex");
if(value.isDouble())
{
pWin->m_stWebParam.nWinIndex = value.toVariant().toInt();
}
}
if(obj.contains("decodeType"))
{
QJsonValue value = obj.value("decodeType");
if(value.isDouble())
{
pWin->m_stWebParam.nDecodeType = value.toVariant().toInt();
}
}
if(obj.contains("connectType"))
{
QJsonValue value = obj.value("connectType");
if(value.isDouble())
{
pWin->m_stWebParam.nProtocolType = value.toVariant().toInt();
}
}
if(obj.contains("url"))
{
QJsonValue value = obj.value("url");
if(value.isString())
{
pWin->m_stWebParam.sUrl = value.toVariant().toString();
}
}
}
}
if(!pWin->m_stWebParam.sUrl.isEmpty() && !pWin->m_stWebParam.sDevId.isEmpty() &&
(pWin->m_stWebParam.nWinIndex >= 0 && pWin->m_stWebParam.nWinIndex < MAX_MEDIA_NUM))
{
MEDIA_DEV_INFO_T stDev;
stDev.nChannel = pWin->m_stWebParam.nWinIndex;
stDev.sDevId = pWin->m_stWebParam.sDevId;
stDev.sUrl = pWin->m_stWebParam.sUrl;
stDev.nDecodeType = pWin->m_stWebParam.nDecodeType;
stDev.nProtocolType = pWin->m_stWebParam.nProtocolType;
emit pWin->sig_playOne(stDev);
}
QJsonObject dataObject;
QJsonObject json;
json.insert("code", pWin->m_stWebParam.nCode);
json.insert("data", QJsonValue(dataObject));
json.insert("id", pWin->m_stWebParam.nID);
json.insert("session", pWin->m_stWebParam.nSession);
json.insert("success", "true");
pWin->SendJsonData(json);
}
void MainWindow::Snapshot(void *pObject, QJsonObject *pJson)
{
MainWindow* pWin = (MainWindow*)pObject;
if(pJson->contains("id"))
{
QJsonValue id = pJson->value("id");
if(id.isDouble())
{
pWin->m_stWebParam.nID = id.toVariant().toInt();
}
}
if(pJson->contains("info"))
{
QJsonValue value = pJson->value("info");
if(value.isObject())
{
QJsonObject obj = value.toObject();
if(obj.contains("winIndex"))
{
QJsonValue value = obj.value("winIndex");
if(value.isDouble())
{
pWin->m_stWebParam.nWinIndex = value.toVariant().toInt();
}
}
}
}
emit pWin->sig_snapshot(pWin->m_stWebParam.nWinIndex);
}
void MainWindow::enableRecord(void *pObject, QJsonObject *pJson)
{
MainWindow* pWin = (MainWindow*)pObject;
if(pJson->contains("id"))
{
QJsonValue id = pJson->value("id");
if(id.isDouble())
{
pWin->m_stWebParam.nID = id.toVariant().toInt();
}
}
if(pJson->contains("info"))
{
QJsonValue value = pJson->value("info");
if(value.isObject())
{
QJsonObject obj = value.toObject();
if(obj.contains("winIndex"))
{
QJsonValue value = obj.value("winIndex");
if(value.isDouble())
{
pWin->m_stWebParam.nWinIndex = value.toVariant().toInt();
}
}
if(obj.contains("isEnable"))
{
QJsonValue value = obj.value("isEnable");
if(value.isBool())
{
pWin->m_stWebParam.bRecord = value.toVariant().toBool();
}
}
}
}
emit pWin->sig_enableRecord(pWin->m_stWebParam.bRecord, pWin->m_stWebParam.nWinIndex);
}
void MainWindow::enableAudio(void *pObject, QJsonObject *pJson)
{
MainWindow* pWin = (MainWindow*)pObject;
if(pJson->contains("id"))
{
QJsonValue id = pJson->value("id");
if(id.isDouble())
{
pWin->m_stWebParam.nID = id.toVariant().toInt();
}
}
if(pJson->contains("info"))
{
QJsonValue value = pJson->value("info");
if(value.isObject())
{
QJsonObject obj = value.toObject();
if(obj.contains("isEnable"))
{
QJsonValue value = obj.value("isEnable");
if(value.isBool())
{
pWin->m_stWebParam.bAudio = value.toVariant().toBool();
ctAudioPlayer::getInstance().isPlay(pWin->m_stWebParam.bAudio);
}
}
}
}
}
void MainWindow::setAudioVolumn(void *pObject, QJsonObject *pJson)
{
MainWindow* pWin = (MainWindow*)pObject;
if(pJson->contains("id"))
{
QJsonValue id = pJson->value("id");
if(id.isDouble())
{
pWin->m_stWebParam.nID = id.toVariant().toInt();
}
}
if(pJson->contains("info"))
{
QJsonValue value = pJson->value("info");
if(value.isObject())
{
QJsonObject obj = value.toObject();
if(obj.contains("volumn"))
{
QJsonValue value = obj.value("volumn");
if(value.isDouble())
{
pWin->m_stWebParam.nVolumn = value.toVariant().toDouble();
qreal nVal = pWin->m_stWebParam.nVolumn / 100.0;
MY_DEBUG << "setAudioVolumn nVal:" << nVal;
ctAudioPlayer::getInstance().setVolumn(nVal);
}
}
}
}
}
void MainWindow::fullScreen(void *pObject, QJsonObject *pJson)
{
Q_UNUSED(pJson)
MainWindow* pWin = (MainWindow*)pObject;
emit pWin->sig_fullscreen(true);
pWin->slot_fullscreen(true);
}
void MainWindow::StopMedia(void *pObject, QJsonObject *pJson)
{
MainWindow* pWin = (MainWindow*)pObject;
if(pJson->contains("id"))
{
QJsonValue id = pJson->value("id");
if(id.isDouble())
{
pWin->m_stWebParam.nID = id.toVariant().toInt();
}
}
if(pJson->contains("info"))
{
QJsonValue value = pJson->value("info");
if(value.isObject())
{
QJsonObject obj = value.toObject();
if(obj.contains("winIndex"))
{
QJsonValue value = obj.value("winIndex");
if(value.isDouble())
{
pWin->m_stWebParam.nWinIndex = value.toVariant().toInt();
}
}
if(obj.contains("isAll"))
{
QJsonValue value = obj.value("isAll");
if(value.isBool())
{
pWin->m_stWebParam.bAllStop = value.toVariant().toBool();
}
}
}
}
if(pWin->m_stWebParam.bAllStop)
emit pWin->sig_stopAll();
else
{
if(pWin->m_stWebParam.nWinIndex >= 0 && pWin->m_stWebParam.nWinIndex < MAX_MEDIA_NUM)
{
emit pWin->sig_stopOne(pWin->m_stWebParam.nWinIndex);
}
}
QJsonObject dataObject;
QJsonObject json;
json.insert("code", pWin->m_stWebParam.nCode);
json.insert("data", QJsonValue(dataObject));
json.insert("id", pWin->m_stWebParam.nID);
json.insert("session", pWin->m_stWebParam.nSession);
json.insert("success", "true");
pWin->SendJsonData(json);
}
void MainWindow::parseJson(QString sData)
{
QJsonParseError jError;
QJsonDocument jDoc = QJsonDocument::fromJson(sData.toLatin1(), &jError);//转换成文档对象
if(!jDoc.isNull() && jError.error == QJsonParseError::NoError)
{
if(jDoc.isObject())
{
QJsonObject object = jDoc.object();
if(object.contains("method"))
{
QJsonValue sMethod = object.value("method");
if(sMethod.isString())
{
QString strMethod = sMethod.toString();
QHashMethodFunIterator iter = m_hashFun.begin();
for(; iter != m_hashFun.end(); ++iter)
{
QString sKey = iter.key();
if(sKey.contains(strMethod))
{
MethodFun fun = m_hashFun.value(sKey);
fun(this, &object);
}
}
}
}
}
}
}
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if(event->key() == Qt::Key_Escape)
{
if(m_bFullScreen == true)
{
emit sig_fullscreen(false);
slot_fullscreen(false);
event->accept();
}
}
return QMainWindow::keyPressEvent(event);
}
void MainWindow::sendWinSelect()
{
QJsonObject infoObject;
infoObject.insert("wndIndex", m_stWebParam.nWinIndex);
infoObject.insert("hwnd", m_stWebParam.nHwnd);
QJsonObject json;
json.insert("method", "window.clicked");
json.insert("info", QJsonValue(infoObject));
json.insert("session", m_stWebParam.nSession);
json.insert("success", "true");
SendJsonData(json);
}
void MainWindow::showFullScreen()
{
m_normalGeo = this->geometry();
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
QScreen *desk = qApp->screenAt(QCursor::pos());
QRect rect = desk->availableGeometry();
#else
QDesktopWidget *desk = qApp->desktop();
int ScreenNum = desk->screenNumber(QCursor::pos());
QRect rect = desk->availableGeometry(ScreenNum);
#endif
setGeometry(rect + QMargins(32,17,17,17));
QMainWindow::show();
}
void MainWindow::showNormal(const QRect &rect)
{
if(rect.isNull())
{
if(m_normalGeo.isNull())
{
m_normalGeo = geometry();
m_normalGeo.setWidth(geometry().width()/2);
m_normalGeo.setHeight(geometry().height()/2);
}
if(m_normalGeo.width() > width() || m_normalGeo.height() > height())
{
m_normalGeo.setWidth(this->width()/2);
m_normalGeo.setHeight(this->height()/2);
}
if(m_normalGeo.y() < 0)
m_normalGeo.setY(0);
setGeometry(m_normalGeo);
}
else
{
setGeometry(m_normalGeo);
}
m_bFullScreen = false;
QMainWindow::show();
}
void MainWindow::on_comboBox_ChangeVideo_activated(int index)
{
emit sig_setScreenType(index);
}
void MainWindow::on_newConnection()
{
m_pSocket = m_pWebSocketServer->nextPendingConnection();
connect(m_pSocket, SIGNAL(textMessageReceived(QString)), this, SLOT(on_processTextMessage(QString)));
connect(m_pSocket, SIGNAL(disconnected()), this, SLOT(on_socketDisconnected()));
QString item = m_pSocket->peerAddress().toString();
MY_DEBUG << item;
m_clientsList << m_pSocket;
}
void MainWindow::on_socketDisconnected()
{
}
void MainWindow::on_processTextMessage(QString message)
{
QString time = m_pCurrentDateTime->currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz ddd");
QString item = m_pSocket->peerAddress().toString();
//MY_DEBUG << time + "" + item + "\n" + message;
parseJson(message);
}
void MainWindow::on_pushButton_PrevPage_clicked()
{
emit sig_prevPage();
}
void MainWindow::on_pushButton_NextPage_clicked()
{
emit sig_nextPage();
}
void MainWindow::slot_curWinIndex(int nIndex)
{
m_stWebParam.nWinIndex = nIndex;
sendWinSelect();
}
void MainWindow::slot_playFailTip(QString sTip)
{
QMessageBox::critical(this, "myFFmpeg", sTip);
}
void MainWindow::slot_setPageInfo(QString sInfo)
{
ui->label_PageNumber->setText(sInfo);
}
void MainWindow::slot_fullscreen(bool bFull)
{
if(bFull)
{
m_bFullScreen = true;
ui->widget_control->hide();
showFullScreen();
}
else
{
m_bFullScreen = false;
ui->widget_control->show();
showNormal();
}
}
三.浏览器内嵌C++播放器的下载
3.1 体验版下载
链接: https://download.csdn.net/download/linyibin_123/88750969
3.2 网页源码与C++播放器源码下载
链接: https://download.csdn.net/download/linyibin_123/88750997