Streaming de rendu de l'unité
Annuaire d'articles
- Streaming de rendu de l'unité
- avant-propos
- Montrer les résultats
- scène ouverte
- sélection des versions
- serveur Web
- Paramètres du projet Unity
- interagir
- Déploiement du serveur extranet
- Serveur Web personnalisé
- Web et Unity s'envoient des messages personnalisés
- Comment les projets d'entreprise sont appliqués
-
- 1. Le choix entre un accès réseau externe clair et fluide
- 2. Bouton Web pour remplacer le fonctionnement du bouton de Unity
- 3. Synchronisation de la résolution, reportez-vous à la méthode de synchronisation de la résolution dans iOS et Android ci-dessous
- 4. Précautions lors de l'accès à l'écran vertical par défaut sur le téléphone mobile et de l'affichage de l'écran horizontal
- 5. Détecter si le côté Unity est déconnecté du service
- 6. Vérifiez s'il existe une connexion utilisateur réussie
- Comment faire iOS et Android
- plan de mise à jour
avant-propos
Lien d'origine
Document officiel UnityRenderStreaming
Unity : 2021.3.8f1c1
RenderStreaming : 3.1.0-exp.4 (pré-version)
RenderStreaming WebServer : 3.1.0-exp.3
Montrer les résultats
Démonstration vidéo réseau externe
scène ouverte
Utilisez l'exemple de modèle HDRP comme scénario de test
sélection des versions
Ce RenderStreamingPackage utilise3.1.0-exp.4, et il n'y a pas de version officielle, mais exp.4 a ouvert l'encodage du flux vidéo, la fréquence d'images, le débit binaire, le rapport de zoom et d'autres paramètres par rapport à la version exp.3, et il est également très stable à utiliser, donc c'est pratique à ajuster en fonction des besoins de votre propre projet.
WebServer utilise3.1.0-exp.3, en raison de la version exp.4 de Server, il y a un problème avec la vérification du chemin d'accès à la page Web.
Adresse Git : UnityRenderStreaming
serveur Web
1. Téléchargement du serveur, installation de Node.js
Choisissez le serveur en fonction de votre propre plate-forme, vous devez installer Node.js à télécharger
et à utiliser node -v
npm -v
pour vérifier si l'installation est réussie
2. Démarrage du serveur
La méthode de démarrage de Windows est la suivante (reportez-vous à la documentation officielle pour les autres méthodes de démarrage de la plate-forme )
Recherchez webserver.exe, entrez cmd dans la barre d'adresse et appuyez sur Entrée
Entrée .\webserver.exe -w
, commencez par WebSocket
Ouvrez le navigateur, entrez l'adresse IP locale, la figure suivante est le contenu après avoir démarré avec WebSocket
Paramètres du projet Unity
1. Installez le rendu en continu
Une fois le code source décompressé, copiez com.unity.renderstreaming
le fichier et placez-le sous les packages de l'exemple de projet, puis créez un nouveau dossier. (Stocker selon votre préférence)
Ouvrez PackageManager
et cliquez sur +, sélectionnez Add package from disk
pour trouver le fichier tout à l'heure package.json
, ouvrez-le
et ouvrez-le comme suit
2. Installez WebRTC
Sélectionnez Add package by name
input com.unity.webrtc
, cliquez sur Add
pour installer comme suit :
3. Rendre les paramètres de streaming
Créez un objet vide, nommez-le RenderStreaming
et ajoutez les composants RenderStreaming
, Broadcast
et Modifiez les paramètres comme suit :VideoStreamSender
4. Transmission audio (ajouter selon vos besoins)
Ajoutez AudioStreamSender
le composant ci-dessus AudioListener
, ce composant doit être dans un
Broadcast
composant objet pensez à ajouter AudioStreamSender
le composant
5. Unity lance le test
L'effet de la sélection est le suivant, vous pouvez ajuster les paramètres en ReceiverSample
fonction de vos propres besoinsVideoStreamSender
interagir
1. Paramètres de base du projet interactif
Installez InputSystem
et certains des paramètres suivants visent àUnité2021.2Version ci-dessus, lien de référence de version basse
(InputSystem a été utilisé par défaut dans cet exemple HDRP)
Après l'apparition de la fenêtre contextuelle, sélectionnezOui, acceptez d'utiliser New Input Sytem ou sélectionnez New ou Both Run In Background
dans ProjectSettings/Player/OtherSettings/ActiveInputHanding pour ouvrir (2021.2) la modification des paramètres du package du système d'entrée (2021.2)
ProjectSettings/Player/Resolution and Presentation/Run in background
2. Paramètres d'interaction avec la scène
Référence de la documentation officielle
Ajouter InputReceiver
des composants et ajouter Broadcast
à
3. Interaction clavier et souris
Ouvrir après avoir créé InputActions, opération : Create/InputActions
Configurer les actions en fonction des exigences de votre propre projet
Faites glisser le fichier InputActions configuré dedans InputReceiver/Actions
, développez Événements, vous pouvez voir les événements correspondant aux actions configurées et les lier en fonction de vos besoins
4. Interaction de l'interface utilisateur
EventSystem utilise InputSystemUIInputModule
la résolution d'affichage pour être cohérent VideoStreamSender
avec cela StreamingSize
, sinon le point de reconnaissance sera décalé
Déploiement du serveur extranet
Pour la plupart des applications WebRTC, le serveur doit relayer le trafic entre pairs, car les sockets directs ne sont généralement pas possibles entre les clients (sauf si les applications se trouvent sur le même réseau local). Une solution courante à ce problème consiste à utiliser un serveur TURN . Le terme désigne la traversée à l'aide de relais NAT, un protocole de relais du trafic réseau.
Reportez-vous aux paramètres du serveur TURN dans la documentation Unity
Le port utilisé par le serveur TURN doit être public et les valeurs maximale et minimale peuvent être définies
protocole | port |
---|---|
TCP | 32355-65535, 3478-3479 |
UDP | 32355-65535, 3478-3479 |
config.js
Modifier le fichier dans le côté Webconfig.iceServers
config.iceServers = [{
urls: ['stun:stun.l.google.com:19302']
}, {
urls: ['turn:xx.xx.xx.xx:3478?transport=tcp'],
username: 'username',
credential: 'password'
}
];
Modification RenderStreaming
des composants dans UnityIce Server
Serveur Web personnalisé
Voici comment créer votre propre serveur Web, consultez la documentation officielle pour plus de détails
1. Téléchargez le code source de exp.3
2. Trouvez la WebApp après décompression 3.
Entrez cmd dans la barre d'adresse
4. Saisissez npm install
ou npm i
installez les dépendances
Si le l'installation est trop lente ou échoue en raison d'un délai d'attente, vous pouvez d'abord l'entrer
npm config set registry https://registry.npm.taobao.org
pour remplacer la source de l'image d'installation npm par une source domestique.
Si les erreurs suivantes se produisent, vous pouvez l'entrer npm config set legacy-peer-deps true
, puis l'entrer à nouveau npm install
ou npm i
installer les dépendances **
5. Build Server, entrez npm run build
6. Start Server, entreznpm run start -- -w
Si le démarrage est anormal, comme illustré dans la figure ci-dessous, utilisez npm run dev -- -w
Démarrer
pour démarrer comme suit. La raison de la situation ci-dessus n'est pas claire. Si vous en connaissez la raison, veuillez indiquer moi, merci.
7. Créez un raccourci de démarrage.
Afin de faciliter le démarrage rapide à l'avenir, vous pouvez créer les fichiers suivants.
8. Pack,npm run pack
attendez que la progression soit terminée et compressez-le dans une
méthode de démarrage exe comme ci-dessus..\webserver.exe -w
9. Modifiez les fichiers Web selon vos besoins
Web et Unity s'envoient des messages personnalisés
La fonction utilisée WebRTC
pour envoyer un message , utilisez la fonction pour recevoir un messageRTCDataChannel
Send
OnMessage
1. Unity envoie un message au Web
Le script créé dans le projet Unity est le suivant, en utilisant les objets inputReceiver
duChannel
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");
}
}
}
N'oubliez pas de l'ajouter au projet
Web pour recevoir comme suit
Trouvez l'objet receiver.js
dans le fichier et utilisez les informations reçues Le contenu du message reçu est le suivant, la chaîne sera en donnéesinputSenderChannel
onmessage
2. Le Web envoie un message à Unity
Utilisez la fonction d'envoi pour envoyer des messages sur le Web
this.inputSenderChannel.send("msg to unity");
Dans OnMessage dans Unity Receiver.cs
, le message envoyé par le Web est reçu via RTCDataChannel
Comment les projets d'entreprise sont appliqués
1. Le choix entre un accès réseau externe clair et fluide
En partant du principe que la bande passante du serveur le permet, améliorez la qualité de transmission autant que possible, comme suit, quels sont mes paramètres dans le projet
Vous pouvez l'ajuster en fonction de votre propre bande passante DepathBuffer,StreamingSize,Framerate,Bitrate,ScaleResolution
.
Vous pouvez également obtenir le composant VideoStreamSender et modifier les paramètres correspondants pendant l'exécution pour faciliter les tests après la publication.
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. Bouton Web pour remplacer le fonctionnement du bouton de Unity
En raison de la limitation actuelle des changements de résolution, il est souvent impossible de cliquer correctement sur les boutons Unity. Les étudiants Web créent donc les boutons correspondants et envoient des messages à Unity via le Web pour répondre aux opérations des boutons correspondants.
Mon côté est de configurer l'identifiant de tous les boutons, le Web envoie l'identifiant du bouton, Unity parcourt la table de configuration après l'avoir reçu et effectue 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. Synchronisation de la résolution, reportez-vous à la méthode de synchronisation de la résolution dans iOS et Android ci-dessous
Faites correspondre différentes résolutions de périphérique, obtenez la résolution actuelle du périphérique via le Web et envoyez un message de largeur et de hauteur de périphérique à Unity une fois la connexion établie, puis réinitialisez la résolution de sortie dans Unity pour que le périphérique Web reste entièrement affiché.
public void ChangeScreenSize(int width,int height)
{
videoStreamSender.SetTextureSize(new Vector2Int(width,height));
}
4. Précautions lors de l'accès à l'écran vertical par défaut sur le téléphone mobile et de l'affichage de l'écran horizontal
Étant donné que l'écran d'affichage vertical du téléphone mobile est trop petit, il affiche généralement le contenu de l'écran horizontal à l'état d'écran vertical. Par conséquent : 1.
Lors du réglage de la résolution mentionnée au point précédent, vous devez faire attention à savoir si l'appareil côté web se trouve un écran horizontal ou un écran vertical
2. Projeter S'il y a une opération de glissement dans , il faut faire attention à inverser l'opération selon les écrans horizontaux et verticaux.
5. Détecter si le côté Unity est déconnecté du service
Dans le projet, le service sera déconnecté toutes les demi-heures à une heure, donc la détection du rythme cardiaque est ajoutée, et l'opération de reconnexion est
la suivante, ISignaling
ajoutezOnHeartBeatHandler
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();
}
}
Modifiez
la fonction SendHeartBeat dans le rapport d'erreur WebSocketSignaling et ajoutez le contenu suivant
public void SendHeartBeat()
{
this.WSSend($"{
{\"type\":\"heart\"}}");
}
Créez RenderStreamingManager
un script, le code est le suivant
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
}
N'oubliez pas de fermer RenderStreaming
et de rechercher et RunOnAwake
WebServer
d'ajouter le contenu suivant Reconstruisez le service et démarrez le serveur, afin que Unity puisse envoyer et recevoir des messages de pulsation avec le serveur via WebSocketwebsocket.ts
websockethandler.ts
6. Vérifiez s'il existe une connexion utilisateur réussie
RenderStreamingManager
Lier les événements dans
private void Awake()
{
// connect & disconnect
_inputReceiver.OnStartedChannel += OnStartChannel;
_inputReceiver.OnStoppedChannel += OnStopChannel;
}
private void OnStartChannel(string connectionid)
{
}
private void OnStopChannel(string connectionid)
{
}
Comment faire iOS et Android
1. Reportez-vous à la scène du récepteur dans l'exemple
2. Personnalisez le contenu de la scène du récepteur
Créez un objet comme indiqué sur la figure, ajoutez RenderStreaming
SingleConnection
VideoStreamReceiver
InputSender
des composants
AudioStreamReceiver
liés au son, ajoutez
un nouveau canevas en fonction de vos propres besoins, créez un RawImage et utilisez-le comme support pour recevoir
le contenu lié à Texture Audio, selon la configuration dans l'exemple , n'ont pas besoin de contenu lié au son peut être ignoré
3. Créez le script de gestion
Obtenez des composants et des événements de liaison dans Awake et
appelez StartConnect pour démarrer la connexion au websocket. Une fois la connexion établie, démarrez la connexion webrtc, puis l'événement OnUpdateReceiveTexture lié à VideoStreamReceiver met à jour l'image dans RawImage.
Dans OnStartedChannel, les informations de résolution de l'appareil actuel sont ajoutées et la structure du message peut être personnalisée. J'ai créé un ScreenMessage ici, ajouté des informations sur la largeur et la hauteur et l'ai envoyé au client.
Le script est le suivant :
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);
}
}
Le message définit la structure, écrivez selon vos propres préférences
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. Traitement adaptatif de l'écran après que le client a reçu le ScreenMessage
Après avoir reçu le message, analysez pour obtenir la largeur et la hauteur, et calculez la résolution appropriée en fonction de la résolution de l'appareil mobile. Utilisez
_videoStreamSender.SetTextureSize pour modifier la résolution de sortie.
Utilisez _inputReceiver.SetInputRange et _inputReceiver.SetEnableInputPositionCorrection(true) pour corriger La résolution La modification de la résolution sur le côté Web est cohérente avec cela.
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. Paramètres du projet avant la publication
Modifications demandées dans les documents WebRTC
6. Test de libération
plan de mise à jour
1. Partage de bogues de versions Mac et iOS