Fluxo de renderização do Unity
Diretório de artigos
- Fluxo de renderização do Unity
- prefácio
- Mostrar resultados
- cena aberta
- seleção de versão
- servidor web
- Configurações do projeto do Unity
- interagir
- Implantação de servidor extranet
- Servidor Web Personalizado
- Web e Unity enviam mensagens personalizadas entre si
- Como os projetos corporativos são aplicados
-
- 1. A escolha entre acesso à rede externa claro e suave
- 2. Botão Web para substituir o botão de operação do Unity
- 3. Sincronização de resolução, consulte o método de sincronização de resolução em iOS e Android abaixo
- 4. Precauções ao acessar a tela vertical padrão no celular e exibir a tela horizontal
- 5. Detecte se o lado do Unity está desconectado do serviço
- 6. Obtenha se há uma conexão de usuário bem-sucedida
- Como fazer iOS e Android
- plano de atualização
prefácio
Link original
UnityRenderStreaming documento oficial
Unity: 2021.3.8f1c1
RenderStreaming: 3.1.0-exp.4 (Pré-lançamento)
RenderStreaming WebServer: 3.1.0-exp.3
Mostrar resultados
Demonstração de vídeo de rede externa
cena aberta
Use o modelo de amostra HDRP como um cenário de teste
seleção de versão
Este RenderStreamingPackage usa3.1.0-exp.4, e não há versão oficial, mas exp.4 abriu codificação de fluxo de vídeo, taxa de quadros, taxa de bits, taxa de zoom e outras configurações em comparação com a versão exp.3, e também é muito estável em uso, por isso é conveniente para ajustar de acordo com suas próprias necessidades de projeto.
WebServer usa3.1.0-exp.3, devido à versão exp.4 do servidor, há um problema com a verificação do caminho de acesso à página da web.
Endereço Git: UnityRenderStreaming
servidor web
1. Download do servidor, instalação do Node.js
Escolha o servidor de acordo com sua própria plataforma, você precisa instalar o Node.js para baixar
e usar node -v
npm -v
para verificar se a instalação foi bem-sucedida
2. Início do servidor
O método de inicialização do Windows é o seguinte (consulte a documentação oficial para outros métodos de inicialização da plataforma )
: Encontre webserver.exe, digite cmd na barra de endereços e pressione Enter
Entrada .\webserver.exe -w
, comece com WebSocket
Abra o navegador, digite o IP local, a figura a seguir é o conteúdo após iniciar com WebSocket
Configurações do projeto do Unity
1. Instale o Render Streaming
Depois que o código-fonte for descompactado, copie com.unity.renderstreaming
o arquivo e coloque-o nos Pacotes do projeto de amostra e crie uma nova pasta. (Guarde de acordo com sua preferência)
Abra o PackageManager
e clique em + , selecione Add package from disk
para encontrar o arquivo agora package.json
, abra-o
e abra-o da seguinte maneira
2. Instale o WebRTC
Selecione Add package by name
a entrada com.unity.webrtc
, clique em Adicionar
para instalar da seguinte maneira:
3. Configurações de renderização de streaming
Crie um objeto vazio, nomeie-o como RenderStreaming
e adicione RenderStreaming
, Broadcast
e VideoStreamSender
componentes.
Modifique os parâmetros da seguinte maneira:
4. Transmissão de áudio (adicione de acordo com suas necessidades)
Adicione AudioStreamSender
o componente acima AudioListener
, este componente deve estar em um
Broadcast
componente de objeto lembre-se de adicionar AudioStreamSender
o componente
5. O Unity inicia o teste
O efeito da seleção é o seguinte, você pode ajustar os parâmetros ReceiverSample
de acordo com suas próprias necessidadesVideoStreamSender
interagir
1. Configurações básicas do projeto interativo
Instale o InputSystem
e algumas das configurações a seguir destinam-se aUnity2021.2Versão acima, link de referência de versão inferior
(o InputSystem foi usado por padrão neste exemplo de HDRP)
Após a janela pop-up aparecer, selecioneSim, concorde em usar o novo sistema de entrada ou selecione novo ou ambos executados em segundo plano
em ProjectSettings/Player/OtherSettings/ActiveInputHanding para abrir (2021.2) modificação de configuração do pacote do sistema de entrada (2021.2)
ProjectSettings/Player/Resolution and Presentation/Run in background
2. Configurações de interação de cena
Referência de documentação oficial
Adicionar InputReceiver
componentes e adicionar Broadcast
a
3. Interação de teclado e mouse
Abrir depois de criar InputActions, operação: Create/InputActions
Configurar ações de acordo com os requisitos do seu projeto
Arraste o arquivo InputActions configurado para ele InputReceiver/Actions
, expanda Eventos, você pode ver os eventos correspondentes às Ações configuradas e vinculá-los de acordo com suas necessidades
4. Interação da interface do usuário
EventSystem usa InputSystemUIInputModule
a resolução de exibição para ser consistente VideoStreamSender
com isso StreamingSize
, caso contrário, o ponto de reconhecimento será deslocado
Implantação de servidor extranet
Para a maioria dos aplicativos WebRTC, o servidor precisa retransmitir o tráfego entre os pares, pois os soquetes diretos geralmente não são possíveis entre os clientes (a menos que os aplicativos estejam na mesma rede local). Uma solução comum para esse problema é usar um servidor TURN . O termo denota passagem usando relé NAT, um protocolo para retransmitir o tráfego de rede.
Consulte as configurações do servidor TURN na documentação do Unity
A porta utilizada pelo servidor TURN precisa ser pública, podendo ser configurados os valores máximo e mínimo
protocolo | porta |
---|---|
TCP | 32355-65535, 3478-3479 |
UDP | 32355-65535, 3478-3479 |
config.js
Altere o arquivo no lado da webconfig.iceServers
config.iceServers = [{
urls: ['stun:stun.l.google.com:19302']
}, {
urls: ['turn:xx.xx.xx.xx:3478?transport=tcp'],
username: 'username',
credential: 'password'
}
];
Modificando RenderStreaming
componentes no UnityIce Server
Servidor Web Personalizado
Veja a seguir como criar seu próprio WebServer, consulte a documentação oficial para obter mais detalhes
1. Baixe o código-fonte do exp.3
2. Encontre o WebApp após a descompactação 3.
Digite cmd na barra de endereços
4. Insira npm install
ou npm i
instale dependências
Se o a instalação é muito lenta ou falha devido ao tempo limite, você pode inseri-la primeiro
npm config set registry https://registry.npm.taobao.org
para substituir a fonte da imagem de instalação npm por uma doméstica.
Se ocorrerem os seguintes erros, você pode inseri-la npm config set legacy-peer-deps true
e, em seguida, inseri-la novamente npm install
ou npm i
instalar dependências**
5. Build Server, digite npm run build
6. Start Server, digitenpm run start -- -w
Se a inicialização for anormal, conforme mostrado na figura abaixo, use npm run dev -- -w
Iniciar
para iniciar da seguinte maneira. Não está claro por que a situação acima ocorre. Se você souber o motivo, informe eu, obrigado.
7. Crie um atalho de inicialização.
Para facilitar a inicialização rápida no futuro, você pode criar os seguintes arquivos.
8. Empacote,npm run pack
aguarde o progresso ser concluído e empacote-o em um
método de inicialização exe conforme acima.\webserver.exe -w
9. Modifique arquivos da web de acordo com suas necessidades
Web e Unity enviam mensagens personalizadas entre si
WebRTC
A função em RTCDataChannel
uso para enviar uma mensagem Send
, use a função para receber uma mensagemOnMessage
1. O Unity envia uma mensagem para a Web
O script criado no projeto Unity é o seguinte, utilizando os objetos inputReceiver
doChannel
Send
using Unity.RenderStreaming;
using UnityEngine;
public class RenderStreamingManager : MonoBehaviour
{
private InputReceiver inputReceiver;
private void Awake()
{
inputReceiver = transform.GetComponent<InputReceiver>();
}
public void SendMsg(string msg)
{
inputReceiver.Channel.Send(msg);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.P))
{
SendMsg("Send Msg to web");
}
}
}
Lembre-se de adicioná-lo ao projeto
Web para receber da seguinte maneira
Encontre o objeto receiver.js
no arquivo e use as informações recebidas O conteúdo da mensagem recebida é o seguinte, a string estará em dadosinputSenderChannel
onmessage
2. A web envia uma mensagem para Unity
Use a função enviar para enviar mensagens na web
this.inputSenderChannel.send("msg to unity");
No OnMessage no Unity Receiver.cs
, a mensagem enviada pela web é recebida por meio do RTCDataChannel
Como os projetos corporativos são aplicados
1. A escolha entre acesso à rede externa claro e suave
Sob a premissa de que a largura de banda do servidor permite, melhore ao máximo a qualidade da transmissão, conforme segue, quais são minhas configurações no projeto
Você pode ajustá-lo de acordo com sua própria largura de banda DepathBuffer,StreamingSize,Framerate,Bitrate,ScaleResolution
.
Você também pode obter o componente VideoStreamSender e modificar os parâmetros correspondentes durante o tempo de execução para facilitar o teste pós-lançamento.
VideoStreamSender videoStreamSender = transform.GetComponent<VideoStreamSender>();
videoStreamSender.SetTextureSize(new Vector2Int(gameConfig.RSResolutionWidth,gameConfig.RSResolutionHeight));
videoStreamSender.SetFrameRate(gameConfig.RSFrameRate);
videoStreamSender.SetScaleResolutionDown(gameConfig.RSScale);
videoStreamSender.SetBitrate((uint)gameConfig.RSMinBitRate,(uint)gameConfig.RSMaxBitRate);
2. Botão Web para substituir o botão de operação do Unity
Devido à limitação atual das alterações de resolução, os botões do Unity muitas vezes não podem ser clicados corretamente, então os alunos da Web criam os botões correspondentes e enviam mensagens para o Unity pela Web para responder às operações dos botões correspondentes.
Meu lado é configurar o id para todos os botões, a web envia o id do botão, o Unity percorre a tabela de configuração após recebê-lo e executa o Invoke
private void OnClickButton(int elementId)
{
// 在配置表中根据id,获取UIName,FunctionName,Para
List<UIMatchFuncModel> uiMatchFuncModels = UniversalConfig.Instance.GetUIMatchModels();
foreach (var item in uiMatchFuncModels.Where(item => item.id == elementId))
{
ExecuteFunction(item);
break;
}
}
private void ExecuteFunction(UIMatchFuncModel matchFuncModel)
{
UIPanel uiPanel = UIKit.GetPanel(matchFuncModel.uiName);
if (uiPanel == null)
{
uiPanel = UIKit.OpenPanel(matchFuncModel.uiName);
}
uiPanel.Invoke(matchFuncModel.functionName,0);
}
3. Sincronização de resolução, consulte o método de sincronização de resolução em iOS e Android abaixo
Combine diferentes resoluções de dispositivos, obtenha a resolução atual do dispositivo por meio da Web e envie uma mensagem de largura e altura do dispositivo para o Unity após a conexão ser bem-sucedida e redefina a resolução de saída no Unity para manter o dispositivo da Web totalmente exibido.
public void ChangeScreenSize(int width,int height)
{
videoStreamSender.SetTextureSize(new Vector2Int(width,height));
}
4. Precauções ao acessar a tela vertical padrão no celular e exibir a tela horizontal
Como a tela de exibição da tela vertical no telefone celular é muito pequena, geralmente exibe o conteúdo da tela horizontal no estado da tela vertical. Portanto:
1. Ao definir a resolução mencionada no ponto anterior, você precisa prestar atenção se o dispositivo do lado web é uma tela horizontal ou uma tela vertical
2. Projeto Se houver uma operação de deslizamento em , é preciso ficar atento para inverter a operação de acordo com as telas horizontal e vertical.
5. Detecte se o lado do Unity está desconectado do serviço
No projeto, o serviço será desconectado a cada meia hora a uma hora, então a detecção de pulsação é adicionada e a operação de reconexão é a
seguinte, ISignaling
adicione emOnHeartBeatHandler
using Unity.WebRTC;
namespace Unity.RenderStreaming.Signaling
{
public delegate void OnStartHandler(ISignaling signaling);
public delegate void OnConnectHandler(ISignaling signaling, string connectionId, bool polite);
public delegate void OnDisconnectHandler(ISignaling signaling, string connectionId);
public delegate void OnOfferHandler(ISignaling signaling, DescData e);
public delegate void OnAnswerHandler(ISignaling signaling, DescData e);
public delegate void OnIceCandidateHandler(ISignaling signaling, CandidateData e);
// add
public delegate void OnHeartBeatHandler(ISignaling signaling);
public interface ISignaling
{
void Start();
void Stop();
event OnStartHandler OnStart;
event OnConnectHandler OnCreateConnection;
event OnDisconnectHandler OnDestroyConnection;
event OnOfferHandler OnOffer;
event OnAnswerHandler OnAnswer;
event OnIceCandidateHandler OnIceCandidate;
// add
event OnHeartBeatHandler OnHeartBeat;
string Url {
get; }
float Interval {
get; }
void OpenConnection(string connectionId);
void CloseConnection(string connectionId);
void SendOffer(string connectionId, RTCSessionDescription offer);
void SendAnswer(string connectionId, RTCSessionDescription answer);
void SendCandidate(string connectionId, RTCIceCandidate candidate);
// add
void SendHeartBeat();
}
}
Modifique
a função SendHeartBeat no relatório de erros WebSocketSignaling e adicione o seguinte conteúdo
public void SendHeartBeat()
{
this.WSSend($"{
{\"type\":\"heart\"}}");
}
Crie RenderStreamingManager
um script, o código é o seguinte
using System.Collections;
using System.Threading;
using Unity.RenderStreaming;
using Unity.RenderStreaming.Signaling;
using UnityEngine;
public class RenderStreamingManager : MonoBehaviour
{
private RenderStreaming _renderStreaming;
private VideoStreamSender _videoStreamSender;
private ISignaling _signaling;
private InputReceiver _inputReceiver;
// heartbeat check
private Coroutine _heartBeatCoroutine;
private bool _isReceiveHeart;
private float _heartBeatInterval;
private void Awake()
{
// websocket
_signaling = new WebSocketSignaling($"{
"ws"}://{
"192.0.0.0"}", 5, SynchronizationContext.Current);
if (_signaling != null)
{
_renderStreaming = transform.GetComponent<RenderStreaming>();
_videoStreamSender = transform.GetComponent<VideoStreamSender>();
_inputReceiver = transform.GetComponent<InputReceiver>();
_renderStreaming.Run(_signaling);
// heart beat
_signaling.OnStart += OnWebSocketStart;
_signaling.OnHeartBeat += OnHeatBeat;
}
}
#region HeartBeat
private void OnWebSocketStart(ISignaling signaling)
{
StartHeartCheck();
}
private void OnHeatBeat(ISignaling signaling)
{
_isReceiveHeart = true;
}
private void StartHeartCheck()
{
_isReceiveHeart = false;
_signaling.SendHeartBeat();
_heartBeatCoroutine = StartCoroutine(HeartBeatCheck());
}
private IEnumerator HeartBeatCheck()
{
yield return new WaitForSeconds(5);
if (!_isReceiveHeart)
{
// not receive msg
Reconnect();
}
else
{
StartHeartCheck();
}
}
private void Reconnect()
{
StartCoroutine(ReconnectDelay());
}
private IEnumerator ReconnectDelay()
{
_signaling.Stop();
yield return new WaitForSeconds(1);
_signaling.Start();
}
#endregion
}
Lembre-se de fechar RenderStreaming
, localizar e RunOnAwake
WebServer
adicionar o seguinte conteúdo Reconstruir o serviço e iniciar o servidor, para que o Unity possa enviar e receber mensagens de heartbeat com o servidor por meio do WebSocketwebsocket.ts
websockethandler.ts
6. Obtenha se há uma conexão de usuário bem-sucedida
RenderStreamingManager
Vincular eventos em
private void Awake()
{
// connect & disconnect
_inputReceiver.OnStartedChannel += OnStartChannel;
_inputReceiver.OnStoppedChannel += OnStopChannel;
}
private void OnStartChannel(string connectionid)
{
}
private void OnStopChannel(string connectionid)
{
}
Como fazer iOS e Android
1. Consulte a cena do Receptor na Amostra
2. Personalize o conteúdo da cena do receptor
Crie um objeto como mostrado na figura, adicione RenderStreaming
SingleConnection
VideoStreamReceiver
InputSender
componentes
AudioStreamReceiver
relacionados ao som, adicione
um novo Canvas de acordo com suas necessidades, crie um RawImage e use-o como um suporte para receber
conteúdo relacionado ao áudio de textura, de acordo com a configuração na amostra , não precisa de conteúdo relacionado a som pode ser ignorado
3. Crie o script de gerenciamento
Obtenha componentes e eventos de ligação em Awake e
chame StartConnect para iniciar a conexão com o websocket. Após a conexão ser bem-sucedida, inicie a conexão webrtc e, em seguida, o evento OnUpdateReceiveTexture vinculado a VideoStreamReceiver atualiza a imagem em RawImage.
No OnStartedChannel, são adicionadas as informações de resolução do dispositivo atual, e a estrutura da mensagem pode ser personalizada.Eu criei uma ScreenMessage aqui, adicionei informações de largura e altura e enviei para o cliente.
O roteiro é o seguinte:
using System;
using System.Threading;
using Unity.RenderStreaming;
using Unity.RenderStreaming.Signaling;
using UnityEngine;
using UnityEngine.UI;
public class RenderStreamingReceiverManager : MonoSingleton<RenderStreamingReceiverManager>
{
private RenderStreaming _renderStreaming;
private VideoStreamReceiver _videoStreamReceiver;
private AudioStreamReceiver _audioStreamReceiver;
private InputSender _inputSender;
private SingleConnection _connection;
[SerializeField] private RawImage remoteVideoImage;
[SerializeField] private AudioSource remoteAudioSource;
private ISignaling _signaling;
private string _connectionId;
private void Awake()
{
// Get Component
_renderStreaming = transform.GetComponent<RenderStreaming>();
_videoStreamReceiver = transform.GetComponent<VideoStreamReceiver>();
_inputSender = transform.GetComponent<InputSender>();
_connection = transform.GetComponent<SingleConnection>();
// Connect DisConnect
_inputSender.OnStartedChannel += OnStartedChannel;
_inputSender.OnStoppedChannel += OnStopChannel;
_videoStreamReceiver.OnUpdateReceiveTexture += OnUpdateReceiveTexture;
_audioStreamReceiver = transform.GetComponent<AudioStreamReceiver>();
if (_audioStreamReceiver)
{
_audioStreamReceiver.OnUpdateReceiveAudioSource += source =>
{
source.loop = true;
source.Play();
};
}
}
// 外部调用Start
public void StartConnect()
{
_signaling = new WebSocketSignaling(
$"{
"ws"}://172.0.0.1:80",
5, SynchronizationContext.Current);
_signaling.OnStart += OnWebSocketStart;
_renderStreaming.Run(_signaling);
}
// 外部调用Stop
public void StopConnect()
{
StopRenderStreaming();
_signaling.OnStart -= OnWebSocketStart;
_renderStreaming.Stop();
}
private void OnWebSocketStart(ISignaling signaling)
{
StartRenderStreaming();
}
private void StartRenderStreaming()
{
if (string.IsNullOrEmpty(_connectionId))
{
_connectionId = Guid.NewGuid().ToString("N");
}
if (_audioStreamReceiver)
{
_audioStreamReceiver.targetAudioSource = remoteAudioSource;
}
_connection.CreateConnection(_connectionId);
}
private void StopRenderStreaming()
{
_connection.DeleteConnection(_connectionId);
_connectionId = String.Empty;
}
void OnUpdateReceiveTexture(Texture texture)
{
remoteVideoImage.texture = texture;
SetInputChange();
}
void OnStartedChannel(string connectionId)
{
Debug.Log("连接成功:" + connectionId);
// 发送当前设备的分辨率到Client
SendScreenToClient();
SetInputChange();
}
private void OnStopChannel(string connectionId)
{
Debug.Log("断开连接:" + connectionId);
}
void SetInputChange()
{
if (!_inputSender.IsConnected || remoteVideoImage.texture == null)
return;
// correct pointer position
Vector3[] corners = new Vector3[4];
remoteVideoImage.rectTransform.GetWorldCorners(corners);
Camera camera = remoteVideoImage.canvas.worldCamera;
var corner0 = RectTransformUtility.WorldToScreenPoint(camera, corners[0]);
var corner2 = RectTransformUtility.WorldToScreenPoint(camera, corners[2]);
var region = new Rect(
corner0.x,
corner0.y,
corner2.x - corner0.x,
corner2.y - corner0.y
);
var size = new Vector2Int(remoteVideoImage.texture.width, remoteVideoImage.texture.height);
_inputSender.SetInputRange(region, size);
_inputSender.EnableInputPositionCorrection(true);
}
private void SendScreenToClient()
{
ScreenMessage screenMessage = new ScreenMessage(MessageType.Screen, Screen.width, Screen.height);
string msg = JsonUtility.ToJson(screenMessage);
SendMsg(msg);
}
private void SendMsg(string msg)
{
_inputSender.Channel.Send(msg);
}
}
A mensagem define a estrutura, escreva de acordo com suas próprias preferências
public enum MessageType
{
Screen
}
public class RenderStreamingMessage
{
public MessageType MessageType;
}
public class ScreenMessage : RenderStreamingMessage
{
public int Width;
public int Height;
public ScreenMessage(MessageType messageType, int width, int height)
{
MessageType = messageType;
Width = width;
Height = height;
}
}
4. Processamento adaptativo de tela após o cliente receber a ScreenMessage
Depois de receber a mensagem, analise para obter a largura e a altura e calcule a resolução apropriada de acordo com a resolução do dispositivo móvel.
Use _videoStreamSender.SetTextureSize para modificar a resolução de saída.
Use _inputReceiver.SetInputRange e _inputReceiver.SetEnableInputPositionCorrection(true) para corrigir a resolução. A modificação da resolução no lado da web é consistente com isso.
private void OnMessage(byte[] bytes)
{
string msg = System.Text.Encoding.Default.GetString(bytes);
if (string.IsNullOrEmpty(msg) || !msg.Contains("MessageType"))
{
return;
}
RenderStreamingMessage messageObject = JsonUtility.FromJson<RenderStreamingMessage>(msg);
if (messageObject == null)
{
return;
}
switch (messageObject.MessageType)
{
case MessageType.Screen:
ScreenMessage screenMessage = JsonUtility.FromJson<ScreenMessage>(msg);
ResetOutputScreen(screenMessage);
break;
default:
break;
}
}
private void ResetOutputScreen(ScreenMessage screenMessage)
{
int width = screenMessage.Width;
int height = screenMessage.Height;
float targetWidth, targetHeight;
int configWidth = 1920;
int configHeight = 1080;
float configRate = (float) configWidth / configHeight;
float curRate = (float) width / height;
if (curRate > configRate)
{
targetWidth = configWidth;
targetHeight = targetWidth / curRate;
}
else
{
targetHeight = configHeight;
targetWidth = targetHeight * curRate;
}
_videoStreamSender.SetTextureSize(new Vector2Int((int) targetWidth, (int) targetHeight));
// 这一步是分辨率偏移修正的关键代码
_inputReceiver.SetInputRange(
new Vector2Int((int) _videoStreamSender.width, (int) _videoStreamSender.height),
new Rect(0, 0, Screen.width, Screen.height));
_inputReceiver.SetEnableInputPositionCorrection(true);
}
5. Configurações do projeto antes de publicar
Alterações conforme solicitado nos documentos do WebRTC
6. Teste de liberação
plano de atualização
1. Mac e iOS liberam compartilhamento de bugs