一.前言
在浏览器中实现播放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();//初始化websocketInitWeb();//窗口置顶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 paramm_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);//websocketm_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();
#elseQDesktopWidget *desk = qApp->desktop();int ScreenNum = desk->screenNumber(QCursor::pos());QRect rect = desk->availableGeometry(ScreenNum);
#endifsetGeometry(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