Latence de l' OSC dans Blender

De Centre de Ressources Numériques - Labomedia
Aller à : navigation, rechercher

Définition: Wikipedia EN

Le buffer UDP est adressé en 16 bits. 2 puissance 16 = 65536 bits soit 8192 octets. Quelques octets sont occupés par des datas d'identification. Par exemple, une image 80 x 80 pixels avec un entier court entre 0 et 99 comme variable occupe 80 x 80 x 2 x 4 bits = 51200 bits. Le maxi serait 65000 / 2 x 4 = 8125 pixels soit 90 x 90 = 8100 pixels ! Avec 2 entiers courts possible sur chaque pixel ( ce n'est pas une image en couleurs ).

Description du symptôme

La latence peut aller jusqu'à 15 secondes ! L'action se déroule a une vitesse normale mais décalée dans le temps de 15 secondes. Après une déconnexion du serveur, Blender tourne encore pendant 15 secondes donc reçoit des datas pendant 15 secondes !

Explication : Le tampon UDP (Buffer UDP)

Merci à dfelinto pour l'explication dans le post #5 sur blenderartists.org. Le code qu'il propose est bon à une erreur d'indentation près.

Si l'envoi de data se fait à une fréquence supérieure à la lecture, le Buffer se remplit et les datas qui sont lues dans ce buffer ne sont pas les dernières envoyées.

C'est un problème de baignoire, de robinet et d'écoulement: Osc 05.jpg Si le débit de sortie est plus faible que le débit d'entrèe, le niveau monte.


En anglais, c'est une Queue LIFO : Last In First Out Lifo queue.png

Latence normale des programmes et des dispositifs

Par exemple, Microsoft annonce une latence de 130 ms avec la Kinect.

Explications : Le Blender Game Engine pulse

Pourquoi ce débit de sortie faible ? Blender est plutôt lent, il pulse à la fréquence que vous définissez avec un maxi de 60 fps, soit une période optimale possible de 16 ms. Il suffit d'envoyer avec une période de 10 ms pour remplir le Buffer. Pure Data peut envoyer avec une période de 10 ms sans problème.

Le test ci-dessous montre des compteurs avec des valeurs de pulse de 0, 1 et 2.

Blender tourne à 60 fps: Pulse 1.png

Les briques logiques: Pulse 2.png

En mode "Pulse", f définit le nombre de frames pendant lesquels la sortie n'est pas activée. Le tableau ci-dessous donne les fréquences et périodes, si le Blender Game Engine est à 60 fps.

Des solutions

1 - Solution avec une initialisation de la connexion à chaque pulsation dans Blender

Si la connexion est réinitialisée à chaque pulsation de Blender, cela vide le Buffer. Mais Pure Data se déconnecte avec cette solution, cela marche avec OSCeleton et le wiiOSC de Julian.

2 - Solution simple : fréquence d'envoi < fréquence de réception

Le tableau ci-dessous donne les fréquences et périodes dans Blender et Pure Data.

Pulse 3.png

La solution la plus rationnelle est de régler la fréquence d' envoi plus basse que la fréquence de réception dans Blender, idem est régler le "timer" dans pure data à une valeur supérieure à la période dans Blender. Le tableau ci-dessus donne la période en fonction du "Pulse Mode" si Blender est à 60 "fps"! Bourrer Blender ne le fera pas aller plus vite !

Régler le "Pulse" à 0 sur tous les Actuators peut sembler efficace, mais cela va probablement faire chuter le "fps" de Blender, si le jeu est un peu lourd.

3 - Définir la taille du Buffer UDP

GameLogic.socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1024)

Ce Fichier:OSC buffer mini 2.49.zip utilise cette option, si elle est commentée le cube est en retard.

4 - Vider le buffer si il contient plus de 1 valeur

C'est la méthode proposée par dfelinto. Ce script a été testé avec un patch avec un timer de 10 ms. Sans vider le buffer, la latence est de 15 secondes, avec ce script il n' ya plus de latence. Bien sûr ce script peut sans doute être amélioré.

import GameLogic
import socket
import OSC

# Get controller and owner
controller = GameLogic.getCurrentController()
owner = controller.owner

# Set init
ip = 'localhost'
data = 0

########################################################################################       
def receive_osc(data, port): 
    try:
        # getting first data value from buffer
        data, port = GameLogic.socket.recvfrom(1024)
         
        # keep trying to get new data until buffer is empty
        try:
            trash = data
            while True:
                data = trash
                trash, port = GameLogic.socket.recvfrom(1024)
                # print deleted values
                print "trash= ", trash
        except:
            # we force this exception to happen
            # that way we know the buffer is empty
            pass
    except Exception as E:
        pass
    return data
     
########################################################################################
# Connect Blender only one time
if not owner['connected']:
    owner['connected'] = True
    print "Blender Connected"
    GameLogic.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    GameLogic.socket.bind((ip, 4951))
    GameLogic.socket.setblocking(0)
    GameLogic.socket.settimeout(0.02)

# If Blender connected, get osc    
else:
    # Get hand wii osc from pd
    data = receive_osc(data, 4951)
    if data != 0:
        GameLogic.handWii = OSC.decodeOSC(data)

5 - Définir la taille du Buffer UDP avec Pure Data

Osc 08.png

bufsize 1024 sur packOSC définit la taille du buffer.

Construire un patch pd correct

Communication entre Pure-data et Blender en OSC propose des exemples pour tester et comprendre.

Ressources