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

news/2024/2/25 14:25:38/文章来源:https://blog.csdn.net/linyibin_123/article/details/135629443

一.前言

    在浏览器中实现播放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.luyixian.cn/news_show_925737.aspx

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

相关文章

HashData湖仓一体方案:方案概览与Hive数据同步

随着云计算、大数据、AI的发展和普及&#xff0c;各行各业的业务场景日益复杂&#xff0c;数据呈现出大规模、多样性的特点&#xff0c;企业对数据仓库的需求也进一步拓展至对多元化数据实时处理的场景。 数据湖是多元数据存储与使用的便捷选择&#xff0c;而云原生具有数据资…

GBASE南大通用提问:如果程序检索到 NULL 值,该怎么办?

可在数据库中存储 NULL 值&#xff0c;但编程语言支持的数据类型不识别 NULL 状态。程序必须 采用某种方式来识别 NULL 项&#xff0c;以免将它作为数据来处理。 在 SQL API 中&#xff0c;指示符变量满足此需要。指示符变量是与可能收到 NULL 项的主变量相 关联的一个附加的变…

PHP面试小结(20240108)

PHP 部分 1. php的包管理工具是如何实现自动加载的 换句话问&#xff1a;composer 实现原理是什么&#xff1f;spl_autoload_register() 首先&#xff0c;Composer 是 PHP 的一个包管理和包依赖管理的工具 &#xff0c; 打开安装之后生成的 "vendor" 文件, 里面有个…

opencv_角点检测

文章内容 一个opencv检测角点的程序 运行效果 #include <opencv2/opencv.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <iostream>using namespace cv; using namespace std;void detectCorners(M…

uniapp中按钮点击跳转页面失效,纠正错误(亲测可用)

不知道伙伴你的错误和我是否一致&#xff1f; 我当时为了点击跳转按钮发现跳转不了&#xff0c;如下错误提示&#xff1a; worker.js?libNameWAAccelerateWorker.js:1 [Deprecation] SharedArrayBuffer will require cross-origin isolation as of M92, around July 2021. S…

Debian12 安装jenkins 公钥配置

jenkins公钥配置 参考&#xff1a;Debian Jenkins 软件包 这是 Jenkins 的 Debian 软件包存储库&#xff0c;用于自动安装和升级。 要使用此存储库&#xff0c;请先将密钥添加到您的系统&#xff08;对于每周发布行&#xff09;&#xff1a; sudo wget -O /usr/share/keyring…

36.有效的数独

36.有效的数独 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例图…

企业微信开发:自建应用:access_token

access_token 过期后接口响应 access_token 已经过期&#xff08;2小时&#xff09;后&#xff0c;调用接口的响应&#xff1b;本文中以发送消息接口为例&#xff0c;说明接口响应的情况。 官方开发文档链接&#xff1a;获取access_token access_token 过期后调用接口 响应体 …

2024年腾讯云主机价格表,附报价明细

腾讯云服务器租用价格表&#xff1a;轻量应用服务器2核2G3M价格62元一年、2核2G4M价格118元一年&#xff0c;540元三年、2核4G5M带宽218元一年&#xff0c;2核4G5M带宽756元三年、轻量4核8G12M服务器446元一年、646元15个月&#xff0c;云服务器CVM S5实例2核2G配置280.8元一年…

机器学习小记——KNN(K近邻)

为了让绝大多数人都可以看懂&#xff0c;所以我就用简单的话语来讲解机器学习每一个算法 第一次写ML的博文&#xff0c;所以可能会有些地方出错&#xff0c;欢迎各位大佬提出意见或错误 祝大家开心进步每一天&#xff5e; 博文代码全部为python 简单的说一下什么是机器学习…

adb wifi 远程调试 安卓手机 命令

使用adb wifi 模式调试需要满足以下前提条件&#xff1a; 手机 和 PC 需要在同一局域网下。手机需要开启开发者模式&#xff0c;然后打开 USB 调试模式。 具体操作步骤如下&#xff1a; 将安卓手机通过 USB 线连接到 PC。&#xff08;连接的时候&#xff0c;会弹出请求&#x…

Android 系统启动过程纪要(基于Android 10)

前言 看过源码的都知道&#xff0c;Launcher系统启动都会经过这三个进程 init ->zygote -> system_server。今天我们就来讲解一下这三个进程以及Launcher系统启动。 init进程 准备Android虚拟机环境&#xff1a;创建和挂载系统文件目录&#xff1b;初始化属性服务&…

AI大模型预先学习笔记二:prompt提问大模型、langchain使用大模型框架、fine tune微调大模型

文章目录 一、Prompt Engineering&#xff08;怎么去提问大模型&#xff09;1&#xff09;环境准备2&#xff09;交互代码的参数备注3&#xff09;交互代码 二、LangChain&#xff08;一个框架去使用大模型&#xff09;1&#xff09;LangChain核心介绍&#xff1a;I/O模块、数据…

Java NIO (二)NIO Buffer类的重要方法

1 allocate()方法 在使用Buffer实例前&#xff0c;我们需要先获取Buffer子类的实例对象&#xff0c;并且分配内存空间。需要获取一个Buffer实例对象时&#xff0c;并不是使用子类的构造器来创建&#xff0c;而是调用子类的allocate()方法。 public class AllocateTest {static…

四、Sharding-JDBC系列04:分库分表后,如何不停机迁移数据?

目录 停机迁移方案 双写迁移方案 一般会有两种方案&#xff1a; 停机迁移方案 这种方案最简单也是最low的。 数据迁移前&#xff0c;在网站或者app挂个公告&#xff0c;说0点到早上6点系统进行维护&#xff0c;无法访问。 接着到0点停机&#xff0c;系统停掉&#xff0c;…

k8s---配置资源管理

目录 配置资源管理的方式 secret pod如何来引用secret&#xff1f;&#xff1f;&#xff1f; 陈述式创建&#xff1a; 声明式创建 Secret创建加密文件 使用token挂载 环境变量使用 docker-registry ConfigMap 陈述式 热更新 总结&#xff1a; 配置资源管理的方式 …

Go-gin-example 第二部分 jwt验证

文章目录 使用 JWT 进行身份校验jwt知识点补充认识JWTTOKEN是什么jwt的使用场景jwt的组成headerpayloadsignature 下载依赖包编写 jwt 工具包jwt中间件编写如何获取token 编写获取token的Apimodels逻辑编写路由逻辑编写修改路由逻辑 验证token将中间件接入Gin功能验证模块 续接…

【开源】基于JAVA语言的固始鹅块销售系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 鹅块类型模块2.3 固始鹅块模块2.4 鹅块订单模块2.5 评论管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 鹅块类型表3.2.2 鹅块表3.2.3 鹅块订单表3.2.4 鹅块评论表 四、系统展示五、核心代码5.…

FPGA之初探

FPGA的构成 基本逻辑单元CLB CLB是FPGA的基本逻辑单元&#xff0c; 一个 CLB 包括了 2 个 Slices&#xff0c;所以知道Slices的数量就可以知道FPGA的“大概”逻辑资源容量了。一个 Slice 等于 4 个6输入LUT8个触发器(flip-flop)算数运算逻辑&#xff0c;每个 Slice 的 4 个触发…

[足式机器人]Part2 Dr. CAN学习笔记-Advanced控制理论 Ch04-17 串讲

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-Advanced控制理论 Ch04-17 串讲