diff --git a/injector/src/main.cpp b/injector/src/main.cpp index df751f50a1..32a8442d8f 100644 --- a/injector/src/main.cpp +++ b/injector/src/main.cpp @@ -14,14 +14,16 @@ #include #include +#include +#include +#include #include #include #include #include #include -char EC2_WEST_AUDIO_SERVER[] = "54.241.92.53"; -const int AUDIO_UDP_LISTEN_PORT = 55443; +const int AVATAR_MIXER_DATA_SEND_INTERVAL_MSECS = 15; const int DEFAULT_INJECTOR_VOLUME = 0xFF; @@ -30,12 +32,12 @@ bool loopAudio = true; float sleepIntervalMin = 1.00; float sleepIntervalMax = 2.00; char *sourceAudioFile = NULL; -const char *allowedParameters = ":rb::t::c::a::f:"; +const char *allowedParameters = ":rb::t::c::a::f::d:"; float floatArguments[4] = {0.0f, 0.0f, 0.0f, 0.0f}; unsigned char volume = DEFAULT_INJECTOR_VOLUME; +float triggerDistance = 0; -void usage(void) -{ +void usage(void) { std::cout << "High Fidelity - Interface audio injector" << std::endl; std::cout << " -r Random sleep mode. If not specified will default to constant loop." << std::endl; std::cout << " -b FLOAT Min. number of seconds to sleep. Only valid in random sleep mode. Default 1.0" << std::endl; @@ -43,10 +45,10 @@ void usage(void) std::cout << " -c FLOAT,FLOAT,FLOAT,FLOAT X,Y,Z,YAW position in universe where audio will be originating from and direction. Defaults to 0,0,0,0" << std::endl; std::cout << " -a 0-255 Attenuation curve modifier, defaults to 255" << std::endl; std::cout << " -f FILENAME Name of audio source file. Required - RAW format, 22050hz 16bit signed mono" << std::endl; + std::cout << " -d FLOAT Trigger distance for injection. If not specified will loop constantly" << std::endl; } -bool processParameters(int parameterCount, char* parameterData[]) -{ +bool processParameters(int parameterCount, char* parameterData[]) { int p; while ((p = getopt(parameterCount, parameterData, allowedParameters)) != -1) { switch (p) { @@ -86,6 +88,10 @@ bool processParameters(int parameterCount, char* parameterData[]) ::volume = atoi(optarg); std::cout << "[DEBUG] Attenuation modifier: " << optarg << std::endl; break; + case 'd': + ::triggerDistance = atof(optarg); + std::cout << "[DEBUG] Trigger distance: " << optarg << std::endl; + break; default: usage(); return false; @@ -94,45 +100,149 @@ bool processParameters(int parameterCount, char* parameterData[]) return true; }; -int main(int argc, char* argv[]) { +bool stopReceiveAgentDataThread; +void *receiveAgentData(void *args) { + sockaddr senderAddress; + ssize_t bytesReceived; + unsigned char incomingPacket[MAX_PACKET_SIZE]; + + AgentList* agentList = AgentList::getInstance(); + + while (!::stopReceiveAgentDataThread) { + if (agentList->getAgentSocket()->receive(&senderAddress, incomingPacket, &bytesReceived)) { + switch (incomingPacket[0]) { + case PACKET_HEADER_BULK_AVATAR_DATA: + // this is the positional data for other agents + // pass that off to the agentList processBulkAgentData method + agentList->processBulkAgentData(&senderAddress, incomingPacket, bytesReceived); + break; + default: + // have the agentList handle list of agents from DS, replies from other agents, etc. + agentList->processAgentData(&senderAddress, incomingPacket, bytesReceived); + break; + } + } + } + + pthread_exit(0); + return NULL; +} + +void createAvatarDataForAgent(Agent* agent) { + if (!agent->getLinkedData()) { + agent->setLinkedData(new AvatarData(agent)); + } +} + +int main(int argc, char* argv[]) { + // new seed for random audio sleep times srand(time(0)); + int AUDIO_UDP_SEND_PORT = 1500 + (rand() % (int)(1500 - 2000 + 1)); - UDPSocket streamSocket(AUDIO_UDP_SEND_PORT); - - sockaddr_in mixerSocket; - mixerSocket.sin_family = AF_INET; - mixerSocket.sin_addr.s_addr = inet_addr(EC2_WEST_AUDIO_SERVER); - mixerSocket.sin_port = htons((uint16_t)AUDIO_UDP_LISTEN_PORT); - - - if (processParameters(argc, argv)) { + if (processParameters(argc, argv)) { if (::sourceAudioFile == NULL) { std::cout << "[FATAL] Source audio file not specified" << std::endl; exit(-1); } else { AudioInjector injector(sourceAudioFile); + // create an AgentList instance to handle communication with other agents + AgentList* agentList = AgentList::createInstance(AGENT_TYPE_AUDIO_INJECTOR, AUDIO_UDP_SEND_PORT); + + pthread_t receiveAgentDataThread; + pthread_create(&receiveAgentDataThread, NULL, receiveAgentData, NULL); + + // start telling the domain server that we are alive + agentList->startDomainServerCheckInThread(); + + // start the agent list thread that will kill off agents when they stop talking + agentList->startSilentAgentRemovalThread(); + injector.setPosition(glm::vec3(::floatArguments[0], ::floatArguments[1], ::floatArguments[2])); injector.setBearing(*(::floatArguments + 3)); injector.setVolume(::volume); - - float delay = 0; - int usecDelay = 0; + + // register the callback for agent data creation + agentList->linkedDataCreateCallback = createAvatarDataForAgent; + + unsigned char broadcastPacket = PACKET_HEADER_INJECT_AUDIO; + + timeval thisSend; + double numMicrosecondsSleep = 0; while (true) { - injector.injectAudio(&streamSocket, (sockaddr*) &mixerSocket); - - if (!::loopAudio) { - delay = randFloatInRange(::sleepIntervalMin, ::sleepIntervalMax); - usecDelay = delay * 1000 * 1000; - usleep(usecDelay); + if (::triggerDistance) { + + // update the thisSend timeval to the current time + gettimeofday(&thisSend, NULL); + + // find the current avatar mixer + Agent* avatarMixer = agentList->soloAgentOfType(AGENT_TYPE_AVATAR_MIXER); + + // make sure we actually have an avatar mixer with an active socket + if (avatarMixer && avatarMixer->getActiveSocket() != NULL) { + // use the UDPSocket instance attached to our agent list to ask avatar mixer for a list of avatars + agentList->getAgentSocket()->send(avatarMixer->getActiveSocket(), + &broadcastPacket, + sizeof(broadcastPacket)); + } + + if (!injector.isInjectingAudio()) { + // enumerate the other agents to decide if one is close enough that we should inject + for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) { + AvatarData* avatarData = (AvatarData*) agent->getLinkedData(); + + if (avatarData) { + glm::vec3 tempVector = injector.getPosition() - avatarData->getPosition(); + float squareDistance = glm::dot(tempVector, tempVector); + + if (squareDistance <= ::triggerDistance) { + // look for an audio mixer in our agent list + Agent* audioMixer = AgentList::getInstance()->soloAgentOfType(AGENT_TYPE_AUDIO_MIXER); + + if (audioMixer) { + // we have an active audio mixer we can send data to + AudioInjectionManager::threadInjector(&injector); + } + } + } + } + } + + // sleep for the correct amount of time to have data send be consistently timed + if ((numMicrosecondsSleep = (AVATAR_MIXER_DATA_SEND_INTERVAL_MSECS * 1000) - + (usecTimestampNow() - usecTimestamp(&thisSend))) > 0) { + usleep(numMicrosecondsSleep); + } + } else { + // look for an audio mixer in our agent list + Agent* audioMixer = AgentList::getInstance()->soloAgentOfType(AGENT_TYPE_AUDIO_MIXER); + + if (audioMixer) { + injector.injectAudio(agentList->getAgentSocket(), audioMixer->getActiveSocket()); + } + + float delay = 0; + int usecDelay = 0; + + if (!::loopAudio) { + delay = randFloatInRange(::sleepIntervalMin, ::sleepIntervalMax); + usecDelay = delay * 1000 * 1000; + usleep(usecDelay); + } } } - } + + // stop the receive agent data thread + stopReceiveAgentDataThread = true; + pthread_join(receiveAgentDataThread, NULL); + + // stop the agent list's threads + agentList->stopDomainServerCheckInThread(); + agentList->stopSilentAgentRemovalThread(); + } } - return 0; } -