參考文檔
https://docs.unrealengine.com/4.27/zh-CN/SharingAndReleasing/PixelStreaming/PixelStreamingIntro/
https://docs.unrealengine.com/5.0/zh-CN/unreal-engine-pixel-streaming-reference/
準備工作
信令服務 和 前端
可以先把信令服務起起來,如
至於前端的話,參考:
https://www.cnblogs.com/makalochen/p/17803468.html
這裏不做贅述
前端示例
<!DOCTYPE html>
<html>
<head>
<!-- 該文件處理瀏覽器和虛幻引擎應用間的通信,接受並顯示來自服務器的媒體流。在非必要的情況下,請勿修改此JavaScript文件 -->
<script type="text/javascript" src="../scripts/webRtcPlayer.js"></script>
<!-- 此文件將設置處理鍵盤、鼠標和觸摸事件的事件監聽器 -->
<script type="text/javascript" src="../scripts/app.js"></script>
<link rel="shortcut icon" href="../images/favicon.ico" type="image/x-icon">
<link rel="icon" type="../image/png" sizes="96x96" href="/images/favicon-96x96.png">
<link rel="icon" type="../image/png" sizes="32x32" href="/images/favicon-32x32.png">
<link rel="icon" type="../image/png" sizes="16x16" href="/images/favicon-16x16.png">
<link type="text/css" rel="stylesheet" href="player.css">
<title>myUI</title>
</head>
<body onload="load()">
<div>
<button onclick="ueSend('1', '1', '')">晴天</button>
<button onclick="ueSend('1', '2', '')">雨天</button>
<button onclick="ueSend('1', '3', '')">雪天</button>
<button onclick="ueSend('1', '4', '')">霧天</button>
<button onclick="ueSend2()">設置時間</button> <input type="text" id='setTime'>
<button onclick="ueSend3('3', '-2', '')">切換到 -2 樓 </button>
<button onclick="ueSend3('3', '-1', '')">切換到 -1 樓 </button>
<button onclick="ueSend3('3', '1', '')">切換到 1 樓 </button>
<button onclick="ueSend3('3', '2', '')">切換到 2 樓 </button>
<button onclick="ueSend3('3', '3', '')">切換到 3 樓 </button>
<button onclick="ueSend3('3', '4', '')">切換到 4 樓 </button>
<button onclick="ueSend3('3', '5', '')">切換到 5 樓 </button>
<button onclick="ueSend3('3', '6', '')">關閉樓層掀蓋 </button>
<button id="480P" type="button" class="btn btn-secondary" onclick="setRes(854, 480)">
<span>480P</span>
</button>
<button id="720p" type="button" class="btn btn-secondary" onclick="setRes(1280, 720)">
<span>720p</span>
</button>
<button id="1080p" type="button" class="btn btn-secondary" onclick="setRes(1920, 1080)">
<span>1080p</span>
</button>
<button id="720p" type="button" class="btn btn-secondary" onclick="setRes(2560,1440)">
<span>2K</span>
</button>
<button id="4k" type="button" class="btn btn-secondary" onclick="setRes(3840, 2160)">
<span>4k</span>
</button>
</div>
<div id="playerUI">
<div id="player"></div>
<div id="overlay" class="overlay text-light bg-dark">
<!-- <div id="overlay" class="overlay text-light bg-dark" hidden="hidden"> -->
<div>
<div id="qualityStatus" class="greyStatus">●</div>
<div id="overlayButton">+</div>
</div>
<div id="overlaySettings">
<div id="kickOthers">
<div class="settings-text">Kick all other players</div>
<label class="btn-overlay">
<input type="button" id="kick-other-players-button" class="overlay-button btn-flat"
value="Kick">
</label>
</div>
<div id="fillWindow">
<div class="settings-text">Enlarge Display to Fill Window</div>
<label class="tgl-switch">
<input type="checkbox" id="enlarge-display-to-fill-window-tgl" class="tgl tgl-flat" checked>
<!-- <input type="checkbox" id="enlarge-display-to-fill-window-tgl" class="tgl tgl-flat"> -->
<div class="tgl-slider"></div>
</label>
</div>
<div id="qualityControlOwnership">
<div class="settings-text">Quality control ownership</div>
<label class="tgl-switch">
<input type="checkbox" id="quality-control-ownership-tgl" class="tgl tgl-flat">
<div class="tgl-slider"></div>
</label>
</div>
<br>
<!-- 編碼設置 -->
<section id="encoderSettings">
<div class="settings-text">Encoder Settings</div>
<div id="encoderParamsContainer" class="collapse">
<div class="form-group">
<label for="encoder-rate-control" class="settings-text">Rate Control</label>
<select id="encoder-rate-control">
<option value="CBR" selected>CBR</option>
<option value="VBR">VBR</option>
<option value="ConstQP">ConstQP</option>
</select>
<label for="encoder-target-bitrate-text">Target Bitrate (kbps)</label>
<input type="number" class="form-control" id="encoder-target-bitrate-text" value="0" min="0"
max="100000" />
<label for="encoder-max-bitrate-text">Max Bitrate (kbps)</label>
<input type="number" class="form-control" id="encoder-max-bitrate-text" value="0" min="0"
max="100000" />
<label for="encoder-min-qp-text">Min QP</label>
<input type="number" class="form-control" id="encoder-min-qp-text" value="0" min="0"
max="999" />
<label for="encoder-max-qp-text">Max QP</label>
<input type="number" class="form-control" id="encoder-max-qp-text" value="0" min="0"
max="999" />
<label for="encoder-multipass" class="settings-text">Multipass</label>
<select id="encoder-multipass">
<option value="DISABLED" selected>DISABLED</option>
<option value="QUARTER">QUARTER</option>
<option value="FULL">FULL</option>
</select>
<div class="settings-text">Filler Data</div>
<label class="tgl-switch">
<input type="checkbox" id="encoder-filler-data-tgl" class="tgl tgl-flat">
<div class="tgl-slider"></div>
</label>
</div>
<input id="encoder-params-submit" class="btn btn-primary btn-lg mt-3" type="button"
value="Apply">
</div>
<br>
</section>
<!-- webRTC 設置 -->
<section id="webRTCSettings">
<div class="settings-text">WebRTC Settings</div>
<div id="webrtcParamsContainer" class="collapse">
<div class="form-group">
<label for="webrtc-degradation-pref">Degradation Pref</label>
<select id="webrtc-degradation-pref">
<option value="BALANCED">BALANCED</option>
<option value="MAINTAIN_FRAMERATE">MAINTAIN_FRAMERATE</option>
<option value="MAINTAIN_RESOLUTION">MAINTAIN_RESOLUTION</option>
</select>
<label for="webrtc-max-fps-text">Max FPS</label>
<input type="number" class="form-control" id="webrtc-max-fps-text" value="1" min="1"
max="999" />
<label for="webrtc-min-bitrate-text">Min Bitrate (kbps)</label>
<input type="number" class="form-control" id="webrtc-min-bitrate-text" value="0" min="0"
max="100000" />
<label for="webrtc-max-bitrate-text">Max Bitrate (kbps)</label>
<input type="number" class="form-control" id="webrtc-max-bitrate-text" value="0" min="0"
max="100000" />
<label for="webrtc-low-qp-text">Low QP Threshold</label>
<input type="number" class="form-control" id="webrtc-low-qp-text" value="0" min="0"
max="999" />
<label for="webrtc-high-qp-text">High QP Threshold</label>
<input type="number" class="form-control" id="webrtc-high-qp-text" value="0" min="0"
max="999" />
</div>
<input id="webrtc-params-submit" class="btn btn-primary btn-lg mt-3" type="button"
value="Apply">
</div>
</section>
<br>
<!-- 顯示FPS -->
<div id="showFPS">
<div class="settings-text">Show FPS</div>
<label class="btn-overlay">
<input type="button" id="show-fps-button" class="overlay-button btn-flat" value="Toggle">
</label>
</div>
<div id="matchViewportResolution">
<div class="settings-text">Match Viewport Resolution</div>
<label class="tgl-switch">
<input type="checkbox" id="match-viewport-res-tgl" class="tgl tgl-flat">
<div class="tgl-slider"></div>
</label>
</div>
<div id="statsPanel">
<div class="settings-text">Show Stats</div>
<label class="tgl-switch">
<!-- <input type="checkbox" id="show-stats-tgl" class="tgl tgl-flat" checked> -->
<input type="checkbox" id="show-stats-tgl" class="tgl tgl-flat">
<div class="tgl-slider"></div>
</label>
<div id="statsContainer" class="statsContainer">
<div id="stats" class="stats"></div>
</div>
<br>
</div>
<div id="latencyTest">
<div class="settings-text">Latency Stats</div>
<label class="btn-overlay">
<input type="button" id="test-latency-button" class="overlay-button btn-flat"
value="Test Latency">
</label>
<div id="latencyStatsContainer" class="statsContainer">
<div id=LatencyStats class="stats">No stats yet...</div>
</div>
</div>
</div>
</div>
</div>
</body>
<script>
// 發送到ue
function api_send(type, action, param, callback) {
let data = { type, action, param };
console.log('data ----------->', data);
// 發送
emitUIInteraction(data);
// 註冊ue response 監聽函數
addResponseEventListener("handle_responses", callback);
}
// 發送ue消息-天氣切換
function ueSend(type, action, param) {
console.log(`type : ${type}, action : ${action}, param : ${param}`);
api_send(type, action, param, (info) => {
console.log(info);
});
}
// 發送ue消息-設置時間
function ueSend2() {
const setTime = document.getElementById('setTime').value;
console.log(`setTime ---------------->, ${setTime}`);
api_send('2', '', setTime, (info) => {
console.log(info);
});
}
// 發送ue消息-切換樓層
function ueSend3(type, action, param) {
console.log(`type : ${type}, action : ${action}, param : ${param}`);
api_send(type, action, param, (info) => {
console.log(info);
});
}
// 重置分辨率
function setRes(width, height) {
const param = 'r.' + 'setRes ' + width + 'x' + height + 'w'
console.log('setRes ->>>>>>>>', param);
api_send('4', '', param, (info) => {
console.log(info);
});
}
</script>
</html>
創建基礎項目
這裏我們隨便創建了建築可視化項目
ue 的像素流插件
必須先啓用像素流插件
設置獨立進程啓動參數
設置 編輯器偏好設置 -> 關卡編輯器 ->播放 -> 額外啓動參數
-AudioMixer -PixelStreamingIP=localhost -PixelStreamingPort=8888 -forceres -ResX=3840 -ResY=2160
上面的意思是 像素流送 強制 以4K 運行
切換分辨率實現
其實切換分辨率 主要是ue 控制檯使用
r.setRes 1920x1080w
命令實現,上面例子 表示切換到 1080P,所以接下來就是要接收h5 傳過來的消息,並且執行
json結構
假設前端傳過來的,消息結構如下
{
"type":"4", // 4 - 表示分辨率切換
"param": "r.setRes 1920x1080w" // 分辨率命令 和 參數
}
ue 基礎藍圖功能
新建遊戲基礎模式
GameBaseModel
遊戲模式重載
添加像素流組件
雙擊打開的遊戲模式
添加像素流輸入事件
添加基本的藍圖功能
下面的藍圖功能很簡單就是:
接收前端傳過來的數據,並且 加上 ue response:+ 前端傳過來的數據 返回
我們先測試鏈路通不通
運行測試
可以看到 已經有ue 的返回了
ue 切換分辨率
在上面我們已經實現了,像素流的接收和返回,保證鏈路沒問題,接下來我們就按照json 結構執行切換分辨率的命令
藍圖如下
運行測試
可以看到480P 畫質 有點糊
再看 4K畫質
應該能看出明顯的區別
需要注意的地方
啓動參數
關於虛幻引擎啓動參數,也就是快捷方式啓動參數
4.27 版本官網這塊沒有,但是5.0有,也可以用
參考:
https://docs.unrealengine.com/5.0/zh-CN/unreal-engine-pixel-streaming-reference/
-forceres -ResX=3840 -ResY=2160
需要加上這個,如果不加,使用
r.setRes
是改變不了分辨率的,只會改變獨立進程的窗口大小,在我理解 ,可能像素流插件默認有一個推送分辨率,只會啓動的時候設置,但是如果啓動參數加上
-forceres -ResX=3840 -ResY=2160
這個參數,就會以4K 分辨率推流
r.setRes
則可以修改,接收的分辨率,兩個可以配合使用,缺一不可