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