C++ Qt實(shí)現(xiàn)瀏覽器網(wǎng)頁(yè)內(nèi)嵌的音視頻播放器
一.前言
在瀏覽器中實(shí)現(xiàn)播放RTSP實(shí)時(shí)視頻流,?體上有如下?個(gè)?案:
?案一:瀏覽器插件?案 ActiveX、NPAPI、PPAPI
ActiveX插件適用于IE瀏覽器,NPAPI與PPAPI插件適用于谷歌瀏覽器,不過(guò)這些插件都已經(jīng)不被瀏覽器所支持。
?案二:先轉(zhuǎn)碼再轉(zhuǎn)流?案
?作原理是架設(shè)一個(gè)視頻流轉(zhuǎn)碼服務(wù)器,將RTSP視頻流轉(zhuǎn)換為flv后用Web Socket或WebRTC推送到前端,前端收到后再轉(zhuǎn)換為Video所?持的MP4后播放。這過(guò)程中需要經(jīng)過(guò)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視頻流通過(guò)Web Socket不斷轉(zhuǎn)發(fā)給前端的JS處理庫(kù),JS處理庫(kù)再把視頻流轉(zhuǎn)換為Video所?持的MP4后播放。此?案不?持IE瀏覽器,最?的問(wèn)題是畫?延遲達(dá)數(shù)秒,?屏內(nèi)容顯?慢,也?法利?本機(jī)硬件加速實(shí)現(xiàn)解碼和渲染播放,CPU占??,播放時(shí)花屏、卡頓現(xiàn)象,體驗(yàn)?較差。此?案的典型代表是Streamedian公司的免插件播放器Html5 RTSP Player。
?案四:Wasm?案
工作原理是通過(guò)Emscripten將音視頻解碼庫(kù)編譯成Js(WebAssembly,簡(jiǎn)稱wasm)運(yùn)行于瀏覽器之中,RTSP視頻流通過(guò)ffmpeg的Wasm版軟解碼成Video所?持的MP4后播放。此方案由于Wasm不?持硬件解碼,對(duì)多路同時(shí)播放來(lái)說(shuō),終端電腦的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)。播放器可以通過(guò)QT或MFC進(jìn)行實(shí)現(xiàn),可以充分利?終端電腦的硬件加速特性。這個(gè)播放窗?同時(shí)提供Web Socket的服務(wù)端和JSON打包命令的解析執(zhí)?模塊,前端就可以通過(guò)Web Socket連接后發(fā)送JSON打包的控制命令實(shí)現(xiàn)控制播放窗?。通過(guò)這種方案實(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-07c++ TCHAR轉(zhuǎn)string導(dǎo)致中文缺失或亂碼問(wèn)題及解決
這篇文章主要介紹了c++ TCHAR轉(zhuǎn)string導(dǎo)致中文缺失或亂碼問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08C語(yǔ)言中單鏈表(不帶頭結(jié)點(diǎn))基本操作的實(shí)現(xiàn)詳解
鏈表是一種物理存儲(chǔ)結(jié)構(gòu)上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過(guò)鏈表中的指針鏈接次序?qū)崿F(xiàn)的。本文主要和大家聊聊C語(yǔ)言中單鏈表(不帶頭結(jié)點(diǎn))的基本操作,感興趣的小伙伴可以了解一下2022-11-11C語(yǔ)言數(shù)據(jù)類型枚舉enum全面詳解示例教程
生活中有很多地方會(huì)用到枚舉,比如一周有7天,可以一一枚舉;性別有男、女...等等都可以可以一一枚舉,今天來(lái)和筆者一起學(xué)習(xí)一下c語(yǔ)言枚舉吧2021-10-10C++內(nèi)核對(duì)象封裝單實(shí)例啟動(dòng)程序的類
這篇文章主要介紹了利用C++內(nèi)核對(duì)象封裝的類,程序只能運(yùn)行單個(gè)實(shí)例,可防止多次啟動(dòng),大家參考使用吧2013-11-11C++超詳細(xì)實(shí)現(xiàn)堆和堆排序過(guò)像
堆是計(jì)算機(jī)科學(xué)中一類特殊的數(shù)據(jù)結(jié)構(gòu)的統(tǒng)稱,通常是一個(gè)可以被看做一棵完全二叉樹的數(shù)組對(duì)象。而堆排序是利用堆這種數(shù)據(jù)結(jié)構(gòu)所設(shè)計(jì)的一種排序算法。本文將通過(guò)圖片詳細(xì)介紹堆排序,需要的可以參考一下2022-06-06