websocket断链重连,不限制连接次数;tts合成语音,角色声音更换一种;socket,csn源路径修改;音频处理控件引入,是搭配Fay控制器传入音频的配置。
Showing
6 changed files
with
192 additions
and
19 deletions
| @@ -45,8 +45,6 @@ import asyncio | @@ -45,8 +45,6 @@ import asyncio | ||
| 45 | import torch | 45 | import torch |
| 46 | from typing import Dict | 46 | from typing import Dict |
| 47 | from logger import logger | 47 | from logger import logger |
| 48 | -from pydub import AudioSegment | ||
| 49 | -from io import BytesIO | ||
| 50 | 48 | ||
| 51 | 49 | ||
| 52 | app = Flask(__name__) | 50 | app = Flask(__name__) |
| @@ -153,7 +151,8 @@ async def human(request): | @@ -153,7 +151,8 @@ async def human(request): | ||
| 153 | {"code": 0, "data":"ok"} | 151 | {"code": 0, "data":"ok"} |
| 154 | ), | 152 | ), |
| 155 | ) | 153 | ) |
| 156 | - | 154 | +from pydub import AudioSegment |
| 155 | +from io import BytesIO | ||
| 157 | async def humanaudio(request): | 156 | async def humanaudio(request): |
| 158 | try: | 157 | try: |
| 159 | params = await request.json() | 158 | params = await request.json() |
| @@ -496,7 +495,6 @@ if __name__ == '__main__': | @@ -496,7 +495,6 @@ if __name__ == '__main__': | ||
| 496 | pagename='rtcpushapi.html' | 495 | pagename='rtcpushapi.html' |
| 497 | logger.info('start http server; http://<serverip>:'+str(opt.listenport)+'/'+pagename) | 496 | logger.info('start http server; http://<serverip>:'+str(opt.listenport)+'/'+pagename) |
| 498 | logger.info('如果使用webrtc,推荐访问webrtc集成前端: http://<serverip>:'+str(opt.listenport)+'/dashboard.html') | 497 | logger.info('如果使用webrtc,推荐访问webrtc集成前端: http://<serverip>:'+str(opt.listenport)+'/dashboard.html') |
| 499 | - | ||
| 500 | def run_server(runner): | 498 | def run_server(runner): |
| 501 | loop = asyncio.new_event_loop() | 499 | loop = asyncio.new_event_loop() |
| 502 | asyncio.set_event_loop(loop) | 500 | asyncio.set_event_loop(loop) |
| @@ -90,7 +90,7 @@ class BaseTTS: | @@ -90,7 +90,7 @@ class BaseTTS: | ||
| 90 | ########################################################################################### | 90 | ########################################################################################### |
| 91 | class EdgeTTS(BaseTTS): | 91 | class EdgeTTS(BaseTTS): |
| 92 | def txt_to_audio(self,msg): | 92 | def txt_to_audio(self,msg): |
| 93 | - voicename = "zh-CN-YunxiaNeural" | 93 | + voicename = "zh-CN-XiaoxiaoNeural" |
| 94 | text,textevent = msg | 94 | text,textevent = msg |
| 95 | t = time.time() | 95 | t = time.time() |
| 96 | asyncio.new_event_loop().run_until_complete(self.__main(voicename,text)) | 96 | asyncio.new_event_loop().run_until_complete(self.__main(voicename,text)) |
| @@ -284,6 +284,7 @@ | @@ -284,6 +284,7 @@ | ||
| 284 | <h1 class="text-center mb-4">livetalking数字人交互平台</h1> | 284 | <h1 class="text-center mb-4">livetalking数字人交互平台</h1> |
| 285 | </div> | 285 | </div> |
| 286 | </div> | 286 | </div> |
| 287 | + <input type="hidden" id="username" value="User"> | ||
| 287 | 288 | ||
| 288 | <div class="row"> | 289 | <div class="row"> |
| 289 | <!-- 视频区域 --> | 290 | <!-- 视频区域 --> |
| @@ -450,6 +451,154 @@ | @@ -450,6 +451,154 @@ | ||
| 450 | } | 451 | } |
| 451 | } | 452 | } |
| 452 | 453 | ||
| 454 | + var ws; | ||
| 455 | + var reconnectInterval = 5000; // 初始重连间隔为5秒 | ||
| 456 | + var reconnectAttempts = 0; | ||
| 457 | + var maxReconnectInterval = 60000; // 最大重连间隔为60秒 | ||
| 458 | + var isReconnecting = false; // 标记是否正在重连中 | ||
| 459 | + | ||
| 460 | + function generateUsername() { | ||
| 461 | + var username = 'User'; | ||
| 462 | + // + Math.floor(Math.random() * 10000) | ||
| 463 | + return username; | ||
| 464 | + } | ||
| 465 | + | ||
| 466 | + function setUsername() { | ||
| 467 | + var storedUsername = localStorage.getItem('username'); | ||
| 468 | + if (!storedUsername) { | ||
| 469 | + storedUsername = generateUsername(); | ||
| 470 | + localStorage.setItem('username', storedUsername); | ||
| 471 | + } | ||
| 472 | + $('#username').val(storedUsername); // Use the username as the session ID | ||
| 473 | + } | ||
| 474 | + | ||
| 475 | + setUsername(); | ||
| 476 | + function connectWebSocket() { | ||
| 477 | + var host = window.location.hostname; | ||
| 478 | + ws = new WebSocket("ws://127.0.0.1:10002"); | ||
| 479 | + ws.onopen = function() { | ||
| 480 | + console.log('Connected to WebSocket on port 10002'); | ||
| 481 | + reconnectAttempts = 0; // 重置重连次数 | ||
| 482 | + reconnectInterval = 5000; // 重置重连间隔 | ||
| 483 | + updateConnectionStatus('connected'); | ||
| 484 | + | ||
| 485 | + // 在连接成功后发送 {"Username": "User"} | ||
| 486 | + var loginMessage = JSON.stringify({ "Username": $('#username').val() }); | ||
| 487 | + ws.send(loginMessage); | ||
| 488 | + loginMessage = JSON.stringify({ "Output": "1" }); | ||
| 489 | + ws.send(loginMessage); | ||
| 490 | + }; | ||
| 491 | + | ||
| 492 | + | ||
| 493 | + function addMessage(text, type = "right") { | ||
| 494 | + const chatOverlay = document.getElementById("chatOverlay"); | ||
| 495 | + const messageDiv = document.createElement("div"); | ||
| 496 | + messageDiv.classList.add("message", type); | ||
| 497 | + | ||
| 498 | + const avatar = document.createElement("img"); | ||
| 499 | + avatar.classList.add("avatar"); | ||
| 500 | + avatar.src = type === "right" ? "images/avatar-right.png" : "images/avatar-left.png"; | ||
| 501 | + | ||
| 502 | + const textContainer = document.createElement("div"); | ||
| 503 | + textContainer.classList.add("text-container"); | ||
| 504 | + | ||
| 505 | + const textElement = document.createElement("div"); | ||
| 506 | + textElement.classList.add("text"); | ||
| 507 | + textElement.textContent = text; | ||
| 508 | + | ||
| 509 | + const timeElement = document.createElement("div"); | ||
| 510 | + timeElement.classList.add("time"); | ||
| 511 | + timeElement.textContent = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); | ||
| 512 | + | ||
| 513 | + textContainer.appendChild(textElement); | ||
| 514 | + textContainer.appendChild(timeElement); | ||
| 515 | + | ||
| 516 | + messageDiv.appendChild(avatar); | ||
| 517 | + messageDiv.appendChild(textContainer); | ||
| 518 | + | ||
| 519 | + chatOverlay.appendChild(messageDiv); | ||
| 520 | + | ||
| 521 | + // 自动滚动到底部 | ||
| 522 | + chatOverlay.scrollTop = chatOverlay.scrollHeight; | ||
| 523 | + } | ||
| 524 | + | ||
| 525 | + ws.onmessage = function(e) { | ||
| 526 | + console.log('WebSocket原始消息(dashboard.html):', e.data); | ||
| 527 | + var messageData = JSON.parse(e.data); | ||
| 528 | + | ||
| 529 | + if (messageData.Data && messageData.Data.Key) { | ||
| 530 | + if(messageData.Data.Key == "audio"){ | ||
| 531 | + var value = messageData.Data.HttpValue; | ||
| 532 | + console.log('Value:', value); | ||
| 533 | + fetch('/humanaudio', { | ||
| 534 | + body: JSON.stringify({ | ||
| 535 | + file_url: value, | ||
| 536 | + sessionid:parseInt(document.getElementById('sessionid').value), | ||
| 537 | + }), | ||
| 538 | + headers: { | ||
| 539 | + 'Content-Type': 'application/json' | ||
| 540 | + }, | ||
| 541 | + method: 'POST' | ||
| 542 | + }); | ||
| 543 | + }else if (messageData.Data.Key == "text") { | ||
| 544 | + var reply = messageData.Data.Value; | ||
| 545 | + addMessage(reply, "left"); | ||
| 546 | + } | ||
| 547 | + } | ||
| 548 | + | ||
| 549 | + }; | ||
| 550 | + | ||
| 551 | + ws.onclose = function(e) { | ||
| 552 | + console.log('WebSocket connection closed'); | ||
| 553 | + attemptReconnect(); | ||
| 554 | + }; | ||
| 555 | + | ||
| 556 | + ws.onerror = function(e) { | ||
| 557 | + console.error('WebSocket error:', e); | ||
| 558 | + ws.close(); // 关闭连接并尝试重连 | ||
| 559 | + }; | ||
| 560 | + } | ||
| 561 | + | ||
| 562 | + function attemptReconnect() { | ||
| 563 | + if (isReconnecting) return; // 防止多次重连 | ||
| 564 | + | ||
| 565 | + isReconnecting = true; | ||
| 566 | + reconnectAttempts++; | ||
| 567 | + | ||
| 568 | + // 使用指数退避算法计算下一次重连间隔 | ||
| 569 | + var currentInterval = Math.min(reconnectInterval * Math.pow(1.5, reconnectAttempts - 1), maxReconnectInterval); | ||
| 570 | + | ||
| 571 | + console.log('Attempting to reconnect... (Attempt ' + reconnectAttempts + ', 间隔: ' + currentInterval/1000 + '秒)'); | ||
| 572 | + updateConnectionStatus('connecting'); | ||
| 573 | + | ||
| 574 | + if(document.getElementById('is_open') && parseInt(document.getElementById('is_open').value) == 1){ | ||
| 575 | + stop() | ||
| 576 | + } | ||
| 577 | + | ||
| 578 | + setTimeout(function() { | ||
| 579 | + isReconnecting = false; | ||
| 580 | + connectWebSocket(); | ||
| 581 | + }, currentInterval); | ||
| 582 | + } | ||
| 583 | + | ||
| 584 | + // 初始化 WebSocket 连接 | ||
| 585 | + connectWebSocket(); | ||
| 586 | + | ||
| 587 | + // 添加页面可见性变化监听,当页面从隐藏变为可见时尝试重连 | ||
| 588 | + document.addEventListener('visibilitychange', function() { | ||
| 589 | + if (document.visibilityState === 'visible') { | ||
| 590 | + // 页面变为可见状态,检查WebSocket连接 | ||
| 591 | + if (!ws || ws.readyState === WebSocket.CLOSED || ws.readyState === WebSocket.CLOSING) { | ||
| 592 | + console.log('页面可见,检测到WebSocket未连接,尝试重连...'); | ||
| 593 | + updateConnectionStatus('connecting'); | ||
| 594 | + // 重置重连计数和间隔,立即尝试重连 | ||
| 595 | + reconnectAttempts = 0; | ||
| 596 | + reconnectInterval = 5000; | ||
| 597 | + connectWebSocket(); | ||
| 598 | + } | ||
| 599 | + } | ||
| 600 | + }); | ||
| 601 | + | ||
| 453 | // 添加聊天消息 | 602 | // 添加聊天消息 |
| 454 | function addChatMessage(message, type = 'user') { | 603 | function addChatMessage(message, type = 'user') { |
| 455 | const messagesContainer = $('#chat-messages'); | 604 | const messagesContainer = $('#chat-messages'); |
| @@ -50,7 +50,7 @@ | @@ -50,7 +50,7 @@ | ||
| 50 | <input type="text" id="audiotype" value="0"> | 50 | <input type="text" id="audiotype" value="0"> |
| 51 | 51 | ||
| 52 | <script src="client.js"></script> | 52 | <script src="client.js"></script> |
| 53 | -<script type="text/javascript" src="http://cdn.sockjs.org/sockjs-0.3.4.js"></script> | 53 | +<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/sockjs-client@1.5.1/dist/sockjs.min.js"></script> |
| 54 | <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script> | 54 | <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script> |
| 55 | </body> | 55 | </body> |
| 56 | <script type="text/javascript" charset="utf-8"> | 56 | <script type="text/javascript" charset="utf-8"> |
| @@ -3,6 +3,8 @@ | @@ -3,6 +3,8 @@ | ||
| 3 | <head> | 3 | <head> |
| 4 | <meta charset="UTF-8"/> | 4 | <meta charset="UTF-8"/> |
| 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| 6 | + <link rel="icon" href="favicon.ico" type="image/x-icon"> | ||
| 7 | + <link rel="shortcut icon" href="favicon.ico" type="image/x-icon"> | ||
| 6 | <title>WebRTC 数字人</title> | 8 | <title>WebRTC 数字人</title> |
| 7 | <style> | 9 | <style> |
| 8 | body { | 10 | body { |
| @@ -276,7 +278,7 @@ | @@ -276,7 +278,7 @@ | ||
| 276 | </div> | 278 | </div> |
| 277 | 279 | ||
| 278 | <script src="client.js"></script> | 280 | <script src="client.js"></script> |
| 279 | -<script type="text/javascript" src="http://cdn.sockjs.org/sockjs-0.3.4.js"></script> | 281 | +<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/sockjs-client@1.5.1/dist/sockjs.min.js"></script> |
| 280 | <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script> | 282 | <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script> |
| 281 | <script type="text/javascript" charset="utf-8"> | 283 | <script type="text/javascript" charset="utf-8"> |
| 282 | 284 | ||
| @@ -387,17 +389,20 @@ | @@ -387,17 +389,20 @@ | ||
| 387 | 389 | ||
| 388 | // WebSocket connection to Fay digital avatar (port 10002) | 390 | // WebSocket connection to Fay digital avatar (port 10002) |
| 389 | var ws; | 391 | var ws; |
| 390 | - var reconnectInterval = 5000; // 每5秒尝试重连一次 | ||
| 391 | - var maxReconnectAttempts = 10; // 最大重连次数 | 392 | + var reconnectInterval = 5000; // 初始重连间隔为5秒 |
| 392 | var reconnectAttempts = 0; | 393 | var reconnectAttempts = 0; |
| 394 | + var maxReconnectInterval = 60000; // 最大重连间隔为60秒 | ||
| 395 | + var isReconnecting = false; // 标记是否正在重连中 | ||
| 393 | 396 | ||
| 394 | function generateUsername() { | 397 | function generateUsername() { |
| 395 | - var username = 'User' + Math.floor(Math.random() * 10000); | 398 | + var username = 'User'; |
| 399 | + // + Math.floor(Math.random() * 10000) | ||
| 396 | return username; | 400 | return username; |
| 397 | } | 401 | } |
| 398 | 402 | ||
| 399 | function setUsername() { | 403 | function setUsername() { |
| 400 | var storedUsername = localStorage.getItem('username'); | 404 | var storedUsername = localStorage.getItem('username'); |
| 405 | + // console.log("当前存有的username:"+storedUsername); | ||
| 401 | if (!storedUsername) { | 406 | if (!storedUsername) { |
| 402 | storedUsername = generateUsername(); | 407 | storedUsername = generateUsername(); |
| 403 | localStorage.setItem('username', storedUsername); | 408 | localStorage.setItem('username', storedUsername); |
| @@ -411,7 +416,7 @@ | @@ -411,7 +416,7 @@ | ||
| 411 | ws.onopen = function() { | 416 | ws.onopen = function() { |
| 412 | console.log('Connected to WebSocket on port 10002'); | 417 | console.log('Connected to WebSocket on port 10002'); |
| 413 | reconnectAttempts = 0; // 重置重连次数 | 418 | reconnectAttempts = 0; // 重置重连次数 |
| 414 | - | 419 | + reconnectInterval = 5000; // 重置重连间隔 |
| 415 | // 在连接成功后发送 {"Username": "User"} | 420 | // 在连接成功后发送 {"Username": "User"} |
| 416 | var loginMessage = JSON.stringify({ "Username": $('#username').val() }); | 421 | var loginMessage = JSON.stringify({ "Username": $('#username').val() }); |
| 417 | ws.send(loginMessage); | 422 | ws.send(loginMessage); |
| @@ -453,6 +458,7 @@ | @@ -453,6 +458,7 @@ | ||
| 453 | } | 458 | } |
| 454 | 459 | ||
| 455 | ws.onmessage = function(e) { | 460 | ws.onmessage = function(e) { |
| 461 | + console.log('WebSocket原始消息(webrtcapi.html):', e.data); | ||
| 456 | var messageData = JSON.parse(e.data); | 462 | var messageData = JSON.parse(e.data); |
| 457 | 463 | ||
| 458 | if (messageData.Data && messageData.Data.Key) { | 464 | if (messageData.Data && messageData.Data.Key) { |
| @@ -489,22 +495,42 @@ | @@ -489,22 +495,42 @@ | ||
| 489 | } | 495 | } |
| 490 | 496 | ||
| 491 | function attemptReconnect() { | 497 | function attemptReconnect() { |
| 492 | - if (reconnectAttempts < maxReconnectAttempts) { | 498 | + if (isReconnecting) return; // 防止多次重连 |
| 499 | + | ||
| 500 | + isReconnecting = true; | ||
| 493 | reconnectAttempts++; | 501 | reconnectAttempts++; |
| 494 | - console.log('Attempting to reconnect... (Attempt ' + reconnectAttempts + ')'); | ||
| 495 | - if(parseInt(document.getElementById('is_open').value) == 1){ | 502 | + |
| 503 | + // 使用指数退避算法计算下一次重连间隔 | ||
| 504 | + var currentInterval = Math.min(reconnectInterval * Math.pow(1.5, reconnectAttempts - 1), maxReconnectInterval); | ||
| 505 | + | ||
| 506 | + console.log('Attempting to reconnect... (Attempt ' + reconnectAttempts + ', 间隔: ' + currentInterval/1000 + '秒)'); | ||
| 507 | + | ||
| 508 | + if(document.getElementById('is_open') && parseInt(document.getElementById('is_open').value) == 1){ | ||
| 496 | stop() | 509 | stop() |
| 497 | } | 510 | } |
| 511 | + | ||
| 498 | setTimeout(function() { | 512 | setTimeout(function() { |
| 513 | + isReconnecting = false; | ||
| 499 | connectWebSocket(); | 514 | connectWebSocket(); |
| 500 | - }, reconnectInterval); | ||
| 501 | - } else { | ||
| 502 | - console.error('Maximum reconnection attempts reached. Could not reconnect to WebSocket.'); | ||
| 503 | - } | 515 | + }, currentInterval); |
| 504 | } | 516 | } |
| 505 | 517 | ||
| 506 | // 初始化 WebSocket 连接 | 518 | // 初始化 WebSocket 连接 |
| 507 | connectWebSocket(); | 519 | connectWebSocket(); |
| 520 | + | ||
| 521 | + // 添加页面可见性变化监听,当页面从隐藏变为可见时尝试重连 | ||
| 522 | + document.addEventListener('visibilitychange', function() { | ||
| 523 | + if (document.visibilityState === 'visible') { | ||
| 524 | + // 页面变为可见状态,检查WebSocket连接 | ||
| 525 | + if (!ws || ws.readyState === WebSocket.CLOSED || ws.readyState === WebSocket.CLOSING) { | ||
| 526 | + console.log('页面可见,检测到WebSocket未连接,尝试重连...'); | ||
| 527 | + // 重置重连计数和间隔,立即尝试重连 | ||
| 528 | + reconnectAttempts = 0; | ||
| 529 | + reconnectInterval = 5000; | ||
| 530 | + connectWebSocket(); | ||
| 531 | + } | ||
| 532 | + } | ||
| 533 | + }); | ||
| 508 | }); | 534 | }); |
| 509 | </script> | 535 | </script> |
| 510 | </body> | 536 | </body> |
| @@ -51,7 +51,7 @@ | @@ -51,7 +51,7 @@ | ||
| 51 | </div> | 51 | </div> |
| 52 | 52 | ||
| 53 | <script src="client.js"></script> | 53 | <script src="client.js"></script> |
| 54 | -<script type="text/javascript" src="http://cdn.sockjs.org/sockjs-0.3.4.js"></script> | 54 | +<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/sockjs-client@1.5.1/dist/sockjs.min.js"></script> |
| 55 | <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script> | 55 | <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script> |
| 56 | </body> | 56 | </body> |
| 57 | <script type="text/javascript" charset="utf-8"> | 57 | <script type="text/javascript" charset="utf-8"> |
-
Please register or login to post a comment