浏览器网页内嵌Qt-C++音视频播放器的实现,支持软硬解码,支持音频,支持录像截图,支持多路播放等,提供源码工程下载

一.前言

    在浏览器中实现播放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>&nbsp;&nbsp;&nbsp;&nbsp;设备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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/627837.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PulsarServerException

orq.apache .pulsar .broker .PulsarServerException:the broker do not have external listener 这个异常通常表示Pulsar Broker没有为外部客户端配置外部监听器。在Pulsar中&#xff0c;外部监听器用于与Pulsar集群之外的客户端进行通信&#xff0c;例如在公共网络上的生产者…

【设计模式-06】Observer观察者模式

简要说明 事件处理模型 场景示例&#xff1a;小朋友睡醒了哭&#xff0c;饿&#xff01; 一、v1版本(披着面向对象的外衣的面向过程) /*** description: 观察者模式-v1版本(披着面向对象的外衣的面向过程)* author: flygo* time: 2022/7/18 16:57*/ public class ObserverMain…

软件测试要学习的基础知识——白盒测试

白盒测试是通过检查软件内部的逻辑结构&#xff0c;对软件中的逻辑路径进行覆盖测试&#xff0c;以确定实际运行状态与预期状态是否一致。 白盒测试又被称为&#xff1a; 透明盒测试 结构化测试 逻辑驱动测试 基于代码的测试 白盒测试的常用技术分类 一、静态分析&#x…

计算机三级(网络技术)一综合题(配置题)

常考题型 ip route ip route 0.0.0.0 0.0.0.0 <下-跳路由器P地址> Ip route 目标网段 掩码 <下一跳路由器IP地址> bandwidth bandwidth 带宽 单位&#xff1a;kbps 1Gbps1000Mbps1000000kbps crc CrC 32/16 默认32 ip address ip address ip地址 子…

Vue-20、Vue.set()的使用

1、添加对象某个属性 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Vue.set()的使用</title><script type"text/javascript" src"https://cdn.jsdelivr.net/npm/vue2/dist…

【SpringCloud】之Sentinel--服务容错的应用

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《SpringCloud开发之Sentinel--服务容错的应用》。…

qemu安装踩坑记(源码编译make版

qemu安装踩坑记&#xff08;源码编译make版 【写在前面】 本篇文章写于6.27号&#xff0c;发现写完但没发博客2333 大家好这里是β-AS&#xff0c;或者也可以喊我贝塔&#xff0c;或许也可也喊我be7a 没有人会永远学qemu&#xff0c;但永远会有人踩坑.jpg 依旧推荐一首歌 -1…

山东特产,乳山生蚝有话说

牡蛎&#xff0c;又叫生蚝&#xff0c;是无数吃货钟爱的海鲜美味。爱吃生蚝的法国人称之为“海中牛奶”&#xff0c;我国民间也有说法&#xff1a;“冬至到清明&#xff0c;蚝肉肥晶晶。”说的就是眼下&#xff0c;生蚝最肥美的冬春时节&#xff0c;也是“中国牡蛎之乡”山东乳…

༺༽༾ཊ—设计-七个-07-原则-模式—ཏ༿༼༻

第七原则&#xff1a;迪米特职责 类与类之间的耦合度尽可能低 换言之&#xff0c;我们可以理解成———只与直接朋友说话&#xff0c;不跟陌生人说话 直接朋友&#xff1a; 通过方法传参传进来的朋友&#xff0c; 类自己的字段&#xff0c; 构造函数进来的也是直接朋友&…

Centos系统安全设置

1 设置密码复杂度&#xff0c;帐号密码有效期3个月 密码复杂度要求&#xff1a;最小长度8位&#xff0c;至少2位大写字母&#xff0c;1位小写字母&#xff0c;4位数字&#xff0c;1位特殊字符 1&#xff09;执行备份&#xff1a; #cp -p /etc/login.defs /etc/login.defs_bak…

vscode显示120字符或者80字符提示线或者显示垂直标尺

vscode显示120字符或者80字符提示线或者显示垂直标尺 一般规定一行代码不超过80或者120个字符。取决于团队的编码规范。 不同公司不同团队有不同的规定。 当单行代码过长。产生横向滚动条。使得代码难以阅读。 打开全局设置的settings.json /C:/Users/xxx/AppData/Roaming/Cod…

WinCC 输入/输出域没有所需要的预定义输出格式该怎么办?

WinCC 输入/输出域没有所需要的预定义输出格式该怎么办&#xff1f; 引文&#xff1a;博途工控人平时在哪里技术交流博途工控人社群 应该大多数人都知道 WinCC 的 IO 域对于浮点数的显示都支持格式预定义。 例如原数 567.89 可以在经过 IO 域的预定义后在画面上显示为 567.9&…

TikTok电商加快闭环,独享IP为运营带来哪些好处?

近日有消息称TikTok电商在加快闭环&#xff0c;以后商家可能无法继续在TikTok上为其他电商平台或独立站引流了。如今“TikTok Shop Shopping Center”平台正在构建&#xff0c;将各种购物渠道整合为一体&#xff0c;这可能是一种趋势&#xff0c;意味着TikTok逐渐从社交应用转型…

Python-OpenCV教程丨从零开始学计算机视觉

文章目录 写在前面入门篇1.生成图片2.转换色彩空间3.拆分颜色通道4.绘制线条5.阈值自适应处理 写在后面 写在前面 探索新大陆&#xff1a;Python&OpenCV&#xff0c;本文主要记录入门计算机视觉的一些简单程序。 入门篇 安装opencv库&#xff1a; pip install -i https…

48 分布式id的生成策略

1.UUID 1.UUID (Universally Unique Identifier)&#xff0c;通用唯一识别码。UUID是基于当前时间、计数器&#xff08;counter&#xff09;和硬件标识&#xff08;通常为无线网卡的MAC地址&#xff09;等数据计算生成的。UUID由以下几部分的组合&#xff1a; 1.当前日期和时…

RabbitMQ脑裂处理

脑裂现象&#xff1a; Network partition detected Mnesia reports that this RabbitMQ cluster has experienced a network partition. There is a risk of losing data. Please read RabbitMQ documentation about network partitions and the possible solutions. 转载请在文…

zotero使用gpt

zotero使用gpt 下载 zotero下载&#xff1a;https://www.zotero.org/download/ 插件下载&#xff1a;https://github.com/MuiseDestiny/zotero-gpt?tabreadme-ov-file 插件安装 zotero中选择 工具->添加组件 选择右上角的齿轮&#xff0c;选择Install add-on from fil…

NeRF算法原理总结概述

简介 本文旨在对NeRF算法进行总结。论文翻译见博客&#xff1a;《NeRF算法论文解析与翻译》 参考链接&#xff1a; 神经网络辐射场NeRF、实时NeRF Baking、有向距离场SDF、占用网络Occupancy、NeRF 自动驾驶 NeRF详解 NeRF入门之体渲染 (Volume Rendering) NeRF中的位置编码 …

C++力扣题目501--二叉搜索树中的众数

给你一个含重复值的二叉搜索树&#xff08;BST&#xff09;的根节点 root &#xff0c;找出并返回 BST 中的所有 众数&#xff08;即&#xff0c;出现频率最高的元素&#xff09;。 如果树中有不止一个众数&#xff0c;可以按 任意顺序 返回。 假定 BST 满足如下定义&#xf…

项目中Ant Design Pro业务问题解决方案

ProTable实现多选反显筛选项多级关联选择 import {forwardRef,useImperativeHandle,useEffect,useRef,useReducer, } from "react"; import { Drawer, Space, Button, message } from "antd"; import * as PC from "ant-design/pro-components";…