Ejemplo n.º 1
0
/* Initializes the input stream */
int initializeInputStream(char* aesKeyData, int aesKeyDataLength,
                          char* aesIv, int aesIvLength) {
	if (aesIvLength != OAES_BLOCK_SIZE)
	{
		Limelog("AES IV is incorrect length. Should be %d\n", aesIvLength);
		return -1;
	}

	oaesContext = oaes_alloc();
	if (oaesContext == NULL)
	{
		Limelog("Failed to allocate OpenAES context\n");
		return -1;
	}

	if (oaes_set_option(oaesContext, OAES_OPTION_CBC, aesIv) != OAES_RET_SUCCESS)
	{
		Limelog("Failed to set CBC and IV on OAES context\n");
		return -1;
	}

	if (oaes_key_import_data(oaesContext, (const unsigned char*)aesKeyData, aesKeyDataLength) != OAES_RET_SUCCESS)
	{
		Limelog("Failed to import AES key data\n");
		return -1;
	}

	LbqInitializeLinkedBlockingQueue(&packetQueue, 30);

	initialized = 1;
	return 0;
}
static PRTP_QUEUE_ENTRY validateQueueConstraints(PRTP_REORDER_QUEUE queue) {
    int needsUpdate = 0;

    // Empty queue is fine
    if (queue->queueHead == NULL) {
        return NULL;
    }

    // Check that the queue's time constraint is satisfied
    if (PltGetMillis() - queue->oldestQueuedTimeMs > queue->maxQueueTimeMs) {
        Limelog("Discarding RTP packet queued for too long\n");
        removeEntry(queue, queue->oldestQueuedEntry);
        free(queue->oldestQueuedEntry->packet);
        needsUpdate = 1;
    }

    // Check that the queue's size constraint is satisfied
    if (!needsUpdate && queue->queueSize == queue->maxSize) {
        Limelog("Discarding RTP packet after queue overgrowth\n");
        removeEntry(queue, queue->oldestQueuedEntry);
        free(queue->oldestQueuedEntry->packet);
        needsUpdate = 1;
    }

    if (needsUpdate) {
        // Recalculate the oldest entry if needed
        updateOldestQueued(queue);

        // Return the lowest seq queued
        return getEntryByLowestSeq(queue);
    }
    else {
        return NULL;
    }
}
Ejemplo n.º 3
0
SOCKET connectTcpSocket(struct sockaddr_storage *dstaddr, SOCKADDR_LEN addrlen, unsigned short port) {
	SOCKET s;
    struct sockaddr_in6 addr;
	int err;
#ifdef LC_DARWIN
    int val;
#endif

	s = socket(dstaddr->ss_family, SOCK_STREAM, IPPROTO_TCP);
	if (s == INVALID_SOCKET) {
        Limelog("socket() failed: %d\n", (int)LastSocketError());
		return INVALID_SOCKET;
	}
    
#ifdef LC_DARWIN
    // Disable SIGPIPE on iOS
    val = 1;
    setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (char* )&val, sizeof(val));
#endif

    memcpy(&addr, dstaddr, sizeof(addr));
	addr.sin6_port = htons(port);
	if (connect(s, (struct sockaddr*) &addr, addrlen) == SOCKET_ERROR) {
		err = LastSocketError();
        Limelog("connect() failed: %d\n", err);
		closesocket(s);
		SetLastSocketError(err);
		return INVALID_SOCKET;
	}

	return s;
}
Ejemplo n.º 4
0
static int resolveHostName(const char *host)
{
    struct addrinfo hints, *res;
    int err;
    
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_flags = AI_ADDRCONFIG;
    err = getaddrinfo(host, NULL, &hints, &res);
    if (err != 0) {
        Limelog("getaddrinfo() failed: %d\n", err);
        return err;
    }
    
    if (res == NULL) {
        Limelog("getaddrinfo() returned success without addresses\n");
        return -1;
    }
    
    // Use the first address in the list
    memcpy(&RemoteAddr, res->ai_addr, res->ai_addrlen);
    RemoteAddrLen = res->ai_addrlen;
    
    freeaddrinfo(res);
    return 0;
}
Ejemplo n.º 5
0
static void requestInvalidateReferenceFrames(void) {
    long long payload[3];
    PQUEUED_FRAME_INVALIDATION_TUPLE qfit;

    LC_ASSERT(VideoCallbacks.capabilities & CAPABILITY_REFERENCE_FRAME_INVALIDATION);

    if (!getNextFrameInvalidationTuple(&qfit)) {
        return;
    }

    LC_ASSERT(qfit->startFrame <= qfit->endFrame);

    payload[0] = qfit->startFrame;
    payload[1] = qfit->endFrame;
    payload[2] = 0;

    // Aggregate all lost frames into one range
    do {
        LC_ASSERT(qfit->endFrame >= payload[1]);
        payload[1] = qfit->endFrame;
        free(qfit);
    } while (getNextFrameInvalidationTuple(&qfit));

    // Send the reference frame invalidation request and read the response
    if (!sendMessageAndDiscardReply(packetTypes[IDX_INVALIDATE_REF_FRAMES],
        payloadLengths[IDX_INVALIDATE_REF_FRAMES], payload)) {
        Limelog("Request Invaldiate Reference Frames: Transaction failed: %d\n", (int) LastSocketError());
        ListenerCallbacks.connectionTerminated(LastSocketError());
        return;
    }

    Limelog("Invalidate reference frame request sent\n");
}
Ejemplo n.º 6
0
static void requestIdrFrame(void) {
    long long payload[3];

    if (ServerMajorVersion == 3) {
        // Form the payload
        payload[0] = 0;
        payload[1] = 0xFFFFF;
        payload[2] = 0;

        // Send the reference frame invalidation request and read the response
        if (!sendMessageAndDiscardReply(packetTypes[IDX_INVALIDATE_REF_FRAMES],
            payloadLengths[IDX_INVALIDATE_REF_FRAMES], payload)) {
            Limelog("Request IDR Frame: Transaction failed: %d\n", (int) LastSocketError());
            ListenerCallbacks.connectionTerminated(LastSocketError());
            return;
        }
    }
    else {
        // Send IDR frame request and read the response
        if (!sendMessageAndDiscardReply(packetTypes[IDX_REQUEST_IDR_FRAME],
            payloadLengths[IDX_REQUEST_IDR_FRAME], preconstructedPayloads[IDX_REQUEST_IDR_FRAME])) {
            Limelog("Request IDR Frame: Transaction failed: %d\n", (int) LastSocketError());
            ListenerCallbacks.connectionTerminated(LastSocketError());
            return;
        }
    }

    Limelog("IDR frame request sent\n");
}
Ejemplo n.º 7
0
/* Send RTSP message and get response */
static int transactRtspMessage(PRTSP_MESSAGE request, PRTSP_MESSAGE response) {
    SOCK_RET err;
    int ret = 0;
	int offset;
	char* serializedMessage = NULL;
	int messageLen;

	sock = connectTcpSocket(remoteAddr, 48010);
	if (sock == INVALID_SOCKET) {
		return ret;
	}
	enableNoDelay(sock);

	serializedMessage = serializeRtspMessage(request, &messageLen);
	if (serializedMessage == NULL) {
		closesocket(sock);
		return ret;
	}

	// Send our message
	err = send(sock, serializedMessage, messageLen, 0);
	if (err == SOCKET_ERROR) {
		goto Exit;
	}

	// Read the response until the server closes the connection
	offset = 0;
	for (;;) {
		err = recv(sock, &responseBuffer[offset], RTSP_MAX_RESP_SIZE - offset, 0);
		if (err <= 0) {
			// Done reading
			break;
		}
		offset += err;

		// Warn if the RTSP message is too big
		if (offset == RTSP_MAX_RESP_SIZE) {
			Limelog("RTSP message too long\n");
			goto Exit;
		}
	}

	if (parseRtspMessage(response, responseBuffer, offset) == RTSP_ERROR_SUCCESS) {
		// Successfully parsed response
		ret = 1;
	}
	else {
		Limelog("Failed to parse RTSP response\n");
	}

Exit:
	if (serializedMessage != NULL) {
		free(serializedMessage);
	}

	closesocket(sock);
	sock = INVALID_SOCKET;
	return ret;
}
Ejemplo n.º 8
0
static void ReceiveThreadProc(void* context) {
	SOCK_RET err;
	PRTP_PACKET rtp;
    int packetSize;
    char* buffer = NULL;

	while (!PltIsThreadInterrupted(&receiveThread)) {
		if (buffer == NULL) {
			buffer = (char*) malloc(MAX_PACKET_SIZE + sizeof(int));
			if (buffer == NULL) {
				Limelog("Receive thread terminating\n");
				listenerCallbacks->connectionTerminated(-1);
				return;
			}
		}

		err = recv(rtpSocket, &buffer[sizeof(int)], MAX_PACKET_SIZE, 0);
		if (err <= 0) {
			Limelog("Receive thread terminating #2\n");
			free(buffer);
			listenerCallbacks->connectionTerminated(LastSocketError());
			return;
		}
        
        packetSize = (int)err;

		if (packetSize < sizeof(RTP_PACKET)) {
			// Runt packet
			continue;
		}

		rtp = (PRTP_PACKET) &buffer[sizeof(int)];
		if (rtp->packetType != 97) {
			// Not audio
			continue;
		}

		memcpy(buffer, &packetSize, sizeof(int));

		err = LbqOfferQueueItem(&packetQueue, buffer);
		if (err == LBQ_SUCCESS) {
			// The queue owns the buffer now
			buffer = NULL;
		}

		if (err == LBQ_BOUND_EXCEEDED) {
			Limelog("Audio packet queue overflow\n");
			freePacketList(LbqFlushQueueItems(&packetQueue));
		}
		else if (err == LBQ_INTERRUPTED) {
			Limelog("Receive thread terminating #2\n");
			free(buffer);
			return;
		}
	}
}
Ejemplo n.º 9
0
static void UdpPingThreadProc(void* context) {
    // Ping in ASCII
    char pingData[] = { 0x50, 0x49, 0x4E, 0x47 };
    struct sockaddr_in6 saddr;
    SOCK_RET err;

    memcpy(&saddr, &RemoteAddr, sizeof(saddr));
    saddr.sin6_port = htons(RTP_PORT);

    // Send PING every second until we get data back then every 5 seconds after that.
    while (!PltIsThreadInterrupted(&udpPingThread)) {
        err = sendto(rtpSocket, pingData, sizeof(pingData), 0, (struct sockaddr*)&saddr, RemoteAddrLen);
        if (err != sizeof(pingData)) {
            Limelog("Audio Ping: sendto() failed: %d\n", (int)LastSocketError());
            ListenerCallbacks.connectionTerminated(LastSocketFail());
            return;
        }

        // Send less frequently if we've received data from our peer
        if (receivedDataFromPeer) {
            PltSleepMsInterruptible(&udpPingThread, 5000);
        }
        else {
            PltSleepMsInterruptible(&udpPingThread, 1000);
        }
    }
}
Ejemplo n.º 10
0
static void DecoderThreadProc(void* context) {
	PRTP_PACKET rtp;
	int length;
	int err;
	char *data;
	unsigned short lastSeq = 0;

	while (!PltIsThreadInterrupted(&decoderThread)) {
		err = LbqWaitForQueueElement(&packetQueue, (void**) &data);
		if (err != LBQ_SUCCESS) {
			Limelog("Decoder thread terminating\n");
			return;
		}

		memcpy(&length, data, sizeof(int));
		rtp = (PRTP_PACKET) &data[sizeof(int)];

		if (length < sizeof(RTP_PACKET)) {
			// Runt packet
			goto freeandcontinue;
		}

		if (rtp->packetType != 97) {
			// Not audio
			goto freeandcontinue;
		}

		rtp->sequenceNumber = htons(rtp->sequenceNumber);

		if (lastSeq != 0 && (unsigned short) (lastSeq + 1) != rtp->sequenceNumber) {
			Limelog("Received OOS audio data (expected %d, but got %d)\n", lastSeq + 1, rtp->sequenceNumber);

			callbacks.decodeAndPlaySample(NULL, 0);
		}

		lastSeq = rtp->sequenceNumber;

		callbacks.decodeAndPlaySample((char *) (rtp + 1), length - sizeof(*rtp));

	freeandcontinue:
		free(data);
	}
}
Ejemplo n.º 11
0
static void lossStatsThreadFunc(void* context) {
	char *lossStatsPayload;
	BYTE_BUFFER byteBuffer;

	lossStatsPayload = malloc(payloadLengths[IDX_LOSS_STATS]);
	if (lossStatsPayload == NULL) {
		Limelog("Loss Stats: malloc() failed\n");
		ListenerCallbacks.connectionTerminated(-1);
		return;
	}

	while (!PltIsThreadInterrupted(&lossStatsThread)) {
		// Construct the payload
		BbInitializeWrappedBuffer(&byteBuffer, lossStatsPayload, 0, payloadLengths[IDX_LOSS_STATS], BYTE_ORDER_LITTLE);
		BbPutInt(&byteBuffer, lossCountSinceLastReport);
		BbPutInt(&byteBuffer, LOSS_REPORT_INTERVAL_MS);
		BbPutInt(&byteBuffer, 1000);
		BbPutLong(&byteBuffer, currentFrame);
		BbPutInt(&byteBuffer, 0);
		BbPutInt(&byteBuffer, 0);
		BbPutInt(&byteBuffer, 0x14);

		// Send the message (and don't expect a response)
		if (!sendMessageAndForget(packetTypes[IDX_LOSS_STATS],
			payloadLengths[IDX_LOSS_STATS], lossStatsPayload)) {
			free(lossStatsPayload);
			Limelog("Loss Stats: Transaction failed: %d\n", (int)LastSocketError());
            ListenerCallbacks.connectionTerminated(LastSocketError());
			return;
		}

		// Clear the transient state
		lossCountSinceLastReport = 0;

		// Wait a bit
		PltSleepMs(LOSS_REPORT_INTERVAL_MS);
	}

	free(lossStatsPayload);
}
Ejemplo n.º 12
0
void setRecvTimeout(SOCKET s, int timeoutSec) {
#if defined(LC_WINDOWS)
    int val = timeoutSec * 1000;
#else
    struct timeval val;
    val.tv_sec = timeoutSec;
    val.tv_usec = 0;
#endif
    
    if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&val, sizeof(val)) < 0) {
        Limelog("setsockopt(SO_RCVTIMEO) failed: %d\n", (int)LastSocketError());
    }
}
Ejemplo n.º 13
0
SOCKET bindUdpSocket(int addrfamily) {
	SOCKET s;
    struct sockaddr_storage addr;
	int val;
	int err;

    LC_ASSERT(addrfamily == AF_INET || addrfamily == AF_INET6);
    
	s = socket(addrfamily, SOCK_DGRAM, IPPROTO_UDP);
	if (s == INVALID_SOCKET) {
        Limelog("socket() failed: %d\n", (int)LastSocketError());
		return INVALID_SOCKET;
	}

	memset(&addr, 0, sizeof(addr));
    addr.ss_family = addrfamily;
	if (bind(s, (struct sockaddr*) &addr,
             addrfamily == AF_INET ?
                sizeof(struct sockaddr_in) :
                sizeof(struct sockaddr_in6)) == SOCKET_ERROR) {
		err = LastSocketError();
        Limelog("bind() failed: %d\n", err);
		closesocket(s);
		SetLastSocketError(err);
		return INVALID_SOCKET;
	}
    
#ifdef LC_DARWIN
    // Disable SIGPIPE on iOS
    val = 1;
    setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (char* )&val, sizeof(val));
#endif

    // Set the receive buffer to 64KB by default
	val = 65536;
	setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*) &val, sizeof(val));

	return s;
}
Ejemplo n.º 14
0
/* Starts the control stream */
int startControlStream(void) {
	int err;

	ctlSock = connectTcpSocket(&RemoteAddr, RemoteAddrLen, 47995);
	if (ctlSock == INVALID_SOCKET) {
		return LastSocketFail();
	}

	enableNoDelay(ctlSock);

	// Send START A
	if (!sendMessageAndDiscardReply(packetTypes[IDX_START_A],
                                    payloadLengths[IDX_START_A],
                                    preconstructedPayloads[IDX_START_A])) {
        Limelog("Start A failed: %d\n", (int)LastSocketError());
        return LastSocketFail();
    }

	// Send START B
    if (!sendMessageAndDiscardReply(packetTypes[IDX_START_B],
                                    payloadLengths[IDX_START_B],
                                    preconstructedPayloads[IDX_START_B])) {
        Limelog("Start B failed: %d\n", (int)LastSocketError());
        return LastSocketFail();
    }

	err = PltCreateThread(lossStatsThreadFunc, NULL, &lossStatsThread);
	if (err != 0) {
		return err;
	}

	err = PltCreateThread(resyncThreadFunc, NULL, &resyncThread);
	if (err != 0) {
		return err;
	}

	return 0;
}
Ejemplo n.º 15
0
static void decodeInputData(PQUEUED_AUDIO_PACKET packet) {
    PRTP_PACKET rtp;

    rtp = (PRTP_PACKET)&packet->data[0];
    if (lastSeq != 0 && (unsigned short)(lastSeq + 1) != rtp->sequenceNumber) {
        Limelog("Received OOS audio data (expected %d, but got %d)\n", lastSeq + 1, rtp->sequenceNumber);

        AudioCallbacks.decodeAndPlaySample(NULL, 0);
    }

    lastSeq = rtp->sequenceNumber;

    AudioCallbacks.decodeAndPlaySample((char*)(rtp + 1), packet->size - sizeof(*rtp));
}
Ejemplo n.º 16
0
static void resyncThreadFunc(void* context) {
	long long payload[3];

	while (!PltIsThreadInterrupted(&resyncThread)) {
		// Wait for a resync request
		PltWaitForEvent(&resyncEvent);

		// Form the payload
		payload[0] = 0;
		payload[1] = 0xFFFFF;
		payload[2] = 0;

		// Done capturing the parameters
		PltClearEvent(&resyncEvent);

		// Send the resync request and read the response
		if (!sendMessageAndDiscardReply(packetTypes[IDX_RESYNC], payloadLengths[IDX_RESYNC], payload)) {
			Limelog("Resync: Transaction failed: %d\n", (int)LastSocketError());
			ListenerCallbacks.connectionTerminated(LastSocketError());
			return;
		}
		Limelog("Resync complete\n");
	}
}
Ejemplo n.º 17
0
// Decoder thread proc
static void DecoderThreadProc(void* context) {
    PQUEUED_DECODE_UNIT qdu;
    while (!PltIsThreadInterrupted(&decoderThread)) {
        if (!getNextQueuedDecodeUnit(&qdu)) {
            return;
        }

        int ret = VideoCallbacks.submitDecodeUnit(&qdu->decodeUnit);

        freeQueuedDecodeUnit(qdu);

        if (ret == DR_NEED_IDR) {
            Limelog("Requesting IDR frame on behalf of DR\n");
            requestDecoderRefresh();
        }
    }
}
Ejemplo n.º 18
0
static int queuePacketToLbq(PQUEUED_AUDIO_PACKET* packet) {
    int err;

    err = LbqOfferQueueItem(&packetQueue, *packet, &(*packet)->q.lentry);
    if (err == LBQ_SUCCESS) {
        // The LBQ owns the buffer now
        *packet = NULL;
    }
    else if (err == LBQ_BOUND_EXCEEDED) {
        Limelog("Audio packet queue overflow\n");
        freePacketList(LbqFlushQueueItems(&packetQueue));
    }
    else if (err == LBQ_INTERRUPTED) {
        return 0;
    }

    return 1;
}
Ejemplo n.º 19
0
// UDP Ping proc
static void UdpPingThreadProc(void* context) {
    char pingData[] = { 0x50, 0x49, 0x4E, 0x47 };
    struct sockaddr_in6 saddr;
    SOCK_RET err;

    memcpy(&saddr, &RemoteAddr, sizeof(saddr));
    saddr.sin6_port = htons(RTP_PORT);

    while (!PltIsThreadInterrupted(&udpPingThread)) {
        err = sendto(rtpSocket, pingData, sizeof(pingData), 0, (struct sockaddr*)&saddr, RemoteAddrLen);
        if (err != sizeof(pingData)) {
            Limelog("Video Ping: send() failed: %d\n", (int)LastSocketError());
            ListenerCallbacks.connectionTerminated(LastSocketError());
            return;
        }

        PltSleepMs(500);
    }
}
Ejemplo n.º 20
0
// This shim callback runs the client's connectionTerminated() callback on a
// separate thread. This is neccessary because other internal threads directly
// invoke this callback. That can result in a deadlock if the client
// calls LiStopConnection() in the callback when the cleanup code
// attempts to join the thread that the termination callback (and LiStopConnection)
// is running on.
static void ClInternalConnectionTerminated(long errorCode)
{
    int err;

    // Avoid recursion and issuing multiple callbacks
    if (alreadyTerminated) {
        return;
    }

    alreadyTerminated = 1;

    // Invoke the termination callback on a separate thread
    err = PltCreateThread(terminationCallbackThreadFunc, NULL, &terminationCallbackThread);
    if (err != 0) {
        // Nothing we can safely do here, so we'll just assert on debug builds
        Limelog("Failed to create termination thread: %d\n", err);
        LC_ASSERT(err == 0);
    }

    // Close the thread handle since we can never wait on it
    PltCloseThread(&terminationCallbackThread);
}
Ejemplo n.º 21
0
static void UdpPingThreadProc(void *context) {
	/* Ping in ASCII */
	char pingData[] = { 0x50, 0x49, 0x4E, 0x47 };
	struct sockaddr_in saddr;
	SOCK_RET err;

	memset(&saddr, 0, sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(RTP_PORT);
	memcpy(&saddr.sin_addr, &remoteHost, sizeof(remoteHost));

	/* Send PING every 500 milliseconds */
	while (!PltIsThreadInterrupted(&udpPingThread)) {
		err = sendto(rtpSocket, pingData, sizeof(pingData), 0, (struct sockaddr*)&saddr, sizeof(saddr));
		if (err != sizeof(pingData)) {
			Limelog("UDP ping thread terminating #1\n");
			listenerCallbacks->connectionTerminated(LastSocketError());
			return;
		}

		PltSleepMs(500);
	}
}
Ejemplo n.º 22
0
/* Stop the connection by undoing the step at the current stage and those before it */
void LiStopConnection(void) {
    // Disable termination callbacks now
    alreadyTerminated = 1;
    
	if (stage == STAGE_INPUT_STREAM_START) {
		Limelog("Stopping input stream...");
		stopInputStream();
		stage--;
		Limelog("done\n");
	}
	if (stage == STAGE_AUDIO_STREAM_START) {
		Limelog("Stopping audio stream...");
		stopAudioStream();
		stage--;
		Limelog("done\n");
	}
	if (stage == STAGE_VIDEO_STREAM_START) {
		Limelog("Stopping video stream...");
		stopVideoStream();
		stage--;
		Limelog("done\n");
	}
	if (stage == STAGE_CONTROL_STREAM_START) {
		Limelog("Stopping control stream...");
		stopControlStream();
		stage--;
		Limelog("done\n");
	}
	if (stage == STAGE_INPUT_STREAM_INIT) {
		Limelog("Cleaning up input stream...");
		destroyInputStream();
		stage--;
		Limelog("done\n");
	}
	if (stage == STAGE_AUDIO_STREAM_INIT) {
		Limelog("Cleaning up audio stream...");
		destroyAudioStream();
		stage--;
		Limelog("done\n");
	}
	if (stage == STAGE_VIDEO_STREAM_INIT) {
		Limelog("Cleaning up video stream...");
		destroyVideoStream();
		stage--;
		Limelog("done\n");
	}
	if (stage == STAGE_CONTROL_STREAM_INIT) {
		Limelog("Cleaning up control stream...");
		destroyControlStream();
		stage--;
		Limelog("done\n");
	}
	if (stage == STAGE_RTSP_HANDSHAKE) {
		Limelog("Terminating RTSP handshake...");
		terminateRtspHandshake();
		stage--;
		Limelog("done\n");
	}
    if (stage == STAGE_NAME_RESOLUTION) {
        // Nothing to do
        stage--;
    }
	if (stage == STAGE_PLATFORM_INIT) {
		Limelog("Cleaning up platform...");
		cleanupPlatform();
		stage--;
		Limelog("done\n");
	}
	LC_ASSERT(stage == STAGE_NONE);
}
Ejemplo n.º 23
0
/* Starts the connection to the streaming machine */
int LiStartConnection(const char* host, PSTREAM_CONFIGURATION streamConfig, PCONNECTION_LISTENER_CALLBACKS clCallbacks,
	PDECODER_RENDERER_CALLBACKS drCallbacks, PAUDIO_RENDERER_CALLBACKS arCallbacks,
	void* renderContext, int drFlags, int _serverMajorVersion) {
	int err;

    ServerMajorVersion = _serverMajorVersion;
    memcpy(&StreamConfig, streamConfig, sizeof(StreamConfig));

	// Replace missing callbacks with placeholders
	fixupMissingCallbacks(&drCallbacks, &arCallbacks, &clCallbacks);
    memcpy(&VideoCallbacks, drCallbacks, sizeof(VideoCallbacks));
    memcpy(&AudioCallbacks, arCallbacks, sizeof(AudioCallbacks));

	// Hook the termination callback so we can avoid issuing a termination callback
	// after LiStopConnection() is called
	originalTerminationCallback = clCallbacks->connectionTerminated;
	memcpy(&ListenerCallbacks, clCallbacks, sizeof(ListenerCallbacks));
    ListenerCallbacks.connectionTerminated = ClInternalConnectionTerminated;
    
    alreadyTerminated = 0;

	Limelog("Initializing platform...");
	ListenerCallbacks.stageStarting(STAGE_PLATFORM_INIT);
	err = initializePlatform();
	if (err != 0) {
		Limelog("failed: %d\n", err);
		ListenerCallbacks.stageFailed(STAGE_PLATFORM_INIT, err);
		goto Cleanup;
	}
	stage++;
	LC_ASSERT(stage == STAGE_PLATFORM_INIT);
	ListenerCallbacks.stageComplete(STAGE_PLATFORM_INIT);
	Limelog("done\n");
    
    Limelog("Resolving host name...");
    ListenerCallbacks.stageStarting(STAGE_NAME_RESOLUTION);
    err = resolveHostName(host);
    if (err != 0) {
        Limelog("failed: %d\n", err);
        ListenerCallbacks.stageFailed(STAGE_NAME_RESOLUTION, err);
        goto Cleanup;
    }
    stage++;
    LC_ASSERT(stage == STAGE_NAME_RESOLUTION);
    ListenerCallbacks.stageComplete(STAGE_NAME_RESOLUTION);
    Limelog("done\n");

	Limelog("Starting RTSP handshake...");
	ListenerCallbacks.stageStarting(STAGE_RTSP_HANDSHAKE);
	err = performRtspHandshake();
	if (err != 0) {
		Limelog("failed: %d\n", err);
		ListenerCallbacks.stageFailed(STAGE_RTSP_HANDSHAKE, err);
		goto Cleanup;
	}
	stage++;
	LC_ASSERT(stage == STAGE_RTSP_HANDSHAKE);
	ListenerCallbacks.stageComplete(STAGE_RTSP_HANDSHAKE);
	Limelog("done\n");

	Limelog("Initializing control stream...");
	ListenerCallbacks.stageStarting(STAGE_CONTROL_STREAM_INIT);
	err = initializeControlStream();
	if (err != 0) {
		Limelog("failed: %d\n", err);
		ListenerCallbacks.stageFailed(STAGE_CONTROL_STREAM_INIT, err);
		goto Cleanup;
	}
	stage++;
	LC_ASSERT(stage == STAGE_CONTROL_STREAM_INIT);
	ListenerCallbacks.stageComplete(STAGE_CONTROL_STREAM_INIT);
	Limelog("done\n");

	Limelog("Initializing video stream...");
	ListenerCallbacks.stageStarting(STAGE_VIDEO_STREAM_INIT);
	initializeVideoStream();
	stage++;
	LC_ASSERT(stage == STAGE_VIDEO_STREAM_INIT);
	ListenerCallbacks.stageComplete(STAGE_VIDEO_STREAM_INIT);
	Limelog("done\n");

	Limelog("Initializing audio stream...");
	ListenerCallbacks.stageStarting(STAGE_AUDIO_STREAM_INIT);
	initializeAudioStream();
	stage++;
	LC_ASSERT(stage == STAGE_AUDIO_STREAM_INIT);
	ListenerCallbacks.stageComplete(STAGE_AUDIO_STREAM_INIT);
	Limelog("done\n");

	Limelog("Initializing input stream...");
	ListenerCallbacks.stageStarting(STAGE_INPUT_STREAM_INIT);
	initializeInputStream(streamConfig->remoteInputAesKey, sizeof(streamConfig->remoteInputAesKey),
		streamConfig->remoteInputAesIv, sizeof(streamConfig->remoteInputAesIv));
	stage++;
	LC_ASSERT(stage == STAGE_INPUT_STREAM_INIT);
	ListenerCallbacks.stageComplete(STAGE_INPUT_STREAM_INIT);
	Limelog("done\n");

	Limelog("Starting control stream...");
	ListenerCallbacks.stageStarting(STAGE_CONTROL_STREAM_START);
	err = startControlStream();
	if (err != 0) {
		Limelog("failed: %d\n", err);
		ListenerCallbacks.stageFailed(STAGE_CONTROL_STREAM_START, err);
		goto Cleanup;
	}
	stage++;
	LC_ASSERT(stage == STAGE_CONTROL_STREAM_START);
	ListenerCallbacks.stageComplete(STAGE_CONTROL_STREAM_START);
	Limelog("done\n");

	Limelog("Starting video stream...");
	ListenerCallbacks.stageStarting(STAGE_VIDEO_STREAM_START);
	err = startVideoStream(renderContext, drFlags);
	if (err != 0) {
		Limelog("Video stream start failed: %d\n", err);
		ListenerCallbacks.stageFailed(STAGE_VIDEO_STREAM_START, err);
		goto Cleanup;
	}
	stage++;
	LC_ASSERT(stage == STAGE_VIDEO_STREAM_START);
	ListenerCallbacks.stageComplete(STAGE_VIDEO_STREAM_START);
	Limelog("done\n");

	Limelog("Starting audio stream...");
	ListenerCallbacks.stageStarting(STAGE_AUDIO_STREAM_START);
	err = startAudioStream();
	if (err != 0) {
		Limelog("Audio stream start failed: %d\n", err);
		ListenerCallbacks.stageFailed(STAGE_AUDIO_STREAM_START, err);
		goto Cleanup;
	}
	stage++;
	LC_ASSERT(stage == STAGE_AUDIO_STREAM_START);
	ListenerCallbacks.stageComplete(STAGE_AUDIO_STREAM_START);
	Limelog("done\n");

	Limelog("Starting input stream...");
	ListenerCallbacks.stageStarting(STAGE_INPUT_STREAM_START);
	err = startInputStream();
	if (err != 0) {
		Limelog("Input stream start failed: %d\n", err);
		ListenerCallbacks.stageFailed(STAGE_INPUT_STREAM_START, err);
		goto Cleanup;
	}
	stage++;
	LC_ASSERT(stage == STAGE_INPUT_STREAM_START);
	ListenerCallbacks.stageComplete(STAGE_INPUT_STREAM_START);
	Limelog("done\n");

	ListenerCallbacks.connectionStarted();

Cleanup:
	return err;
}
Ejemplo n.º 24
0
static void ReceiveThreadProc(void* context) {
    PRTP_PACKET rtp;
    PQUEUED_AUDIO_PACKET packet;
    int queueStatus;
    int useSelect;
    int packetsToDrop = 100;

    packet = NULL;

    if (setNonFatalRecvTimeoutMs(rtpSocket, UDP_RECV_POLL_TIMEOUT_MS) < 0) {
        // SO_RCVTIMEO failed, so use select() to wait
        useSelect = 1;
    }
    else {
        // SO_RCVTIMEO timeout set for recv()
        useSelect = 0;
    }

    while (!PltIsThreadInterrupted(&receiveThread)) {
        if (packet == NULL) {
            packet = (PQUEUED_AUDIO_PACKET)malloc(sizeof(*packet));
            if (packet == NULL) {
                Limelog("Audio Receive: malloc() failed\n");
                ListenerCallbacks.connectionTerminated(-1);
                break;
            }
        }

        packet->size = recvUdpSocket(rtpSocket, &packet->data[0], MAX_PACKET_SIZE, useSelect);
        if (packet->size < 0) {
            Limelog("Audio Receive: recvUdpSocket() failed: %d\n", (int)LastSocketError());
            ListenerCallbacks.connectionTerminated(LastSocketFail());
            break;
        }
        else if (packet->size == 0) {
            // Receive timed out; try again

            // If we hit this path, there are no queued audio packets on the host PC,
            // so we don't need to drop anything.
            packetsToDrop = 0;
            continue;
        }

        if (packet->size < sizeof(RTP_PACKET)) {
            // Runt packet
            continue;
        }

        rtp = (PRTP_PACKET)&packet->data[0];
        if (rtp->packetType != 97) {
            // Not audio
            continue;
        }

        // We've received data, so we can stop sending our ping packets
        // as quickly, since we're now just keeping the NAT session open.
        receivedDataFromPeer = 1;

        // GFE accumulates audio samples before we are ready to receive them,
        // so we will drop the first 100 packets to avoid accumulating latency
        // by sending audio frames to the player faster than they can be played.
        if (packetsToDrop > 0) {
            packetsToDrop--;
            continue;
        }

        // RTP sequence number must be in host order for the RTP queue
        rtp->sequenceNumber = htons(rtp->sequenceNumber);

        queueStatus = RtpqAddPacket(&rtpReorderQueue, (PRTP_PACKET)packet, &packet->q.rentry);
        if (RTPQ_HANDLE_NOW(queueStatus)) {
            if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
                if (!queuePacketToLbq(&packet)) {
                    // An exit signal was received
                    break;
                }
            }
            else {
                decodeInputData(packet);
            }
        }
        else {
            if (RTPQ_PACKET_CONSUMED(queueStatus)) {
                // The queue consumed our packet, so we must allocate a new one
                packet = NULL;
            }

            if (RTPQ_PACKET_READY(queueStatus)) {
                // If packets are ready, pull them and send them to the decoder
                while ((packet = (PQUEUED_AUDIO_PACKET)RtpqGetQueuedPacket(&rtpReorderQueue)) != NULL) {
                    if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
                        if (!queuePacketToLbq(&packet)) {
                            // An exit signal was received
                            break;
                        }
                    }
                    else {
                        decodeInputData(packet);
                        free(packet);
                    }
                }
                
                // Break on exit
                if (packet != NULL) {
                    break;
                }
            }
        }
    }
    
    if (packet != NULL) {
        free(packet);
    }
}
Ejemplo n.º 25
0
/* Perform RTSP Handshake with the streaming server machine as part of the connection process */
int performRtspHandshake(IP_ADDRESS addr, PSTREAM_CONFIGURATION streamConfigPtr) {
	struct in_addr inaddr;

	// Initialize global state
	remoteAddr = addr;
    memcpy(&inaddr, &addr, sizeof(addr));
	sprintf(rtspTargetUrl, "rtsp://%s", inet_ntoa(inaddr));
	currentSeqNumber = 1;
	hasSessionId = 0;
    
    if (serverMajorVersion == 3) {
        rtspClientVersion = 10;
    }
    else {
        rtspClientVersion = 11;
    }

	{
		RTSP_MESSAGE response;

		if (!requestOptions(&response)) {
			Limelog("RTSP OPTIONS request failed\n");
			return -1;
		}

		if (response.message.response.statusCode != 200) {
			Limelog("RTSP OPTIONS request failed: %d\n",
				response.message.response.statusCode);
			return -1;
		}

		freeMessage(&response);
	}

	{
		RTSP_MESSAGE response;

		if (!requestDescribe(&response)) {
			Limelog("RTSP DESCRIBE request failed\n");
			return -1;
		}

		if (response.message.response.statusCode != 200) {
			Limelog("RTSP DESCRIBE request failed: %d\n",
				response.message.response.statusCode);
			return -1;
		}

		freeMessage(&response);
	}

	{
		RTSP_MESSAGE response;
		char* sessionId;

		if (!setupStream(&response, "streamid=audio")) {
			Limelog("RTSP SETUP streamid=audio request failed\n");
			return -1;
		}

		if (response.message.response.statusCode != 200) {
			Limelog("RTSP SETUP streamid=audio request failed: %d\n",
				response.message.response.statusCode);
			return -1;
		}

		sessionId = getOptionContent(response.options, "Session");
		if (sessionId == NULL) {
			Limelog("RTSP SETUP streamid=audio is missing session attribute");
			return -1;
		}

		strcpy(sessionIdString, sessionId);
		hasSessionId = 1;

		freeMessage(&response);
	}

	{
		RTSP_MESSAGE response;

		if (!setupStream(&response, "streamid=video")) {
			Limelog("RTSP SETUP streamid=video request failed\n");
			return -1;
		}

		if (response.message.response.statusCode != 200) {
			Limelog("RTSP SETUP streamid=video request failed: %d\n",
				response.message.response.statusCode);
			return -1;
		}

		freeMessage(&response);
	}

	{
		RTSP_MESSAGE response;

		if (!sendVideoAnnounce(&response, streamConfigPtr)) {
			Limelog("RTSP ANNOUNCE request failed\n");
			return -1;
		}

		if (response.message.response.statusCode != 200) {
			Limelog("RTSP ANNOUNCE request failed: %d\n",
				response.message.response.statusCode);
			return -1;
		}

		freeMessage(&response);
	}

	{
		RTSP_MESSAGE response;

		if (!playStream(&response, "streamid=video")) {
			Limelog("RTSP PLAY streamid=video request failed\n");
			return -1;
		}

		if (response.message.response.statusCode != 200) {
			Limelog("RTSP PLAY streamid=video failed: %d\n",
				response.message.response.statusCode);
			return -1;
		}

		freeMessage(&response);
	}

	{
		RTSP_MESSAGE response;

		if (!playStream(&response, "streamid=audio")) {
			Limelog("RTSP PLAY streamid=audio request failed\n");
			return -1;
		}

		if (response.message.response.statusCode != 200) {
			Limelog("RTSP PLAY streamid=audio failed: %d\n",
				response.message.response.statusCode);
			return -1;
		}

		freeMessage(&response);
	}

	return 0;
}
Ejemplo n.º 26
0
SOCKET connectTcpSocket(struct sockaddr_storage* dstaddr, SOCKADDR_LEN addrlen, unsigned short port, int timeoutSec) {
    SOCKET s;
    struct sockaddr_in6 addr;
    int err;
#if defined(LC_DARWIN) || defined(FIONBIO)
    int val;
#endif

    s = socket(dstaddr->ss_family, SOCK_STREAM, IPPROTO_TCP);
    if (s == INVALID_SOCKET) {
        Limelog("socket() failed: %d\n", (int)LastSocketError());
        return INVALID_SOCKET;
    }
    
#ifdef LC_DARWIN
    // Disable SIGPIPE on iOS
    val = 1;
    setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (char*)&val, sizeof(val));
#endif
    
#ifdef FIONBIO
    // Enable non-blocking I/O for connect timeout support
    val = 1;
    ioctlsocket(s, FIONBIO, &val);
#endif

    // Start connection
    memcpy(&addr, dstaddr, sizeof(addr));
    addr.sin6_port = htons(port);
    err = connect(s, (struct sockaddr*) &addr, addrlen);
    if (err < 0) {
        err = (int)LastSocketError();
    }
    
#ifdef FIONBIO
    {
        struct fd_set writefds, exceptfds;
        struct timeval tv;
        
        FD_ZERO(&writefds);
        FD_ZERO(&exceptfds);
        FD_SET(s, &writefds);
        FD_SET(s, &exceptfds);
        
        tv.tv_sec = timeoutSec;
        tv.tv_usec = 0;
        
        // Wait for the connection to complete or the timeout to elapse
        err = select(s + 1, NULL, &writefds, &exceptfds, &tv);
        if (err < 0) {
            // select() failed
            err = LastSocketError();
            Limelog("select() failed: %d\n", err);
            closeSocket(s);
            SetLastSocketError(err);
            return INVALID_SOCKET;
        }
        else if (err == 0) {
            // select() timed out
            Limelog("select() timed out after %d seconds\n", timeoutSec);
            closeSocket(s);
#if defined(LC_WINDOWS)
            SetLastSocketError(WSAEWOULDBLOCK);
#else
            SetLastSocketError(EWOULDBLOCK);
#endif
            return INVALID_SOCKET;
        }
        else if (FD_ISSET(s, &writefds) || FD_ISSET(s, &exceptfds)) {
            // The socket was signalled
            SOCKADDR_LEN len = sizeof(err);
            getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&err, &len);
            if (err != 0 || FD_ISSET(s, &exceptfds)) {
                // Get the error code
                err = (err != 0) ? err : LastSocketFail();
            }
        }
        
        // Disable non-blocking I/O now that the connection is established
        val = 0;
        ioctlsocket(s, FIONBIO, &val);
    }
#endif
    
    if (err != 0) {
        Limelog("connect() failed: %d\n", err);
        closeSocket(s);
        SetLastSocketError(err);
        return INVALID_SOCKET;
    }

    return s;
}
Ejemplo n.º 27
0
// Receive thread proc
static void ReceiveThreadProc(void* context) {
    int err;
    int bufferSize, receiveSize;
    char* buffer;
    int queueStatus;

    receiveSize = StreamConfig.packetSize + MAX_RTP_HEADER_SIZE;
    bufferSize = receiveSize + sizeof(int) + sizeof(RTP_QUEUE_ENTRY);
    buffer = NULL;

    while (!PltIsThreadInterrupted(&receiveThread)) {
        PRTP_PACKET packet;

        if (buffer == NULL) {
            buffer = (char*)malloc(bufferSize);
            if (buffer == NULL) {
                Limelog("Video Receive: malloc() failed\n");
                ListenerCallbacks.connectionTerminated(-1);
                return;
            }
        }

        err = recvUdpSocket(rtpSocket, buffer, receiveSize);
        if (err < 0) {
            Limelog("Video Receive: recvUdpSocket() failed: %d\n", (int)LastSocketError());
            ListenerCallbacks.connectionTerminated(LastSocketError());
            break;
        }
        else if  (err == 0) {
            // Receive timed out; try again
            continue;
        }

        memcpy(&buffer[receiveSize], &err, sizeof(int));

        // RTP sequence number must be in host order for the RTP queue
        packet = (PRTP_PACKET)&buffer[0];
        packet->sequenceNumber = htons(packet->sequenceNumber);

        queueStatus = RtpqAddPacket(&rtpQueue, packet, (PRTP_QUEUE_ENTRY)&buffer[receiveSize + sizeof(int)]);
        if (queueStatus == RTPQ_RET_HANDLE_IMMEDIATELY) {
            // queueRtpPacket() copies the data it needs to we can reuse the buffer
            queueRtpPacket(packet, err);
        }
        else if (queueStatus == RTPQ_RET_QUEUED_PACKETS_READY) {
            // The packet queue now has packets ready
            while ((buffer = (char*)RtpqGetQueuedPacket(&rtpQueue)) != NULL) {
                memcpy(&err, &buffer[receiveSize], sizeof(int));
                queueRtpPacket((PRTP_PACKET)buffer, err);
                free(buffer);
            }
        }
        else if (queueStatus == RTPQ_RET_QUEUED_NOTHING_READY) {
            // The queue owns the buffer
            buffer = NULL;
        }
    }

    if (buffer != NULL) {
        free(buffer);
    }
}
Ejemplo n.º 28
0
/* Input thread proc */
static void inputSendThreadProc(void* context) {
	SOCK_RET err;
	PPACKET_HOLDER holder;
	char encryptedBuffer[MAX_INPUT_PACKET_SIZE];
	size_t encryptedSize;

	while (!PltIsThreadInterrupted(&inputSendThread)) {
		int encryptedLengthPrefix;

		err = LbqWaitForQueueElement(&packetQueue, (void**) &holder);
		if (err != LBQ_SUCCESS) {
			return;
		}
        
        // If it's a multi-controller packet we can do batching
        if (holder->packet.multiController.header.packetType == htonl(PACKET_TYPE_MULTI_CONTROLLER)) {
            PPACKET_HOLDER controllerBatchHolder;
            PNV_MULTI_CONTROLLER_PACKET origPkt;
            int dirs[6];
            
            memset(dirs, 0, sizeof(dirs));
            
            origPkt = &holder->packet.multiController;
            for (;;) {
                PNV_MULTI_CONTROLLER_PACKET newPkt;
                
                // Peek at the next packet
                if (LbqPeekQueueElement(&packetQueue, (void**)&controllerBatchHolder) != LBQ_SUCCESS) {
                    break;
                }
                
                // If it's not a controller packet, we're done
                if (controllerBatchHolder->packet.multiController.header.packetType != htonl(PACKET_TYPE_MULTI_CONTROLLER)) {
                    break;
                }
                
                // Check if it's able to be batched
                newPkt = &controllerBatchHolder->packet.multiController;
                if (newPkt->buttonFlags != origPkt->buttonFlags ||
                    newPkt->controllerNumber != origPkt->controllerNumber ||
                    !checkDirs(origPkt->leftTrigger, newPkt->leftTrigger, &dirs[0]) ||
                    !checkDirs(origPkt->rightTrigger, newPkt->rightTrigger, &dirs[1]) ||
                    !checkDirs(origPkt->leftStickX, newPkt->leftStickX, &dirs[2]) ||
                    !checkDirs(origPkt->leftStickY, newPkt->leftStickY, &dirs[3]) ||
                    !checkDirs(origPkt->rightStickX, newPkt->rightStickX, &dirs[4]) ||
                    !checkDirs(origPkt->rightStickY, newPkt->rightStickY, &dirs[5])) {
                    // Batching not allowed
                    break;
                }
                
                // Remove the batchable controller packet
                if (LbqPollQueueElement(&packetQueue, (void**)&controllerBatchHolder) != LBQ_SUCCESS) {
                    break;
                }
                
                // Update the original packet
                origPkt->leftTrigger = newPkt->leftTrigger;
                origPkt->rightTrigger = newPkt->rightTrigger;
                origPkt->leftStickX = newPkt->leftStickX;
                origPkt->leftStickY = newPkt->leftStickY;
                origPkt->rightStickX = newPkt->rightStickX;
                origPkt->rightStickY = newPkt->rightStickY;
                
                // Free the batched packet holder
                free(controllerBatchHolder);
            }
        }
        // If it's a mouse move packet, we can also do batching
        else if (holder->packet.mouseMove.header.packetType == htonl(PACKET_TYPE_MOUSE_MOVE)) {
            PPACKET_HOLDER mouseBatchHolder;
            int totalDeltaX = (short)htons(holder->packet.mouseMove.deltaX);
            int totalDeltaY = (short)htons(holder->packet.mouseMove.deltaY);
            
            for (;;) {
                int partialDeltaX;
                int partialDeltaY;
                
                // Peek at the next packet
                if (LbqPeekQueueElement(&packetQueue, (void**)&mouseBatchHolder) != LBQ_SUCCESS) {
                    break;
                }
                
                // If it's not a mouse move packet, we're done
                if (mouseBatchHolder->packet.mouseMove.header.packetType != htonl(PACKET_TYPE_MOUSE_MOVE)) {
                    break;
                }
                
                partialDeltaX = (short)htons(mouseBatchHolder->packet.mouseMove.deltaX);
                partialDeltaY = (short)htons(mouseBatchHolder->packet.mouseMove.deltaY);
                
                // Check for overflow
                if (partialDeltaX + totalDeltaX > INT16_MAX ||
                    partialDeltaX + totalDeltaX < INT16_MIN ||
                    partialDeltaY + totalDeltaY > INT16_MAX ||
                    partialDeltaY + totalDeltaY < INT16_MIN) {
                    // Total delta would overflow our 16-bit short
                    break;
                }
                
                // Remove the batchable mouse move packet
                if (LbqPollQueueElement(&packetQueue, (void**)&mouseBatchHolder) != LBQ_SUCCESS) {
                    break;
                }
                
                totalDeltaX += partialDeltaX;
                totalDeltaY += partialDeltaY;
                
                // Free the batched packet holder
                free(mouseBatchHolder);
            }
            
            // Update the original packet
            holder->packet.mouseMove.deltaX = htons((short)totalDeltaX);
            holder->packet.mouseMove.deltaY = htons((short)totalDeltaY);
        }

		encryptedSize = sizeof(encryptedBuffer);
		err = oaes_encrypt(oaesContext, (const unsigned char*) &holder->packet, holder->packetLength,
			(unsigned char*) encryptedBuffer, &encryptedSize);
		free(holder);
		if (err != OAES_RET_SUCCESS) {
			Limelog("Input: Encryption failed: %d\n", (int)err);
			ListenerCallbacks.connectionTerminated(err);
			return;
		}

		// The first 32-bytes of the output are internal OAES stuff that we want to ignore
		encryptedSize -= OAES_DATA_OFFSET;

		// Overwrite the last 4 bytes before the encrypted data with the length so
		// we can send the message all at once. GFE can choke if it gets the header
		// before the rest of the message.
		encryptedLengthPrefix = htonl((unsigned long) encryptedSize);
		memcpy(&encryptedBuffer[OAES_DATA_OFFSET - sizeof(encryptedLengthPrefix)],
			&encryptedLengthPrefix, sizeof(encryptedLengthPrefix));

		// Send the encrypted payload
		err = send(inputSock, (const char*) &encryptedBuffer[OAES_DATA_OFFSET - sizeof(encryptedLengthPrefix)],
			(int)(encryptedSize + sizeof(encryptedLengthPrefix)), 0);
		if (err <= 0) {
			Limelog("Input: send() failed: %d\n", (int)LastSocketError());
			ListenerCallbacks.connectionTerminated(LastSocketError());
			return;
		}
	}
}
Ejemplo n.º 29
0
int startAudioStream(void* audioContext, int arFlags) {
    int err;
    POPUS_MULTISTREAM_CONFIGURATION chosenConfig;

    if (StreamConfig.audioConfiguration == AUDIO_CONFIGURATION_STEREO) {
        chosenConfig = &opusStereoConfig;
    }
    else if (StreamConfig.audioConfiguration == AUDIO_CONFIGURATION_51_SURROUND) {
        if (HighQualitySurroundEnabled) {
            chosenConfig = &opus51HighSurroundConfig;
        }
        else {
            chosenConfig = &opus51SurroundConfig;
        }
    }
    else {
        Limelog("Invalid audio configuration: %d\n", StreamConfig.audioConfiguration);
        return -1;
    }

    err = AudioCallbacks.init(StreamConfig.audioConfiguration, chosenConfig, audioContext, arFlags);
    if (err != 0) {
        return err;
    }

    rtpSocket = bindUdpSocket(RemoteAddr.ss_family, RTP_RECV_BUFFER);
    if (rtpSocket == INVALID_SOCKET) {
        err = LastSocketFail();
        AudioCallbacks.cleanup();
        return err;
    }

    AudioCallbacks.start();

    err = PltCreateThread(ReceiveThreadProc, NULL, &receiveThread);
    if (err != 0) {
        AudioCallbacks.stop();
        closeSocket(rtpSocket);
        AudioCallbacks.cleanup();
        return err;
    }

    if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
        err = PltCreateThread(DecoderThreadProc, NULL, &decoderThread);
        if (err != 0) {
            AudioCallbacks.stop();
            PltInterruptThread(&receiveThread);
            PltJoinThread(&receiveThread);
            PltCloseThread(&receiveThread);
            closeSocket(rtpSocket);
            AudioCallbacks.cleanup();
            return err;
        }
    }

    // Don't start pinging (which will cause GFE to start sending us traffic)
    // until everything else is started. Otherwise we could accumulate a
    // bunch of audio packets in the socket receive buffer while our audio
    // backend is starting up and create audio latency.
    err = PltCreateThread(UdpPingThreadProc, NULL, &udpPingThread);
    if (err != 0) {
        AudioCallbacks.stop();
        PltInterruptThread(&receiveThread);
        if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
            // Signal threads waiting on the LBQ
            LbqSignalQueueShutdown(&packetQueue);
            PltInterruptThread(&decoderThread);
        }
        PltJoinThread(&receiveThread);
        if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
            PltJoinThread(&decoderThread);
        }
        PltCloseThread(&receiveThread);
        if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
            PltCloseThread(&decoderThread);
        }
        closeSocket(rtpSocket);
        AudioCallbacks.cleanup();
        return err;
    }

    return 0;
}