Merge pull request #906 from birarda/assignment

replace audio-mixer and avatar-mixer with assignment-client
This commit is contained in:
ZappoMan 2013-09-06 11:33:30 -07:00
commit 1bf4a073d5
29 changed files with 663 additions and 293 deletions

View file

@ -13,9 +13,8 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
add_subdirectory(animation-server)
add_subdirectory(assignment-client)
add_subdirectory(assignment-server)
add_subdirectory(avatar-mixer)
add_subdirectory(audio-mixer)
add_subdirectory(domain-server)
add_subdirectory(eve)
add_subdirectory(interface)

View file

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 2.8)
set(TARGET_NAME "avatar-mixer")
set(TARGET_NAME assignment-client)
set(ROOT_DIR ..)
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
@ -8,15 +8,11 @@ set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
# setup for find modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
# setup the project
include(${MACRO_DIR}/SetupHifiProject.cmake)
setup_hifi_project(${TARGET_NAME} TRUE)
# include glm
include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
# link required hifi libraries
# link in the shared library
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR})

View file

@ -0,0 +1,88 @@
//
// main.cpp
// assignment-client
//
// Created by Stephen Birarda on 8/22/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <arpa/inet.h>
#include <sys/time.h>
#include <Assignment.h>
#include <AudioMixer.h>
#include <AvatarMixer.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
const int ASSIGNMENT_REQUEST_INTERVAL_USECS = 1 * 1000 * 1000;
int main(int argc, char* const argv[]) {
setvbuf(stdout, NULL, _IOLBF, 0);
// create a NodeList as an unassigned client
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_UNASSIGNED);
// change the timeout on the nodelist socket to be as often as we want to re-request
nodeList->getNodeSocket()->setBlockingReceiveTimeoutInUsecs(ASSIGNMENT_REQUEST_INTERVAL_USECS);
unsigned char packetData[MAX_PACKET_SIZE];
ssize_t receivedBytes = 0;
// loop the parameters to see if we were passed a pool
int parameter = -1;
const char ALLOWED_PARAMETERS[] = "p::";
const char POOL_PARAMETER_CHAR = 'p';
char* assignmentPool = NULL;
while ((parameter = getopt(argc, argv, ALLOWED_PARAMETERS)) != -1) {
if (parameter == POOL_PARAMETER_CHAR) {
// copy the passed assignment pool
int poolLength = strlen(optarg);
assignmentPool = new char[poolLength + sizeof(char)];
strcpy(assignmentPool, optarg);
}
}
// create a request assignment, accept all assignments, pass the desired pool (if it exists)
Assignment requestAssignment(Assignment::Request, Assignment::All, assignmentPool);
while (true) {
// if we're here we have no assignment, so send a request
qDebug() << "Sending an assignment request -" << requestAssignment;
nodeList->sendAssignment(requestAssignment);
while (nodeList->getNodeSocket()->receive(packetData, &receivedBytes)) {
if (packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT && packetVersionMatch(packetData)) {
// construct the deployed assignment from the packet data
Assignment deployedAssignment(packetData, receivedBytes);
qDebug() << "Received an assignment - " << deployedAssignment << "\n";
// switch our nodelist DOMAIN_IP to the ip receieved in the assignment
if (deployedAssignment.getDomainSocket()->sa_family == AF_INET) {
in_addr domainSocketAddr = ((sockaddr_in*) deployedAssignment.getDomainSocket())->sin_addr;
nodeList->setDomainIP(inet_ntoa(domainSocketAddr));
qDebug() << "Changed domain IP to " << inet_ntoa(domainSocketAddr);
}
if (deployedAssignment.getType() == Assignment::AudioMixer) {
AudioMixer::run();
} else {
AvatarMixer::run();
}
qDebug() << "Assignment finished or never started - waiting for new assignment";
// reset our NodeList by switching back to unassigned and clearing the list
nodeList->setOwnerType(NODE_TYPE_UNASSIGNED);
nodeList->clear();
}
}
}
}

View file

@ -12,19 +12,17 @@
#include <QtCore/QString>
#include <Assignment.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <UDPSocket.h>
const int MAX_PACKET_SIZE_BYTES = 1400;
struct Assignment {
QString scriptFilename;
};
const long long NUM_DEFAULT_ASSIGNMENT_STALENESS_USECS = 10 * 1000 * 1000;
int main(int argc, const char* argv[]) {
std::queue<Assignment> assignmentQueue;
std::deque<Assignment*> assignmentQueue;
sockaddr_in senderSocket;
unsigned char senderData[MAX_PACKET_SIZE_BYTES] = {};
@ -33,46 +31,78 @@ int main(int argc, const char* argv[]) {
UDPSocket serverSocket(ASSIGNMENT_SERVER_PORT);
unsigned char assignmentPacket[MAX_PACKET_SIZE_BYTES];
int numSendHeaderBytes = populateTypeAndVersion(assignmentPacket, PACKET_TYPE_SEND_ASSIGNMENT);
int numSendHeaderBytes = populateTypeAndVersion(assignmentPacket, PACKET_TYPE_DEPLOY_ASSIGNMENT);
while (true) {
if (serverSocket.receive((sockaddr*) &senderSocket, &senderData, &receivedBytes)) {
int numHeaderBytes = numBytesForPacketHeader(senderData);
if (senderData[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) {
qDebug() << "Assignment request received.\n";
// grab the FI assignment in the queue, if it exists
if (senderData[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) {
// construct the requested assignment from the packet data
Assignment requestAssignment(senderData, receivedBytes);
qDebug() << "Received request for assignment:" << requestAssignment;
qDebug() << "Current queue size is" << assignmentQueue.size();
// make sure there are assignments in the queue at all
if (assignmentQueue.size() > 0) {
Assignment firstAssignment = assignmentQueue.front();
assignmentQueue.pop();
QString scriptURL = QString("http://base8-compute.s3.amazonaws.com/%1").arg(firstAssignment.scriptFilename);
std::deque<Assignment*>::iterator assignment = assignmentQueue.begin();
qDebug() << "Sending assignment with URL" << scriptURL << "\n";
int scriptURLBytes = scriptURL.size();
memcpy(assignmentPacket + numSendHeaderBytes, scriptURL.toLocal8Bit().constData(), scriptURLBytes);
// send the assignment
serverSocket.send((sockaddr*) &senderSocket, assignmentPacket, numHeaderBytes + scriptURLBytes);
// enumerate assignments until we find one to give this client (if possible)
while (assignment != assignmentQueue.end()) {
// if this assignment is stale then get rid of it and check the next one
if (usecTimestampNow() - usecTimestamp(&((*assignment)->getTime()))
>= NUM_DEFAULT_ASSIGNMENT_STALENESS_USECS) {
delete *assignment;
assignment = assignmentQueue.erase(assignment);
continue;
}
bool eitherHasPool = ((*assignment)->getPool() || requestAssignment.getPool());
bool bothHavePool = ((*assignment)->getPool() && requestAssignment.getPool());
// make sure there is a pool match for the created and requested assignment
// or that neither has a designated pool
if ((eitherHasPool && bothHavePool
&& strcmp((*assignment)->getPool(), requestAssignment.getPool()) == 0)
|| !eitherHasPool) {
int numAssignmentBytes = (*assignment)->packToBuffer(assignmentPacket + numSendHeaderBytes);
// send the assignment
serverSocket.send((sockaddr*) &senderSocket,
assignmentPacket,
numSendHeaderBytes + numAssignmentBytes);
// delete this assignment now that it has been sent out
delete *assignment;
// remove it from the deque and make the iterator the next assignment
assignmentQueue.erase(assignment);
// stop looping - we've handed out an assignment
break;
} else {
// push forward the iterator
assignment++;
}
}
}
} else if (senderData[0] == PACKET_TYPE_SEND_ASSIGNMENT) {
Assignment newAssignment;
} else if (senderData[0] == PACKET_TYPE_CREATE_ASSIGNMENT && packetVersionMatch(senderData)) {
// construct the create assignment from the packet data
Assignment* createdAssignment = new Assignment(senderData, receivedBytes);
senderData[receivedBytes] = '\0';
newAssignment.scriptFilename = QString((const char*)senderData + numHeaderBytes);
qDebug() << "Received a created assignment:" << *createdAssignment;
qDebug() << "Current queue size is" << assignmentQueue.size();
// assignment server is on a public server
// assume that the address we now have for the sender is the public address/port
// and store that with the assignment so it can be given to the requestor later
createdAssignment->setDomainSocket((sockaddr*) &senderSocket);
qDebug() << "Added an assignment with script with filename" << newAssignment.scriptFilename << "\n";
// add this assignment to the queue
// we're not a queue right now, only keep one assignment
if (assignmentQueue.size() > 0) {
assignmentQueue.pop();
}
assignmentQueue.push(newAssignment);
assignmentQueue.push_back(createdAssignment);
}
}
}

View file

@ -1,4 +0,0 @@
socket
sftp-config.json
.DS_Store
*.raw

View file

@ -1,27 +0,0 @@
cmake_minimum_required(VERSION 2.8)
set(ROOT_DIR ..)
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
# setup for find modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
set(TARGET_NAME audio-mixer)
include(${MACRO_DIR}/SetupHifiProject.cmake)
setup_hifi_project(${TARGET_NAME} TRUE)
# set up the external glm library
include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
# link the shared hifi library
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})
# link the stk library
set(STK_ROOT_DIR ${ROOT_DIR}/externals/stk)
find_package(STK REQUIRED)
target_link_libraries(${TARGET_NAME} ${STK_LIBRARIES})
include_directories(${STK_INCLUDE_DIRS})

View file

@ -2,7 +2,7 @@ MACRO(SETUP_HIFI_PROJECT TARGET INCLUDE_QT)
project(${TARGET})
# grab the implemenation and header files
file(GLOB TARGET_SRCS src/*.cpp src/*.h)
file(GLOB TARGET_SRCS src/*.cpp src/*.h src/*.c)
# add the executable
add_executable(${TARGET} ${TARGET_SRCS})

View file

@ -17,6 +17,7 @@
// M - Audio Mixer
//
#include <arpa/inet.h>
#include <fcntl.h>
#include <map>
#include <math.h>
@ -24,6 +25,7 @@
#include <stdio.h>
#include <stdlib.h>
#include "Assignment.h"
#include "NodeList.h"
#include "NodeTypes.h"
#include "Logstash.h"
@ -46,14 +48,13 @@ unsigned char* addNodeToBroadcastPacket(unsigned char* currentPosition, Node* no
return currentPosition;
}
int main(int argc, const char * argv[])
{
int main(int argc, char* const argv[]) {
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_DOMAIN, DOMAIN_LISTEN_PORT);
// If user asks to run in "local" mode then we do NOT replace the IP
// with the EC2 IP. Otherwise, we will replace the IP like we used to
// this allows developers to run a local domain without recompiling the
// domain server
bool isLocalMode = cmdOptionExists(argc, argv, "--local");
bool isLocalMode = cmdOptionExists(argc, (const char**) argv, "--local");
if (isLocalMode) {
printf("NOTE: Running in local mode!\n");
} else {
@ -84,7 +85,56 @@ int main(int argc, const char * argv[])
timeval lastStatSendTime = {};
// loop the parameters to see if we were passed a pool for assignment
int parameter = -1;
const char ALLOWED_PARAMETERS[] = "p::-local::";
const char POOL_PARAMETER_CHAR = 'p';
char* assignmentPool = NULL;
while ((parameter = getopt(argc, argv, ALLOWED_PARAMETERS)) != -1) {
if (parameter == POOL_PARAMETER_CHAR) {
// copy the passed assignment pool
int poolLength = strlen(optarg);
assignmentPool = new char[poolLength + sizeof(char)];
strcpy(assignmentPool, optarg);
}
}
// use a map to keep track of iterations of silence for assignment creation requests
const int ASSIGNMENT_SILENCE_MAX_ITERATIONS = 5;
std::map<Assignment*, int> assignmentSilenceCount;
// as a domain-server we will always want an audio mixer and avatar mixer
// setup the create assignments for those
Assignment audioAssignment(Assignment::Create, Assignment::AudioMixer, assignmentPool);
Assignment avatarAssignment(Assignment::Create, Assignment::AvatarMixer, assignmentPool);
while (true) {
if (!nodeList->soloNodeOfType(NODE_TYPE_AUDIO_MIXER)) {
if (assignmentSilenceCount[&audioAssignment] == ASSIGNMENT_SILENCE_MAX_ITERATIONS) {
nodeList->sendAssignment(audioAssignment);
assignmentSilenceCount[&audioAssignment] = 0;
} else {
assignmentSilenceCount[&audioAssignment]++;
}
} else {
assignmentSilenceCount[&audioAssignment] = 0;
}
if (!nodeList->soloNodeOfType(NODE_TYPE_AVATAR_MIXER)) {
if (assignmentSilenceCount[&avatarAssignment] == ASSIGNMENT_SILENCE_MAX_ITERATIONS) {
nodeList->sendAssignment(avatarAssignment);
assignmentSilenceCount[&avatarAssignment] = 0;
} else {
assignmentSilenceCount[&avatarAssignment]++;
}
} else {
assignmentSilenceCount[&avatarAssignment] = 0;
}
if (nodeList->getNodeSocket()->receive((sockaddr *)&nodePublicAddress, packetData, &receivedBytes) &&
(packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY || packetData[0] == PACKET_TYPE_DOMAIN_LIST_REQUEST) &&
packetVersionMatch(packetData)) {
@ -117,68 +167,71 @@ int main(int argc, const char * argv[])
nodeType,
nodeList->getLastNodeID());
if (newNode->getNodeID() == nodeList->getLastNodeID()) {
nodeList->increaseNodeID();
}
currentBufferPos = broadcastPacket + numHeaderBytes;
startPointer = currentBufferPos;
unsigned char* nodeTypesOfInterest = packetData + numBytesSenderHeader + sizeof(NODE_TYPE)
// if addOrUpdateNode returns NULL this was a solo node we already have, don't talk back to it
if (newNode) {
if (newNode->getNodeID() == nodeList->getLastNodeID()) {
nodeList->increaseNodeID();
}
currentBufferPos = broadcastPacket + numHeaderBytes;
startPointer = currentBufferPos;
unsigned char* nodeTypesOfInterest = packetData + numBytesSenderHeader + sizeof(NODE_TYPE)
+ numBytesSocket + sizeof(unsigned char);
int numInterestTypes = *(nodeTypesOfInterest - 1);
if (numInterestTypes > 0) {
// if the node has sent no types of interest, assume they want nothing but their own ID back
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (!node->matches((sockaddr*) &nodePublicAddress, (sockaddr*) &nodeLocalAddress, nodeType) &&
int numInterestTypes = *(nodeTypesOfInterest - 1);
if (numInterestTypes > 0) {
// if the node has sent no types of interest, assume they want nothing but their own ID back
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (!node->matches((sockaddr*) &nodePublicAddress, (sockaddr*) &nodeLocalAddress, nodeType) &&
memchr(nodeTypesOfInterest, node->getType(), numInterestTypes)) {
// this is not the node themselves
// and this is an node of a type in the passed node types of interest
// or the node did not pass us any specific types they are interested in
if (memchr(SOLO_NODE_TYPES, node->getType(), sizeof(SOLO_NODE_TYPES)) == NULL) {
// this is an node of which there can be multiple, just add them to the packet
// don't send avatar nodes to other avatars, that will come from avatar mixer
if (nodeType != NODE_TYPE_AGENT || node->getType() != NODE_TYPE_AGENT) {
currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, &(*node));
}
} else {
// solo node, we need to only send newest
if (newestSoloNodes[node->getType()] == NULL ||
newestSoloNodes[node->getType()]->getWakeMicrostamp() < node->getWakeMicrostamp()) {
// we have to set the newer solo node to add it to the broadcast later
newestSoloNodes[node->getType()] = &(*node);
// this is not the node themselves
// and this is an node of a type in the passed node types of interest
// or the node did not pass us any specific types they are interested in
if (memchr(SOLO_NODE_TYPES, node->getType(), sizeof(SOLO_NODE_TYPES)) == NULL) {
// this is an node of which there can be multiple, just add them to the packet
// don't send avatar nodes to other avatars, that will come from avatar mixer
if (nodeType != NODE_TYPE_AGENT || node->getType() != NODE_TYPE_AGENT) {
currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, &(*node));
}
} else {
// solo node, we need to only send newest
if (newestSoloNodes[node->getType()] == NULL ||
newestSoloNodes[node->getType()]->getWakeMicrostamp() < node->getWakeMicrostamp()) {
// we have to set the newer solo node to add it to the broadcast later
newestSoloNodes[node->getType()] = &(*node);
}
}
}
}
for (std::map<char, Node *>::iterator soloNode = newestSoloNodes.begin();
soloNode != newestSoloNodes.end();
soloNode++) {
// this is the newest alive solo node, add them to the packet
currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, soloNode->second);
}
}
for (std::map<char, Node *>::iterator soloNode = newestSoloNodes.begin();
soloNode != newestSoloNodes.end();
soloNode++) {
// this is the newest alive solo node, add them to the packet
currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, soloNode->second);
// update last receive to now
uint64_t timeNow = usecTimestampNow();
newNode->setLastHeardMicrostamp(timeNow);
if (packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY
&& memchr(SOLO_NODE_TYPES, nodeType, sizeof(SOLO_NODE_TYPES))) {
newNode->setWakeMicrostamp(timeNow);
}
// add the node ID to the end of the pointer
currentBufferPos += packNodeId(currentBufferPos, newNode->getNodeID());
// send the constructed list back to this node
nodeList->getNodeSocket()->send(destinationSocket,
broadcastPacket,
(currentBufferPos - startPointer) + numHeaderBytes);
}
// update last receive to now
uint64_t timeNow = usecTimestampNow();
newNode->setLastHeardMicrostamp(timeNow);
if (packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY
&& memchr(SOLO_NODE_TYPES, nodeType, sizeof(SOLO_NODE_TYPES))) {
newNode->setWakeMicrostamp(timeNow);
}
// add the node ID to the end of the pointer
currentBufferPos += packNodeId(currentBufferPos, newNode->getNodeID());
// send the constructed list back to this node
nodeList->getNodeSocket()->send(destinationSocket,
broadcastPacket,
(currentBufferPos - startPointer) + numHeaderBytes);
}
if (Logstash::shouldSendStats()) {

View file

@ -8,6 +8,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cm
set(TARGET_NAME audio)
# set up the external glm library
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
setup_hifi_library(${TARGET_NAME})
@ -15,4 +16,10 @@ include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
# link the stk library
set(STK_ROOT_DIR ${ROOT_DIR}/externals/stk)
find_package(STK REQUIRED)
target_link_libraries(${TARGET_NAME} ${STK_LIBRARIES})
include_directories(${STK_INCLUDE_DIRS})

View file

@ -1,9 +1,9 @@
//
// main.cpp
// mixer
// AudioMixer.cpp
// hifi
//
// Created by Stephen Birarda on 2/1/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
// Created by Stephen Birarda on 8/22/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include <errno.h>
@ -40,11 +40,13 @@
#include <SharedUtil.h>
#include <StdDev.h>
#include <AudioRingBuffer.h>
#include "AudioRingBuffer.h"
#include "AvatarAudioRingBuffer.h"
#include "InjectedAudioRingBuffer.h"
#include "AudioMixer.h"
const unsigned short MIXER_LISTEN_PORT = 55443;
const short JITTER_BUFFER_MSECS = 12;
@ -65,36 +67,21 @@ void attachNewBufferToNode(Node *newNode) {
}
}
bool wantLocalDomain = false;
int main(int argc, const char* argv[]) {
setvbuf(stdout, NULL, _IOLBF, 0);
void AudioMixer::run() {
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AUDIO_MIXER, MIXER_LISTEN_PORT);
// Handle Local Domain testing with the --local command line
const char* local = "--local";
::wantLocalDomain = cmdOptionExists(argc, argv,local);
if (::wantLocalDomain) {
printf("Local Domain MODE!\n");
nodeList->setDomainIPToLocalhost();
}
const char* domainIP = getCmdOption(argc, argv, "--domain");
if (domainIP) {
NodeList::getInstance()->setDomainHostname(domainIP);
}
NodeList *nodeList = NodeList::getInstance();
nodeList->setOwnerType(NODE_TYPE_AUDIO_MIXER);
ssize_t receivedBytes = 0;
nodeList->linkedDataCreateCallback = attachNewBufferToNode;
nodeList->startSilentNodeRemovalThread();
unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE];
sockaddr* nodeAddress = new sockaddr;
// make sure our node socket is non-blocking
nodeList->getNodeSocket()->setBlocking(false);
@ -123,6 +110,10 @@ int main(int argc, const char* argv[]) {
}
while (true) {
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
break;
}
if (Logstash::shouldSendStats()) {
gettimeofday(&beginSendTime, NULL);
}
@ -174,155 +165,155 @@ int main(int argc, const char* argv[]) {
float attenuationCoefficient = 1.0f;
int numSamplesDelay = 0;
float weakChannelAmplitudeRatio = 1.0f;
stk::TwoPole* otherNodeTwoPole = NULL;
// only do axis/distance attenuation when in normal mode
if (otherNode != node && nodeRingBuffer->getListeningMode() == AudioRingBuffer::NORMAL) {
glm::vec3 listenerPosition = nodeRingBuffer->getPosition();
glm::vec3 relativePosition = otherNodeBuffer->getPosition() - nodeRingBuffer->getPosition();
glm::quat inverseOrientation = glm::inverse(nodeRingBuffer->getOrientation());
float distanceSquareToSource = glm::dot(relativePosition, relativePosition);
float radius = 0.0f;
if (otherNode->getType() == NODE_TYPE_AUDIO_INJECTOR) {
InjectedAudioRingBuffer* injectedBuffer = (InjectedAudioRingBuffer*) otherNodeBuffer;
radius = injectedBuffer->getRadius();
attenuationCoefficient *= injectedBuffer->getAttenuationRatio();
}
if (radius == 0 || (distanceSquareToSource > radius * radius)) {
// this is either not a spherical source, or the listener is outside the sphere
if (radius > 0) {
// this is a spherical source - the distance used for the coefficient
// needs to be the closest point on the boundary to the source
// ovveride the distance to the node with the distance to the point on the
// boundary of the sphere
distanceSquareToSource -= (radius * radius);
} else {
// calculate the angle delivery for off-axis attenuation
glm::vec3 rotatedListenerPosition = glm::inverse(otherNodeBuffer->getOrientation())
* relativePosition;
* relativePosition;
float angleOfDelivery = glm::angle(glm::vec3(0.0f, 0.0f, -1.0f),
glm::normalize(rotatedListenerPosition));
const float MAX_OFF_AXIS_ATTENUATION = 0.2f;
const float OFF_AXIS_ATTENUATION_FORMULA_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f;
float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION +
(OFF_AXIS_ATTENUATION_FORMULA_STEP * (angleOfDelivery / 90.0f));
(OFF_AXIS_ATTENUATION_FORMULA_STEP * (angleOfDelivery / 90.0f));
// multiply the current attenuation coefficient by the calculated off axis coefficient
attenuationCoefficient *= offAxisCoefficient;
}
glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition;
const float DISTANCE_SCALE = 2.5f;
const float GEOMETRIC_AMPLITUDE_SCALAR = 0.3f;
const float DISTANCE_LOG_BASE = 2.5f;
const float DISTANCE_SCALE_LOG = logf(DISTANCE_SCALE) / logf(DISTANCE_LOG_BASE);
// calculate the distance coefficient using the distance to this node
float distanceCoefficient = powf(GEOMETRIC_AMPLITUDE_SCALAR,
DISTANCE_SCALE_LOG +
(0.5f * logf(distanceSquareToSource) / logf(DISTANCE_LOG_BASE)) - 1);
DISTANCE_SCALE_LOG +
(0.5f * logf(distanceSquareToSource) / logf(DISTANCE_LOG_BASE)) - 1);
distanceCoefficient = std::min(1.0f, distanceCoefficient);
// multiply the current attenuation coefficient by the distance coefficient
attenuationCoefficient *= distanceCoefficient;
// project the rotated source position vector onto the XZ plane
rotatedSourcePosition.y = 0.0f;
// produce an oriented angle about the y-axis
bearingRelativeAngleToSource = glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f),
glm::normalize(rotatedSourcePosition),
glm::vec3(0.0f, 1.0f, 0.0f));
const float PHASE_AMPLITUDE_RATIO_AT_90 = 0.5;
// figure out the number of samples of delay and the ratio of the amplitude
// in the weak channel for audio spatialization
float sinRatio = fabsf(sinf(glm::radians(bearingRelativeAngleToSource)));
numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio;
weakChannelAmplitudeRatio = 1 - (PHASE_AMPLITUDE_RATIO_AT_90 * sinRatio);
// grab the TwoPole object for this source, add it if it doesn't exist
TwoPoleNodeMap& nodeTwoPoles = nodeRingBuffer->getTwoPoles();
TwoPoleNodeMap::iterator twoPoleIterator = nodeTwoPoles.find(otherNode->getNodeID());
if (twoPoleIterator == nodeTwoPoles.end()) {
// setup the freeVerb effect for this source for this client
otherNodeTwoPole = nodeTwoPoles[otherNode->getNodeID()] = new stk::TwoPole;
} else {
otherNodeTwoPole = twoPoleIterator->second;
}
// calculate the reasonance for this TwoPole based on angle to source
float TWO_POLE_CUT_OFF_FREQUENCY = 800.0f;
float TWO_POLE_MAX_FILTER_STRENGTH = 0.4f;
otherNodeTwoPole->setResonance(TWO_POLE_CUT_OFF_FREQUENCY,
TWO_POLE_MAX_FILTER_STRENGTH
* fabsf(bearingRelativeAngleToSource) / 180.0f,
true);
TWO_POLE_MAX_FILTER_STRENGTH
* fabsf(bearingRelativeAngleToSource) / 180.0f,
true);
}
}
int16_t* sourceBuffer = otherNodeBuffer->getNextOutput();
int16_t* goodChannel = (bearingRelativeAngleToSource > 0.0f)
? clientSamples
: clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
? clientSamples
: clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
int16_t* delayedChannel = (bearingRelativeAngleToSource > 0.0f)
? clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL
: clientSamples;
? clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL
: clientSamples;
int16_t* delaySamplePointer = otherNodeBuffer->getNextOutput() == otherNodeBuffer->getBuffer()
? otherNodeBuffer->getBuffer() + RING_BUFFER_LENGTH_SAMPLES - numSamplesDelay
: otherNodeBuffer->getNextOutput() - numSamplesDelay;
? otherNodeBuffer->getBuffer() + RING_BUFFER_LENGTH_SAMPLES - numSamplesDelay
: otherNodeBuffer->getNextOutput() - numSamplesDelay;
for (int s = 0; s < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; s++) {
// load up the stkFrameBuffer with this source's samples
stkFrameBuffer[s] = (stk::StkFloat) sourceBuffer[s];
}
// perform the TwoPole effect on the stkFrameBuffer
if (otherNodeTwoPole) {
otherNodeTwoPole->tick(stkFrameBuffer);
}
for (int s = 0; s < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; s++) {
if (s < numSamplesDelay) {
// pull the earlier sample for the delayed channel
int earlierSample = delaySamplePointer[s] * attenuationCoefficient * weakChannelAmplitudeRatio;
delayedChannel[s] = glm::clamp(delayedChannel[s] + earlierSample,
MIN_SAMPLE_VALUE,
MAX_SAMPLE_VALUE);
}
int16_t currentSample = stkFrameBuffer[s] * attenuationCoefficient;
goodChannel[s] = glm::clamp(goodChannel[s] + currentSample,
MIN_SAMPLE_VALUE,
MAX_SAMPLE_VALUE);
if (s + numSamplesDelay < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) {
int sumSample = delayedChannel[s + numSamplesDelay]
+ (currentSample * weakChannelAmplitudeRatio);
+ (currentSample * weakChannelAmplitudeRatio);
delayedChannel[s + numSamplesDelay] = glm::clamp(sumSample,
MIN_SAMPLE_VALUE,
MAX_SAMPLE_VALUE);
}
if (s >= BUFFER_LENGTH_SAMPLES_PER_CHANNEL - PHASE_DELAY_AT_90) {
// this could be a delayed sample on the next pass
// so store the affected back in the ARB
@ -356,11 +347,11 @@ int main(int argc, const char* argv[]) {
packetVersionMatch(packetData)) {
if (packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO ||
packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO) {
unsigned char* currentBuffer = packetData + numBytesForPacketHeader(packetData);
uint16_t sourceID;
memcpy(&sourceID, currentBuffer, sizeof(sourceID));
Node* avatarNode = nodeList->addOrUpdateNode(nodeAddress,
nodeAddress,
NODE_TYPE_AGENT,
@ -377,7 +368,7 @@ int main(int argc, const char* argv[]) {
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getLinkedData()) {
InjectedAudioRingBuffer* ringBuffer = (InjectedAudioRingBuffer*) node->getLinkedData();
if (memcmp(ringBuffer->getStreamIdentifier(),
packetData + numBytesForPacketHeader(packetData),
@ -400,8 +391,8 @@ int main(int argc, const char* argv[]) {
// give the new audio data to the matching injector node
nodeList->updateNodeWithData(matchingInjector, packetData, receivedBytes);
} else if (packetData[0] == PACKET_TYPE_PING) {
} else if (packetData[0] == PACKET_TYPE_PING || packetData[0] == PACKET_TYPE_DOMAIN) {
// If the packet is a ping, let processNodeData handle it.
nodeList->processNodeData(nodeAddress, packetData, receivedBytes);
}
@ -414,7 +405,7 @@ int main(int argc, const char* argv[]) {
gettimeofday(&endSendTime, NULL);
float percentageOfMaxElapsed = ((float) (usecTimestamp(&endSendTime) - usecTimestamp(&beginSendTime))
/ BUFFER_SEND_INTERVAL_USECS) * 100.0f;
/ BUFFER_SEND_INTERVAL_USECS) * 100.0f;
sumFrameTimePercentages += percentageOfMaxElapsed;
@ -429,6 +420,4 @@ int main(int argc, const char* argv[]) {
std::cout << "Took too much time, not sleeping!\n";
}
}
return 0;
}
}

View file

@ -0,0 +1,17 @@
//
// AudioMixer.h
// hifi
//
// Created by Stephen Birarda on 8/22/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#ifndef __hifi__AudioMixer__
#define __hifi__AudioMixer__
class AudioMixer {
public:
static void run();
};
#endif /* defined(__hifi__AudioMixer__) */

View file

@ -9,7 +9,7 @@
#ifndef __hifi__InjectedAudioRingBuffer__
#define __hifi__InjectedAudioRingBuffer__
#include <AudioInjector.h>
#include "AudioInjector.h"
#include "PositionalAudioRingBuffer.h"

View file

@ -12,7 +12,7 @@
#include <vector>
#include <glm/gtx/quaternion.hpp>
#include <AudioRingBuffer.h>
#include "AudioRingBuffer.h"
class PositionalAudioRingBuffer : public AudioRingBuffer {
public:

View file

@ -1,44 +1,26 @@
//
// main.cpp
// Avatar Mixer
// AvatarMixer.cpp
// hifi
//
// Created by Leonardo Murillo on 03/25/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved
// Created by Stephen Birarda on 9/5/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
// Original avatar-mixer main created by Leonardo Murillo on 03/25/13.
//
// The avatar mixer receives head, hand and positional data from all connected
// nodes, and broadcasts that data back to them, every BROADCAST_INTERVAL ms.
//
//
#include <iostream>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>
#include <fstream>
#include <limits>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <NodeList.h>
#include <SharedUtil.h>
#include <PacketHeaders.h>
#include <NodeTypes.h>
#include <StdDev.h>
#include <UDPSocket.h>
#include <SharedUtil.h>
#include "AvatarData.h"
const int AVATAR_LISTEN_PORT = 55444;
#include "AvatarMixer.h"
unsigned char* addNodeToBroadcastPacket(unsigned char *currentPosition, Node *nodeToAdd) {
currentPosition += packNodeId(currentPosition, nodeToAdd->getNodeID());
AvatarData *nodeData = (AvatarData *)nodeToAdd->getLinkedData();
currentPosition += nodeData->getBroadcastData(currentPosition);
@ -55,7 +37,7 @@ void attachAvatarDataToNode(Node* newNode) {
// 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present
// if the avatar is not in view or in the keyhole.
// 2) after culling for view frustum, sort order the avatars by distance, send the closest ones first.
// 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
// 4) we should optimize the avatar data format to be more compact (100 bytes is pretty wasteful).
void broadcastAvatarData(NodeList* nodeList, sockaddr* nodeAddress) {
@ -66,13 +48,13 @@ void broadcastAvatarData(NodeList* nodeList, sockaddr* nodeAddress) {
unsigned char* currentBufferPosition = broadcastPacket + numHeaderBytes;
int packetLength = currentBufferPosition - broadcastPacket;
int packetsSent = 0;
// send back a packet with other active node data to this node
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getLinkedData() && !socketMatch(nodeAddress, node->getActiveSocket())) {
unsigned char* avatarDataEndpoint = addNodeToBroadcastPacket((unsigned char*)&avatarDataBuffer[0], &*node);
int avatarDataLength = avatarDataEndpoint - (unsigned char*)&avatarDataBuffer;
if (avatarDataLength + packetLength <= MAX_PACKET_SIZE) {
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
packetLength += avatarDataLength;
@ -98,17 +80,9 @@ void broadcastAvatarData(NodeList* nodeList, sockaddr* nodeAddress) {
nodeList->getNodeSocket()->send(nodeAddress, broadcastPacket, currentBufferPosition - broadcastPacket);
}
int main(int argc, const char* argv[]) {
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AVATAR_MIXER, AVATAR_LISTEN_PORT);
setvbuf(stdout, NULL, _IOLBF, 0);
// Handle Local Domain testing with the --local command line
const char* local = "--local";
if (cmdOptionExists(argc, argv, local)) {
printf("Local Domain MODE!\n");
nodeList->setDomainIPToLocalhost();
}
void AvatarMixer::run() {
NodeList* nodeList = NodeList::getInstance();
nodeList->setOwnerType(NODE_TYPE_AVATAR_MIXER);
nodeList->linkedDataCreateCallback = attachAvatarDataToNode;
@ -119,16 +93,19 @@ int main(int argc, const char* argv[]) {
unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE];
uint16_t nodeID = 0;
Node* avatarNode = NULL;
timeval lastDomainServerCheckIn = {};
// we only need to hear back about avatar nodes from the DS
NodeList::getInstance()->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1);
nodeList->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1);
while (true) {
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
break;
}
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
gettimeofday(&lastDomainServerCheckIn, NULL);
@ -162,9 +139,6 @@ int main(int argc, const char* argv[]) {
}
}
break;
case PACKET_TYPE_DOMAIN:
// ignore the DS packet, for now nodes are added only when they communicate directly with us
break;
default:
// hand this off to the NodeList
nodeList->processNodeData(nodeAddress, packetData, receivedBytes);
@ -174,6 +148,4 @@ int main(int argc, const char* argv[]) {
}
nodeList->stopSilentNodeRemovalThread();
return 0;
}
}

View file

@ -0,0 +1,19 @@
//
// AvatarMixer.h
// hifi
//
// Created by Stephen Birarda on 9/5/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#ifndef __hifi__AvatarMixer__
#define __hifi__AvatarMixer__
#include <iostream>
class AvatarMixer {
public:
static void run();
};
#endif /* defined(__hifi__AvatarMixer__) */

View file

@ -0,0 +1,138 @@
//
// Assignment.cpp
// hifi
//
// Created by Stephen Birarda on 8/22/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include <sys/time.h>
#include "PacketHeaders.h"
#include "Assignment.h"
const char IPv4_ADDRESS_DESIGNATOR = 4;
const char IPv6_ADDRESS_DESIGNATOR = 6;
Assignment::Assignment(Assignment::Direction direction, Assignment::Type type, const char* pool) :
_direction(direction),
_type(type),
_pool(NULL),
_domainSocket(NULL)
{
// set the create time on this assignment
gettimeofday(&_time, NULL);
// copy the pool, if we got one
if (pool) {
int poolLength = strlen(pool);
// create the char array and make it large enough for string and null termination
_pool = new char[poolLength + sizeof(char)];
strcpy(_pool, pool);
}
}
Assignment::Assignment(const unsigned char* dataBuffer, int numBytes) :
_pool(NULL),
_domainSocket(NULL)
{
// set the create time on this assignment
gettimeofday(&_time, NULL);
int numBytesRead = 0;
if (dataBuffer[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) {
_direction = Assignment::Request;
} else if (dataBuffer[0] == PACKET_TYPE_CREATE_ASSIGNMENT) {
_direction = Assignment::Create;
} else if (dataBuffer[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT) {
_direction = Assignment::Deploy;
}
numBytesRead += numBytesForPacketHeader(dataBuffer);
memcpy(&_type, dataBuffer + numBytesRead, sizeof(Assignment::Type));
numBytesRead += sizeof(Assignment::Type);
if (dataBuffer[numBytesRead] != 0) {
int poolLength = strlen((const char*) dataBuffer + numBytesRead);
_pool = new char[poolLength + sizeof(char)];
strcpy(_pool, (char*) dataBuffer + numBytesRead);
numBytesRead += poolLength + sizeof(char);
} else {
numBytesRead += sizeof(char);
}
if (numBytes > numBytesRead) {
if (dataBuffer[numBytesRead++] == IPv4_ADDRESS_DESIGNATOR) {
// IPv4 address
sockaddr_in destinationSocket = {};
memcpy(&destinationSocket, dataBuffer + numBytesRead, sizeof(sockaddr_in));
destinationSocket.sin_family = AF_INET;
setDomainSocket((sockaddr*) &destinationSocket);
} else {
// IPv6 address
sockaddr_in6 destinationSocket = {};
memcpy(&destinationSocket, dataBuffer + numBytesRead, sizeof(sockaddr_in6));
setDomainSocket((sockaddr*) &destinationSocket);
}
}
}
Assignment::~Assignment() {
delete _domainSocket;
delete _pool;
}
int Assignment::packToBuffer(unsigned char* buffer) {
int numPackedBytes = 0;
memcpy(buffer + numPackedBytes, &_type, sizeof(_type));
numPackedBytes += sizeof(_type);
if (_pool) {
int poolLength = strlen(_pool);
strcpy((char*) buffer + numPackedBytes, _pool);
numPackedBytes += poolLength + sizeof(char);
} else {
buffer[numPackedBytes] = '\0';
numPackedBytes += sizeof(char);
}
if (_domainSocket) {
buffer[numPackedBytes++] = (_domainSocket->sa_family == AF_INET) ? IPv4_ADDRESS_DESIGNATOR : IPv6_ADDRESS_DESIGNATOR;
int numSocketBytes = (_domainSocket->sa_family == AF_INET) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
memcpy(buffer + numPackedBytes, _domainSocket, numSocketBytes);
numPackedBytes += numSocketBytes;
}
return numPackedBytes;
}
void Assignment::setDomainSocket(const sockaddr* domainSocket) {
if (_domainSocket) {
// delete the old _domainSocket if it exists
delete _domainSocket;
_domainSocket = NULL;
}
// create a new sockaddr or sockaddr_in depending on what type of address this is
if (domainSocket->sa_family == AF_INET) {
_domainSocket = (sockaddr*) new sockaddr_in;
memcpy(_domainSocket, domainSocket, sizeof(sockaddr_in));
} else {
_domainSocket = (sockaddr*) new sockaddr_in6;
memcpy(_domainSocket, domainSocket, sizeof(sockaddr_in6));
}
}
QDebug operator<<(QDebug debug, const Assignment &assignment) {
debug << "T:" << assignment.getType() << "P:" << assignment.getPool();
return debug.nospace();
}

View file

@ -0,0 +1,54 @@
//
// Assignment.h
// hifi
//
// Created by Stephen Birarda on 8/22/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#ifndef __hifi__Assignment__
#define __hifi__Assignment__
#include "NodeList.h"
class Assignment {
public:
enum Type {
AudioMixer,
AvatarMixer,
All
};
enum Direction {
Create,
Deploy,
Request
};
Assignment(Assignment::Direction direction, Assignment::Type type, const char* pool = NULL);
Assignment(const unsigned char* dataBuffer, int numBytes);
~Assignment();
Assignment::Direction getDirection() const { return _direction; }
Assignment::Type getType() const { return _type; }
const char* getPool() const { return _pool; }
const timeval& getTime() const { return _time; }
const sockaddr* getDomainSocket() { return _domainSocket; }
void setDomainSocket(const sockaddr* domainSocket);
int packToBuffer(unsigned char* buffer);
private:
Assignment::Direction _direction;
Assignment::Type _type;
char* _pool;
sockaddr* _domainSocket;
timeval _time;
};
QDebug operator<<(QDebug debug, const Assignment &assignment);
#endif /* defined(__hifi__Assignment__) */

View file

@ -13,6 +13,7 @@
#include <QtCore/QDebug>
#include "Assignment.h"
#include "NodeList.h"
#include "NodeTypes.h"
#include "PacketHeaders.h"
@ -63,7 +64,8 @@ NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) :
_ownerType(newOwnerType),
_nodeTypesOfInterest(NULL),
_ownerID(UNKNOWN_NODE_ID),
_lastNodeID(UNKNOWN_NODE_ID + 1)
_lastNodeID(UNKNOWN_NODE_ID + 1),
_numNoReplyDomainCheckIns(0)
{
memcpy(_domainHostname, DEFAULT_DOMAIN_HOSTNAME, sizeof(DEFAULT_DOMAIN_HOSTNAME));
memcpy(_domainIP, DEFAULT_DOMAIN_IP, sizeof(DEFAULT_DOMAIN_IP));
@ -328,9 +330,15 @@ void NodeList::sendDomainServerCheckIn() {
}
_nodeSocket.send(_domainIP, DEFAULT_DOMAINSERVER_PORT, checkInPacket, checkInPacketSize);
// increment the count of un-replied check-ins
_numNoReplyDomainCheckIns++;
}
int NodeList::processDomainServerList(unsigned char* packetData, size_t dataBytes) {
// this is a packet from the domain server, reset the count of un-replied check-ins
_numNoReplyDomainCheckIns = 0;
int readNodes = 0;
char nodeType;
@ -366,13 +374,21 @@ int NodeList::processDomainServerList(unsigned char* packetData, size_t dataByte
return readNodes;
}
void NodeList::sendAssignmentRequest() {
const char ASSIGNMENT_SERVER_HOSTNAME[] = "assignment.highfidelity.io";
const char ASSIGNMENT_SERVER_HOSTNAME[] = "assignment.highfidelity.io";
const sockaddr_in assignmentServerSocket = socketForHostnameAndHostOrderPort(ASSIGNMENT_SERVER_HOSTNAME,
ASSIGNMENT_SERVER_PORT);
void NodeList::sendAssignment(Assignment& assignment) {
unsigned char assignmentPacket[MAX_PACKET_SIZE];
static sockaddr_in assignmentServerSocket = socketForHostname(ASSIGNMENT_SERVER_HOSTNAME);
assignmentServerSocket.sin_port = htons(ASSIGNMENT_SERVER_PORT);
PACKET_TYPE assignmentPacketType = assignment.getDirection() == Assignment::Create
? PACKET_TYPE_CREATE_ASSIGNMENT
: PACKET_TYPE_REQUEST_ASSIGNMENT;
_nodeSocket.send((sockaddr*) &assignmentServerSocket, &PACKET_TYPE_REQUEST_ASSIGNMENT, 1);
int numHeaderBytes = populateTypeAndVersion(assignmentPacket, assignmentPacketType);
int numAssignmentBytes = assignment.packToBuffer(assignmentPacket + numHeaderBytes);
_nodeSocket.send((sockaddr*) &assignmentServerSocket, assignmentPacket, numHeaderBytes + numAssignmentBytes);
}
Node* NodeList::addOrUpdateNode(sockaddr* publicSocket, sockaddr* localSocket, char nodeType, uint16_t nodeId) {
@ -385,9 +401,14 @@ Node* NodeList::addOrUpdateNode(sockaddr* publicSocket, sockaddr* localSocket, c
break;
}
}
}
}
if (node == end()) {
// if we already had this node AND it's a solo type then bust out of here
if (soloNodeOfType(nodeType)) {
return NULL;
}
// we didn't have this node, so add them
Node* newNode = new Node(publicSocket, localSocket, nodeType, nodeId);

View file

@ -28,7 +28,6 @@ const int MAX_NUM_NODES = 10000;
const int NODES_PER_BUCKET = 100;
const int MAX_PACKET_SIZE = 1500;
const unsigned short int NODE_SOCKET_LISTEN_PORT = 40103;
const int NODE_SILENCE_THRESHOLD_USECS = 2 * 1000000;
const int DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000;
@ -43,6 +42,9 @@ extern const int DEFAULT_DOMAINSERVER_PORT;
const int UNKNOWN_NODE_ID = 0;
const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5;
class Assignment;
class NodeListIterator;
// Callers who want to hook add/kill callbacks should implement this class
@ -55,7 +57,7 @@ public:
class NodeList {
public:
static NodeList* createInstance(char ownerType, unsigned short int socketListenPort = NODE_SOCKET_LISTEN_PORT);
static NodeList* createInstance(char ownerType, unsigned short int socketListenPort = 0);
static NodeList* getInstance();
typedef NodeListIterator iterator;
@ -88,6 +90,8 @@ public:
int size() { return _numNodes; }
int getNumAliveNodes() const;
int getNumNoReplyDomainCheckIns() const { return _numNoReplyDomainCheckIns; }
void clear();
void setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest);
@ -95,7 +99,7 @@ public:
void sendDomainServerCheckIn();
int processDomainServerList(unsigned char *packetData, size_t dataBytes);
void sendAssignmentRequest();
void sendAssignment(Assignment& assignment);
Node* nodeWithAddress(sockaddr *senderAddress);
Node* nodeWithID(uint16_t nodeID);
@ -146,6 +150,7 @@ private:
uint16_t _lastNodeID;
pthread_t removeSilentNodesThread;
pthread_t checkInWithDomainServerThread;
int _numNoReplyDomainCheckIns;
void handlePingReply(sockaddr *nodeAddress);
void timePingReply(sockaddr *nodeAddress, unsigned char *packetData);

View file

@ -67,7 +67,7 @@ int numBytesForPacketVersion(const unsigned char* packetVersion) {
}
}
int numBytesForPacketHeader(unsigned char* packetHeader) {
int numBytesForPacketHeader(const unsigned char* packetHeader) {
// int numBytesType = numBytesForPacketType(packetHeader);
// return numBytesType + numBytesForPacketVersion(packetHeader + numBytesType);

View file

@ -36,7 +36,8 @@ const PACKET_TYPE PACKET_TYPE_ENVIRONMENT_DATA = 'e';
const PACKET_TYPE PACKET_TYPE_DOMAIN_LIST_REQUEST = 'L';
const PACKET_TYPE PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY = 'C';
const PACKET_TYPE PACKET_TYPE_REQUEST_ASSIGNMENT = 'r';
const PACKET_TYPE PACKET_TYPE_SEND_ASSIGNMENT = 's';
const PACKET_TYPE PACKET_TYPE_CREATE_ASSIGNMENT = 's';
const PACKET_TYPE PACKET_TYPE_DEPLOY_ASSIGNMENT = 'd';
const PACKET_TYPE PACKET_TYPE_VOXEL_STATS = '#';
const PACKET_TYPE PACKET_TYPE_VOXEL_JURISDICTION = 'J';
const PACKET_TYPE PACKET_TYPE_VOXEL_JURISDICTION_REQUEST = 'j';
@ -48,7 +49,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type);
bool packetVersionMatch(unsigned char* packetHeader);
int populateTypeAndVersion(unsigned char* destinationHeader, PACKET_TYPE type);
int numBytesForPacketHeader(unsigned char* packetHeader);
int numBytesForPacketHeader(const unsigned char* packetHeader);
const int MAX_PACKET_HEADER_BYTES = sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION);
@ -57,6 +58,6 @@ const int MAX_PACKET_HEADER_BYTES = sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION)
#define ADD_SCENE_COMMAND "add scene"
#define TEST_COMMAND "a message"
const int ASSIGNMENT_SERVER_PORT = 7007;
const unsigned short ASSIGNMENT_SERVER_PORT = 7007;
#endif

View file

@ -26,7 +26,7 @@
#include "PacketHeaders.h"
#include "SharedUtil.h"
uint64_t usecTimestamp(timeval *time) {
uint64_t usecTimestamp(const timeval *time) {
return (time->tv_sec * 1000000 + time->tv_usec);
}

View file

@ -38,7 +38,7 @@ static const float DECIMETER = 0.1f;
static const float CENTIMETER = 0.01f;
static const float MILLIIMETER = 0.001f;
uint64_t usecTimestamp(timeval *time);
uint64_t usecTimestamp(const timeval *time);
uint64_t usecTimestampNow();
float randFloat();

View file

@ -6,6 +6,7 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <arpa/inet.h>
#include <cstdio>
#include <errno.h>
#include <fcntl.h>
@ -118,14 +119,18 @@ unsigned short loadBufferWithSocketInfo(char* addressBuffer, sockaddr* socket) {
}
}
sockaddr_in socketForHostname(const char* hostname) {
sockaddr_in socketForHostnameAndHostOrderPort(const char* hostname, unsigned short port) {
struct hostent* pHostInfo;
sockaddr_in newSocket;
sockaddr_in newSocket = {};
if ((pHostInfo = gethostbyname(hostname))) {
memcpy(&newSocket.sin_addr, pHostInfo->h_addr_list[0], pHostInfo->h_length);
}
if (port != 0) {
newSocket.sin_port = htons(port);
}
return newSocket;
}
@ -162,11 +167,8 @@ UDPSocket::UDPSocket(unsigned short int listeningPort) :
_listeningPort = ntohs(bind_address.sin_port);
}
// set timeout on socket recieve to 0.5 seconds
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500000;
setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv);
const int DEFAULT_BLOCKING_SOCKET_TIMEOUT_USECS = 0.5 * 1000000;
setBlockingReceiveTimeoutInUsecs(DEFAULT_BLOCKING_SOCKET_TIMEOUT_USECS);
qDebug("Created UDP socket listening on port %hu.\n", _listeningPort);
}
@ -223,6 +225,11 @@ void UDPSocket::setBlocking(bool blocking) {
#endif
}
void UDPSocket::setBlockingReceiveTimeoutInUsecs(int timeoutUsecs) {
struct timeval tv = {timeoutUsecs / 1000000, timeoutUsecs % 1000000};
setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
}
// Receive data on this socket with retrieving address of sender
bool UDPSocket::receive(void* receivedData, ssize_t* receivedBytes) const {
return receive((sockaddr*) &senderAddress, receivedData, receivedBytes);

View file

@ -22,12 +22,17 @@ class UDPSocket {
public:
UDPSocket(unsigned short int listeningPort);
~UDPSocket();
bool init();
unsigned short int getListeningPort() const { return _listeningPort; }
void setBlocking(bool blocking);
bool isBlocking() const { return blocking; }
void setBlockingReceiveTimeoutInUsecs(int timeoutUsecs);
int send(sockaddr* destAddress, const void* data, size_t byteLength) const;
int send(char* destAddress, int destPort, const void* data, size_t byteLength) const;
bool receive(void* receivedData, ssize_t* receivedBytes) const;
bool receive(sockaddr* recvAddress, void* receivedData, ssize_t* receivedBytes) const;
private:
@ -42,6 +47,6 @@ int packSocket(unsigned char* packStore, sockaddr* socketToPack);
int unpackSocket(unsigned char* packedData, sockaddr* unpackDestSocket);
int getLocalAddress();
unsigned short loadBufferWithSocketInfo(char* addressBuffer, sockaddr* socket);
sockaddr_in socketForHostname(const char* hostname);
sockaddr_in socketForHostnameAndHostOrderPort(const char* hostname, unsigned short port = 0);
#endif /* defined(__interface__UDPSocket__) */