Subversion Repositories tpanel

Rev

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;
}