若望 發表於 2025-9-17 19:13:00

记录---Vue3对接UE,通过MQTT完成通讯

<h1 data-id="heading-0">🧑‍💻 写在开头</h1>
<p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<p>&nbsp;</p>
<h3 data-id="heading-1">概述</h3>
<blockquote>
<p>一个基于Vue3的实时视频流显示系统,主要用于连接和显示Unreal Engine (UE) 服务器的实时渲染内容。该页面集成了PixelStreaming技术和MQTT通信协议,提供了完整的视频流控制和交互功能。</p>
</blockquote>
<h4 data-id="heading-2">主要功能</h4>
<ul>
<li>实时视频流显示:连接UE服务器,显示实时渲染的3D场景</li>
<li>协议支持:支持PixelStreaming连接</li>
<li>MQTT通信:通过MQTT协议进行消息通信和控制</li>
<li>连接状态监控:实时显示UE和MQTT连接状态</li>
<li>错误处理与重连:自动处理连接错误并尝试重连</li>
</ul>
<h3 data-id="heading-3">快速开始</h3>
<h4 data-id="heading-4">安装依赖</h4>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">npm install @epicgames-ps/lib-pixelstreamingfrontend-ue5.3
npm install @epicgames-ps/lib-pixelstreamingfrontend-ui-ue5.3
npm install mqtt</pre>
</div>
<h4 data-id="heading-5">基本使用</h4>
<ol>
<li>确保UE服务器运行</li>
<li>确保MQTT服务器运行</li>
</ol>
<h3 data-id="heading-6">技术架构</h3>
<h4 data-id="heading-7">核心技术栈</h4>
<ul>
<li>前端框架: Vue 3 (Composition API)</li>
<li>视频流技术: Epic Games PixelStreaming</li>
<li>消息协议: MQTT</li>
</ul>
<h4 data-id="heading-8">连接方式</h4>
<ol>
<li>PixelStreaming链接</li>
<li>MQTT连接(消息通信)</li>
</ol>
<h3 data-id="heading-9">代码详情</h3>
<h4 data-id="heading-10">1. 页面结构</h4>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">&lt;template&gt;链接链接
&lt;div class="scene-display-container"&gt;
    &lt;!-- 连接状态 --&gt;
    &lt;div class="connection-status"&gt;
      &lt;span&gt;UE: {{ ueStatus }}&lt;/span&gt;
      &lt;span&gt;MQTT: {{ mqttStatus }}&lt;/span&gt;
    &lt;/div&gt;

    &lt;!-- 视频流容器 --&gt;
    &lt;div class="video-container"&gt;
      &lt;div id="streamingVideo" class="streaming-video"&gt;&lt;/div&gt;

      &lt;!-- 错误状态 --&gt;
      &lt;div v-if="errorMessage" class="error-overlay"&gt;
      &lt;p&gt;{{ errorMessage }}&lt;/p&gt;
      &lt;button @click="reconnect"&gt;重新连接&lt;/button&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;/template&gt;</pre>
</div>
<p>说明:</p>
<ul>
<li><code>connection-status</code>:显示UE和MQTT的连接状态</li>
<li><code>video-container</code>:视频流显示容器</li>
<li><code>error-overlay</code>:错误状态覆盖层,提供重新连接功能</li>
</ul>
<h4 data-id="heading-11">2. 响应式数据</h4>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// 响应式数据
const errorMessage = ref('') // 存储错误信息
const ueStatus = ref('未连接') // UE连接状态
const mqttStatus = ref('未连接') // MQTT连接状态

// 实例
let pixelStreaming = null // PixelStreaming实例
let mqttClient = null // MQTT客户端实例</pre>
</div>
<h4 data-id="heading-12">3. UE连接初始化</h4>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// 初始化UE连接
const initPixelStreaming = () =&gt; {
try {
    const style = new PixelStreamingApplicationStyle();
    style.applyStyleSheet();
    Logger.SetLoggerVerbosity(1);

    const config = new Config({ useUrlParams: false });
    config.setFlagEnabled(Flags.AutoPlayVideo, true);
    config.setFlagEnabled(Flags.AutoConnect, true);
    config.setTextSetting(TextParameters.SignallingServerUrl, "此处输入UE服务器地址");
    config.setFlagEnabled(Flags.HoveringMouseMode, ControlSchemeType.HoveringMouse);
    config.setFlagEnabled(Flags.MouseInput, true);
    config.setFlagEnabled(Flags.KeyboardInput, true);
    config.setFlagEnabled(Flags.TouchInput, true);
    config.setFlagEnabled(Flags.GamepadInput, false);
    config.setFlagEnabled(Flags.XRControllerInput, false);

    pixelStreaming = new PixelStreaming(config);

    const application = new Application({
      stream: pixelStreaming,
      settingsPanelConfig: { isEnabled: false, visibilityButtonConfig: { creationMode: UIElementCreationMode.Disable } },
      statsPanelConfig: { isEnabled: false, visibilityButtonConfig: { creationMode: UIElementCreationMode.Disable } },
      fullScreenControlsConfig: { creationMode: UIElementCreationMode.Disable },
      xrControlsConfig: { creationMode: UIElementCreationMode.Disable },
      videoQpIndicatorConfig: { disableIndicator: true },
    });

    const container = document.getElementById("streamingVideo");
    if (container) {
      container.innerHTML = '';
      container.appendChild(application.rootElement);
    }

    setupPixelStreamingEvents();
} catch (error) {
    errorMessage.value = `UE连接失败: ${error}`;
}
}</pre>
</div>
<p>配置说明:</p>
<ul>
<li><code>AutoPlayVideo: true</code>:自动播放视频</li>
<li><code>AutoConnect: true</code>:自动连接</li>
<li><code>SignallingServerUrl</code>:信令服务器地址</li>
<li><code>HoveringMouseMode</code>:鼠标悬停模式</li>
<li>各种输入控制:鼠标、键盘、触屏等</li>
</ul>
<h4 data-id="heading-13">4. 事件监听器设置</h4>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// 设置PixelStreaming事件监听器
const setupPixelStreamingEvents = () =&gt; {
if (!pixelStreaming) return

pixelStreaming.addEventListener('streamReady', () =&gt; {
    ueStatus.value = '已连接'
    errorMessage.value = ''
})

pixelStreaming.addEventListener('connectionEstablished', () =&gt; {
    ueStatus.value = '已连接'
    errorMessage.value = ''
})

pixelStreaming.addEventListener('connectionClose', () =&gt; {
    ueStatus.value = '连接断开'
})

pixelStreaming.addEventListener('connectionError', (error) =&gt; {
    ueStatus.value = '连接失败'
    errorMessage.value = `UE连接错误: ${error}`
})

pixelStreaming.addEventListener('webRTCConnected', () =&gt; {
    ueStatus.value = '已连接'
})

pixelStreaming.addEventListener('webRTCDisconnected', () =&gt; {
    ueStatus.value = '连接断开'
})
}</pre>
</div>
<p>事件说明:</p>
<ul>
<li><code>streamReady</code>:视频流准备就绪</li>
<li><code>connectionEstablished</code>:连接建立成功</li>
<li><code>connectionClose</code>:连接关闭</li>
<li><code>connectionError</code>:连接错误</li>
<li><code>webRTCConnected</code>:WebRTC连接成功</li>
<li><code>webRTCDisconnected</code>:WebRTC连接断开</li>
</ul>
<h4 data-id="heading-14">5. MQTT连接初始化</h4>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// 初始化MQTT连接
const initMqtt = () =&gt; {
try {
    const options = {
      username: 'admin',
      password: 'public',
      clientId: `vue_client_${Math.random().toString(16).substr(2, 8)}`,
      clean: true,
      reconnectPeriod: 5000,
      connectTimeout: 30 * 1000,
      keepalive: 60,
      protocolVersion: 4
    }

    mqttClient = mqtt.connect('此处输入MQTT地址', options) // 地址末尾需要加:/mqtt

    mqttClient.on('connect', () =&gt; {
      mqttStatus.value = '已连接'
      mqttClient.subscribe('此处输入订阅主题的路径')
    })

    mqttClient.on('error', (error) =&gt; {
      mqttStatus.value = '连接失败'
    })

    mqttClient.on('close', () =&gt; {
      mqttStatus.value = '连接断开'
    })

    mqttClient.on('message', (topic, message) =&gt; {
      // 处理MQTT消息
      console.log(`收到MQTT消息 [${topic}]: ${message.toString()}`)
    })
} catch (error) {
    mqttStatus.value = '初始化失败'
}
}</pre>
</div>
<p>MQTT配置说明:</p>
<ul>
<li><code>username/password</code>:MQTT认证信息</li>
<li><code>clientId</code>:客户端唯一标识</li>
<li><code>reconnectPeriod</code>:重连间隔(5秒)</li>
<li><code>connectTimeout</code>:连接超时(30秒)</li>
<li><code>keepalive</code>:心跳间隔(60秒)</li>
</ul>
<h4 data-id="heading-15">6. 重新连接功能</h4>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">const reconnect = () =&gt; {
errorMessage.value = ''

if (pixelStreaming) {
    try {
      pixelStreaming.close()
    } catch (error) {
      console.warn('PixelStreaming断开连接时出错:', error)
    }
    pixelStreaming = null
}

if (mqttClient) {
    mqttClient.end()
    mqttClient = null
}

ueStatus.value = '未连接'
mqttStatus.value = '未连接'

setTimeout(() =&gt; {
    initPixelStreaming()
    initMqtt()
}, 1000)
}</pre>
</div>
<p>重连流程:</p>
<ol>
<li>清空错误信息</li>
<li>关闭现有连接</li>
<li>重置状态</li>
<li>延迟1秒后重新初始化</li>
</ol>
<h4 data-id="heading-16">7. 生命周期管理</h4>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// 生命周期
onMounted(() =&gt; {
initPixelStreaming()
initMqtt()
})

onUnmounted(() =&gt; {
if (pixelStreaming) {
    try {
      pixelStreaming.close()
    } catch (error) {
      console.warn('PixelStreaming清理时出错:', error)
    }
}

if (mqttClient) {
    mqttClient.end()
}
})</pre>
</div>
<p>说明:</p>
<ul>
<li><code>onMounted</code>:组件挂载时初始化连接</li>
<li><code>onUnmounted</code>:组件卸载时清理连接</li>
</ul>
<h4 data-id="heading-17">常见问题及解决方案</h4>
<h5 data-id="heading-18">1. 连接超时问题</h5>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// 检查网络连接
const checkNetworkConnection = () =&gt; {
const testUrl = 'wss://192.168.110.176'
const testSocket = new WebSocket(testUrl)

testSocket.onopen = () =&gt; {
    console.log('网络连接正常')
    testSocket.close()
}

testSocket.onerror = (error) =&gt; {
    console.log(`网络连接失败: ${error}`)
    console.log('请检查服务器地址和网络连接')
}
}</pre>
</div>
<h5 data-id="heading-19">2. MQTT连接失败</h5>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// MQTT连接测试
const testMqttConnection = () =&gt; {
const testClient = mqtt.connect('ws://192.168.110.176:8083/mqtt', {
    username: 'admin',
    password: 'public',
    clientId: `test_client_${Date.now()}`,
    clean: true,
    connectTimeout: 5000
})

testClient.on('connect', () =&gt; {
    console.log('MQTT连接成功')
    testClient.end()
})

testClient.on('error', (error) =&gt; {
    console.log(`MQTT连接失败: ${error.message}`)
    console.log('请检查MQTT服务器状态和认证信息')
    testClient.end()
})
}</pre>
</div>
<h3 data-id="heading-20">自动重连机制</h3>
<h4 data-id="heading-21">WebSocket自动重连</h4>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">nativeWebSocket.onclose = (event) =&gt; {
console.log(`UE WebSocket连接已关闭 (代码: ${event.code})`)
isConnected.value = false
connectionStatus.type = 'danger'
connectionStatus.text = '连接断开'

// 自动重连
if (event.code !== 1000) { // 不是正常关闭
    console.log('5秒后尝试重新连接...')
    setTimeout(() =&gt; {
      if (!isConnected.value) {
      initNativeWebSocket()
      }
    }, 5000)
}
}</pre>
</div>
<h4 data-id="heading-22">MQTT自动重连</h4>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">const mqttOptions = {
reconnectPeriod: 5000, // 5秒重连间隔
connectTimeout: 30 * 1000, // 30秒连接超时
keepalive: 60 // 60秒心跳
}
</pre>
</div>
<p>  </p>
<div>
<h2>本文转载于:https://juejin.cn/post/7550571202013397043</h2>
</div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
<p><em><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></em></p><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/19097385
頁: [1]
查看完整版本: 记录---Vue3对接UE,通过MQTT完成通讯