C++ Qt實(shí)現(xiàn)瀏覽器網(wǎng)頁(yè)內(nèi)嵌的音視頻播放器
一.前言
在瀏覽器中實(shí)現(xiàn)播放RTSP實(shí)時(shí)視頻流,?體上有如下?個(gè)?案:
?案一:瀏覽器插件?案 ActiveX、NPAPI、PPAPI
ActiveX插件適用于IE瀏覽器,NPAPI與PPAPI插件適用于谷歌瀏覽器,不過這些插件都已經(jīng)不被瀏覽器所支持。
?案二:先轉(zhuǎn)碼再轉(zhuǎn)流?案
?作原理是架設(shè)一個(gè)視頻流轉(zhuǎn)碼服務(wù)器,將RTSP視頻流轉(zhuǎn)換為flv后用Web Socket或WebRTC推送到前端,前端收到后再轉(zhuǎn)換為Video所?持的MP4后播放。這過程中需要經(jīng)過2次轉(zhuǎn)碼才播放,畫?延遲時(shí)間?幅增加。如果有多路視頻流時(shí),服務(wù)器端轉(zhuǎn)碼和轉(zhuǎn)流對(duì)CPU、內(nèi)存、?絡(luò)帶寬的壓??幅度增加,長(zhǎng)期使?綜合成本很?,對(duì)?分辨率的視頻流播放經(jīng)常出現(xiàn)花屏、卡頓現(xiàn)象。此?案要求瀏覽器?持流媒體擴(kuò)展特性(MSE),且?法利?本機(jī)硬件加速實(shí)現(xiàn)解碼和渲染播放。優(yōu)點(diǎn)是可兼容移動(dòng)端?頁(yè)播放。此?案在國(guó)內(nèi)有TSINGSEE的免插件EasyPlayer RTSP播放器。
?案三:先轉(zhuǎn)碼再轉(zhuǎn)流?案
?作原理是架設(shè)?個(gè)Web Socket的視頻流轉(zhuǎn)發(fā)服務(wù)器,前端連接到此服務(wù)器后,服務(wù)端不斷把RTSP視頻流通過Web Socket不斷轉(zhuǎn)發(fā)給前端的JS處理庫(kù),JS處理庫(kù)再把視頻流轉(zhuǎn)換為Video所?持的MP4后播放。此?案不?持IE瀏覽器,最?的問題是畫?延遲達(dá)數(shù)秒,?屏內(nèi)容顯?慢,也?法利?本機(jī)硬件加速實(shí)現(xiàn)解碼和渲染播放,CPU占??,播放時(shí)花屏、卡頓現(xiàn)象,體驗(yàn)?較差。此?案的典型代表是Streamedian公司的免插件播放器Html5 RTSP Player。
?案四:Wasm?案
工作原理是通過Emscripten將音視頻解碼庫(kù)編譯成Js(WebAssembly,簡(jiǎn)稱wasm)運(yùn)行于瀏覽器之中,RTSP視頻流通過ffmpeg的Wasm版軟解碼成Video所?持的MP4后播放。此方案由于Wasm不?持硬件解碼,對(duì)多路同時(shí)播放來說,終端電腦的CPU和內(nèi)存占?會(huì)?較?,性能也堪憂。此方案有Jessibuca,Jessibuca項(xiàng)目地址:https://gitee.com/InternetJava/jessibuca

?案五:網(wǎng)頁(yè)調(diào)用VLC插件方式播放
其原理是底層調(diào)用VLC的ActiveX控件可實(shí)現(xiàn)在網(wǎng)頁(yè)中內(nèi)嵌播放多路RTSP的實(shí)時(shí)視頻流。
?案六:瀏覽器內(nèi)嵌C++播放器
基本原理在瀏覽器?頁(yè)中的指定位置和??,實(shí)現(xiàn)?個(gè)內(nèi)嵌到?頁(yè)中顯?的播放窗?,前端還必須可對(duì)這個(gè)內(nèi)嵌播放窗?進(jìn)?控制,?且播放窗?必須跟隨瀏覽器窗?的移動(dòng)和縮放、?頁(yè)滾動(dòng)、標(biāo)簽頁(yè)切換、關(guān)閉等操作進(jìn)??動(dòng)聯(lián)動(dòng)。播放器可以通過QT或MFC進(jìn)行實(shí)現(xiàn),可以充分利?終端電腦的硬件加速特性。這個(gè)播放窗?同時(shí)提供Web Socket的服務(wù)端和JSON打包命令的解析執(zhí)?模塊,前端就可以通過Web Socket連接后發(fā)送JSON打包的控制命令實(shí)現(xiàn)控制播放窗?。通過這種方案實(shí)現(xiàn)的有大華的視頻插件。
二.瀏覽器內(nèi)嵌C++播放器的實(shí)現(xiàn)
2.1 播放器功能介紹
該播放器仿照大華視頻插件,支持軟硬解碼,支持錄像截圖,支持音頻播放,支持多路播放,支持右鍵菜單欄操作,支持多路分頁(yè)顯示,支持全屏顯示等功能,如下圖:



2.2 播放器部分代碼分享
網(wǎng)頁(yè)部分:
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" rel="external nofollow" >
<link rel="stylesheet" type="text/css" href="css/slider/jquery-ui-slider-pips.min.css" rel="external nofollow" >
<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;">
播放器設(shè)置:
</h1>
<tr>
<td>
<label>當(dāng)前窗口: </label>
<input class="input_style" style="width: 300px" type="number" name="Index" id="windowIndex" value="0"/>
</td>
</tr>
<tr>
<td>
<label> 設(shè)備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=" 請(qǐng)輸入碼流地址" />
</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="關(guān)閉音頻" />
</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="窗口創(chuàng)建" />
<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: 界面功能實(shí)現(xiàn)
* 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, //初始化創(chuàng)建窗口個(gè)數(shù)
windowType: 3,
connectSuccess: () => {
console.log('連接成功')
},
createSuccess: (e) => {
console.log('窗口創(chuàng)建成功')
},
clickWindow: (wndIndex) => { //獲取當(dāng)前點(diǎn)擊的窗口
g_currentIndex = wndIndex
$('#windowIndex').val(wndIndex)
console.log("當(dāng)前點(diǎn)擊了第${wndIndex}個(gè)窗口")
}
})
}
//初始化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()
}
//設(shè)置全屏
function setFullScreen() {
g_videoPlayer.setFullScreen()
}
//銷毀視頻窗口
function destroyPlayer() {
if (!g_videoPlayer) {
alert('請(qǐng)先創(chuàng)建視頻窗口')
}
g_videoPlayer.destroy()
g_videoPlayer = null
}
實(shí)時(shí)預(yù)覽
//播放
function PlayRealStream() {
if(!g_videoPlayer) {
alert('請(qǐng)先創(chuàng)建視頻窗口')
}
var sUrl = $('#realInput').val()
if (!sUrl) {
alert("實(shí)時(shí)地址不能為空")
return false
}
var windowIndex = parseInt($('#windowIndex').val());
if(windowIndex < 0 || windowIndex > 64) {
alert("當(dāng)前窗口號(hào)需要設(shè)置(>=0 && <64)的數(shù)字")
return false
}
var devid = $('#devid').val();
if(!devid) {
alert("設(shè)備ID不能為空")
return false
}
g_videoPlayer.playReal({
devId: devid,
winIndex: windowIndex,
url: sUrl,
decodeType: g_decodeType,
connectType: g_protocolType
})
}
//停止
function StopPlay() {
if(!g_videoPlayer) {
alert('請(qǐng)先創(chuàng)建視頻窗口')
}
var windowIndex = parseInt($('#windowIndex').val());
if(windowIndex < 0 || windowIndex > 64) {
alert("當(dāng)前窗口號(hào)需要設(shè)置(>=0 && <64)的數(shù)字")
return false
}
g_videoPlayer.stopVideo(windowIndex)
}
//全部停止
function StopAllPlay() {
if(!g_videoPlayer) {
alert('請(qǐng)先創(chuàng)建視頻窗口')
}
g_videoPlayer.stopVideo('')
}
//視頻操作
//開始錄像
function StartRecord() {
if(!g_videoPlayer) {
alert('請(qǐng)先創(chuàng)建視頻窗口')
}
var windowIndex = parseInt($('#windowIndex').val());
if(windowIndex < 0 || windowIndex > 64) {
alert("當(dāng)前窗口號(hào)需要設(shè)置(>=0 && <64)的數(shù)字")
return false
}
g_videoPlayer.enableRecord({
winIndex: windowIndex,
isEnable: true
})
}
//結(jié)束錄像
function StopRecord() {
if(!g_videoPlayer) {
alert('請(qǐng)先創(chuàng)建視頻窗口')
}
var windowIndex = parseInt($('#windowIndex').val());
if(windowIndex < 0 || windowIndex > 64) {
alert("當(dāng)前窗口號(hào)需要設(shè)置(>=0 && <64)的數(shù)字")
return false
}
g_videoPlayer.enableRecord({
winIndex: windowIndex,
isEnable: false
})
}
//截圖
function Snapshot() {
if(!g_videoPlayer) {
alert('請(qǐng)先創(chuàng)建視頻窗口')
}
var windowIndex = parseInt($('#windowIndex').val());
if(windowIndex < 0 || windowIndex > 64) {
alert("當(dāng)前窗口號(hào)需要設(shè)置(>=0 && <64)的數(shù)字")
return false
}
g_videoPlayer.snapshot(windowIndex)
}
//開啟音頻
function OpenAudio() {
if(!g_videoPlayer) {
alert('請(qǐng)先創(chuàng)建視頻窗口')
}
var windowIndex = parseInt($('#windowIndex').val());
if(windowIndex < 0 || windowIndex > 64) {
alert("當(dāng)前窗口號(hào)需要設(shè)置(>=0 && <64)的數(shù)字")
return false
}
g_videoPlayer.enableAudio({
winIndex: windowIndex,
isEnable: true
})
}
//關(guān)閉音頻
function CloseAudio() {
if(!g_videoPlayer) {
alert('請(qǐng)先創(chuàng)建視頻窗口')
}
var windowIndex = parseInt($('#windowIndex').val());
if(windowIndex < 0 || windowIndex > 64) {
alert("當(dāng)前窗口號(hào)需要設(shè)置(>=0 && <64)的數(shù)字")
return false
}
g_videoPlayer.enableAudio({
winIndex: windowIndex,
isEnable: false
})
}
//設(shè)置音量
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);
//設(shè)置背景
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)
{
//構(gòu)建 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);//轉(zhuǎn)換成文檔對(duì)象
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++ Qt實(shí)現(xiàn)瀏覽器網(wǎng)頁(yè)內(nèi)嵌的音視頻播放器的詳細(xì)內(nèi)容,更多關(guān)于Qt音視頻播放器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++簡(jiǎn)單實(shí)現(xiàn)的全排列算法示例
這篇文章主要介紹了C++簡(jiǎn)單實(shí)現(xiàn)的全排列算法,結(jié)合實(shí)例形式分析了C++排序操作的實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-07-07
c++ TCHAR轉(zhuǎn)string導(dǎo)致中文缺失或亂碼問題及解決
這篇文章主要介紹了c++ TCHAR轉(zhuǎn)string導(dǎo)致中文缺失或亂碼問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
C語言中單鏈表(不帶頭結(jié)點(diǎn))基本操作的實(shí)現(xiàn)詳解
鏈表是一種物理存儲(chǔ)結(jié)構(gòu)上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過鏈表中的指針鏈接次序?qū)崿F(xiàn)的。本文主要和大家聊聊C語言中單鏈表(不帶頭結(jié)點(diǎn))的基本操作,感興趣的小伙伴可以了解一下2022-11-11
C語言數(shù)據(jù)類型枚舉enum全面詳解示例教程
生活中有很多地方會(huì)用到枚舉,比如一周有7天,可以一一枚舉;性別有男、女...等等都可以可以一一枚舉,今天來和筆者一起學(xué)習(xí)一下c語言枚舉吧2021-10-10
C++內(nèi)核對(duì)象封裝單實(shí)例啟動(dòng)程序的類
這篇文章主要介紹了利用C++內(nèi)核對(duì)象封裝的類,程序只能運(yùn)行單個(gè)實(shí)例,可防止多次啟動(dòng),大家參考使用吧2013-11-11
C++超詳細(xì)實(shí)現(xiàn)堆和堆排序過像
堆是計(jì)算機(jī)科學(xué)中一類特殊的數(shù)據(jù)結(jié)構(gòu)的統(tǒng)稱,通常是一個(gè)可以被看做一棵完全二叉樹的數(shù)組對(duì)象。而堆排序是利用堆這種數(shù)據(jù)結(jié)構(gòu)所設(shè)計(jì)的一種排序算法。本文將通過圖片詳細(xì)介紹堆排序,需要的可以參考一下2022-06-06

