
/*---------------------------------------------------------------------------*/
/* ios_screen_capture_receiver.cpp                                           */
/* copyright (c) innovaphone 2015                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <sys/ioctl.h>
#include <ifaddrs.h>
#include <net/ethernet.h>
#include <sys/types.h>

#include "platform/platform.h"
#include "common/os/iomux.h"
#include "common/interface/socket.h"
#include "common/interface/media.h"
#include "common/interface/appsharing.h"
#include "client/linux/appsharing/image_buffer.h"
#include "ios_screen_capture_receiver.h"

extern class IMediaEncoder * appSharingEncoder;
extern class UMediaEncoder * appSharingEncoderUser;
extern unsigned appSharingFullIntraRequest;
extern class ImageBuffer * theImageBuffer;

#define TOS_DSCP_VOICE			0xb8    // RFC 3246 - Expedited Forwarding
#define TOS_DSCP_VIDEO			0x88    // RFC 4594 - 
#define TOS_DSCP_SIGNALING		0x68	// RFC 2597 - Assured Forwarding 31

/*---------------------------------------------------------------------------*/
/* IosScreenCaptureReceiver                                                  */
/*---------------------------------------------------------------------------*/

IosScreenCaptureReceiver::IosScreenCaptureReceiver(class IIoMux * iomux, UIosScreenCaptureReceiver *user)
{
    this->iomux = iomux;
    this->user = user;
    frameSize = 128 * 1024;
    frameBuf = (byte *)(malloc(frameSize));
    framePos = 0;
    running = false;
    keyframe = false;
    fullIntraAck = 0;

    FDIIoMux = socket(AF_INET, SOCK_DGRAM
#ifdef SOCK_NONBLOCK
        | SOCK_NONBLOCK
#endif
        , IPPROTO_UDP);
    if (FDIIoMux < 0) {
        debug->printf("IosScreenCaptureReceiver::OpenSocket socket() failed(%u : %s)", errno, strerror(errno));
        return;
    }
#ifndef SOCK_NONBLOCK
    unsigned int nonBlocking = 1;
    ioctl(FDIIoMux, FIONBIO, &nonBlocking);
    // listening sockets must be asynchronous, since otherwise indicated accept(2)s may disappear
    // between poll and accept and then our accept would block
    long flags = fcntl(FDIIoMux, F_GETFL);
    if (flags == -1) {
        debug->printf("IosScreenCaptureReceiver::OpenSocket fcntl(F_GETFL) failed (%u : %s)", errno, strerror(errno));
    }
    // do not block if there is no incoming connection
    flags |= O_NONBLOCK;
    if (fcntl(FDIIoMux, F_SETFL, flags) == -1) {
        debug->printf("IosScreenCaptureReceiver::OpenSocket fcntl(F_SETFL) failed (%u : %s)", errno, strerror(errno));
    }
#endif
#ifdef _IOS_
    int noSigPipe = 1;
    if (setsockopt(FDIIoMux, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(noSigPipe)) < 0) {
        debug->printf("IosScreenCaptureReceiver::OpenSocket setsockopt(SO_NOSIGPIPE) failed (%u : %s)", errno, strerror(errno));
    }
#endif
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGPIPE);
    if (sigprocmask(SIG_BLOCK, &set, 0) < 0) {
        debug->printf("IosScreenCaptureReceiver::OpenSocket sigprocmask(SIG_BLOCK) failed (%u : %s)", errno, strerror(errno));
    }
    int broadcastPermission = 1;          /* Socket opt to set permission to broadcast */
    if (setsockopt(FDIIoMux, SOL_SOCKET, SO_BROADCAST, (void *)&broadcastPermission, sizeof(broadcastPermission)) < 0) {
        debug->printf("IosScreenCaptureReceiver::OpenSocket setsockopt(SO_BROADCAST) failed (%u : %s)", errno, strerror(errno));
    }
    int tos = TOS_DSCP_VOICE;
    if(setsockopt(FDIIoMux, IPPROTO_IP, IP_TOS, &tos, (socklen_t)(sizeof(tos))) < 0) {
        debug->printf("IosScreenCaptureReceiver::OpenSocket setsockopt(IP_TOS) failed (%u : %s)", errno, strerror(errno));
    }

    struct sockaddr_storage localAddr;
    memset(&localAddr, 0, sizeof(struct sockaddr_in));
    localAddr.ss_family = AF_INET;
    (((struct sockaddr_in *)&localAddr)->sin_addr).s_addr = htonl(INADDR_LOOPBACK);
    ((struct sockaddr_in *)&localAddr)->sin_port = htons(10010);
    int res = bind(FDIIoMux, (struct sockaddr *)(&localAddr), sizeof(struct sockaddr_in));
    if (res != 0) {
        debug->printf("IosScreenCaptureReceiver::BindIPv4 bind() failed (%u : %s)", errno, strerror(errno));
    }
    iomux->FDAdd(FDIIoMux, this, false);
    socklen_t l = sizeof(struct sockaddr_in);
    getsockname(FDIIoMux, (struct sockaddr *)&localAddr, &l);
}

IosScreenCaptureReceiver::~IosScreenCaptureReceiver()
{
    shutdown(FDIIoMux, SHUT_RDWR);
    iomux->FDClose(this);
    FDIIoMux = -1;
    free(frameBuf);
}

void IosScreenCaptureReceiver::IoContextNotify(bool indRX, bool indTX)
{
    if (indRX) {
        int bytesRead;
        socklen_t addrLen;
        struct sockaddr_storage recvAddr;

        addrLen = sizeof(struct sockaddr_in);
        bytesRead = recvfrom(FDIIoMux, &frameBuf[framePos], frameSize - framePos, 0, (struct sockaddr *)(&recvAddr), &addrLen);
/*
        debug->printf("IosScreenCaptureReceiver::SocketRecvFromResult len:%i addr:%u.%u.%u.%u:%u",
            bytesRead,
            ((byte *)(&((struct sockaddr_in *)&recvAddr)->sin_addr))[0], ((byte *)(&((struct sockaddr_in *)&recvAddr)->sin_addr))[1], ((byte *)(&((struct sockaddr_in *)&recvAddr)->sin_addr))[2], ((byte *)(&((struct sockaddr_in *)&recvAddr)->sin_addr))[3],
            htons(((struct sockaddr_in *)&recvAddr)->sin_port));
*/
        if (bytesRead < 0) {
            debug->printf("IosScreenCaptureReceiver::SocketRecvFromResult negative");
            if (keyframe) {
                keyframe = false;
                if (running) {
                    appSharingFullIntraRequest++;
                }
            }
            framePos = 0;
        }
        else if (((((struct sockaddr_in *)&recvAddr)->sin_addr).s_addr != htonl(INADDR_LOOPBACK)) || (((struct sockaddr_in *)&recvAddr)->sin_port != htons(10011))) {
            debug->printf("IosScreenCaptureReceiver::SocketRecvFromResult foreign len:%i addr:%u.%u.%u.%u:%u",
                bytesRead,
                ((byte *)(&((struct sockaddr_in *)&recvAddr)->sin_addr))[0], ((byte *)(&((struct sockaddr_in *)&recvAddr)->sin_addr))[1], ((byte *)(&((struct sockaddr_in *)&recvAddr)->sin_addr))[2], ((byte *)(&((struct sockaddr_in *)&recvAddr)->sin_addr))[3],
                htons(((struct sockaddr_in *)&recvAddr)->sin_port));
        }
        else if (bytesRead >= frameSize - framePos) {
            debug->printf("IosScreenCaptureReceiver::SocketRecvFromResult overrun");
            if (keyframe) {
                keyframe = false;
                if (running) {
                    appSharingFullIntraRequest++;
                }
            }
            framePos = 0;
        }
        else if (!(bytesRead & 1)) {
            framePos += bytesRead;
        }
        else if (framePos + bytesRead < 16) {
            debug->printf("IosScreenCaptureReceiver::SocketRecvFromResult corrupt %u", framePos);
            if (keyframe) {
                keyframe = false;
                if (running) {
                    appSharingFullIntraRequest++;
                }
            }
            framePos = 0;
        }
        else {
            framePos += bytesRead;
            dword l = frameBuf[framePos - 16] | (frameBuf[framePos - 15] << 8) | (frameBuf[framePos - 14] << 16) | (frameBuf[framePos - 13] << 24);
            dword timestamp = frameBuf[framePos - 12] | (frameBuf[framePos - 11] << 8) | (frameBuf[framePos - 10] << 16) | (frameBuf[framePos - 9] << 24);
            word duration = frameBuf[framePos - 8] | (frameBuf[framePos - 7] << 8);
            word flags = frameBuf[framePos - 6] | (frameBuf[framePos - 5] << 8);
            word width = frameBuf[framePos - 4] | (frameBuf[framePos - 3] << 8);
            word height = frameBuf[framePos - 2] | (frameBuf[framePos - 1] << 8);
            if ((framePos != l + 16) && (framePos != l + 17)) {
                debug->printf("IosScreenCaptureReceiver::SocketRecvFromResult corrupt %u %u", framePos, l);
                if (keyframe) {
                    keyframe = false;
                    if (running) {
                        appSharingFullIntraRequest++;
                    }
                }
            }
            else if (l == 0) {
                if (running) {
                    running = false;
                    user->IosScreenCaptureRunning(false);
                }
            }
            else {
                if (!theImageBuffer) {
                    debug->printf("IosScreenCaptureReceiver::SocketRecvFromResult not capturing");
                }
                else {
                    theImageBuffer->user->ImageBufferSample(0, NULL, 0, width, height, timestamp, 100 * 90);
                    if (!appSharingEncoder) {
                        debug->printf("IosScreenCaptureReceiver::SocketRecvFromResult not sharing");
                    }
                    else {
//                        debug->printf("IosScreenCaptureReceiver::SocketRecvFromResult frame %u %u %u", l, timestamp, duration);
                        if (flags & 1/*FRAME_IS_KEY*/) {
                            debug->printf("IosScreenCaptureReceiver::Keyframe");
                            keyframe = true;
                        }
                        else if (!running) {
                            appSharingFullIntraRequest++;
                        }
                        if (!running) {
                            running = true;
                            user->IosScreenCaptureRunning(true);
                        }
                        if (keyframe) {
                            appSharingEncoderUser->EncodeResult(appSharingEncoder, frameBuf, l, timestamp, duration);
                        }
                    }
                }
            }
            framePos = 0;
        }
        if (appSharingFullIntraRequest != fullIntraAck) {
            struct sockaddr_storage sockAddr;
            byte b[1] = { 0 };
            debug->printf("IosScreenCaptureReceiver::FullIntraRequest");
            fullIntraAck = appSharingFullIntraRequest;
            memset(&sockAddr, 0, sizeof(struct sockaddr_in));
            sockAddr.ss_family = AF_INET;
            (((struct sockaddr_in *)&sockAddr)->sin_addr).s_addr = htonl(INADDR_LOOPBACK);
            ((struct sockaddr_in *)&sockAddr)->sin_port = htons(10011);
/*
            debug->printf("IosScreenCaptureReceiver::SendTo len:%i addr:%u.%u.%u.%u:%u",
                sizeof(b),
                ((byte *)(&((struct sockaddr_in *)&sockAddr)->sin_addr))[0], ((byte *)(&((struct sockaddr_in *)&sockAddr)->sin_addr))[1], ((byte *)(&((struct sockaddr_in *)&sockAddr)->sin_addr))[2], ((byte *)(&((struct sockaddr_in *)&sockAddr)->sin_addr))[3],
                htons(((struct sockaddr_in *)&sockAddr)->sin_port));
*/
            sendto(FDIIoMux, b, sizeof(b), 0, (struct sockaddr *)(&sockAddr), sizeof(struct sockaddr_in));
        }
    }
    if (isError) {
        int errNum;
        socklen_t errNumLen = sizeof(errNum);
        if (getsockopt(FDIIoMux, SOL_SOCKET, SO_ERROR, &errNum, &errNumLen) != 0) {
            debug->printf("IosScreenCaptureReceiver::IoContextNotify getsockopt() failed (%u : %s)", errno, strerror(errno));
        }
        else {
            debug->printf("IosScreenCaptureReceiver::IoContextNotify socket error (%u : %s)", errNum, strerror(errNum));
        }
    }
}

/*---------------------------------------------------------------------------*/
