//*****************************************************************
/*
JackTrip: A System for High-Quality Audio Network Performance
over the Internet
Copyright (c) 2008 Juan-Pablo Caceres, Chris Chafe.
SoundWIRE group at CCRMA, Stanford University.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
//*****************************************************************
/**
* \file DataProtocol.cpp
* \author Juan-Pablo Caceres
* \date June 2008
*/
#include "DataProtocol.h"
#include "jacktrip_globals.h"
#include "JackAudioInterface.h"
#include "PacketHeader.h"
#include <iostream>
#include <cstdlib>
#include <QHostInfo>
#include <QHostAddress>
using std::cout; using std::endl;
//*******************************************************************************
DataProtocol::DataProtocol(const runModeT runmode,
const packetHeaderTypeT headertype) :
mRunMode(runmode), mStopped(false), mHasPacketsToReceive(false), mHeader(NULL)
{
//--------PROTOTYPE-------------------------
if ( headertype == DEFAULT ) {
mHeader = new DefaultHeader;
}
else if ( headertype == JAMLINK ) {
mHeader = new JamLinkHeader;
}
//------------------------------------------
// Base ports gInputPort_0 and gOutputPort_0defined at globals.h
if (mRunMode == RECEIVER) {
mLocalPort = gInputPort_0;
mPeerPort = gOutputPort_0;
}
else if (mRunMode == SENDER) {
mLocalPort = gOutputPort_0;
mPeerPort = gInputPort_0;
}
this->setLocalIPv4Address();
}
//*******************************************************************************
DataProtocol::~DataProtocol()
{
delete mHeader;
}
//*******************************************************************************
void DataProtocol::stop()
{
mStopped = true;
}
//*******************************************************************************
void DataProtocol::setLocalIPv4Address()
{
bzero(&mLocalIPv4Addr, sizeof(mLocalIPv4Addr));
mLocalIPv4Addr.sin_family = AF_INET;//AF_INET: IPv4 Protocol
mLocalIPv4Addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY: let the kernel decide the active address
mLocalIPv4Addr.sin_port = htons(mLocalPort);//set local port
//std::cout << "mLocalPort = " << mLocalPort << std::endl;
}
//*******************************************************************************
void DataProtocol::setPeerIPv4Address(const char* peerHostOrIP)
{
const char* peerAddress; // dotted decimal address to use in the struct below
/// \todo Improve this to make it work also with local ip numbers, in a LAN,
/// that don't have an assigned host name
/*
// Resolve Peer IPv4 with either doted integer IP or hostname
//----------------------------------------------------------
std::cout << "Resolving Peer IPv4 address..." << std::endl;
QHostInfo info = QHostInfo::fromName(peerHostOrIP);
if ( !info.addresses().isEmpty() ) {
std::cout << "Peer Address Found" << std::endl;
QHostAddress address = info.addresses().first(); // use the first address in list
peerAddress = address.toString().toLatin1();
}
else {
std::cerr << "ERROR: Could not set Peer IP Address" << std::endl;
std::cerr << "Check that it's public or that the hostname exists" << std::endl;
std::exit(1);
}
*/
// temporary implementation to make this work
/// \todo change this
peerAddress = peerHostOrIP;
// Set the Peer IPv4 Address struct
//---------------------------------
bzero(&mPeerIPv4Addr, sizeof(mPeerIPv4Addr));
mPeerIPv4Addr.sin_family = AF_INET;//AF_INET: IPv4 Protocol
mPeerIPv4Addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY: let the kernel decide the active address
mPeerIPv4Addr.sin_port = htons(mPeerPort);//set Peer port
//std::cout << "mPeerPort = " << mPeerPort << std::endl;
int nPeer = inet_pton(AF_INET, peerAddress, &mPeerIPv4Addr.sin_addr);
if ( nPeer == 1 ) {
std::cout << "Successful Set Peer Address" << std::endl;
}
else if ( nPeer == 0 ) {
std::cout << "Error: Incorrect presentation format for address" << std::endl;
std::exit(1);
}
else {
std::cout << "Error: Could not set Peer Address" << std::endl;
std::exit(1);
}
}
//*******************************************************************************
void DataProtocol::setRingBuffer(std::tr1::shared_ptr<RingBuffer> RingBuffer)
{
mRingBuffer = RingBuffer;
}
//*******************************************************************************
void DataProtocol::run()
{
std::cout << "Running DataProtocol Thread" << std::endl;
std::cout << gPrintSeparator << std::endl;
size_t packet_size = getAudioPacketSize();
int8_t packet[packet_size];
switch ( mRunMode )
{
case RECEIVER :
//-----------------------------------------------------------------------------------
// Wait for the first packet to be ready and obtain address
// from that packet
/// \todo here is the place to read the datagram and check if the settings match
/// the local ones. Extract this information from the header
std::cout << "Waiting for Peer..." << std::endl;
this->receivePacket( (char*) packet, packet_size); // This blocks waiting for the first packet
std::cout << "Received Connection for Peer!" << std::endl;
while ( !mStopped )
{
//std::cout << "RECEIVING PACKETS" << std::endl;
/// \todo Set a timer to report packats arriving too late
//std::cout << "RECIEVING THREAD" << std::endl;
this->receivePacket( (char*) packet, packet_size);
/// \todo Change this to match buffer size
//std::cout << "PACKET RECIEVED" << std::endl;
mRingBuffer->insertSlotBlocking(packet);
//std::cout << buf << std::endl;
}
break;
case SENDER :
//-----------------------------------------------------------------------------------
while ( !mStopped )
{
//std::cout << "SENDING PACKETS --------------------------" << std::endl;
/// \todo This should be blocking, since we don't want to send trash
mRingBuffer->readSlotBlocking(packet);
//std::cout << "SENDING PACKETS" << std::endl;
this->sendPacket( (char*) packet, packet_size);
//std::cout << "SENDING PACKETS DONE!!!" << std::endl;
//this->sendPacket( sendtest, 64);
}
break;
}
}
void DataProtocol::setAudioPacketSize(size_t size_bytes)
{
mAudioPacketSize = size_bytes;
}
size_t DataProtocol::getAudioPacketSize()
{
return(mAudioPacketSize);
}