Démarrage et cycle de vie¶
Le paquet Unity inclut un assistant statique, VisioForgeEnvironment, qui prépare le runtime natif fourni avant le chargement de la première scène. Vous ne l'appelez pas depuis vos scripts — Unity l'invoque automatiquement via RuntimeInitializeOnLoadMethod. Cette page explique ce qu'il fait sur chaque plateforme et les règles de cycle de vie que vos scripts doivent respecter.
Si vous voulez seulement déployer le SDK, vous pouvez sauter cette page : déposez MediaBlocksPlayer ou RTSPViewerPlayer dans une scène et appuyez sur Play. Revenez ici quand vous construisez vos propres scripts, rencontrez une erreur à l'exécution ou voulez comprendre pourquoi les réglages Éditeur sont obligatoires.
Les deux points d'entrée¶
VisioForgeEnvironment expose exactement deux méthodes publiques avec lesquelles votre code interagit :
| Méthode | Quand elle s'exécute | Ce qu'elle fait |
|---|---|---|
Configure() | Automatiquement, avant le chargement de la première scène ([RuntimeInitializeOnLoadMethod(BeforeSceneLoad)]). | Prépare le runtime natif pour la plateforme courante — chemin de recherche DLL, variables d'environnement, bundle CA, bootstrap Java. Idempotente. |
InitializeSdk() | Vous l'appelez une fois avant de construire un pipeline. Les players d'exemple le font dans Start(). | Appelle VisioForgeX.InitSDK() et scanne le registre de plugins fourni. Idempotente. |
Les deux méthodes sont statiques. Les deux ne marquent leur succès qu'après l'exécution réussie de chaque étape — un échec récupérable (par exemple le bootstrap Java Android avant que currentActivity ne soit prête) laisse le drapeau désactivé pour qu'un appel ultérieur relance le travail au lieu d'un no-op silencieux vers un état cassé.
Ce que fait Configure() sur chaque plateforme¶
Configure() dispatch sur les symboles de compilation (UNITY_STANDALONE_WIN, UNITY_STANDALONE_OSX, UNITY_ANDROID, UNITY_IOS) — Unity en définit exactement un par target de build. Le corps de chaque branche est le minimum dont la plateforme a besoin pour que GStreamer trouve ses plugins, localise ses racines TLS et résolve le reste du runtime via son chargeur natif.
- Valide que le dossier de natifs fourni existe (
StreamingAssets/VisioForge/x64). Refuse de modifier l'état du processus sinon — un dossier absent ne doit pas empoisonner lePATHdu processus. - Retire toute entrée GStreamer système du PATH du processus (une installation homebrew / système sur
PATHchargerait une seconde copie physique degstreamer-1.0-0.dll, enregistrerait les types GLib en double et bloquerait le pipeline). - Pointe le chargeur DLL Win32 vers les natifs fournis via
SetDllDirectoryW. - Préfixe le dossier de natifs au
PATHdu processus pour que les dépendances core-lib transitives de chaque plugin se résolvent (SetDllDirectoryseul ne suffit pas). - Définit
GST_PLUGIN_PATH/GST_PLUGIN_SYSTEM_PATHau même dossier plat. - Définit
SSL_CERT_FILEetCA_CERTIFICATESauca-certificates.crtfourni pour que RTSPS et HTTPS vérifient les pairs.
Le PATH utilisateur / système n'est jamais touché — seule la copie vive du processus.
- Résout le chemin des natifs en sondant les layouts connus d'Unity :
Plugins/macOS(Éditeur et certaines versions du Standalone Player), puisContents/PlugIns,Contents/PlugIns/macOS,Contents/FrameworksetContents/Resources/Data/Plugins/macOSpour un.appStandalone. Le premier dossier qui contientlibgstreamer-1.0.0.dylibgagne. Le résultat est mis en cache. - Élimine tout GStreamer système / homebrew de
DYLD_LIBRARY_PATH(/opt/homebrew/lib,/usr/local/lib) — même mode de défaillance double-init que le nettoyagePATHWindows. - Définit
GST_PLUGIN_PATH/GST_PLUGIN_SYSTEM_PATHau dossier des natifs pour que GStreamer puisse énumérer les plugins fournis. - Définit
GIO_MODULE_DIRpour que GIO trouvelibgioopenssl.so(le backend TLS via lequel RTSPS / HTTPS vérifient les pairs). - Définit
SSL_CERT_FILEetCA_CERTIFICATESau bundle CA fourni.
Chaque .dylib fourni a son @rpath / @loader_path précuit par le NuGet pack, donc une fois que dyld a chargé l'un d'eux via le premier [DllImport], les autres résolvent leurs voisins automatiquement — pas d'équivalent SetDllDirectory nécessaire.
- Acquiert
com.unity3d.player.UnityPlayer.currentActivityvia JNI. Si le champ est null — variantes Wear OS / Android TV, hôtes Unity-as-a-library qui ne l'ont pas encore assigné, paths de démarrageBeforeSceneLoadtrès précoces sur certains Unity 6 — échoue rapidement avec une exception descriptive pour que le client voie mieux qu'unNullReferenceExceptionopaque depuis l'intérieur de JNI. - Résout
getFilesDir()etgetCacheDir()depuis l'Activity. - Capture les valeurs antérieures de
TMP,TEMP,TMPDIR,XDG_RUNTIME_DIR,XDG_CACHE_HOME,HOME,GST_REGISTRY,SSL_CERT_FILE,CA_CERTIFICATESpour qu'une défaillance ultérieure puisse les restaurer. - Pointe GLib vers les répertoires accessibles en écriture privés de l'app en définissant les variables ci-dessus (seuls les répertoires privés de l'app sont accessibles en écriture sur Android).
- Extrait la ressource embarquée
ca-certificates.crtversfilesDir/ssl/certs/et pointeSSL_CERT_FILE/CA_CERTIFICATESlà. - Appelle
org.freedesktop.gstreamer.GStreamer.init(activity)— cela chargelibgstreamer_android.so, capture la JavaVM dansJNI_OnLoad, exécutegst_initet enregistre chaque plugin statiquement lié au monolithe. - Si l'un d'eux lève une exception, restaure l'environnement capturé et la relance.
- Extrait la ressource embarquée
ca-certificates.crtversApplication.persistentDataPath/ssl/certs/. - Définit
SSL_CERT_FILE/CA_CERTIFICATESà ce chemin pour que le backend OpenSSL de GIO puisse vérifier les pairs RTSPS / HTTPS. - Précharge le cache
NativesPathsur le thread principal (s_cachedNativesPath = Application.dataPath.Replace('\\', '/')). Sans ce préchargement, un lecteur sur un thread d'arrière-plan — le bus GStreamer, un callback de log async, un callback pad-added — accéderait plus tard au getter évalué à la demande et appelleraitApplication.dataPathhors du thread principal, ce qui lèveUnityException. La formule de calcul correspond au getter évalué à la demande octet pour octet.
iOS n'a pas besoin de scan de plugins : chaque plugin GStreamer est enregistré statiquement dans GStreamerX.framework à gst_plugin_register_static(), et dyld résout @rpath/GStreamerX.framework/GStreamerX automatiquement quand le premier [DllImport] se déclenche.
Configure() définit le drapeau de succès et journalise un avertissement. Aucun runtime natif n'existe pour la plateforme courante, donc tout [DllImport] ultérieur lancera DllNotFoundException. Compilez pour une des quatre plateformes prises en charge à la place.
Ce que fait InitializeSdk()¶
Après que Configure() a préparé le runtime, InitializeSdk() finalise le démarrage :
- Refuse de s'exécuter si
Configure()n'a jamais réussi — l'échec rapide ici fait remonter une erreur actionnable au lieu d'unDllNotFoundExceptionau fond du SDK. - Refuse de s'exécuter sur une valeur
Application.platformnon prise en charge à l'exécution (Windows / Android / macOS / iOS sont admis ; tout le reste court-circuite avec un avertissement). - Sur Windows et macOS, avant d'appeler le SDK natif, revérifie que le dossier de natifs résolu existe sur le disque. Cela attrape le décalage entre déclinaisons (un
.unitypackageWindows seulement importé dans un hôte macOS, ou l'inverse) avec un message clair plutôt qu'unDllNotFoundExceptionopaque. Android et iOS sautent ce contrôle (pas de dossier à sonder). - Appelle
VisioForgeX.InitSDK(). Capture et journalise en cas d'échec ; laisse le drapeau désactivé pour qu'une tentative ultérieure puisse réussir. - Sur Windows et macOS, scanne explicitement le dossier de plugins fourni avec
Gst.Registry.Get().ScanPath(NativesPath). Le scan in-process des plugins d'Unity n'honore pas de façon fiableGST_PLUGIN_PATHsur l'une ou l'autre plateforme ; le scan explicite est ce qui fait charger des blocs commeBufferSinkBlock(qui dépend deappsink). Android enregistre les plugins statiquement dansGStreamer.init; iOS les enregistre statiquement dans le framework — les deux sautent le scan. - Définit le drapeau de succès.
Les players d'exemple (MediaBlocksPlayer, RTSPViewerPlayer) appellent InitializeSdk() depuis leur méthode Start(), avant de construire un pipeline. Vos scripts devraient suivre le même schéma.
Cycle de vie Éditeur¶
Le SDK s'initialise une fois par processus Éditeur et est réutilisé entre les sessions Play → Stop → Play. Deux conséquences :
- Disable Domain Reload est obligatoire. Avec lui activé, sortir du mode Play déclenche un rechargement de domaine alors que le thread de la boucle principale GLib du SDK tourne encore, ce qui peut bloquer l'Éditeur. La boîte de dialogue de configuration que le paquet affiche au premier import le configure pour vous ; réglez-le manuellement dans Edit → Project Settings → Editor → Enter Play Mode Settings si vous avez sauté ce dialogue.
- N'appelez pas
VisioForgeX.DestroySDK()sur Stop ou dansOnDestroy. Legst_deinitde GStreamer ne peut pas être réinitialisé dans le même processus — détruire le SDK sur Stop et tenter de l'utiliser au prochain Play plante au sein du registre natif. Les players d'exemple respectent cette règle : leurOnDestroyne libère que le pipeline par-Play. Le SDK reste vivant pour le reste du cycle de vie du processus.
Il y a un guard Éditeur-seul que le paquet installe automatiquement : un VisioForgeEditorReloadGuard qui appelle VisioForgeX.StopMainLoop() sur beforeAssemblyReload et EditorApplication.quitting. La boucle principale GLib tourne sur un thread d'arrière-plan dédié, bloqué dans un appel natif qu'Unity ne peut pas abandonner — sans ce guard, le rechargement de domaine suivant une recompilation de script se bloquerait. Le guard n'appelle pas DestroySDK (voir ci-dessus) ; il arrête seulement le thread de la boucle, et le prochain Play reconstruit la boucle. Ce guard est interne — vos scripts doivent l'ignorer.
Foire aux questions¶
Dois-je appeler Configure() manuellement ?¶
Non. L'attribut [RuntimeInitializeOnLoadMethod(BeforeSceneLoad)] d'Unity l'exécute pour vous avant le chargement de la première scène. Le seul cas où vous l'appelleriez à nouveau est depuis un chemin de récupération personnalisé quand une tentative antérieure a échoué — et Configure() est idempotente, donc un appel redondant est inoffensif.
Pourquoi Configure() modifie-t-elle des variables d'environnement plutôt que de passer des arguments ?¶
GLib lit GST_PLUGIN_PATH, GIO_MODULE_DIR, SSL_CERT_FILE, HOME, etc. depuis environ C directement pendant gst_init et à nouveau à la première utilisation TLS. Le SDK n'a pas d'API pour les surcharger — la seule façon correcte de pointer le runtime vers les assets fournis est de définir les variables avant la construction de tout pipeline. Les mutations sont limitées au processus ; les environnements utilisateur et système restent intacts.
Que se passe-t-il si j'appelle InitializeSdk() avant que Configure() ait réussi ?¶
Cela journalise une erreur et retourne. Le drapeau de succès reste désactivé pour qu'une tentative ultérieure puisse réussir une fois Configure() fonctionnel. Ce guard existe parce que InitSDK() planterait sinon au fond du code natif avec une erreur bien moins actionnable.
Puis-je exécuter deux pipelines en parallèle ?¶
Oui. InitializeSdk() démarre le SDK une fois par processus ; après ça vous pouvez construire autant d'instances MediaBlocksPipeline que vous voulez. Chacune est indépendante — le motif multi-caméra RTSP de l'exemple consiste à attacher un RTSPViewerPlayer par RawImage, et chacun construit et démolit son propre pipeline.
Voir aussi¶
- Utiliser VisioForge dans Unity — vue d'ensemble du paquet et fonctionnement du rendu
- Compilation pour Windows — réglages Éditeur et Standalone pour Windows
- Compilation pour Android — réglages IL2CPP pour Android
- Compilation pour macOS — réglages Standalone pour macOS
- Compilation pour iOS — réglages d'appareil pour iOS
- Dépannage — erreurs courantes de bootstrap et runtime