mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 06:19:02 +02:00
re-work AvatarMixer to new QCA infrastructure, closes #1300
This commit is contained in:
parent
606eaa579a
commit
ed854dcb7b
2 changed files with 118 additions and 90 deletions
|
@ -10,6 +10,9 @@
|
||||||
// The avatar mixer receives head, hand and positional data from all connected
|
// The avatar mixer receives head, hand and positional data from all connected
|
||||||
// nodes, and broadcasts that data back to them, every BROADCAST_INTERVAL ms.
|
// nodes, and broadcasts that data back to them, every BROADCAST_INTERVAL ms.
|
||||||
|
|
||||||
|
#include <QtCore/QCoreApplication>
|
||||||
|
#include <QtCore/QTimer>
|
||||||
|
|
||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
#include <PacketHeaders.h>
|
#include <PacketHeaders.h>
|
||||||
|
@ -22,6 +25,14 @@
|
||||||
|
|
||||||
const char AVATAR_MIXER_LOGGING_NAME[] = "avatar-mixer";
|
const char AVATAR_MIXER_LOGGING_NAME[] = "avatar-mixer";
|
||||||
|
|
||||||
|
const unsigned int AVATAR_DATA_SEND_INTERVAL_USECS = (1 / 60.0) * 1000 * 1000;
|
||||||
|
|
||||||
|
AvatarMixer::AvatarMixer(const unsigned char* dataBuffer, int numBytes) :
|
||||||
|
ThreadedAssignment(dataBuffer, numBytes)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char* addNodeToBroadcastPacket(unsigned char *currentPosition, Node *nodeToAdd) {
|
unsigned char* addNodeToBroadcastPacket(unsigned char *currentPosition, Node *nodeToAdd) {
|
||||||
QByteArray rfcUUID = nodeToAdd->getUUID().toRfc4122();
|
QByteArray rfcUUID = nodeToAdd->getUUID().toRfc4122();
|
||||||
memcpy(currentPosition, rfcUUID.constData(), rfcUUID.size());
|
memcpy(currentPosition, rfcUUID.constData(), rfcUUID.size());
|
||||||
|
@ -46,7 +57,7 @@ void attachAvatarDataToNode(Node* newNode) {
|
||||||
// 3) if we need to rate limit the amount of data we send, we can use a distance weighted "semi-random" function to
|
// 3) if we need to rate limit the amount of data we send, we can use a distance weighted "semi-random" function to
|
||||||
// determine which avatars are included in the packet stream
|
// determine which avatars are included in the packet stream
|
||||||
// 4) we should optimize the avatar data format to be more compact (100 bytes is pretty wasteful).
|
// 4) we should optimize the avatar data format to be more compact (100 bytes is pretty wasteful).
|
||||||
void broadcastAvatarData(NodeList* nodeList, const QUuid& receiverUUID, const HifiSockAddr& receiverSockAddr) {
|
void broadcastAvatarData() {
|
||||||
static unsigned char broadcastPacketBuffer[MAX_PACKET_SIZE];
|
static unsigned char broadcastPacketBuffer[MAX_PACKET_SIZE];
|
||||||
static unsigned char avatarDataBuffer[MAX_PACKET_SIZE];
|
static unsigned char avatarDataBuffer[MAX_PACKET_SIZE];
|
||||||
unsigned char* broadcastPacket = (unsigned char*)&broadcastPacketBuffer[0];
|
unsigned char* broadcastPacket = (unsigned char*)&broadcastPacketBuffer[0];
|
||||||
|
@ -55,40 +66,91 @@ void broadcastAvatarData(NodeList* nodeList, const QUuid& receiverUUID, const Hi
|
||||||
int packetLength = currentBufferPosition - broadcastPacket;
|
int packetLength = currentBufferPosition - broadcastPacket;
|
||||||
int packetsSent = 0;
|
int packetsSent = 0;
|
||||||
|
|
||||||
// send back a packet with other active node data to this node
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
|
||||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||||
if (node->getLinkedData() && node->getUUID() != receiverUUID) {
|
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT && node->getActiveSocket()) {
|
||||||
unsigned char* avatarDataEndpoint = addNodeToBroadcastPacket((unsigned char*)&avatarDataBuffer[0], &*node);
|
// this is an AGENT we have received head data from
|
||||||
int avatarDataLength = avatarDataEndpoint - (unsigned char*)&avatarDataBuffer;
|
// send back a packet with other active node data to this node
|
||||||
|
for (NodeList::iterator otherNode = nodeList->begin(); otherNode != nodeList->end(); otherNode++) {
|
||||||
if (avatarDataLength + packetLength <= MAX_PACKET_SIZE) {
|
if (otherNode->getLinkedData() && otherNode->getUUID() != node->getUUID()) {
|
||||||
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
|
unsigned char* avatarDataEndpoint = addNodeToBroadcastPacket((unsigned char*)&avatarDataBuffer[0], &*node);
|
||||||
packetLength += avatarDataLength;
|
int avatarDataLength = avatarDataEndpoint - (unsigned char*)&avatarDataBuffer;
|
||||||
currentBufferPosition += avatarDataLength;
|
|
||||||
} else {
|
if (avatarDataLength + packetLength <= MAX_PACKET_SIZE) {
|
||||||
packetsSent++;
|
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
|
||||||
//printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength);
|
packetLength += avatarDataLength;
|
||||||
nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket, currentBufferPosition - broadcastPacket,
|
currentBufferPosition += avatarDataLength;
|
||||||
receiverSockAddr.getAddress(), receiverSockAddr.getPort());
|
} else {
|
||||||
|
packetsSent++;
|
||||||
// reset the packet
|
//printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength);
|
||||||
currentBufferPosition = broadcastPacket + numHeaderBytes;
|
nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket, currentBufferPosition - broadcastPacket,
|
||||||
packetLength = currentBufferPosition - broadcastPacket;
|
node->getActiveSocket()->getAddress(),
|
||||||
|
node->getActiveSocket()->getPort());
|
||||||
// copy the avatar that didn't fit into the next packet
|
|
||||||
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
|
// reset the packet
|
||||||
packetLength += avatarDataLength;
|
currentBufferPosition = broadcastPacket + numHeaderBytes;
|
||||||
currentBufferPosition += avatarDataLength;
|
packetLength = currentBufferPosition - broadcastPacket;
|
||||||
|
|
||||||
|
// copy the avatar that didn't fit into the next packet
|
||||||
|
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
|
||||||
|
packetLength += avatarDataLength;
|
||||||
|
currentBufferPosition += avatarDataLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
packetsSent++;
|
||||||
|
//printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength);
|
||||||
|
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) broadcastPacket, currentBufferPosition - broadcastPacket,
|
||||||
|
node->getActiveSocket()->getAddress(),
|
||||||
|
node->getActiveSocket()->getPort());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
packetsSent++;
|
|
||||||
//printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength);
|
|
||||||
nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket, currentBufferPosition - broadcastPacket,
|
|
||||||
receiverSockAddr.getAddress(), receiverSockAddr.getPort());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarMixer::AvatarMixer(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes) {
|
void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
|
||||||
|
|
||||||
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
|
||||||
|
switch (dataByteArray.data()[0]) {
|
||||||
|
case PACKET_TYPE_HEAD_DATA: {
|
||||||
|
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader((unsigned char*) dataByteArray.data()),
|
||||||
|
NUM_BYTES_RFC4122_UUID));
|
||||||
|
|
||||||
|
// add or update the node in our list
|
||||||
|
Node* avatarNode = nodeList->nodeWithUUID(nodeUUID);
|
||||||
|
|
||||||
|
if (avatarNode) {
|
||||||
|
// parse positional data from an node
|
||||||
|
nodeList->updateNodeWithData(avatarNode, senderSockAddr,
|
||||||
|
(unsigned char*) dataByteArray.data(), dataByteArray.size());
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case PACKET_TYPE_KILL_NODE:
|
||||||
|
case PACKET_TYPE_AVATAR_URLS:
|
||||||
|
case PACKET_TYPE_AVATAR_FACE_VIDEO: {
|
||||||
|
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader((unsigned char*) dataByteArray.data()),
|
||||||
|
NUM_BYTES_RFC4122_UUID));
|
||||||
|
// let everyone else know about the update
|
||||||
|
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||||
|
if (node->getActiveSocket() && node->getUUID() != nodeUUID) {
|
||||||
|
nodeList->getNodeSocket().writeDatagram(dataByteArray,
|
||||||
|
node->getActiveSocket()->getAddress(),
|
||||||
|
node->getActiveSocket()->getPort());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// let node kills fall through to default behavior
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// hand this off to the NodeList
|
||||||
|
nodeList->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,76 +165,39 @@ void AvatarMixer::run() {
|
||||||
|
|
||||||
nodeList->linkedDataCreateCallback = attachAvatarDataToNode;
|
nodeList->linkedDataCreateCallback = attachAvatarDataToNode;
|
||||||
|
|
||||||
nodeList->startSilentNodeRemovalThread();
|
QTimer* domainServerTimer = new QTimer(this);
|
||||||
|
connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
|
||||||
|
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
|
||||||
|
|
||||||
HifiSockAddr nodeSockAddr;
|
QTimer* pingNodesTimer = new QTimer(this);
|
||||||
ssize_t receivedBytes = 0;
|
connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes()));
|
||||||
|
pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000);
|
||||||
|
|
||||||
unsigned char packetData[MAX_PACKET_SIZE];
|
QTimer* silentNodeTimer = new QTimer(this);
|
||||||
|
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||||
|
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||||
|
|
||||||
QUuid nodeUUID;
|
int nextFrame = 0;
|
||||||
Node* avatarNode = NULL;
|
timeval startTime;
|
||||||
|
|
||||||
timeval lastDomainServerCheckIn = {};
|
gettimeofday(&startTime, NULL);
|
||||||
|
|
||||||
while (true) {
|
while (!_isFinished) {
|
||||||
|
|
||||||
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
QCoreApplication::processEvents();
|
||||||
|
|
||||||
|
if (_isFinished) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
|
broadcastAvatarData();
|
||||||
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
|
|
||||||
gettimeofday(&lastDomainServerCheckIn, NULL);
|
|
||||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeList->possiblyPingInactiveNodes();
|
int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * AVATAR_DATA_SEND_INTERVAL_USECS) - usecTimestampNow();
|
||||||
|
|
||||||
if (nodeList->getNodeSocket().hasPendingDatagrams() &&
|
if (usecToSleep > 0) {
|
||||||
(receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE,
|
usleep(usecToSleep);
|
||||||
nodeSockAddr.getAddressPointer(),
|
} else {
|
||||||
nodeSockAddr.getPortPointer())) &&
|
qDebug() << "Took too much time, not sleeping!\n";
|
||||||
packetVersionMatch(packetData)) {
|
|
||||||
switch (packetData[0]) {
|
|
||||||
case PACKET_TYPE_HEAD_DATA:
|
|
||||||
nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData),
|
|
||||||
NUM_BYTES_RFC4122_UUID));
|
|
||||||
|
|
||||||
// add or update the node in our list
|
|
||||||
avatarNode = nodeList->nodeWithUUID(nodeUUID);
|
|
||||||
|
|
||||||
if (avatarNode) {
|
|
||||||
// parse positional data from an node
|
|
||||||
nodeList->updateNodeWithData(avatarNode, nodeSockAddr, packetData, receivedBytes);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PACKET_TYPE_INJECT_AUDIO:
|
|
||||||
broadcastAvatarData(nodeList, nodeUUID, nodeSockAddr);
|
|
||||||
break;
|
|
||||||
case PACKET_TYPE_KILL_NODE:
|
|
||||||
case PACKET_TYPE_AVATAR_URLS:
|
|
||||||
case PACKET_TYPE_AVATAR_FACE_VIDEO:
|
|
||||||
nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData),
|
|
||||||
NUM_BYTES_RFC4122_UUID));
|
|
||||||
// let everyone else know about the update
|
|
||||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
|
||||||
if (node->getActiveSocket() && node->getUUID() != nodeUUID) {
|
|
||||||
nodeList->getNodeSocket().writeDatagram((char*) packetData, receivedBytes,
|
|
||||||
node->getActiveSocket()->getAddress(),
|
|
||||||
node->getActiveSocket()->getPort());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// let node kills fall through to default behavior
|
|
||||||
|
|
||||||
default:
|
|
||||||
// hand this off to the NodeList
|
|
||||||
nodeList->processNodeData(nodeSockAddr, packetData, receivedBytes);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeList->stopSilentNodeRemovalThread();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,18 @@
|
||||||
#ifndef __hifi__AvatarMixer__
|
#ifndef __hifi__AvatarMixer__
|
||||||
#define __hifi__AvatarMixer__
|
#define __hifi__AvatarMixer__
|
||||||
|
|
||||||
#include <Assignment.h>
|
#include "../ThreadedAssignment.h"
|
||||||
|
|
||||||
/// Handles assignments of type AvatarMixer - distribution of avatar data to various clients
|
/// Handles assignments of type AvatarMixer - distribution of avatar data to various clients
|
||||||
class AvatarMixer : public Assignment {
|
class AvatarMixer : public ThreadedAssignment {
|
||||||
public:
|
public:
|
||||||
AvatarMixer(const unsigned char* dataBuffer, int numBytes);
|
AvatarMixer(const unsigned char* dataBuffer, int numBytes);
|
||||||
|
|
||||||
|
public slots:
|
||||||
/// runs the avatar mixer
|
/// runs the avatar mixer
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* defined(__hifi__AvatarMixer__) */
|
#endif /* defined(__hifi__AvatarMixer__) */
|
||||||
|
|
Loading…
Reference in a new issue