Rev 458 | Rev 460 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* Copyright (C) 2024 by Andreas Theofilu <andreas@theosys.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sched.h>
#include "tqintercom.h"
#include "terror.h"
#define PACKETSIZE 16384
using std::min;
TQIntercom::TQIntercom()
{
DECL_TRACER("TQIntercom::TQIntercom()");
}
TQIntercom::TQIntercom(INTERCOM_t ic)
{
DECL_TRACER("TQIntercom::TQIntercom(INTERCOM_t ic)");
setIntercom(ic);
}
TQIntercom::~TQIntercom()
{
DECL_TRACER("TQIntercom::~TQIntercom()");
}
void TQIntercom::setIntercom(INTERCOM_t ic)
{
if (ic.ip.empty())
{
MSG_ERROR("No valid IP address!");
return;
}
if (ic.rxPort < 0 || ic.rxPort > 0x0ffff)
{
MSG_ERROR("Receiver port is invalid! (" << ic.rxPort << ")");
return;
}
if (ic.txPort < 0 || ic.txPort > 0x0ffff)
{
MSG_ERROR("Transmit port is invalid! (" << ic.txPort << ")");
return;
}
if (ic.rxPort == 0 && ic.txPort == 0)
{
MSG_ERROR("No transmit and no receive port!");
return;
}
if (ic.mode < 0 || ic.mode > 2)
{
MSG_ERROR("Invalid mode " << ic.mode << "!");
return;
}
if (ic.mode == 0 && ic.rxPort == 0) // listen / receive
{
MSG_ERROR("No network port for listening!");
return;
}
if (ic.mode == 1 && ic.txPort == 0) // talk / send
{
MSG_ERROR("No network port for talking!");
return;
}
mIntercom = ic;
mChunkSize = mAudioChannels * mAudioBytesPerSample;
mBufferSize = mBufferChunk * mAudioChannels * mAudioBytesPerSample;
if (ic.mode == 2 || ic.mode == 0)
{
if ((mServer = configureServer(ic.rxPort)) == -1)
return;
}
if (ic.mode == 1 || ic.mode == 0)
{
if ((mClient = configureClient()) == -1)
return;
}
setScheduler();
mInitialized = true;
}
void TQIntercom::setSpeakerLevel(int level)
{
DECL_TRACER("TQIntercom::setSpeakerLevel(int level)");
if (level < 0 || level > 100)
return;
mSpkLevel = level;
// TODO: Add code to set volume
}
void TQIntercom::setMicrophonLevel(int level)
{
DECL_TRACER("TQIntercom::setMicrophonLevel(int level)");
if (level < 0 || level > 100)
return;
// TODO: Add code to set microphon level
}
void TQIntercom::start()
{
DECL_TRACER("TQIntercom::start()");
// TODO: Add code to start
}
void TQIntercom::stop()
{
DECL_TRACER("TQIntercom::stop()");
// TODO: Add code to stop
}
void TQIntercom::setMute(bool mute)
{
DECL_TRACER("TQIntercom::setMute(bool mute)");
// TODO: Add code to mute/unmute microphon
}
int TQIntercom::configureServer(int port)
{
struct sockaddr_in sin;
int server = -1;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
if((server = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
{
MSG_ERROR("Couldn't initialize socket: " << strerror(errno));
return -1;
}
if(bind(server, (struct sockaddr*)&sin, sizeof(sin)) == -1)
{
MSG_ERROR("Couldn't bind to server: " << strerror(errno));
return -1;
}
return server;
}
void TQIntercom::setScheduler()
{
struct sched_param sched_param;
if (sched_getparam(0, &sched_param) < 0)
{
MSG_ERROR("Scheduler getparam failed: " << strerror(errno));
return;
}
sched_param.sched_priority = sched_get_priority_max(SCHED_FIFO);
if (!sched_setscheduler(0, SCHED_FIFO, &sched_param))
{
MSG_DEBUG("Scheduler set to Round Robin with priority " << sched_param.sched_priority);
fflush(stdout);
return;
}
MSG_ERROR("Failed to set scheduler to Round Robin with priority " << sched_param.sched_priority << ": " << strerror(errno));
}
int TQIntercom::configureClient()
{
struct sockaddr_in sin_server;
int connection = -1;
sin_server.sin_addr.s_addr = inet_addr(mIntercom.ip.c_str());
sin_server.sin_family = AF_INET;
sin_server.sin_port = htons(mIntercom.txPort);
if ((connection = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
MSG_ERROR("Socket error: " << strerror(errno));
return -1;
}
int flags;
flags = fcntl(connection, F_GETFL, 0);
fcntl(connection, F_SETFL, flags | O_NONBLOCK);
flags = 1;
setsockopt(connection, SOL_SOCKET, SO_BROADCAST, &flags, sizeof(flags));
return connection;
}
int TQIntercom::sendBuffer(const char *buffer, size_t size)
{
if(size > 0 && size < mBufferChunk)
MSG_DEBUG("Short write: " << size);
struct sockaddr_in sin_server;
size_t datasent = 0;
sin_server.sin_addr.s_addr = inet_addr(mIntercom.ip.c_str());
sin_server.sin_family = AF_INET;
sin_server.sin_port = htons(mIntercom.txPort);
if (size == mBufferChunk)
{
for(datasent = 0; datasent < size;)
{
int result = sendto(mClient, buffer + datasent, min(size - datasent, static_cast<size_t>(PACKETSIZE)), MSG_NOSIGNAL, (struct sockaddr*)&sin_server, sizeof(sin_server));
if (result == -1 && errno == EWOULDBLOCK)
result = 0;
if (result == -1)
{
MSG_ERROR("Send error: " << strerror(errno));
break;
}
datasent += result;
}
}
return datasent;
}