mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2025-08-18 01:15:46 +00:00
Fix inconsistent whitespace
This commit is contained in:
parent
349d1baa53
commit
dfdfe4f0f6
@ -48,247 +48,247 @@ static POPUS_MULTISTREAM_CONFIGURATION opusConfigArray[] = {
|
||||
};
|
||||
|
||||
typedef struct _QUEUED_AUDIO_PACKET {
|
||||
// data must remain at the front
|
||||
char data[MAX_PACKET_SIZE];
|
||||
// data must remain at the front
|
||||
char data[MAX_PACKET_SIZE];
|
||||
|
||||
int size;
|
||||
union {
|
||||
RTP_QUEUE_ENTRY rentry;
|
||||
LINKED_BLOCKING_QUEUE_ENTRY lentry;
|
||||
} q;
|
||||
int size;
|
||||
union {
|
||||
RTP_QUEUE_ENTRY rentry;
|
||||
LINKED_BLOCKING_QUEUE_ENTRY lentry;
|
||||
} q;
|
||||
} QUEUED_AUDIO_PACKET, *PQUEUED_AUDIO_PACKET;
|
||||
|
||||
/* Initialize the audio stream */
|
||||
void initializeAudioStream(void) {
|
||||
if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
LbqInitializeLinkedBlockingQueue(&packetQueue, 30);
|
||||
}
|
||||
RtpqInitializeQueue(&rtpReorderQueue, RTPQ_DEFAULT_MAX_SIZE, RTPQ_DEFAULT_QUEUE_TIME);
|
||||
if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
LbqInitializeLinkedBlockingQueue(&packetQueue, 30);
|
||||
}
|
||||
RtpqInitializeQueue(&rtpReorderQueue, RTPQ_DEFAULT_MAX_SIZE, RTPQ_DEFAULT_QUEUE_TIME);
|
||||
lastSeq = 0;
|
||||
}
|
||||
|
||||
static void freePacketList(PLINKED_BLOCKING_QUEUE_ENTRY entry) {
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY nextEntry;
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY nextEntry;
|
||||
|
||||
while (entry != NULL) {
|
||||
nextEntry = entry->flink;
|
||||
while (entry != NULL) {
|
||||
nextEntry = entry->flink;
|
||||
|
||||
// The entry is stored within the data allocation
|
||||
free(entry->data);
|
||||
// The entry is stored within the data allocation
|
||||
free(entry->data);
|
||||
|
||||
entry = nextEntry;
|
||||
}
|
||||
entry = nextEntry;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tear down the audio stream once we're done with it */
|
||||
void destroyAudioStream(void) {
|
||||
if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
freePacketList(LbqDestroyLinkedBlockingQueue(&packetQueue));
|
||||
}
|
||||
RtpqCleanupQueue(&rtpReorderQueue);
|
||||
if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
freePacketList(LbqDestroyLinkedBlockingQueue(&packetQueue));
|
||||
}
|
||||
RtpqCleanupQueue(&rtpReorderQueue);
|
||||
}
|
||||
|
||||
static void UdpPingThreadProc(void *context) {
|
||||
/* Ping in ASCII */
|
||||
char pingData[] = { 0x50, 0x49, 0x4E, 0x47 };
|
||||
struct sockaddr_in6 saddr;
|
||||
SOCK_RET err;
|
||||
/* 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);
|
||||
saddr.sin6_port = htons(RTP_PORT);
|
||||
|
||||
/* Send PING every 500 milliseconds */
|
||||
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(LastSocketError());
|
||||
return;
|
||||
}
|
||||
/* Send PING every 500 milliseconds */
|
||||
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(LastSocketError());
|
||||
return;
|
||||
}
|
||||
|
||||
PltSleepMs(500);
|
||||
}
|
||||
PltSleepMs(500);
|
||||
}
|
||||
}
|
||||
|
||||
static int queuePacketToLbq(PQUEUED_AUDIO_PACKET *packet) {
|
||||
int err;
|
||||
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) {
|
||||
free(*packet);
|
||||
return 0;
|
||||
}
|
||||
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) {
|
||||
free(*packet);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void decodeInputData(PQUEUED_AUDIO_PACKET packet) {
|
||||
PRTP_PACKET rtp;
|
||||
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);
|
||||
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);
|
||||
}
|
||||
AudioCallbacks.decodeAndPlaySample(NULL, 0);
|
||||
}
|
||||
|
||||
lastSeq = rtp->sequenceNumber;
|
||||
lastSeq = rtp->sequenceNumber;
|
||||
|
||||
AudioCallbacks.decodeAndPlaySample((char *) (rtp + 1), packet->size - sizeof(*rtp));
|
||||
AudioCallbacks.decodeAndPlaySample((char *) (rtp + 1), packet->size - sizeof(*rtp));
|
||||
}
|
||||
|
||||
static void ReceiveThreadProc(void* context) {
|
||||
PRTP_PACKET rtp;
|
||||
PQUEUED_AUDIO_PACKET packet;
|
||||
int queueStatus;
|
||||
PRTP_PACKET rtp;
|
||||
PQUEUED_AUDIO_PACKET packet;
|
||||
int queueStatus;
|
||||
|
||||
packet = NULL;
|
||||
packet = NULL;
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
}
|
||||
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);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
packet->size = (int) recv(rtpSocket, &packet->data[0], MAX_PACKET_SIZE, 0);
|
||||
if (packet->size <= 0) {
|
||||
Limelog("Audio Receive: recv() failed: %d\n", (int)LastSocketError());
|
||||
free(packet);
|
||||
ListenerCallbacks.connectionTerminated(LastSocketError());
|
||||
return;
|
||||
}
|
||||
packet->size = (int) recv(rtpSocket, &packet->data[0], MAX_PACKET_SIZE, 0);
|
||||
if (packet->size <= 0) {
|
||||
Limelog("Audio Receive: recv() failed: %d\n", (int)LastSocketError());
|
||||
free(packet);
|
||||
ListenerCallbacks.connectionTerminated(LastSocketError());
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet->size < sizeof(RTP_PACKET)) {
|
||||
// Runt packet
|
||||
continue;
|
||||
}
|
||||
if (packet->size < sizeof(RTP_PACKET)) {
|
||||
// Runt packet
|
||||
continue;
|
||||
}
|
||||
|
||||
rtp = (PRTP_PACKET) &packet->data[0];
|
||||
if (rtp->packetType != 97) {
|
||||
// Not audio
|
||||
continue;
|
||||
}
|
||||
rtp = (PRTP_PACKET) &packet->data[0];
|
||||
if (rtp->packetType != 97) {
|
||||
// Not audio
|
||||
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 (queueStatus == RTPQ_RET_HANDLE_IMMEDIATELY) {
|
||||
if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
if (!queuePacketToLbq(&packet)) {
|
||||
// An exit signal was received
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
decodeInputData(packet);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (queueStatus != RTPQ_RET_REJECTED) {
|
||||
// The queue consumed our packet, so we must allocate a new one
|
||||
packet = NULL;
|
||||
}
|
||||
queueStatus = RtpqAddPacket(&rtpReorderQueue, (PRTP_PACKET) packet, &packet->q.rentry);
|
||||
if (queueStatus == RTPQ_RET_HANDLE_IMMEDIATELY) {
|
||||
if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
if (!queuePacketToLbq(&packet)) {
|
||||
// An exit signal was received
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
decodeInputData(packet);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (queueStatus != RTPQ_RET_REJECTED) {
|
||||
// The queue consumed our packet, so we must allocate a new one
|
||||
packet = NULL;
|
||||
}
|
||||
|
||||
if (queueStatus == RTPQ_RET_QUEUED_PACKETS_READY) {
|
||||
// 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
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
decodeInputData(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (queueStatus == RTPQ_RET_QUEUED_PACKETS_READY) {
|
||||
// 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
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
decodeInputData(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DecoderThreadProc(void* context) {
|
||||
int err;
|
||||
PQUEUED_AUDIO_PACKET packet;
|
||||
int err;
|
||||
PQUEUED_AUDIO_PACKET packet;
|
||||
|
||||
while (!PltIsThreadInterrupted(&decoderThread)) {
|
||||
err = LbqWaitForQueueElement(&packetQueue, (void**) &packet);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
while (!PltIsThreadInterrupted(&decoderThread)) {
|
||||
err = LbqWaitForQueueElement(&packetQueue, (void**) &packet);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
// An exit signal was received
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
decodeInputData(packet);
|
||||
decodeInputData(packet);
|
||||
|
||||
free(packet);
|
||||
}
|
||||
free(packet);
|
||||
}
|
||||
}
|
||||
|
||||
void stopAudioStream(void) {
|
||||
PltInterruptThread(&udpPingThread);
|
||||
PltInterruptThread(&receiveThread);
|
||||
if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
PltInterruptThread(&decoderThread);
|
||||
}
|
||||
PltInterruptThread(&udpPingThread);
|
||||
PltInterruptThread(&receiveThread);
|
||||
if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
PltInterruptThread(&decoderThread);
|
||||
}
|
||||
|
||||
if (rtpSocket != INVALID_SOCKET) {
|
||||
closesocket(rtpSocket);
|
||||
rtpSocket = INVALID_SOCKET;
|
||||
}
|
||||
if (rtpSocket != INVALID_SOCKET) {
|
||||
closesocket(rtpSocket);
|
||||
rtpSocket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
PltJoinThread(&udpPingThread);
|
||||
PltJoinThread(&receiveThread);
|
||||
if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
PltJoinThread(&decoderThread);
|
||||
}
|
||||
PltJoinThread(&udpPingThread);
|
||||
PltJoinThread(&receiveThread);
|
||||
if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
PltJoinThread(&decoderThread);
|
||||
}
|
||||
|
||||
PltCloseThread(&udpPingThread);
|
||||
PltCloseThread(&receiveThread);
|
||||
if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
PltCloseThread(&decoderThread);
|
||||
}
|
||||
PltCloseThread(&udpPingThread);
|
||||
PltCloseThread(&receiveThread);
|
||||
if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
PltCloseThread(&decoderThread);
|
||||
}
|
||||
|
||||
AudioCallbacks.cleanup();
|
||||
}
|
||||
|
||||
int startAudioStream(void) {
|
||||
int err;
|
||||
int err;
|
||||
|
||||
AudioCallbacks.init(StreamConfig.audioConfiguration,
|
||||
opusConfigArray[StreamConfig.audioConfiguration]);
|
||||
|
||||
rtpSocket = bindUdpSocket(RemoteAddr.ss_family, RTP_RECV_BUFFER);
|
||||
if (rtpSocket == INVALID_SOCKET) {
|
||||
return LastSocketFail();
|
||||
}
|
||||
rtpSocket = bindUdpSocket(RemoteAddr.ss_family, RTP_RECV_BUFFER);
|
||||
if (rtpSocket == INVALID_SOCKET) {
|
||||
return LastSocketFail();
|
||||
}
|
||||
|
||||
err = PltCreateThread(UdpPingThreadProc, NULL, &udpPingThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
err = PltCreateThread(UdpPingThreadProc, NULL, &udpPingThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = PltCreateThread(ReceiveThreadProc, NULL, &receiveThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
err = PltCreateThread(ReceiveThreadProc, NULL, &receiveThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
err = PltCreateThread(DecoderThreadProc, NULL, &decoderThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
err = PltCreateThread(DecoderThreadProc, NULL, &decoderThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
@ -1,147 +1,147 @@
|
||||
#include "ByteBuffer.h"
|
||||
|
||||
void BbInitializeWrappedBuffer(PBYTE_BUFFER buff, char* data, int offset, int length, int byteOrder) {
|
||||
buff->buffer = data;
|
||||
buff->offset = offset;
|
||||
buff->length = length;
|
||||
buff->position = 0;
|
||||
buff->byteOrder = byteOrder;
|
||||
buff->buffer = data;
|
||||
buff->offset = offset;
|
||||
buff->length = length;
|
||||
buff->position = 0;
|
||||
buff->byteOrder = byteOrder;
|
||||
}
|
||||
|
||||
/* Get the long long in the correct byte order */
|
||||
static long long byteSwapLongLong(PBYTE_BUFFER buff, long long l) {
|
||||
if (buff->byteOrder == BYTE_ORDER_BIG) {
|
||||
return HTONLL(l);
|
||||
}
|
||||
else {
|
||||
return l;
|
||||
}
|
||||
if (buff->byteOrder == BYTE_ORDER_BIG) {
|
||||
return HTONLL(l);
|
||||
}
|
||||
else {
|
||||
return l;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the int in the correct byte order */
|
||||
static int byteSwapInt(PBYTE_BUFFER buff, int i) {
|
||||
if (buff->byteOrder == BYTE_ORDER_BIG) {
|
||||
return htonl(i);
|
||||
}
|
||||
else {
|
||||
return i;
|
||||
}
|
||||
if (buff->byteOrder == BYTE_ORDER_BIG) {
|
||||
return htonl(i);
|
||||
}
|
||||
else {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the short in the correct byte order */
|
||||
static int byteSwapShort(PBYTE_BUFFER buff, short s) {
|
||||
if (buff->byteOrder == BYTE_ORDER_BIG) {
|
||||
return htons(s);
|
||||
}
|
||||
else {
|
||||
return s;
|
||||
}
|
||||
if (buff->byteOrder == BYTE_ORDER_BIG) {
|
||||
return htons(s);
|
||||
}
|
||||
else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get a byte from the byte buffer */
|
||||
int BbGet(PBYTE_BUFFER buff, char *c) {
|
||||
if (buff->position + sizeof(*c) > buff->length) {
|
||||
return 0;
|
||||
}
|
||||
if (buff->position + sizeof(*c) > buff->length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(c, &buff->buffer[buff->position], sizeof(*c));
|
||||
buff->position += sizeof(*c);
|
||||
memcpy(c, &buff->buffer[buff->position], sizeof(*c));
|
||||
buff->position += sizeof(*c);
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Get a short from the byte buffer */
|
||||
int BbGetShort(PBYTE_BUFFER buff, short *s) {
|
||||
if (buff->position + sizeof(*s) >= buff->length) {
|
||||
return 0;
|
||||
}
|
||||
if (buff->position + sizeof(*s) >= buff->length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(s, &buff->buffer[buff->position], sizeof(*s));
|
||||
buff->position += sizeof(*s);
|
||||
memcpy(s, &buff->buffer[buff->position], sizeof(*s));
|
||||
buff->position += sizeof(*s);
|
||||
|
||||
*s = byteSwapShort(buff, *s);
|
||||
*s = byteSwapShort(buff, *s);
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Get an int from the byte buffer */
|
||||
int BbGetInt(PBYTE_BUFFER buff, int *i) {
|
||||
if (buff->position + sizeof(*i) > buff->length) {
|
||||
return 0;
|
||||
}
|
||||
if (buff->position + sizeof(*i) > buff->length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(i, &buff->buffer[buff->position], sizeof(*i));
|
||||
buff->position += sizeof(*i);
|
||||
memcpy(i, &buff->buffer[buff->position], sizeof(*i));
|
||||
buff->position += sizeof(*i);
|
||||
|
||||
*i = byteSwapInt(buff, *i);
|
||||
*i = byteSwapInt(buff, *i);
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Get a long from the byte buffer */
|
||||
int BbGetLong(PBYTE_BUFFER buff, long long *l) {
|
||||
if (buff->position + sizeof(*l) > buff->length) {
|
||||
return 0;
|
||||
}
|
||||
if (buff->position + sizeof(*l) > buff->length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(l, &buff->buffer[buff->position], sizeof(*l));
|
||||
buff->position += sizeof(*l);
|
||||
memcpy(l, &buff->buffer[buff->position], sizeof(*l));
|
||||
buff->position += sizeof(*l);
|
||||
|
||||
*l = byteSwapLongLong(buff, *l);
|
||||
*l = byteSwapLongLong(buff, *l);
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Put an int into the byte buffer */
|
||||
int BbPutInt(PBYTE_BUFFER buff, int i) {
|
||||
if (buff->position + sizeof(i) > buff->length) {
|
||||
return 0;
|
||||
}
|
||||
if (buff->position + sizeof(i) > buff->length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
i = byteSwapInt(buff, i);
|
||||
i = byteSwapInt(buff, i);
|
||||
|
||||
memcpy(&buff->buffer[buff->position], &i, sizeof(i));
|
||||
buff->position += sizeof(i);
|
||||
memcpy(&buff->buffer[buff->position], &i, sizeof(i));
|
||||
buff->position += sizeof(i);
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Put a long into the byte buffer */
|
||||
int BbPutLong(PBYTE_BUFFER buff, long long l) {
|
||||
if (buff->position + sizeof(l) > buff->length) {
|
||||
return 0;
|
||||
}
|
||||
if (buff->position + sizeof(l) > buff->length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
l = byteSwapLongLong(buff, l);
|
||||
l = byteSwapLongLong(buff, l);
|
||||
|
||||
memcpy(&buff->buffer[buff->position], &l, sizeof(l));
|
||||
buff->position += sizeof(l);
|
||||
memcpy(&buff->buffer[buff->position], &l, sizeof(l));
|
||||
buff->position += sizeof(l);
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Put a short into the byte buffer */
|
||||
int BbPutShort(PBYTE_BUFFER buff, short s) {
|
||||
if (buff->position + sizeof(s) > buff->length) {
|
||||
return 0;
|
||||
}
|
||||
if (buff->position + sizeof(s) > buff->length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s = byteSwapShort(buff, s);
|
||||
s = byteSwapShort(buff, s);
|
||||
|
||||
memcpy(&buff->buffer[buff->position], &s, sizeof(s));
|
||||
buff->position += sizeof(s);
|
||||
memcpy(&buff->buffer[buff->position], &s, sizeof(s));
|
||||
buff->position += sizeof(s);
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Put a byte into the buffer */
|
||||
int BbPut(PBYTE_BUFFER buff, char c) {
|
||||
if (buff->position + sizeof(c) > buff->length) {
|
||||
return 0;
|
||||
}
|
||||
if (buff->position + sizeof(c) > buff->length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&buff->buffer[buff->position], &c, sizeof(c));
|
||||
buff->position += sizeof(c);
|
||||
memcpy(&buff->buffer[buff->position], &c, sizeof(c));
|
||||
buff->position += sizeof(c);
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
@ -7,22 +7,22 @@
|
||||
|
||||
#ifndef HTONLL
|
||||
#define HTONLL(x) \
|
||||
((((x) & 0xff00000000000000ull) >> 56) \
|
||||
| (((x) & 0x00ff000000000000ull) >> 40) \
|
||||
| (((x) & 0x0000ff0000000000ull) >> 24) \
|
||||
| (((x) & 0x000000ff00000000ull) >> 8) \
|
||||
| (((x) & 0x00000000ff000000ull) << 8) \
|
||||
| (((x) & 0x0000000000ff0000ull) << 24) \
|
||||
| (((x) & 0x000000000000ff00ull) << 40) \
|
||||
| (((x) & 0x00000000000000ffull) << 56))
|
||||
((((x) & 0xff00000000000000ull) >> 56) \
|
||||
| (((x) & 0x00ff000000000000ull) >> 40) \
|
||||
| (((x) & 0x0000ff0000000000ull) >> 24) \
|
||||
| (((x) & 0x000000ff00000000ull) >> 8) \
|
||||
| (((x) & 0x00000000ff000000ull) << 8) \
|
||||
| (((x) & 0x0000000000ff0000ull) << 24) \
|
||||
| (((x) & 0x000000000000ff00ull) << 40) \
|
||||
| (((x) & 0x00000000000000ffull) << 56))
|
||||
#endif
|
||||
|
||||
typedef struct _BYTE_BUFFER {
|
||||
char* buffer;
|
||||
unsigned int offset;
|
||||
unsigned int length;
|
||||
unsigned int position;
|
||||
unsigned int byteOrder;
|
||||
char* buffer;
|
||||
unsigned int offset;
|
||||
unsigned int length;
|
||||
unsigned int position;
|
||||
unsigned int byteOrder;
|
||||
} BYTE_BUFFER, *PBYTE_BUFFER;
|
||||
|
||||
void BbInitializeWrappedBuffer(PBYTE_BUFFER buff, char* data, int offset, int length, int byteOrder);
|
||||
|
@ -18,23 +18,23 @@ AUDIO_RENDERER_CALLBACKS AudioCallbacks;
|
||||
|
||||
/* Connection stages */
|
||||
static const char* stageNames[STAGE_MAX] = {
|
||||
"none",
|
||||
"platform initialization",
|
||||
"none",
|
||||
"platform initialization",
|
||||
"name resolution",
|
||||
"RTSP handshake",
|
||||
"control stream initialization",
|
||||
"video stream initialization",
|
||||
"audio stream initialization",
|
||||
"input stream initialization",
|
||||
"control stream establishment",
|
||||
"video stream establishment",
|
||||
"audio stream establishment",
|
||||
"input stream establishment"
|
||||
"RTSP handshake",
|
||||
"control stream initialization",
|
||||
"video stream initialization",
|
||||
"audio stream initialization",
|
||||
"input stream initialization",
|
||||
"control stream establishment",
|
||||
"video stream establishment",
|
||||
"audio stream establishment",
|
||||
"input stream establishment"
|
||||
};
|
||||
|
||||
/* Get the name of the current stage based on its number */
|
||||
const char* LiGetStageName(int stage) {
|
||||
return stageNames[stage];
|
||||
return stageNames[stage];
|
||||
}
|
||||
|
||||
/* Stop the connection by undoing the step at the current stage and those before it */
|
||||
@ -42,71 +42,71 @@ 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_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);
|
||||
if (stage == STAGE_PLATFORM_INIT) {
|
||||
Limelog("Cleaning up platform...");
|
||||
cleanupPlatform();
|
||||
stage--;
|
||||
Limelog("done\n");
|
||||
}
|
||||
LC_ASSERT(stage == STAGE_NONE);
|
||||
}
|
||||
|
||||
static void terminationCallbackThreadFunc(void* context)
|
||||
@ -173,38 +173,38 @@ static int resolveHostName(const char *host)
|
||||
|
||||
/* 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;
|
||||
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);
|
||||
// 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));
|
||||
// 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("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);
|
||||
@ -219,111 +219,111 @@ int LiStartConnection(const char* host, PSTREAM_CONFIGURATION streamConfig, PCON
|
||||
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("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 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 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 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("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 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 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 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");
|
||||
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();
|
||||
ListenerCallbacks.connectionStarted();
|
||||
|
||||
Cleanup:
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
|
@ -6,14 +6,14 @@
|
||||
|
||||
/* NV control stream packet header */
|
||||
typedef struct _NVCTL_PACKET_HEADER {
|
||||
unsigned short type;
|
||||
unsigned short payloadLength;
|
||||
unsigned short type;
|
||||
unsigned short payloadLength;
|
||||
} NVCTL_PACKET_HEADER, *PNVCTL_PACKET_HEADER;
|
||||
|
||||
typedef struct _QUEUED_FRAME_INVALIDATION_TUPLE {
|
||||
int startFrame;
|
||||
int endFrame;
|
||||
LINKED_BLOCKING_QUEUE_ENTRY entry;
|
||||
int startFrame;
|
||||
int endFrame;
|
||||
LINKED_BLOCKING_QUEUE_ENTRY entry;
|
||||
} QUEUED_FRAME_INVALIDATION_TUPLE, *PQUEUED_FRAME_INVALIDATION_TUPLE;
|
||||
|
||||
static SOCKET ctlSock = INVALID_SOCKET;
|
||||
@ -85,8 +85,8 @@ static char **preconstructedPayloads;
|
||||
|
||||
/* Initializes the control stream */
|
||||
int initializeControlStream(void) {
|
||||
PltCreateEvent(&invalidateRefFramesEvent);
|
||||
LbqInitializeLinkedBlockingQueue(&invalidReferenceFrameTuples, 20);
|
||||
PltCreateEvent(&invalidateRefFramesEvent);
|
||||
LbqInitializeLinkedBlockingQueue(&invalidReferenceFrameTuples, 20);
|
||||
|
||||
if (ServerMajorVersion == 3) {
|
||||
packetTypes = (short*)packetTypesGen3;
|
||||
@ -103,17 +103,17 @@ int initializeControlStream(void) {
|
||||
currentFrame = 0;
|
||||
lossCountSinceLastReport = 0;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void freeFrameInvalidationList(PLINKED_BLOCKING_QUEUE_ENTRY entry) {
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY nextEntry;
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY nextEntry;
|
||||
|
||||
while (entry != NULL) {
|
||||
nextEntry = entry->flink;
|
||||
free(entry->data);
|
||||
entry = nextEntry;
|
||||
}
|
||||
while (entry != NULL) {
|
||||
nextEntry = entry->flink;
|
||||
free(entry->data);
|
||||
entry = nextEntry;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleans up control stream */
|
||||
@ -123,8 +123,8 @@ void destroyControlStream(void) {
|
||||
}
|
||||
|
||||
int getNextFrameInvalidationTuple(PQUEUED_FRAME_INVALIDATION_TUPLE *qfit) {
|
||||
int err = LbqPollQueueElement(&invalidReferenceFrameTuples, (void**) qfit);
|
||||
return (err == LBQ_SUCCESS);
|
||||
int err = LbqPollQueueElement(&invalidReferenceFrameTuples, (void**) qfit);
|
||||
return (err == LBQ_SUCCESS);
|
||||
}
|
||||
|
||||
void queueFrameInvalidationTuple(int startFrame, int endFrame) {
|
||||
@ -170,63 +170,63 @@ void connectionDetectedFrameLoss(int startFrame, int endFrame) {
|
||||
|
||||
/* When we receive a frame, update the number of our current frame */
|
||||
void connectionReceivedFrame(int frameIndex) {
|
||||
currentFrame = frameIndex;
|
||||
currentFrame = frameIndex;
|
||||
}
|
||||
|
||||
/* When we lose packets, update our packet loss count */
|
||||
void connectionLostPackets(int lastReceivedPacket, int nextReceivedPacket) {
|
||||
lossCountSinceLastReport += (nextReceivedPacket - lastReceivedPacket) - 1;
|
||||
lossCountSinceLastReport += (nextReceivedPacket - lastReceivedPacket) - 1;
|
||||
}
|
||||
|
||||
/* Reads an NV control stream packet */
|
||||
static PNVCTL_PACKET_HEADER readNvctlPacket(void) {
|
||||
NVCTL_PACKET_HEADER staticHeader;
|
||||
PNVCTL_PACKET_HEADER fullPacket;
|
||||
SOCK_RET err;
|
||||
NVCTL_PACKET_HEADER staticHeader;
|
||||
PNVCTL_PACKET_HEADER fullPacket;
|
||||
SOCK_RET err;
|
||||
|
||||
err = recv(ctlSock, (char*) &staticHeader, sizeof(staticHeader), 0);
|
||||
if (err != sizeof(staticHeader)) {
|
||||
return NULL;
|
||||
}
|
||||
err = recv(ctlSock, (char*) &staticHeader, sizeof(staticHeader), 0);
|
||||
if (err != sizeof(staticHeader)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fullPacket = (PNVCTL_PACKET_HEADER) malloc(staticHeader.payloadLength + sizeof(staticHeader));
|
||||
if (fullPacket == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
fullPacket = (PNVCTL_PACKET_HEADER) malloc(staticHeader.payloadLength + sizeof(staticHeader));
|
||||
if (fullPacket == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(fullPacket, &staticHeader, sizeof(staticHeader));
|
||||
if (staticHeader.payloadLength != 0) {
|
||||
err = recv(ctlSock, (char*) (fullPacket + 1), staticHeader.payloadLength, 0);
|
||||
if (err != staticHeader.payloadLength) {
|
||||
free(fullPacket);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
memcpy(fullPacket, &staticHeader, sizeof(staticHeader));
|
||||
if (staticHeader.payloadLength != 0) {
|
||||
err = recv(ctlSock, (char*) (fullPacket + 1), staticHeader.payloadLength, 0);
|
||||
if (err != staticHeader.payloadLength) {
|
||||
free(fullPacket);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return fullPacket;
|
||||
return fullPacket;
|
||||
}
|
||||
|
||||
static int sendMessageAndForget(short ptype, short paylen, const void* payload) {
|
||||
PNVCTL_PACKET_HEADER packet;
|
||||
SOCK_RET err;
|
||||
PNVCTL_PACKET_HEADER packet;
|
||||
SOCK_RET err;
|
||||
|
||||
packet = malloc(sizeof(*packet) + paylen);
|
||||
if (packet == NULL) {
|
||||
return 0;
|
||||
}
|
||||
packet = malloc(sizeof(*packet) + paylen);
|
||||
if (packet == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
packet->type = ptype;
|
||||
packet->payloadLength = paylen;
|
||||
memcpy(&packet[1], payload, paylen);
|
||||
packet->type = ptype;
|
||||
packet->payloadLength = paylen;
|
||||
memcpy(&packet[1], payload, paylen);
|
||||
|
||||
err = send(ctlSock, (char*) packet, sizeof(*packet) + paylen, 0);
|
||||
free(packet);
|
||||
err = send(ctlSock, (char*) packet, sizeof(*packet) + paylen, 0);
|
||||
free(packet);
|
||||
|
||||
if (err != sizeof(*packet) + paylen) {
|
||||
return 0;
|
||||
}
|
||||
if (err != sizeof(*packet) + paylen) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static PNVCTL_PACKET_HEADER sendMessage(short ptype, short paylen, const void* payload) {
|
||||
@ -234,7 +234,7 @@ static PNVCTL_PACKET_HEADER sendMessage(short ptype, short paylen, const void* p
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return readNvctlPacket();
|
||||
return readNvctlPacket();
|
||||
}
|
||||
|
||||
static int sendMessageAndDiscardReply(short ptype, short paylen, const void* payload) {
|
||||
@ -250,44 +250,44 @@ static int sendMessageAndDiscardReply(short ptype, short paylen, const void* pay
|
||||
}
|
||||
|
||||
static void lossStatsThreadFunc(void* context) {
|
||||
char *lossStatsPayload;
|
||||
BYTE_BUFFER byteBuffer;
|
||||
char *lossStatsPayload;
|
||||
BYTE_BUFFER byteBuffer;
|
||||
|
||||
lossStatsPayload = malloc(payloadLengths[IDX_LOSS_STATS]);
|
||||
if (lossStatsPayload == NULL) {
|
||||
Limelog("Loss Stats: malloc() failed\n");
|
||||
ListenerCallbacks.connectionTerminated(-1);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
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());
|
||||
// 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;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the transient state
|
||||
lossCountSinceLastReport = 0;
|
||||
// Clear the transient state
|
||||
lossCountSinceLastReport = 0;
|
||||
|
||||
// Wait a bit
|
||||
PltSleepMs(LOSS_REPORT_INTERVAL_MS);
|
||||
}
|
||||
// Wait a bit
|
||||
PltSleepMs(LOSS_REPORT_INTERVAL_MS);
|
||||
}
|
||||
|
||||
free(lossStatsPayload);
|
||||
free(lossStatsPayload);
|
||||
}
|
||||
|
||||
static void requestIdrFrame(void) {
|
||||
@ -380,43 +380,43 @@ static void invalidateRefFramesFunc(void* context) {
|
||||
|
||||
/* Stops the control stream */
|
||||
int stopControlStream(void) {
|
||||
PltInterruptThread(&lossStatsThread);
|
||||
PltInterruptThread(&invalidateRefFramesThread);
|
||||
PltInterruptThread(&lossStatsThread);
|
||||
PltInterruptThread(&invalidateRefFramesThread);
|
||||
|
||||
if (ctlSock != INVALID_SOCKET) {
|
||||
closesocket(ctlSock);
|
||||
ctlSock = INVALID_SOCKET;
|
||||
}
|
||||
if (ctlSock != INVALID_SOCKET) {
|
||||
closesocket(ctlSock);
|
||||
ctlSock = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
PltJoinThread(&lossStatsThread);
|
||||
PltJoinThread(&invalidateRefFramesThread);
|
||||
PltJoinThread(&lossStatsThread);
|
||||
PltJoinThread(&invalidateRefFramesThread);
|
||||
|
||||
PltCloseThread(&lossStatsThread);
|
||||
PltCloseThread(&invalidateRefFramesThread);
|
||||
PltCloseThread(&lossStatsThread);
|
||||
PltCloseThread(&invalidateRefFramesThread);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Starts the control stream */
|
||||
int startControlStream(void) {
|
||||
int err;
|
||||
int err;
|
||||
|
||||
ctlSock = connectTcpSocket(&RemoteAddr, RemoteAddrLen, 47995);
|
||||
if (ctlSock == INVALID_SOCKET) {
|
||||
return LastSocketFail();
|
||||
}
|
||||
ctlSock = connectTcpSocket(&RemoteAddr, RemoteAddrLen, 47995);
|
||||
if (ctlSock == INVALID_SOCKET) {
|
||||
return LastSocketFail();
|
||||
}
|
||||
|
||||
enableNoDelay(ctlSock);
|
||||
enableNoDelay(ctlSock);
|
||||
|
||||
// Send START A
|
||||
if (!sendMessageAndDiscardReply(packetTypes[IDX_START_A],
|
||||
// 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
|
||||
// Send START B
|
||||
if (!sendMessageAndDiscardReply(packetTypes[IDX_START_B],
|
||||
payloadLengths[IDX_START_B],
|
||||
preconstructedPayloads[IDX_START_B])) {
|
||||
@ -424,15 +424,15 @@ int startControlStream(void) {
|
||||
return LastSocketFail();
|
||||
}
|
||||
|
||||
err = PltCreateThread(lossStatsThreadFunc, NULL, &lossStatsThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
err = PltCreateThread(lossStatsThreadFunc, NULL, &lossStatsThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = PltCreateThread(invalidateRefFramesFunc, NULL, &invalidateRefFramesThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
err = PltCreateThread(invalidateRefFramesFunc, NULL, &invalidateRefFramesThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
@ -5,9 +5,9 @@ static void fakeDrCleanup(void) {}
|
||||
static int fakeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) { return DR_OK; }
|
||||
|
||||
static DECODER_RENDERER_CALLBACKS fakeDrCallbacks = {
|
||||
.setup = fakeDrSetup,
|
||||
.cleanup = fakeDrCleanup,
|
||||
.submitDecodeUnit = fakeDrSubmitDecodeUnit,
|
||||
.setup = fakeDrSetup,
|
||||
.cleanup = fakeDrCleanup,
|
||||
.submitDecodeUnit = fakeDrSubmitDecodeUnit,
|
||||
};
|
||||
|
||||
static void fakeArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) {}
|
||||
@ -15,9 +15,9 @@ static void fakeArCleanup(void) {}
|
||||
static void fakeArDecodeAndPlaySample(char* sampleData, int sampleLength) {}
|
||||
|
||||
AUDIO_RENDERER_CALLBACKS fakeArCallbacks = {
|
||||
.init = fakeArInit,
|
||||
.cleanup = fakeArCleanup,
|
||||
.decodeAndPlaySample = fakeArDecodeAndPlaySample,
|
||||
.init = fakeArInit,
|
||||
.cleanup = fakeArCleanup,
|
||||
.decodeAndPlaySample = fakeArDecodeAndPlaySample,
|
||||
};
|
||||
|
||||
static void fakeClStageStarting(int stage) {}
|
||||
@ -29,72 +29,72 @@ static void fakeClDisplayMessage(char* message) {}
|
||||
static void fakeClDisplayTransientMessage(char* message) {}
|
||||
|
||||
static CONNECTION_LISTENER_CALLBACKS fakeClCallbacks = {
|
||||
.stageStarting = fakeClStageStarting,
|
||||
.stageComplete = fakeClStageComplete,
|
||||
.stageFailed = fakeClStageFailed,
|
||||
.connectionStarted = fakeClConnectionStarted,
|
||||
.connectionTerminated = fakeClConnectionTerminated,
|
||||
.displayMessage = fakeClDisplayMessage,
|
||||
.displayTransientMessage = fakeClDisplayTransientMessage,
|
||||
.stageStarting = fakeClStageStarting,
|
||||
.stageComplete = fakeClStageComplete,
|
||||
.stageFailed = fakeClStageFailed,
|
||||
.connectionStarted = fakeClConnectionStarted,
|
||||
.connectionTerminated = fakeClConnectionTerminated,
|
||||
.displayMessage = fakeClDisplayMessage,
|
||||
.displayTransientMessage = fakeClDisplayTransientMessage,
|
||||
};
|
||||
|
||||
void fixupMissingCallbacks(PDECODER_RENDERER_CALLBACKS *drCallbacks, PAUDIO_RENDERER_CALLBACKS *arCallbacks,
|
||||
PCONNECTION_LISTENER_CALLBACKS *clCallbacks)
|
||||
PCONNECTION_LISTENER_CALLBACKS *clCallbacks)
|
||||
{
|
||||
if (*drCallbacks == NULL) {
|
||||
*drCallbacks = &fakeDrCallbacks;
|
||||
}
|
||||
else {
|
||||
if ((*drCallbacks)->setup == NULL) {
|
||||
(*drCallbacks)->setup = fakeDrSetup;
|
||||
}
|
||||
if ((*drCallbacks)->cleanup == NULL) {
|
||||
(*drCallbacks)->cleanup = fakeDrCleanup;
|
||||
}
|
||||
if ((*drCallbacks)->submitDecodeUnit == NULL) {
|
||||
(*drCallbacks)->submitDecodeUnit = fakeDrSubmitDecodeUnit;
|
||||
}
|
||||
}
|
||||
if (*drCallbacks == NULL) {
|
||||
*drCallbacks = &fakeDrCallbacks;
|
||||
}
|
||||
else {
|
||||
if ((*drCallbacks)->setup == NULL) {
|
||||
(*drCallbacks)->setup = fakeDrSetup;
|
||||
}
|
||||
if ((*drCallbacks)->cleanup == NULL) {
|
||||
(*drCallbacks)->cleanup = fakeDrCleanup;
|
||||
}
|
||||
if ((*drCallbacks)->submitDecodeUnit == NULL) {
|
||||
(*drCallbacks)->submitDecodeUnit = fakeDrSubmitDecodeUnit;
|
||||
}
|
||||
}
|
||||
|
||||
if (*arCallbacks == NULL) {
|
||||
*arCallbacks = &fakeArCallbacks;
|
||||
}
|
||||
else {
|
||||
if ((*arCallbacks)->init == NULL) {
|
||||
(*arCallbacks)->init = fakeArInit;
|
||||
}
|
||||
if ((*arCallbacks)->cleanup == NULL) {
|
||||
if (*arCallbacks == NULL) {
|
||||
*arCallbacks = &fakeArCallbacks;
|
||||
}
|
||||
else {
|
||||
if ((*arCallbacks)->init == NULL) {
|
||||
(*arCallbacks)->init = fakeArInit;
|
||||
}
|
||||
if ((*arCallbacks)->cleanup == NULL) {
|
||||
(*arCallbacks)->cleanup = fakeArCleanup;
|
||||
}
|
||||
if ((*arCallbacks)->decodeAndPlaySample == NULL) {
|
||||
(*arCallbacks)->decodeAndPlaySample = fakeArDecodeAndPlaySample;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((*arCallbacks)->decodeAndPlaySample == NULL) {
|
||||
(*arCallbacks)->decodeAndPlaySample = fakeArDecodeAndPlaySample;
|
||||
}
|
||||
}
|
||||
|
||||
if (*clCallbacks == NULL) {
|
||||
*clCallbacks = &fakeClCallbacks;
|
||||
}
|
||||
else {
|
||||
if ((*clCallbacks)->stageStarting == NULL) {
|
||||
(*clCallbacks)->stageStarting = fakeClStageStarting;
|
||||
}
|
||||
if ((*clCallbacks)->stageComplete == NULL) {
|
||||
(*clCallbacks)->stageComplete = fakeClStageComplete;
|
||||
}
|
||||
if ((*clCallbacks)->stageFailed == NULL) {
|
||||
(*clCallbacks)->stageFailed = fakeClStageFailed;
|
||||
}
|
||||
if ((*clCallbacks)->connectionStarted == NULL) {
|
||||
(*clCallbacks)->connectionStarted = fakeClConnectionStarted;
|
||||
}
|
||||
if ((*clCallbacks)->connectionTerminated == NULL) {
|
||||
(*clCallbacks)->connectionTerminated = fakeClConnectionTerminated;
|
||||
}
|
||||
if ((*clCallbacks)->displayMessage == NULL) {
|
||||
(*clCallbacks)->displayMessage = fakeClDisplayMessage;
|
||||
}
|
||||
if ((*clCallbacks)->displayTransientMessage == NULL) {
|
||||
(*clCallbacks)->displayTransientMessage = fakeClDisplayTransientMessage;
|
||||
}
|
||||
}
|
||||
if (*clCallbacks == NULL) {
|
||||
*clCallbacks = &fakeClCallbacks;
|
||||
}
|
||||
else {
|
||||
if ((*clCallbacks)->stageStarting == NULL) {
|
||||
(*clCallbacks)->stageStarting = fakeClStageStarting;
|
||||
}
|
||||
if ((*clCallbacks)->stageComplete == NULL) {
|
||||
(*clCallbacks)->stageComplete = fakeClStageComplete;
|
||||
}
|
||||
if ((*clCallbacks)->stageFailed == NULL) {
|
||||
(*clCallbacks)->stageFailed = fakeClStageFailed;
|
||||
}
|
||||
if ((*clCallbacks)->connectionStarted == NULL) {
|
||||
(*clCallbacks)->connectionStarted = fakeClConnectionStarted;
|
||||
}
|
||||
if ((*clCallbacks)->connectionTerminated == NULL) {
|
||||
(*clCallbacks)->connectionTerminated = fakeClConnectionTerminated;
|
||||
}
|
||||
if ((*clCallbacks)->displayMessage == NULL) {
|
||||
(*clCallbacks)->displayMessage = fakeClDisplayMessage;
|
||||
}
|
||||
if ((*clCallbacks)->displayTransientMessage == NULL) {
|
||||
(*clCallbacks)->displayTransientMessage = fakeClDisplayTransientMessage;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,33 +3,33 @@
|
||||
#pragma pack(push, 1)
|
||||
|
||||
typedef struct _NV_INPUT_HEADER {
|
||||
int packetType;
|
||||
int packetType;
|
||||
} NV_INPUT_HEADER, PNV_INPUT_HEADER;
|
||||
|
||||
#define PACKET_TYPE_KEYBOARD 0x0A
|
||||
typedef struct _NV_KEYBOARD_PACKET {
|
||||
NV_INPUT_HEADER header;
|
||||
char keyAction;
|
||||
int zero1;
|
||||
short keyCode;
|
||||
char modifiers;
|
||||
short zero2;
|
||||
NV_INPUT_HEADER header;
|
||||
char keyAction;
|
||||
int zero1;
|
||||
short keyCode;
|
||||
char modifiers;
|
||||
short zero2;
|
||||
} NV_KEYBOARD_PACKET, *PNV_KEYBOARD_PACKET;
|
||||
|
||||
#define PACKET_TYPE_MOUSE_MOVE 0x08
|
||||
#define MOUSE_MOVE_MAGIC 0x06000000
|
||||
typedef struct _NV_MOUSE_MOVE_PACKET {
|
||||
NV_INPUT_HEADER header;
|
||||
int magic;
|
||||
short deltaX;
|
||||
short deltaY;
|
||||
NV_INPUT_HEADER header;
|
||||
int magic;
|
||||
short deltaX;
|
||||
short deltaY;
|
||||
} NV_MOUSE_MOVE_PACKET, *PNV_MOUSE_MOVE_PACKET;
|
||||
|
||||
#define PACKET_TYPE_MOUSE_BUTTON 0x05
|
||||
typedef struct _NV_MOUSE_BUTTON_PACKET {
|
||||
NV_INPUT_HEADER header;
|
||||
char action;
|
||||
int button;
|
||||
NV_INPUT_HEADER header;
|
||||
char action;
|
||||
int button;
|
||||
} NV_MOUSE_BUTTON_PACKET, *PNV_MOUSE_BUTTON_PACKET;
|
||||
|
||||
#define PACKET_TYPE_CONTROLLER 0x18
|
||||
@ -38,18 +38,18 @@ typedef struct _NV_MOUSE_BUTTON_PACKET {
|
||||
#define C_TAIL_A 0x0000009C
|
||||
#define C_TAIL_B 0x0055
|
||||
typedef struct _NV_CONTROLLER_PACKET {
|
||||
NV_INPUT_HEADER header;
|
||||
int headerA;
|
||||
short headerB;
|
||||
short buttonFlags;
|
||||
unsigned char leftTrigger;
|
||||
unsigned char rightTrigger;
|
||||
short leftStickX;
|
||||
short leftStickY;
|
||||
short rightStickX;
|
||||
short rightStickY;
|
||||
int tailA;
|
||||
short tailB;
|
||||
NV_INPUT_HEADER header;
|
||||
int headerA;
|
||||
short headerB;
|
||||
short buttonFlags;
|
||||
unsigned char leftTrigger;
|
||||
unsigned char rightTrigger;
|
||||
short leftStickX;
|
||||
short leftStickY;
|
||||
short rightStickX;
|
||||
short rightStickY;
|
||||
int tailA;
|
||||
short tailB;
|
||||
} NV_CONTROLLER_PACKET, *PNV_CONTROLLER_PACKET;
|
||||
|
||||
#define PACKET_TYPE_MULTI_CONTROLLER 0x1E
|
||||
@ -80,13 +80,13 @@ typedef struct _NV_MULTI_CONTROLLER_PACKET {
|
||||
#define PACKET_TYPE_SCROLL 0xA
|
||||
#define MAGIC_A 0x09
|
||||
typedef struct _NV_SCROLL_PACKET {
|
||||
NV_INPUT_HEADER header;
|
||||
char magicA;
|
||||
char zero1;
|
||||
short zero2;
|
||||
short scrollAmt1;
|
||||
short scrollAmt2;
|
||||
short zero3;
|
||||
NV_INPUT_HEADER header;
|
||||
char magicA;
|
||||
char zero1;
|
||||
short zero2;
|
||||
short scrollAmt1;
|
||||
short scrollAmt2;
|
||||
short zero3;
|
||||
} NV_SCROLL_PACKET, *PNV_SCROLL_PACKET;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
@ -18,75 +18,75 @@ static OAES_CTX* oaesContext;
|
||||
|
||||
/* Contains input stream packets */
|
||||
typedef struct _PACKET_HOLDER {
|
||||
int packetLength;
|
||||
union {
|
||||
NV_KEYBOARD_PACKET keyboard;
|
||||
NV_MOUSE_MOVE_PACKET mouseMove;
|
||||
NV_MOUSE_BUTTON_PACKET mouseButton;
|
||||
NV_CONTROLLER_PACKET controller;
|
||||
int packetLength;
|
||||
union {
|
||||
NV_KEYBOARD_PACKET keyboard;
|
||||
NV_MOUSE_MOVE_PACKET mouseMove;
|
||||
NV_MOUSE_BUTTON_PACKET mouseButton;
|
||||
NV_CONTROLLER_PACKET controller;
|
||||
NV_MULTI_CONTROLLER_PACKET multiController;
|
||||
NV_SCROLL_PACKET scroll;
|
||||
} packet;
|
||||
LINKED_BLOCKING_QUEUE_ENTRY entry;
|
||||
NV_SCROLL_PACKET scroll;
|
||||
} packet;
|
||||
LINKED_BLOCKING_QUEUE_ENTRY entry;
|
||||
} PACKET_HOLDER, *PPACKET_HOLDER;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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_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;
|
||||
}
|
||||
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);
|
||||
LbqInitializeLinkedBlockingQueue(&packetQueue, 30);
|
||||
|
||||
initialized = 1;
|
||||
return 0;
|
||||
initialized = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Destroys and cleans up the input stream */
|
||||
void destroyInputStream(void) {
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY entry, nextEntry;
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY entry, nextEntry;
|
||||
|
||||
if (oaesContext != NULL)
|
||||
{
|
||||
// FIXME: This crashes trying to free ctx->key
|
||||
if (oaesContext != NULL)
|
||||
{
|
||||
// FIXME: This crashes trying to free ctx->key
|
||||
// oaes_free(oaesContext);
|
||||
oaesContext = NULL;
|
||||
}
|
||||
oaesContext = NULL;
|
||||
}
|
||||
|
||||
entry = LbqDestroyLinkedBlockingQueue(&packetQueue);
|
||||
entry = LbqDestroyLinkedBlockingQueue(&packetQueue);
|
||||
|
||||
while (entry != NULL) {
|
||||
nextEntry = entry->flink;
|
||||
while (entry != NULL) {
|
||||
nextEntry = entry->flink;
|
||||
|
||||
// The entry is stored in the data buffer
|
||||
free(entry->data);
|
||||
// The entry is stored in the data buffer
|
||||
free(entry->data);
|
||||
|
||||
entry = nextEntry;
|
||||
}
|
||||
entry = nextEntry;
|
||||
}
|
||||
|
||||
initialized = 0;
|
||||
initialized = 0;
|
||||
}
|
||||
|
||||
// Checks if values are compatible with controller batching
|
||||
@ -122,18 +122,18 @@ static int checkDirs(short currentVal, short newVal, int* dir) {
|
||||
|
||||
/* Input thread proc */
|
||||
static void inputSendThreadProc(void* context) {
|
||||
SOCK_RET err;
|
||||
PPACKET_HOLDER holder;
|
||||
char encryptedBuffer[MAX_INPUT_PACKET_SIZE];
|
||||
size_t encryptedSize;
|
||||
SOCK_RET err;
|
||||
PPACKET_HOLDER holder;
|
||||
char encryptedBuffer[MAX_INPUT_PACKET_SIZE];
|
||||
size_t encryptedSize;
|
||||
|
||||
while (!PltIsThreadInterrupted(&inputSendThread)) {
|
||||
int encryptedLengthPrefix;
|
||||
while (!PltIsThreadInterrupted(&inputSendThread)) {
|
||||
int encryptedLengthPrefix;
|
||||
|
||||
err = LbqWaitForQueueElement(&packetQueue, (void**) &holder);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
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)) {
|
||||
@ -237,154 +237,154 @@ static void inputSendThreadProc(void* context) {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
// 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));
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Begin the input stream */
|
||||
int startInputStream(void) {
|
||||
int err;
|
||||
int err;
|
||||
|
||||
inputSock = connectTcpSocket(&RemoteAddr, RemoteAddrLen, 35043);
|
||||
if (inputSock == INVALID_SOCKET) {
|
||||
return LastSocketFail();
|
||||
}
|
||||
inputSock = connectTcpSocket(&RemoteAddr, RemoteAddrLen, 35043);
|
||||
if (inputSock == INVALID_SOCKET) {
|
||||
return LastSocketFail();
|
||||
}
|
||||
|
||||
enableNoDelay(inputSock);
|
||||
enableNoDelay(inputSock);
|
||||
|
||||
err = PltCreateThread(inputSendThreadProc, NULL, &inputSendThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
err = PltCreateThread(inputSendThreadProc, NULL, &inputSendThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Stops the input stream */
|
||||
int stopInputStream(void) {
|
||||
PltInterruptThread(&inputSendThread);
|
||||
PltInterruptThread(&inputSendThread);
|
||||
|
||||
if (inputSock != INVALID_SOCKET) {
|
||||
closesocket(inputSock);
|
||||
inputSock = INVALID_SOCKET;
|
||||
}
|
||||
if (inputSock != INVALID_SOCKET) {
|
||||
closesocket(inputSock);
|
||||
inputSock = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
PltJoinThread(&inputSendThread);
|
||||
PltCloseThread(&inputSendThread);
|
||||
PltJoinThread(&inputSendThread);
|
||||
PltCloseThread(&inputSendThread);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Send a mouse move event to the streaming machine */
|
||||
int LiSendMouseMoveEvent(short deltaX, short deltaY) {
|
||||
PPACKET_HOLDER holder;
|
||||
int err;
|
||||
PPACKET_HOLDER holder;
|
||||
int err;
|
||||
|
||||
if (!initialized) {
|
||||
return -2;
|
||||
}
|
||||
if (!initialized) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
holder = malloc(sizeof(*holder));
|
||||
if (holder == NULL) {
|
||||
return -1;
|
||||
}
|
||||
holder = malloc(sizeof(*holder));
|
||||
if (holder == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
holder->packetLength = sizeof(NV_MOUSE_MOVE_PACKET);
|
||||
holder->packet.mouseMove.header.packetType = htonl(PACKET_TYPE_MOUSE_MOVE);
|
||||
holder->packet.mouseMove.magic = htonl(MOUSE_MOVE_MAGIC);
|
||||
holder->packet.mouseMove.deltaX = htons(deltaX);
|
||||
holder->packet.mouseMove.deltaY = htons(deltaY);
|
||||
holder->packetLength = sizeof(NV_MOUSE_MOVE_PACKET);
|
||||
holder->packet.mouseMove.header.packetType = htonl(PACKET_TYPE_MOUSE_MOVE);
|
||||
holder->packet.mouseMove.magic = htonl(MOUSE_MOVE_MAGIC);
|
||||
holder->packet.mouseMove.deltaX = htons(deltaX);
|
||||
holder->packet.mouseMove.deltaY = htons(deltaY);
|
||||
|
||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
free(holder);
|
||||
}
|
||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
free(holder);
|
||||
}
|
||||
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Send a mouse button event to the streaming machine */
|
||||
int LiSendMouseButtonEvent(char action, int button) {
|
||||
PPACKET_HOLDER holder;
|
||||
int err;
|
||||
PPACKET_HOLDER holder;
|
||||
int err;
|
||||
|
||||
if (!initialized) {
|
||||
return -2;
|
||||
}
|
||||
if (!initialized) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
holder = malloc(sizeof(*holder));
|
||||
if (holder == NULL) {
|
||||
return -1;
|
||||
}
|
||||
holder = malloc(sizeof(*holder));
|
||||
if (holder == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
holder->packetLength = sizeof(NV_MOUSE_BUTTON_PACKET);
|
||||
holder->packet.mouseButton.header.packetType = htonl(PACKET_TYPE_MOUSE_BUTTON);
|
||||
holder->packet.mouseButton.action = action;
|
||||
holder->packet.mouseButton.button = htonl(button);
|
||||
holder->packetLength = sizeof(NV_MOUSE_BUTTON_PACKET);
|
||||
holder->packet.mouseButton.header.packetType = htonl(PACKET_TYPE_MOUSE_BUTTON);
|
||||
holder->packet.mouseButton.action = action;
|
||||
holder->packet.mouseButton.button = htonl(button);
|
||||
|
||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
free(holder);
|
||||
}
|
||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
free(holder);
|
||||
}
|
||||
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Send a key press event to the streaming machine */
|
||||
int LiSendKeyboardEvent(short keyCode, char keyAction, char modifiers) {
|
||||
PPACKET_HOLDER holder;
|
||||
int err;
|
||||
PPACKET_HOLDER holder;
|
||||
int err;
|
||||
|
||||
if (!initialized) {
|
||||
return -2;
|
||||
}
|
||||
if (!initialized) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
holder = malloc(sizeof(*holder));
|
||||
if (holder == NULL) {
|
||||
return -1;
|
||||
}
|
||||
holder = malloc(sizeof(*holder));
|
||||
if (holder == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
holder->packetLength = sizeof(NV_KEYBOARD_PACKET);
|
||||
holder->packet.keyboard.header.packetType = htonl(PACKET_TYPE_KEYBOARD);
|
||||
holder->packet.keyboard.keyAction = keyAction;
|
||||
holder->packet.keyboard.zero1 = 0;
|
||||
holder->packet.keyboard.keyCode = keyCode;
|
||||
holder->packet.keyboard.modifiers = modifiers;
|
||||
holder->packet.keyboard.zero2 = 0;
|
||||
holder->packetLength = sizeof(NV_KEYBOARD_PACKET);
|
||||
holder->packet.keyboard.header.packetType = htonl(PACKET_TYPE_KEYBOARD);
|
||||
holder->packet.keyboard.keyAction = keyAction;
|
||||
holder->packet.keyboard.zero1 = 0;
|
||||
holder->packet.keyboard.keyCode = keyCode;
|
||||
holder->packet.keyboard.modifiers = modifiers;
|
||||
holder->packet.keyboard.zero2 = 0;
|
||||
|
||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
free(holder);
|
||||
}
|
||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
free(holder);
|
||||
}
|
||||
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sendControllerEventInternal(short controllerNumber, short buttonFlags, unsigned char leftTrigger, unsigned char rightTrigger,
|
||||
@ -439,7 +439,7 @@ static int sendControllerEventInternal(short controllerNumber, short buttonFlags
|
||||
holder->packet.multiController.tailB = MC_TAIL_B;
|
||||
}
|
||||
|
||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
free(holder);
|
||||
}
|
||||
@ -449,7 +449,7 @@ static int sendControllerEventInternal(short controllerNumber, short buttonFlags
|
||||
|
||||
/* Send a controller event to the streaming machine */
|
||||
int LiSendControllerEvent(short buttonFlags, unsigned char leftTrigger, unsigned char rightTrigger,
|
||||
short leftStickX, short leftStickY, short rightStickX, short rightStickY)
|
||||
short leftStickX, short leftStickY, short rightStickX, short rightStickY)
|
||||
{
|
||||
return sendControllerEventInternal(0, buttonFlags, leftTrigger, rightTrigger,
|
||||
leftStickX, leftStickY, rightStickX, rightStickY);
|
||||
@ -465,31 +465,31 @@ int LiSendMultiControllerEvent(short controllerNumber, short buttonFlags, unsign
|
||||
|
||||
/* Send a scroll event to the streaming machine */
|
||||
int LiSendScrollEvent(signed char scrollClicks) {
|
||||
PPACKET_HOLDER holder;
|
||||
int err;
|
||||
PPACKET_HOLDER holder;
|
||||
int err;
|
||||
|
||||
if (!initialized) {
|
||||
return -2;
|
||||
}
|
||||
if (!initialized) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
holder = malloc(sizeof(*holder));
|
||||
if (holder == NULL) {
|
||||
return -1;
|
||||
}
|
||||
holder = malloc(sizeof(*holder));
|
||||
if (holder == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
holder->packetLength = sizeof(NV_SCROLL_PACKET);
|
||||
holder->packet.scroll.header.packetType = htonl(PACKET_TYPE_SCROLL);
|
||||
holder->packet.scroll.magicA = MAGIC_A;
|
||||
holder->packet.scroll.zero1 = 0;
|
||||
holder->packet.scroll.zero2 = 0;
|
||||
holder->packet.scroll.scrollAmt1 = htons(scrollClicks * 120);
|
||||
holder->packet.scroll.scrollAmt2 = holder->packet.scroll.scrollAmt1;
|
||||
holder->packet.scroll.zero3 = 0;
|
||||
holder->packetLength = sizeof(NV_SCROLL_PACKET);
|
||||
holder->packet.scroll.header.packetType = htonl(PACKET_TYPE_SCROLL);
|
||||
holder->packet.scroll.magicA = MAGIC_A;
|
||||
holder->packet.scroll.zero1 = 0;
|
||||
holder->packet.scroll.zero2 = 0;
|
||||
holder->packet.scroll.scrollAmt1 = htons(scrollClicks * 120);
|
||||
holder->packet.scroll.scrollAmt2 = holder->packet.scroll.scrollAmt1;
|
||||
holder->packet.scroll.zero3 = 0;
|
||||
|
||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
free(holder);
|
||||
}
|
||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||
if (err != LBQ_SUCCESS) {
|
||||
free(holder);
|
||||
}
|
||||
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ extern AUDIO_RENDERER_CALLBACKS AudioCallbacks;
|
||||
int isBeforeSignedInt(int numA, int numB, int ambiguousCase);
|
||||
|
||||
void fixupMissingCallbacks(PDECODER_RENDERER_CALLBACKS *drCallbacks, PAUDIO_RENDERER_CALLBACKS *arCallbacks,
|
||||
PCONNECTION_LISTENER_CALLBACKS *clCallbacks);
|
||||
PCONNECTION_LISTENER_CALLBACKS *clCallbacks);
|
||||
|
||||
char* getSdpPayloadForStreamConfig(int rtspClientVersion, int *length);
|
||||
|
||||
|
@ -12,18 +12,18 @@ extern "C" {
|
||||
//#define LC_DEBUG
|
||||
|
||||
typedef struct _STREAM_CONFIGURATION {
|
||||
// Dimensions in pixels of the desired video stream
|
||||
int width;
|
||||
int height;
|
||||
// Dimensions in pixels of the desired video stream
|
||||
int width;
|
||||
int height;
|
||||
|
||||
// FPS of the desired video stream
|
||||
int fps;
|
||||
// FPS of the desired video stream
|
||||
int fps;
|
||||
|
||||
// Bitrate of the desired video stream (audio adds another ~1 Mbps)
|
||||
int bitrate;
|
||||
// Bitrate of the desired video stream (audio adds another ~1 Mbps)
|
||||
int bitrate;
|
||||
|
||||
// Max video packet size in bytes (use 1024 if unsure)
|
||||
int packetSize;
|
||||
// Max video packet size in bytes (use 1024 if unsure)
|
||||
int packetSize;
|
||||
|
||||
// Set to non-zero value to enable remote (over the Internet)
|
||||
// streaming optimizations. If unsure, set to 0.
|
||||
@ -33,34 +33,34 @@ typedef struct _STREAM_CONFIGURATION {
|
||||
// See AUDIO_CONFIGURATION_XXX constants below.
|
||||
int audioConfiguration;
|
||||
|
||||
// AES encryption data for the remote input stream. This must be
|
||||
// the same as what was passed as rikey and rikeyid
|
||||
// in /launch and /resume requests.
|
||||
char remoteInputAesKey[16];
|
||||
char remoteInputAesIv[16];
|
||||
// AES encryption data for the remote input stream. This must be
|
||||
// the same as what was passed as rikey and rikeyid
|
||||
// in /launch and /resume requests.
|
||||
char remoteInputAesKey[16];
|
||||
char remoteInputAesIv[16];
|
||||
} STREAM_CONFIGURATION, *PSTREAM_CONFIGURATION;
|
||||
|
||||
// Use this function to zero the stream configuration when allocated on the stack or heap
|
||||
void LiInitializeStreamConfiguration(PSTREAM_CONFIGURATION streamConfig);
|
||||
|
||||
typedef struct _LENTRY {
|
||||
// Pointer to the next entry or NULL if this is the last entry
|
||||
struct _LENTRY *next;
|
||||
// Pointer to the next entry or NULL if this is the last entry
|
||||
struct _LENTRY *next;
|
||||
|
||||
// Pointer to data (never NULL)
|
||||
char* data;
|
||||
// Pointer to data (never NULL)
|
||||
char* data;
|
||||
|
||||
// Size of data in bytes (never <= 0)
|
||||
int length;
|
||||
// Size of data in bytes (never <= 0)
|
||||
int length;
|
||||
} LENTRY, *PLENTRY;
|
||||
|
||||
// A decode unit describes a buffer chain of H264 data from multiple packets
|
||||
typedef struct _DECODE_UNIT {
|
||||
// Length of the entire buffer chain in bytes
|
||||
int fullLength;
|
||||
// Length of the entire buffer chain in bytes
|
||||
int fullLength;
|
||||
|
||||
// Head of the buffer chain (never NULL)
|
||||
PLENTRY bufferList;
|
||||
// Head of the buffer chain (never NULL)
|
||||
PLENTRY bufferList;
|
||||
} DECODE_UNIT, *PDECODE_UNIT;
|
||||
|
||||
// Specifies that the audio stream should be encoded in stereo (default)
|
||||
@ -98,10 +98,10 @@ typedef void(*DecoderRendererCleanup)(void);
|
||||
typedef int(*DecoderRendererSubmitDecodeUnit)(PDECODE_UNIT decodeUnit);
|
||||
|
||||
typedef struct _DECODER_RENDERER_CALLBACKS {
|
||||
DecoderRendererSetup setup;
|
||||
DecoderRendererCleanup cleanup;
|
||||
DecoderRendererSubmitDecodeUnit submitDecodeUnit;
|
||||
int capabilities;
|
||||
DecoderRendererSetup setup;
|
||||
DecoderRendererCleanup cleanup;
|
||||
DecoderRendererSubmitDecodeUnit submitDecodeUnit;
|
||||
int capabilities;
|
||||
} DECODER_RENDERER_CALLBACKS, *PDECODER_RENDERER_CALLBACKS;
|
||||
|
||||
// Use this function to zero the video callbacks when allocated on the stack or heap
|
||||
@ -141,10 +141,10 @@ typedef void(*AudioRendererCleanup)(void);
|
||||
typedef void(*AudioRendererDecodeAndPlaySample)(char* sampleData, int sampleLength);
|
||||
|
||||
typedef struct _AUDIO_RENDERER_CALLBACKS {
|
||||
AudioRendererInit init;
|
||||
AudioRendererCleanup cleanup;
|
||||
AudioRendererDecodeAndPlaySample decodeAndPlaySample;
|
||||
int capabilities;
|
||||
AudioRendererInit init;
|
||||
AudioRendererCleanup cleanup;
|
||||
AudioRendererDecodeAndPlaySample decodeAndPlaySample;
|
||||
int capabilities;
|
||||
} AUDIO_RENDERER_CALLBACKS, *PAUDIO_RENDERER_CALLBACKS;
|
||||
|
||||
// Use this function to zero the audio callbacks when allocated on the stack or heap
|
||||
@ -190,13 +190,13 @@ typedef void(*ConnListenerDisplayMessage)(char* message);
|
||||
typedef void(*ConnListenerDisplayTransientMessage)(char* message);
|
||||
|
||||
typedef struct _CONNECTION_LISTENER_CALLBACKS {
|
||||
ConnListenerStageStarting stageStarting;
|
||||
ConnListenerStageComplete stageComplete;
|
||||
ConnListenerStageFailed stageFailed;
|
||||
ConnListenerConnectionStarted connectionStarted;
|
||||
ConnListenerConnectionTerminated connectionTerminated;
|
||||
ConnListenerDisplayMessage displayMessage;
|
||||
ConnListenerDisplayTransientMessage displayTransientMessage;
|
||||
ConnListenerStageStarting stageStarting;
|
||||
ConnListenerStageComplete stageComplete;
|
||||
ConnListenerStageFailed stageFailed;
|
||||
ConnListenerConnectionStarted connectionStarted;
|
||||
ConnListenerConnectionTerminated connectionTerminated;
|
||||
ConnListenerDisplayMessage displayMessage;
|
||||
ConnListenerDisplayTransientMessage displayTransientMessage;
|
||||
} CONNECTION_LISTENER_CALLBACKS, *PCONNECTION_LISTENER_CALLBACKS;
|
||||
|
||||
// Use this function to zero the connection callbacks when allocated on the stack or heap
|
||||
@ -210,7 +210,7 @@ void LiInitializeConnectionCallbacks(PCONNECTION_LISTENER_CALLBACKS clCallbacks)
|
||||
// _serverMajorVersion is the major version number of the 'appversion' tag in the /serverinfo request
|
||||
//
|
||||
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);
|
||||
PDECODER_RENDERER_CALLBACKS drCallbacks, PAUDIO_RENDERER_CALLBACKS arCallbacks, void* renderContext, int drFlags, int _serverMajorVersion);
|
||||
|
||||
// This function stops streaming.
|
||||
void LiStopConnection(void);
|
||||
@ -258,7 +258,7 @@ int LiSendKeyboardEvent(short keyCode, char keyAction, char modifiers);
|
||||
// This function queues a controller event to be sent to the remote server. It will
|
||||
// be seen by the computer as the first controller.
|
||||
int LiSendControllerEvent(short buttonFlags, unsigned char leftTrigger, unsigned char rightTrigger,
|
||||
short leftStickX, short leftStickY, short rightStickX, short rightStickY);
|
||||
short leftStickX, short leftStickY, short rightStickX, short rightStickY);
|
||||
|
||||
// This function queues a controller event to be sent to the remote server. The controllerNumber
|
||||
// parameter is a zero-based index of which controller this event corresponds to. The largest legal
|
||||
|
@ -2,87 +2,87 @@
|
||||
|
||||
/* Destroy the linked blocking queue and associated mutex and event */
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY LbqDestroyLinkedBlockingQueue(PLINKED_BLOCKING_QUEUE queueHead) {
|
||||
PltDeleteMutex(&queueHead->mutex);
|
||||
PltCloseEvent(&queueHead->containsDataEvent);
|
||||
PltDeleteMutex(&queueHead->mutex);
|
||||
PltCloseEvent(&queueHead->containsDataEvent);
|
||||
|
||||
return queueHead->head;
|
||||
return queueHead->head;
|
||||
}
|
||||
|
||||
/* Flush the queue */
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY LbqFlushQueueItems(PLINKED_BLOCKING_QUEUE queueHead) {
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY head;
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY head;
|
||||
|
||||
PltLockMutex(&queueHead->mutex);
|
||||
PltLockMutex(&queueHead->mutex);
|
||||
|
||||
// Save the old head
|
||||
head = queueHead->head;
|
||||
// Save the old head
|
||||
head = queueHead->head;
|
||||
|
||||
// Reinitialize the queue to empty
|
||||
queueHead->head = NULL;
|
||||
queueHead->tail = NULL;
|
||||
queueHead->currentSize = 0;
|
||||
PltClearEvent(&queueHead->containsDataEvent);
|
||||
// Reinitialize the queue to empty
|
||||
queueHead->head = NULL;
|
||||
queueHead->tail = NULL;
|
||||
queueHead->currentSize = 0;
|
||||
PltClearEvent(&queueHead->containsDataEvent);
|
||||
|
||||
PltUnlockMutex(&queueHead->mutex);
|
||||
PltUnlockMutex(&queueHead->mutex);
|
||||
|
||||
return head;
|
||||
return head;
|
||||
}
|
||||
|
||||
/* Linked blocking queue init */
|
||||
int LbqInitializeLinkedBlockingQueue(PLINKED_BLOCKING_QUEUE queueHead, int sizeBound) {
|
||||
int err;
|
||||
int err;
|
||||
|
||||
err = PltCreateEvent(&queueHead->containsDataEvent);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
err = PltCreateEvent(&queueHead->containsDataEvent);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = PltCreateMutex(&queueHead->mutex);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
err = PltCreateMutex(&queueHead->mutex);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
queueHead->head = NULL;
|
||||
queueHead->tail = NULL;
|
||||
queueHead->sizeBound = sizeBound;
|
||||
queueHead->currentSize = 0;
|
||||
queueHead->head = NULL;
|
||||
queueHead->tail = NULL;
|
||||
queueHead->sizeBound = sizeBound;
|
||||
queueHead->currentSize = 0;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LbqOfferQueueItem(PLINKED_BLOCKING_QUEUE queueHead, void* data, PLINKED_BLOCKING_QUEUE_ENTRY entry) {
|
||||
entry->flink = NULL;
|
||||
entry->data = data;
|
||||
entry->flink = NULL;
|
||||
entry->data = data;
|
||||
|
||||
PltLockMutex(&queueHead->mutex);
|
||||
PltLockMutex(&queueHead->mutex);
|
||||
|
||||
if (queueHead->currentSize == queueHead->sizeBound) {
|
||||
PltUnlockMutex(&queueHead->mutex);
|
||||
return LBQ_BOUND_EXCEEDED;
|
||||
}
|
||||
if (queueHead->currentSize == queueHead->sizeBound) {
|
||||
PltUnlockMutex(&queueHead->mutex);
|
||||
return LBQ_BOUND_EXCEEDED;
|
||||
}
|
||||
|
||||
if (queueHead->head == NULL) {
|
||||
LC_ASSERT(queueHead->currentSize == 0);
|
||||
LC_ASSERT(queueHead->tail == NULL);
|
||||
queueHead->head = entry;
|
||||
queueHead->tail = entry;
|
||||
entry->blink = NULL;
|
||||
}
|
||||
else {
|
||||
LC_ASSERT(queueHead->currentSize >= 1);
|
||||
LC_ASSERT(queueHead->head != NULL);
|
||||
queueHead->tail->flink = entry;
|
||||
entry->blink = queueHead->tail;
|
||||
queueHead->tail = entry;
|
||||
}
|
||||
if (queueHead->head == NULL) {
|
||||
LC_ASSERT(queueHead->currentSize == 0);
|
||||
LC_ASSERT(queueHead->tail == NULL);
|
||||
queueHead->head = entry;
|
||||
queueHead->tail = entry;
|
||||
entry->blink = NULL;
|
||||
}
|
||||
else {
|
||||
LC_ASSERT(queueHead->currentSize >= 1);
|
||||
LC_ASSERT(queueHead->head != NULL);
|
||||
queueHead->tail->flink = entry;
|
||||
entry->blink = queueHead->tail;
|
||||
queueHead->tail = entry;
|
||||
}
|
||||
|
||||
queueHead->currentSize++;
|
||||
queueHead->currentSize++;
|
||||
|
||||
PltUnlockMutex(&queueHead->mutex);
|
||||
PltUnlockMutex(&queueHead->mutex);
|
||||
|
||||
PltSetEvent(&queueHead->containsDataEvent);
|
||||
PltSetEvent(&queueHead->containsDataEvent);
|
||||
|
||||
return LBQ_SUCCESS;
|
||||
return LBQ_SUCCESS;
|
||||
}
|
||||
|
||||
// This must be synchronized with LbqFlushQueueItems by the caller
|
||||
@ -140,42 +140,42 @@ int LbqPollQueueElement(PLINKED_BLOCKING_QUEUE queueHead, void** data) {
|
||||
}
|
||||
|
||||
int LbqWaitForQueueElement(PLINKED_BLOCKING_QUEUE queueHead, void** data) {
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY entry;
|
||||
int err;
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY entry;
|
||||
int err;
|
||||
|
||||
for (;;) {
|
||||
err = PltWaitForEvent(&queueHead->containsDataEvent);
|
||||
if (err != PLT_WAIT_SUCCESS) {
|
||||
return LBQ_INTERRUPTED;
|
||||
}
|
||||
for (;;) {
|
||||
err = PltWaitForEvent(&queueHead->containsDataEvent);
|
||||
if (err != PLT_WAIT_SUCCESS) {
|
||||
return LBQ_INTERRUPTED;
|
||||
}
|
||||
|
||||
PltLockMutex(&queueHead->mutex);
|
||||
PltLockMutex(&queueHead->mutex);
|
||||
|
||||
if (queueHead->head == NULL) {
|
||||
PltClearEvent(&queueHead->containsDataEvent);
|
||||
PltUnlockMutex(&queueHead->mutex);
|
||||
continue;
|
||||
}
|
||||
if (queueHead->head == NULL) {
|
||||
PltClearEvent(&queueHead->containsDataEvent);
|
||||
PltUnlockMutex(&queueHead->mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
entry = queueHead->head;
|
||||
queueHead->head = entry->flink;
|
||||
queueHead->currentSize--;
|
||||
if (queueHead->head == NULL) {
|
||||
LC_ASSERT(queueHead->currentSize == 0);
|
||||
queueHead->tail = NULL;
|
||||
PltClearEvent(&queueHead->containsDataEvent);
|
||||
}
|
||||
else {
|
||||
LC_ASSERT(queueHead->currentSize != 0);
|
||||
queueHead->head->blink = NULL;
|
||||
}
|
||||
entry = queueHead->head;
|
||||
queueHead->head = entry->flink;
|
||||
queueHead->currentSize--;
|
||||
if (queueHead->head == NULL) {
|
||||
LC_ASSERT(queueHead->currentSize == 0);
|
||||
queueHead->tail = NULL;
|
||||
PltClearEvent(&queueHead->containsDataEvent);
|
||||
}
|
||||
else {
|
||||
LC_ASSERT(queueHead->currentSize != 0);
|
||||
queueHead->head->blink = NULL;
|
||||
}
|
||||
|
||||
*data = entry->data;
|
||||
*data = entry->data;
|
||||
|
||||
PltUnlockMutex(&queueHead->mutex);
|
||||
PltUnlockMutex(&queueHead->mutex);
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return LBQ_SUCCESS;
|
||||
return LBQ_SUCCESS;
|
||||
}
|
||||
|
@ -9,18 +9,18 @@
|
||||
#define LBQ_NO_ELEMENT 3
|
||||
|
||||
typedef struct _LINKED_BLOCKING_QUEUE_ENTRY {
|
||||
struct _LINKED_BLOCKING_QUEUE_ENTRY *flink;
|
||||
struct _LINKED_BLOCKING_QUEUE_ENTRY *blink;
|
||||
void* data;
|
||||
struct _LINKED_BLOCKING_QUEUE_ENTRY *flink;
|
||||
struct _LINKED_BLOCKING_QUEUE_ENTRY *blink;
|
||||
void* data;
|
||||
} LINKED_BLOCKING_QUEUE_ENTRY, *PLINKED_BLOCKING_QUEUE_ENTRY;
|
||||
|
||||
typedef struct _LINKED_BLOCKING_QUEUE {
|
||||
PLT_MUTEX mutex;
|
||||
PLT_EVENT containsDataEvent;
|
||||
int sizeBound;
|
||||
int currentSize;
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY head;
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY tail;
|
||||
PLT_MUTEX mutex;
|
||||
PLT_EVENT containsDataEvent;
|
||||
int sizeBound;
|
||||
int currentSize;
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY head;
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY tail;
|
||||
} LINKED_BLOCKING_QUEUE, *PLINKED_BLOCKING_QUEUE;
|
||||
|
||||
int LbqInitializeLinkedBlockingQueue(PLINKED_BLOCKING_QUEUE queueHead, int sizeBound);
|
||||
|
@ -1,20 +1,20 @@
|
||||
#include "Limelight-internal.h"
|
||||
|
||||
int isBeforeSignedInt(int numA, int numB, int ambiguousCase) {
|
||||
// This should be the common case for most callers
|
||||
if (numA == numB) {
|
||||
return 0;
|
||||
}
|
||||
// This should be the common case for most callers
|
||||
if (numA == numB) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If numA and numB have the same signs,
|
||||
// we can just do a regular comparison.
|
||||
if ((numA < 0 && numB < 0) || (numA >= 0 && numB >= 0)) {
|
||||
return numA < numB;
|
||||
}
|
||||
else {
|
||||
// The sign switch is ambiguous
|
||||
return ambiguousCase;
|
||||
}
|
||||
// If numA and numB have the same signs,
|
||||
// we can just do a regular comparison.
|
||||
if ((numA < 0 && numB < 0) || (numA >= 0 && numB >= 0)) {
|
||||
return numA < numB;
|
||||
}
|
||||
else {
|
||||
// The sign switch is ambiguous
|
||||
return ambiguousCase;
|
||||
}
|
||||
}
|
||||
|
||||
void LiInitializeStreamConfiguration(PSTREAM_CONFIGURATION streamConfig) {
|
||||
|
@ -29,8 +29,8 @@
|
||||
*/
|
||||
#if 0 // LIMELIGHT
|
||||
static const char _NR[] = {
|
||||
0x4e,0x61,0x62,0x69,0x6c,0x20,0x53,0x2e,0x20,
|
||||
0x41,0x6c,0x20,0x52,0x61,0x6d,0x6c,0x69,0x00 };
|
||||
0x4e,0x61,0x62,0x69,0x6c,0x20,0x53,0x2e,0x20,
|
||||
0x41,0x6c,0x20,0x52,0x61,0x6d,0x6c,0x69,0x00 };
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
@ -40,10 +40,10 @@ static const char _NR[] = {
|
||||
#include "oaes_base64.h"
|
||||
|
||||
static const char _oaes_base64_table[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
OAES_RET oaes_base64_encode(
|
||||
const uint8_t *in, size_t in_len, char *out, size_t *out_len)
|
||||
const uint8_t *in, size_t in_len, char *out, size_t *out_len)
|
||||
{
|
||||
size_t _i = 0, _j = 0;
|
||||
unsigned char _buf1[3];
|
||||
@ -65,10 +65,10 @@ OAES_RET oaes_base64_encode(
|
||||
memset(out, 0, *out_len);
|
||||
*out_len = 0;
|
||||
while( in_len-- )
|
||||
{
|
||||
{
|
||||
_buf1[_i++] = *(in++);
|
||||
if( _i == 3 )
|
||||
{
|
||||
{
|
||||
_buf2[0] = (_buf1[0] & 0xfc) >> 2;
|
||||
_buf2[1] = ((_buf1[0] & 0x03) << 4) + ((_buf1[1] & 0xf0) >> 4);
|
||||
_buf2[2] = ((_buf1[1] & 0x0f) << 2) + ((_buf1[2] & 0xc0) >> 6);
|
||||
@ -111,7 +111,7 @@ OAES_RET oaes_base64_encode(
|
||||
}
|
||||
|
||||
OAES_RET oaes_base64_decode(
|
||||
const char *in, size_t in_len, uint8_t *out, size_t *out_len )
|
||||
const char *in, size_t in_len, uint8_t *out, size_t *out_len )
|
||||
{
|
||||
size_t _i = 0, _j = 0, _idx = 0;
|
||||
uint8_t _buf2[4], _buf1[3];
|
||||
@ -132,10 +132,10 @@ OAES_RET oaes_base64_decode(
|
||||
memset(out, 0, *out_len);
|
||||
*out_len = 0;
|
||||
while( in_len-- && strchr(_oaes_base64_table, in[_idx++]) )
|
||||
{
|
||||
{
|
||||
_buf2[_i++] = in[_idx - 1];
|
||||
if( _i ==4 )
|
||||
{
|
||||
{
|
||||
for (_i = 0; _i < 4; _i++)
|
||||
_buf2[_i] = strchr(_oaes_base64_table, _buf2[_i]) - _oaes_base64_table;
|
||||
|
||||
@ -153,7 +153,7 @@ OAES_RET oaes_base64_decode(
|
||||
}
|
||||
|
||||
if( _i )
|
||||
{
|
||||
{
|
||||
for( _j = _i; _j <4; _j++ )
|
||||
_buf2[_j] = 0;
|
||||
|
||||
|
@ -38,10 +38,10 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
OAES_API OAES_RET oaes_base64_encode(
|
||||
const uint8_t *in, size_t in_len, char *out, size_t *out_len );
|
||||
const uint8_t *in, size_t in_len, char *out, size_t *out_len );
|
||||
|
||||
OAES_API OAES_RET oaes_base64_decode(
|
||||
const char *in, size_t in_len, uint8_t *out, size_t *out_len );
|
||||
const char *in, size_t in_len, uint8_t *out, size_t *out_len );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -38,36 +38,36 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef OAES_SHARED
|
||||
# ifdef oaes_lib_EXPORTS
|
||||
# define OAES_API __declspec(dllexport)
|
||||
# else
|
||||
# define OAES_API __declspec(dllimport)
|
||||
# endif
|
||||
# else
|
||||
# define OAES_API
|
||||
# endif
|
||||
# ifdef OAES_SHARED
|
||||
# ifdef oaes_lib_EXPORTS
|
||||
# define OAES_API __declspec(dllexport)
|
||||
# else
|
||||
# define OAES_API __declspec(dllimport)
|
||||
# endif
|
||||
# else
|
||||
# define OAES_API
|
||||
# endif
|
||||
#else
|
||||
# define OAES_API
|
||||
# define OAES_API
|
||||
#endif // WIN32
|
||||
|
||||
#define OAES_VERSION "0.9.0"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
OAES_RET_FIRST = 0,
|
||||
OAES_RET_SUCCESS = 0,
|
||||
OAES_RET_ERROR,
|
||||
OAES_RET_ARG1,
|
||||
OAES_RET_ARG2,
|
||||
OAES_RET_ARG3,
|
||||
OAES_RET_ARG4,
|
||||
OAES_RET_ARG5,
|
||||
OAES_RET_NOKEY,
|
||||
OAES_RET_MEM,
|
||||
OAES_RET_BUF,
|
||||
OAES_RET_HEADER,
|
||||
OAES_RET_COUNT
|
||||
OAES_RET_FIRST = 0,
|
||||
OAES_RET_SUCCESS = 0,
|
||||
OAES_RET_ERROR,
|
||||
OAES_RET_ARG1,
|
||||
OAES_RET_ARG2,
|
||||
OAES_RET_ARG3,
|
||||
OAES_RET_ARG4,
|
||||
OAES_RET_ARG5,
|
||||
OAES_RET_NOKEY,
|
||||
OAES_RET_MEM,
|
||||
OAES_RET_BUF,
|
||||
OAES_RET_HEADER,
|
||||
OAES_RET_COUNT
|
||||
} OAES_RET;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -38,17 +38,17 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef OAES_SHARED
|
||||
# ifdef oaes_lib_EXPORTS
|
||||
# define OAES_API __declspec(dllexport)
|
||||
# else
|
||||
# define OAES_API __declspec(dllimport)
|
||||
# endif
|
||||
# else
|
||||
# define OAES_API
|
||||
# endif
|
||||
# ifdef OAES_SHARED
|
||||
# ifdef oaes_lib_EXPORTS
|
||||
# define OAES_API __declspec(dllexport)
|
||||
# else
|
||||
# define OAES_API __declspec(dllimport)
|
||||
# endif
|
||||
# else
|
||||
# define OAES_API
|
||||
# endif
|
||||
#else
|
||||
# define OAES_API
|
||||
# define OAES_API
|
||||
#endif // WIN32
|
||||
|
||||
#define OAES_BLOCK_SIZE 16
|
||||
@ -70,10 +70,10 @@ typedef void OAES_CTX;
|
||||
|
||||
#ifdef OAES_DEBUG
|
||||
typedef int ( * oaes_step_cb ) (
|
||||
const uint8_t state[OAES_BLOCK_SIZE],
|
||||
const char * step_name,
|
||||
int step_count,
|
||||
void * user_data );
|
||||
const uint8_t state[OAES_BLOCK_SIZE],
|
||||
const char * step_name,
|
||||
int step_count,
|
||||
void * user_data );
|
||||
// enable state stepping mode
|
||||
// value is required, must pass oaes_step_cb to receive the state at each step
|
||||
#define OAES_OPTION_STEP_ON 4
|
||||
@ -123,7 +123,7 @@ OAES_API OAES_CTX * oaes_alloc();
|
||||
OAES_API OAES_RET oaes_free( OAES_CTX ** ctx );
|
||||
|
||||
OAES_API OAES_RET oaes_set_option( OAES_CTX * ctx,
|
||||
OAES_OPTION option, const void * value );
|
||||
OAES_OPTION option, const void * value );
|
||||
|
||||
OAES_API OAES_RET oaes_key_gen_128( OAES_CTX * ctx );
|
||||
|
||||
@ -134,32 +134,32 @@ OAES_API OAES_RET oaes_key_gen_256( OAES_CTX * ctx );
|
||||
// export key with header information
|
||||
// set data == NULL to get the required data_len
|
||||
OAES_API OAES_RET oaes_key_export( OAES_CTX * ctx,
|
||||
uint8_t * data, size_t * data_len );
|
||||
uint8_t * data, size_t * data_len );
|
||||
|
||||
// directly export the data from key
|
||||
// set data == NULL to get the required data_len
|
||||
OAES_API OAES_RET oaes_key_export_data( OAES_CTX * ctx,
|
||||
uint8_t * data, size_t * data_len );
|
||||
uint8_t * data, size_t * data_len );
|
||||
|
||||
// import key with header information
|
||||
OAES_API OAES_RET oaes_key_import( OAES_CTX * ctx,
|
||||
const uint8_t * data, size_t data_len );
|
||||
const uint8_t * data, size_t data_len );
|
||||
|
||||
// directly import data into key
|
||||
OAES_API OAES_RET oaes_key_import_data( OAES_CTX * ctx,
|
||||
const uint8_t * data, size_t data_len );
|
||||
const uint8_t * data, size_t data_len );
|
||||
|
||||
// set c == NULL to get the required c_len
|
||||
OAES_API OAES_RET oaes_encrypt( OAES_CTX * ctx,
|
||||
const uint8_t * m, size_t m_len, uint8_t * c, size_t * c_len );
|
||||
const uint8_t * m, size_t m_len, uint8_t * c, size_t * c_len );
|
||||
|
||||
// set m == NULL to get the required m_len
|
||||
OAES_API OAES_RET oaes_decrypt( OAES_CTX * ctx,
|
||||
const uint8_t * c, size_t c_len, uint8_t * m, size_t * m_len );
|
||||
const uint8_t * c, size_t c_len, uint8_t * m, size_t * m_len );
|
||||
|
||||
// set buf == NULL to get the required buf_len
|
||||
OAES_API OAES_RET oaes_sprintf(
|
||||
char * buf, size_t * buf_len, const uint8_t * data, size_t data_len );
|
||||
char * buf, size_t * buf_len, const uint8_t * data, size_t data_len );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -6,14 +6,14 @@ void cleanupPlatformSockets(void);
|
||||
|
||||
#if defined(LC_WINDOWS)
|
||||
void LimelogWindows(char* Format, ...) {
|
||||
va_list va;
|
||||
char buffer[1024];
|
||||
va_list va;
|
||||
char buffer[1024];
|
||||
|
||||
va_start(va, Format);
|
||||
vsprintf(buffer, Format, va);
|
||||
va_end(va);
|
||||
va_start(va, Format);
|
||||
vsprintf(buffer, Format, va);
|
||||
va_end(va);
|
||||
|
||||
OutputDebugStringA(buffer);
|
||||
OutputDebugStringA(buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -22,13 +22,13 @@ PLT_MUTEX thread_list_lock;
|
||||
PLT_THREAD *thread_head;
|
||||
|
||||
DWORD WINAPI ThreadProc(LPVOID lpParameter) {
|
||||
struct thread_context *ctx = (struct thread_context *)lpParameter;
|
||||
struct thread_context *ctx = (struct thread_context *)lpParameter;
|
||||
|
||||
ctx->entry(ctx->context);
|
||||
ctx->entry(ctx->context);
|
||||
|
||||
free(ctx);
|
||||
free(ctx);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
void* ThreadProc(void* context) {
|
||||
@ -36,15 +36,15 @@ void* ThreadProc(void* context) {
|
||||
|
||||
ctx->entry(ctx->context);
|
||||
|
||||
free(ctx);
|
||||
free(ctx);
|
||||
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
void PltSleepMs(int ms) {
|
||||
#if defined(LC_WINDOWS)
|
||||
WaitForSingleObjectEx(GetCurrentThread(), ms, FALSE);
|
||||
WaitForSingleObjectEx(GetCurrentThread(), ms, FALSE);
|
||||
#else
|
||||
useconds_t usecs = ms * 1000;
|
||||
usleep(usecs);
|
||||
@ -53,11 +53,11 @@ void PltSleepMs(int ms) {
|
||||
|
||||
int PltCreateMutex(PLT_MUTEX *mutex) {
|
||||
#if defined(LC_WINDOWS)
|
||||
*mutex = CreateMutexEx(NULL, NULL, 0, MUTEX_ALL_ACCESS);
|
||||
if (!*mutex) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
*mutex = CreateMutexEx(NULL, NULL, 0, MUTEX_ALL_ACCESS);
|
||||
if (!*mutex) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
return pthread_mutex_init(mutex, NULL);
|
||||
#endif
|
||||
@ -65,7 +65,7 @@ int PltCreateMutex(PLT_MUTEX *mutex) {
|
||||
|
||||
void PltDeleteMutex(PLT_MUTEX *mutex) {
|
||||
#if defined(LC_WINDOWS)
|
||||
CloseHandle(*mutex);
|
||||
CloseHandle(*mutex);
|
||||
#else
|
||||
pthread_mutex_destroy(mutex);
|
||||
#endif
|
||||
@ -73,11 +73,11 @@ void PltDeleteMutex(PLT_MUTEX *mutex) {
|
||||
|
||||
void PltLockMutex(PLT_MUTEX *mutex) {
|
||||
#if defined(LC_WINDOWS)
|
||||
int err;
|
||||
err = WaitForSingleObjectEx(*mutex, INFINITE, FALSE);
|
||||
if (err != WAIT_OBJECT_0) {
|
||||
LC_ASSERT(FALSE);
|
||||
}
|
||||
int err;
|
||||
err = WaitForSingleObjectEx(*mutex, INFINITE, FALSE);
|
||||
if (err != WAIT_OBJECT_0) {
|
||||
LC_ASSERT(FALSE);
|
||||
}
|
||||
#else
|
||||
pthread_mutex_lock(mutex);
|
||||
#endif
|
||||
@ -85,7 +85,7 @@ void PltLockMutex(PLT_MUTEX *mutex) {
|
||||
|
||||
void PltUnlockMutex(PLT_MUTEX *mutex) {
|
||||
#if defined(LC_WINDOWS)
|
||||
ReleaseMutex(*mutex);
|
||||
ReleaseMutex(*mutex);
|
||||
#else
|
||||
pthread_mutex_unlock(mutex);
|
||||
#endif
|
||||
@ -93,7 +93,7 @@ void PltUnlockMutex(PLT_MUTEX *mutex) {
|
||||
|
||||
void PltJoinThread(PLT_THREAD *thread) {
|
||||
#if defined(LC_WINDOWS)
|
||||
WaitForSingleObjectEx(thread->handle, INFINITE, FALSE);
|
||||
WaitForSingleObjectEx(thread->handle, INFINITE, FALSE);
|
||||
#else
|
||||
pthread_join(*thread, NULL);
|
||||
#endif
|
||||
@ -101,102 +101,102 @@ void PltJoinThread(PLT_THREAD *thread) {
|
||||
|
||||
void PltCloseThread(PLT_THREAD *thread) {
|
||||
#if defined(LC_WINDOWS)
|
||||
PLT_THREAD *current_thread;
|
||||
PLT_THREAD *current_thread;
|
||||
|
||||
PltLockMutex(&thread_list_lock);
|
||||
PltLockMutex(&thread_list_lock);
|
||||
|
||||
if (thread_head == thread)
|
||||
{
|
||||
// Remove the thread from the head
|
||||
thread_head = thread_head->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find the thread in the list
|
||||
current_thread = thread_head;
|
||||
while (current_thread != NULL) {
|
||||
if (current_thread->next == thread) {
|
||||
break;
|
||||
}
|
||||
if (thread_head == thread)
|
||||
{
|
||||
// Remove the thread from the head
|
||||
thread_head = thread_head->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find the thread in the list
|
||||
current_thread = thread_head;
|
||||
while (current_thread != NULL) {
|
||||
if (current_thread->next == thread) {
|
||||
break;
|
||||
}
|
||||
|
||||
current_thread = current_thread->next;
|
||||
}
|
||||
current_thread = current_thread->next;
|
||||
}
|
||||
|
||||
LC_ASSERT(current_thread != NULL);
|
||||
LC_ASSERT(current_thread != NULL);
|
||||
|
||||
// Unlink this thread
|
||||
current_thread->next = thread->next;
|
||||
}
|
||||
// Unlink this thread
|
||||
current_thread->next = thread->next;
|
||||
}
|
||||
|
||||
PltUnlockMutex(&thread_list_lock);
|
||||
PltUnlockMutex(&thread_list_lock);
|
||||
|
||||
CloseHandle(thread->termRequested);
|
||||
CloseHandle(thread->handle);
|
||||
CloseHandle(thread->termRequested);
|
||||
CloseHandle(thread->handle);
|
||||
#else
|
||||
#endif
|
||||
}
|
||||
|
||||
int PltIsThreadInterrupted(PLT_THREAD *thread) {
|
||||
#if defined(LC_WINDOWS)
|
||||
return thread->cancelled;
|
||||
return thread->cancelled;
|
||||
#else
|
||||
// The thread will die here if a cancellation was requested
|
||||
pthread_testcancel();
|
||||
return 0;
|
||||
// The thread will die here if a cancellation was requested
|
||||
pthread_testcancel();
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void PltInterruptThread(PLT_THREAD *thread) {
|
||||
#if defined(LC_WINDOWS)
|
||||
thread->cancelled = 1;
|
||||
SetEvent(thread->termRequested);
|
||||
thread->cancelled = 1;
|
||||
SetEvent(thread->termRequested);
|
||||
#else
|
||||
pthread_cancel(*thread);
|
||||
pthread_cancel(*thread);
|
||||
#endif
|
||||
}
|
||||
|
||||
int PltCreateThread(ThreadEntry entry, void* context, PLT_THREAD *thread) {
|
||||
struct thread_context *ctx;
|
||||
int err;
|
||||
struct thread_context *ctx;
|
||||
int err;
|
||||
|
||||
ctx = (struct thread_context *)malloc(sizeof(*ctx));
|
||||
if (ctx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
ctx = (struct thread_context *)malloc(sizeof(*ctx));
|
||||
if (ctx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->entry = entry;
|
||||
ctx->context = context;
|
||||
ctx->entry = entry;
|
||||
ctx->context = context;
|
||||
|
||||
#if defined(LC_WINDOWS)
|
||||
{
|
||||
thread->termRequested = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
|
||||
if (thread->termRequested == NULL) {
|
||||
free(ctx);
|
||||
return -1;
|
||||
}
|
||||
{
|
||||
thread->termRequested = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
|
||||
if (thread->termRequested == NULL) {
|
||||
free(ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
thread->cancelled = 0;
|
||||
thread->cancelled = 0;
|
||||
|
||||
thread->handle = CreateThread(NULL, 0, ThreadProc, ctx, CREATE_SUSPENDED, &thread->tid);
|
||||
if (thread->handle == NULL) {
|
||||
CloseHandle(thread->termRequested);
|
||||
free(ctx);
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
// Add this thread to the thread list
|
||||
PltLockMutex(&thread_list_lock);
|
||||
thread->next = thread_head;
|
||||
thread_head = thread;
|
||||
PltUnlockMutex(&thread_list_lock);
|
||||
thread->handle = CreateThread(NULL, 0, ThreadProc, ctx, CREATE_SUSPENDED, &thread->tid);
|
||||
if (thread->handle == NULL) {
|
||||
CloseHandle(thread->termRequested);
|
||||
free(ctx);
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
// Add this thread to the thread list
|
||||
PltLockMutex(&thread_list_lock);
|
||||
thread->next = thread_head;
|
||||
thread_head = thread;
|
||||
PltUnlockMutex(&thread_list_lock);
|
||||
|
||||
// Now the thread can run
|
||||
ResumeThread(thread->handle);
|
||||
// Now the thread can run
|
||||
ResumeThread(thread->handle);
|
||||
|
||||
err = 0;
|
||||
}
|
||||
err = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
err = pthread_create(thread, NULL, ThreadProc, ctx);
|
||||
@ -206,17 +206,17 @@ int PltCreateThread(ThreadEntry entry, void* context, PLT_THREAD *thread) {
|
||||
}
|
||||
#endif
|
||||
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
|
||||
int PltCreateEvent(PLT_EVENT *event) {
|
||||
#if defined(LC_WINDOWS)
|
||||
*event = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
|
||||
if (!*event) {
|
||||
return -1;
|
||||
}
|
||||
*event = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
|
||||
if (!*event) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
#else
|
||||
pthread_mutex_init(&event->mutex, NULL);
|
||||
pthread_cond_init(&event->cond, NULL);
|
||||
@ -227,7 +227,7 @@ int PltCreateEvent(PLT_EVENT *event) {
|
||||
|
||||
void PltCloseEvent(PLT_EVENT *event) {
|
||||
#if defined(LC_WINDOWS)
|
||||
CloseHandle(*event);
|
||||
CloseHandle(*event);
|
||||
#else
|
||||
pthread_mutex_destroy(&event->mutex);
|
||||
pthread_cond_destroy(&event->cond);
|
||||
@ -236,7 +236,7 @@ void PltCloseEvent(PLT_EVENT *event) {
|
||||
|
||||
void PltSetEvent(PLT_EVENT *event) {
|
||||
#if defined(LC_WINDOWS)
|
||||
SetEvent(*event);
|
||||
SetEvent(*event);
|
||||
#else
|
||||
event->signalled = 1;
|
||||
pthread_cond_broadcast(&event->cond);
|
||||
@ -245,7 +245,7 @@ void PltSetEvent(PLT_EVENT *event) {
|
||||
|
||||
void PltClearEvent(PLT_EVENT *event) {
|
||||
#if defined(LC_WINDOWS)
|
||||
ResetEvent(*event);
|
||||
ResetEvent(*event);
|
||||
#else
|
||||
event->signalled = 0;
|
||||
#endif
|
||||
@ -253,80 +253,80 @@ void PltClearEvent(PLT_EVENT *event) {
|
||||
|
||||
int PltWaitForEvent(PLT_EVENT *event) {
|
||||
#if defined(LC_WINDOWS)
|
||||
DWORD error;
|
||||
PLT_THREAD *current_thread;
|
||||
HANDLE objects[2];
|
||||
DWORD error;
|
||||
PLT_THREAD *current_thread;
|
||||
HANDLE objects[2];
|
||||
|
||||
PltLockMutex(&thread_list_lock);
|
||||
current_thread = thread_head;
|
||||
while (current_thread != NULL) {
|
||||
if (current_thread->tid == GetCurrentThreadId()) {
|
||||
break;
|
||||
}
|
||||
PltLockMutex(&thread_list_lock);
|
||||
current_thread = thread_head;
|
||||
while (current_thread != NULL) {
|
||||
if (current_thread->tid == GetCurrentThreadId()) {
|
||||
break;
|
||||
}
|
||||
|
||||
current_thread = current_thread->next;
|
||||
}
|
||||
PltUnlockMutex(&thread_list_lock);
|
||||
current_thread = current_thread->next;
|
||||
}
|
||||
PltUnlockMutex(&thread_list_lock);
|
||||
|
||||
LC_ASSERT(current_thread != NULL);
|
||||
LC_ASSERT(current_thread != NULL);
|
||||
|
||||
objects[0] = *event;
|
||||
objects[1] = current_thread->termRequested;
|
||||
error = WaitForMultipleObjectsEx(2, objects, FALSE, INFINITE, FALSE);
|
||||
if (error == WAIT_OBJECT_0) {
|
||||
return PLT_WAIT_SUCCESS;
|
||||
}
|
||||
else if (error == WAIT_OBJECT_0 + 1) {
|
||||
return PLT_WAIT_INTERRUPTED;
|
||||
}
|
||||
else {
|
||||
LC_ASSERT(0);
|
||||
return -1;
|
||||
}
|
||||
objects[0] = *event;
|
||||
objects[1] = current_thread->termRequested;
|
||||
error = WaitForMultipleObjectsEx(2, objects, FALSE, INFINITE, FALSE);
|
||||
if (error == WAIT_OBJECT_0) {
|
||||
return PLT_WAIT_SUCCESS;
|
||||
}
|
||||
else if (error == WAIT_OBJECT_0 + 1) {
|
||||
return PLT_WAIT_INTERRUPTED;
|
||||
}
|
||||
else {
|
||||
LC_ASSERT(0);
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
pthread_mutex_lock(&event->mutex);
|
||||
pthread_mutex_lock(&event->mutex);
|
||||
while (!event->signalled) {
|
||||
pthread_cond_wait(&event->cond, &event->mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&event->mutex);
|
||||
return PLT_WAIT_SUCCESS;
|
||||
pthread_mutex_unlock(&event->mutex);
|
||||
return PLT_WAIT_SUCCESS;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t PltGetMillis(void) {
|
||||
#if defined(LC_WINDOWS)
|
||||
return GetTickCount64();
|
||||
return GetTickCount64();
|
||||
#else
|
||||
struct timeval tv;
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
|
||||
return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
int initializePlatform(void) {
|
||||
int err;
|
||||
int err;
|
||||
|
||||
err = initializePlatformSockets();
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
err = initializePlatformSockets();
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
#if defined(LC_WINDOWS)
|
||||
return PltCreateMutex(&thread_list_lock);
|
||||
return PltCreateMutex(&thread_list_lock);
|
||||
#else
|
||||
return 0;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void cleanupPlatform(void) {
|
||||
cleanupPlatformSockets();
|
||||
cleanupPlatformSockets();
|
||||
|
||||
#if defined(LC_WINDOWS)
|
||||
LC_ASSERT(thread_head == NULL);
|
||||
LC_ASSERT(thread_head == NULL);
|
||||
|
||||
PltDeleteMutex(&thread_list_lock);
|
||||
PltDeleteMutex(&thread_list_lock);
|
||||
#else
|
||||
#endif
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
|
||||
void addrToUrlSafeString(struct sockaddr_storage *addr, char* string)
|
||||
{
|
||||
char addrstr[INET6_ADDRSTRLEN];
|
||||
char addrstr[INET6_ADDRSTRLEN];
|
||||
|
||||
if (addr->ss_family == AF_INET6) {
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
|
||||
@ -14,7 +14,7 @@ void addrToUrlSafeString(struct sockaddr_storage *addr, char* string)
|
||||
}
|
||||
else {
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)addr;
|
||||
inet_ntop(addr->ss_family, &sin->sin_addr, addrstr, sizeof(addrstr));
|
||||
inet_ntop(addr->ss_family, &sin->sin_addr, addrstr, sizeof(addrstr));
|
||||
|
||||
// IPv4 addresses are returned without changes
|
||||
sprintf(string, "%s", addrstr);
|
||||
@ -22,33 +22,33 @@ void addrToUrlSafeString(struct sockaddr_storage *addr, char* string)
|
||||
}
|
||||
|
||||
SOCKET bindUdpSocket(int addrfamily, int bufferSize) {
|
||||
SOCKET s;
|
||||
SOCKET s;
|
||||
struct sockaddr_storage addr;
|
||||
#ifdef LC_DARWIN
|
||||
int val;
|
||||
int val;
|
||||
#endif
|
||||
int err;
|
||||
int err;
|
||||
|
||||
LC_ASSERT(addrfamily == AF_INET || addrfamily == AF_INET6);
|
||||
|
||||
s = socket(addrfamily, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (s == INVALID_SOCKET) {
|
||||
s = socket(addrfamily, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (s == INVALID_SOCKET) {
|
||||
Limelog("socket() failed: %d\n", (int)LastSocketError());
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.ss_family = addrfamily;
|
||||
if (bind(s, (struct sockaddr*) &addr,
|
||||
if (bind(s, (struct sockaddr*) &addr,
|
||||
addrfamily == AF_INET ?
|
||||
sizeof(struct sockaddr_in) :
|
||||
sizeof(struct sockaddr_in6)) == SOCKET_ERROR) {
|
||||
err = LastSocketError();
|
||||
err = LastSocketError();
|
||||
Limelog("bind() failed: %d\n", err);
|
||||
closesocket(s);
|
||||
SetLastSocketError(err);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
closesocket(s);
|
||||
SetLastSocketError(err);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
#ifdef LC_DARWIN
|
||||
// Disable SIGPIPE on iOS
|
||||
@ -56,24 +56,24 @@ SOCKET bindUdpSocket(int addrfamily, int bufferSize) {
|
||||
setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (char* )&val, sizeof(val));
|
||||
#endif
|
||||
|
||||
setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*) &bufferSize, sizeof(bufferSize));
|
||||
setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*) &bufferSize, sizeof(bufferSize));
|
||||
|
||||
return s;
|
||||
return s;
|
||||
}
|
||||
|
||||
SOCKET connectTcpSocket(struct sockaddr_storage *dstaddr, SOCKADDR_LEN addrlen, unsigned short port) {
|
||||
SOCKET s;
|
||||
SOCKET s;
|
||||
struct sockaddr_in6 addr;
|
||||
int err;
|
||||
int err;
|
||||
#ifdef LC_DARWIN
|
||||
int val;
|
||||
#endif
|
||||
|
||||
s = socket(dstaddr->ss_family, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (s == INVALID_SOCKET) {
|
||||
s = socket(dstaddr->ss_family, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (s == INVALID_SOCKET) {
|
||||
Limelog("socket() failed: %d\n", (int)LastSocketError());
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
#ifdef LC_DARWIN
|
||||
// Disable SIGPIPE on iOS
|
||||
@ -82,35 +82,35 @@ SOCKET connectTcpSocket(struct sockaddr_storage *dstaddr, SOCKADDR_LEN addrlen,
|
||||
#endif
|
||||
|
||||
memcpy(&addr, dstaddr, sizeof(addr));
|
||||
addr.sin6_port = htons(port);
|
||||
if (connect(s, (struct sockaddr*) &addr, addrlen) == SOCKET_ERROR) {
|
||||
err = LastSocketError();
|
||||
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;
|
||||
}
|
||||
closesocket(s);
|
||||
SetLastSocketError(err);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
return s;
|
||||
return s;
|
||||
}
|
||||
|
||||
int enableNoDelay(SOCKET s) {
|
||||
int err;
|
||||
int val;
|
||||
int err;
|
||||
int val;
|
||||
|
||||
val = 1;
|
||||
err = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(val));
|
||||
if (err == SOCKET_ERROR) {
|
||||
return LastSocketError();
|
||||
}
|
||||
val = 1;
|
||||
err = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(val));
|
||||
if (err == SOCKET_ERROR) {
|
||||
return LastSocketError();
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int initializePlatformSockets(void) {
|
||||
#if defined(LC_WINDOWS)
|
||||
WSADATA data;
|
||||
return WSAStartup(MAKEWORD(2, 0), &data);
|
||||
WSADATA data;
|
||||
return WSAStartup(MAKEWORD(2, 0), &data);
|
||||
#elif defined(LC_POSIX)
|
||||
// Disable SIGPIPE signals to avoid us getting
|
||||
// killed when a socket gets an EPIPE error
|
||||
@ -124,13 +124,13 @@ int initializePlatformSockets(void) {
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
return 0;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void cleanupPlatformSockets(void) {
|
||||
#if defined(LC_WINDOWS)
|
||||
WSACleanup();
|
||||
WSACleanup();
|
||||
#else
|
||||
#endif
|
||||
}
|
||||
|
@ -6,18 +6,18 @@
|
||||
typedef void (*ThreadEntry)(void *context);
|
||||
|
||||
struct thread_context {
|
||||
ThreadEntry entry;
|
||||
void* context;
|
||||
ThreadEntry entry;
|
||||
void* context;
|
||||
};
|
||||
|
||||
#if defined(LC_WINDOWS)
|
||||
typedef struct _PLT_THREAD {
|
||||
HANDLE handle;
|
||||
int cancelled;
|
||||
DWORD tid;
|
||||
HANDLE termRequested;
|
||||
HANDLE handle;
|
||||
int cancelled;
|
||||
DWORD tid;
|
||||
HANDLE termRequested;
|
||||
|
||||
struct _PLT_THREAD *next;
|
||||
struct _PLT_THREAD *next;
|
||||
} PLT_THREAD;
|
||||
typedef HANDLE PLT_MUTEX;
|
||||
typedef HANDLE PLT_EVENT;
|
||||
|
@ -2,264 +2,264 @@
|
||||
#include "RtpReorderQueue.h"
|
||||
|
||||
void RtpqInitializeQueue(PRTP_REORDER_QUEUE queue, int maxSize, int maxQueueTimeMs) {
|
||||
queue->maxSize = maxSize;
|
||||
queue->maxQueueTimeMs = maxQueueTimeMs;
|
||||
queue->queueHead = NULL;
|
||||
queue->queueTail = NULL;
|
||||
queue->nextRtpSequenceNumber = UINT16_MAX;
|
||||
queue->oldestQueuedTimeMs = UINT64_MAX;
|
||||
queue->oldestQueuedEntry = NULL;
|
||||
queue->maxSize = maxSize;
|
||||
queue->maxQueueTimeMs = maxQueueTimeMs;
|
||||
queue->queueHead = NULL;
|
||||
queue->queueTail = NULL;
|
||||
queue->nextRtpSequenceNumber = UINT16_MAX;
|
||||
queue->oldestQueuedTimeMs = UINT64_MAX;
|
||||
queue->oldestQueuedEntry = NULL;
|
||||
}
|
||||
|
||||
void RtpqCleanupQueue(PRTP_REORDER_QUEUE queue) {
|
||||
while (queue->queueHead != NULL) {
|
||||
PRTP_QUEUE_ENTRY entry = queue->queueHead;
|
||||
queue->queueHead = entry->next;
|
||||
free(entry->packet);
|
||||
}
|
||||
while (queue->queueHead != NULL) {
|
||||
PRTP_QUEUE_ENTRY entry = queue->queueHead;
|
||||
queue->queueHead = entry->next;
|
||||
free(entry->packet);
|
||||
}
|
||||
}
|
||||
|
||||
// newEntry is contained within the packet buffer so we free the whole entry by freeing entry->packet
|
||||
static int queuePacket(PRTP_REORDER_QUEUE queue, PRTP_QUEUE_ENTRY newEntry, int head, PRTP_PACKET packet) {
|
||||
if (queue->nextRtpSequenceNumber != UINT16_MAX) {
|
||||
PRTP_QUEUE_ENTRY entry;
|
||||
if (queue->nextRtpSequenceNumber != UINT16_MAX) {
|
||||
PRTP_QUEUE_ENTRY entry;
|
||||
|
||||
// Don't queue packets we're already ahead of
|
||||
if (isBeforeSignedInt(packet->sequenceNumber, queue->nextRtpSequenceNumber, 0)) {
|
||||
return 0;
|
||||
}
|
||||
// Don't queue packets we're already ahead of
|
||||
if (isBeforeSignedInt(packet->sequenceNumber, queue->nextRtpSequenceNumber, 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Don't queue duplicates either
|
||||
entry = queue->queueHead;
|
||||
while (entry != NULL) {
|
||||
if (entry->packet->sequenceNumber == packet->sequenceNumber) {
|
||||
return 0;
|
||||
}
|
||||
// Don't queue duplicates either
|
||||
entry = queue->queueHead;
|
||||
while (entry != NULL) {
|
||||
if (entry->packet->sequenceNumber == packet->sequenceNumber) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
newEntry->packet = packet;
|
||||
newEntry->queueTimeMs = PltGetMillis();
|
||||
newEntry->prev = NULL;
|
||||
newEntry->next = NULL;
|
||||
newEntry->packet = packet;
|
||||
newEntry->queueTimeMs = PltGetMillis();
|
||||
newEntry->prev = NULL;
|
||||
newEntry->next = NULL;
|
||||
|
||||
if (queue->oldestQueuedTimeMs == UINT64_MAX) {
|
||||
queue->oldestQueuedTimeMs = newEntry->queueTimeMs;
|
||||
if (queue->oldestQueuedTimeMs == UINT64_MAX) {
|
||||
queue->oldestQueuedTimeMs = newEntry->queueTimeMs;
|
||||
queue->oldestQueuedEntry = newEntry;
|
||||
}
|
||||
}
|
||||
|
||||
if (queue->queueHead == NULL) {
|
||||
LC_ASSERT(queue->queueSize == 0);
|
||||
queue->queueHead = queue->queueTail = newEntry;
|
||||
}
|
||||
else if (head) {
|
||||
LC_ASSERT(queue->queueSize > 0);
|
||||
PRTP_QUEUE_ENTRY oldHead = queue->queueHead;
|
||||
newEntry->next = oldHead;
|
||||
LC_ASSERT(oldHead->prev == NULL);
|
||||
oldHead->prev = newEntry;
|
||||
queue->queueHead = newEntry;
|
||||
}
|
||||
else {
|
||||
LC_ASSERT(queue->queueSize > 0);
|
||||
PRTP_QUEUE_ENTRY oldTail = queue->queueTail;
|
||||
newEntry->prev = oldTail;
|
||||
LC_ASSERT(oldTail->next == NULL);
|
||||
oldTail->next = newEntry;
|
||||
queue->queueTail = newEntry;
|
||||
}
|
||||
queue->queueSize++;
|
||||
if (queue->queueHead == NULL) {
|
||||
LC_ASSERT(queue->queueSize == 0);
|
||||
queue->queueHead = queue->queueTail = newEntry;
|
||||
}
|
||||
else if (head) {
|
||||
LC_ASSERT(queue->queueSize > 0);
|
||||
PRTP_QUEUE_ENTRY oldHead = queue->queueHead;
|
||||
newEntry->next = oldHead;
|
||||
LC_ASSERT(oldHead->prev == NULL);
|
||||
oldHead->prev = newEntry;
|
||||
queue->queueHead = newEntry;
|
||||
}
|
||||
else {
|
||||
LC_ASSERT(queue->queueSize > 0);
|
||||
PRTP_QUEUE_ENTRY oldTail = queue->queueTail;
|
||||
newEntry->prev = oldTail;
|
||||
LC_ASSERT(oldTail->next == NULL);
|
||||
oldTail->next = newEntry;
|
||||
queue->queueTail = newEntry;
|
||||
}
|
||||
queue->queueSize++;
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void updateOldestQueued(PRTP_REORDER_QUEUE queue) {
|
||||
PRTP_QUEUE_ENTRY entry;
|
||||
PRTP_QUEUE_ENTRY entry;
|
||||
|
||||
queue->oldestQueuedTimeMs = UINT64_MAX;
|
||||
queue->oldestQueuedEntry = NULL;
|
||||
queue->oldestQueuedTimeMs = UINT64_MAX;
|
||||
queue->oldestQueuedEntry = NULL;
|
||||
|
||||
entry = queue->queueHead;
|
||||
while (entry != NULL) {
|
||||
if (entry->queueTimeMs < queue->oldestQueuedTimeMs) {
|
||||
queue->oldestQueuedEntry = entry;
|
||||
queue->oldestQueuedTimeMs = entry->queueTimeMs;
|
||||
}
|
||||
entry = queue->queueHead;
|
||||
while (entry != NULL) {
|
||||
if (entry->queueTimeMs < queue->oldestQueuedTimeMs) {
|
||||
queue->oldestQueuedEntry = entry;
|
||||
queue->oldestQueuedTimeMs = entry->queueTimeMs;
|
||||
}
|
||||
|
||||
entry = entry->next;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
static PRTP_QUEUE_ENTRY getEntryByLowestSeq(PRTP_REORDER_QUEUE queue) {
|
||||
PRTP_QUEUE_ENTRY lowestSeqEntry, entry;
|
||||
PRTP_QUEUE_ENTRY lowestSeqEntry, entry;
|
||||
|
||||
lowestSeqEntry = queue->queueHead;
|
||||
entry = queue->queueHead;
|
||||
while (entry != NULL) {
|
||||
if (isBeforeSignedInt(entry->packet->sequenceNumber, lowestSeqEntry->packet->sequenceNumber, 1)) {
|
||||
lowestSeqEntry = entry;
|
||||
}
|
||||
lowestSeqEntry = queue->queueHead;
|
||||
entry = queue->queueHead;
|
||||
while (entry != NULL) {
|
||||
if (isBeforeSignedInt(entry->packet->sequenceNumber, lowestSeqEntry->packet->sequenceNumber, 1)) {
|
||||
lowestSeqEntry = entry;
|
||||
}
|
||||
|
||||
entry = entry->next;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
// Remember the updated lowest sequence number
|
||||
if (lowestSeqEntry != NULL) {
|
||||
queue->nextRtpSequenceNumber = lowestSeqEntry->packet->sequenceNumber;
|
||||
}
|
||||
// Remember the updated lowest sequence number
|
||||
if (lowestSeqEntry != NULL) {
|
||||
queue->nextRtpSequenceNumber = lowestSeqEntry->packet->sequenceNumber;
|
||||
}
|
||||
|
||||
return lowestSeqEntry;
|
||||
return lowestSeqEntry;
|
||||
}
|
||||
|
||||
static void removeEntry(PRTP_REORDER_QUEUE queue, PRTP_QUEUE_ENTRY entry) {
|
||||
LC_ASSERT(entry != NULL);
|
||||
LC_ASSERT(queue->queueSize > 0);
|
||||
LC_ASSERT(queue->queueHead != NULL);
|
||||
LC_ASSERT(queue->queueTail != NULL);
|
||||
LC_ASSERT(entry != NULL);
|
||||
LC_ASSERT(queue->queueSize > 0);
|
||||
LC_ASSERT(queue->queueHead != NULL);
|
||||
LC_ASSERT(queue->queueTail != NULL);
|
||||
|
||||
if (queue->queueHead == entry) {
|
||||
queue->queueHead = entry->next;
|
||||
}
|
||||
if (queue->queueTail == entry) {
|
||||
queue->queueTail = entry->prev;
|
||||
}
|
||||
if (queue->queueHead == entry) {
|
||||
queue->queueHead = entry->next;
|
||||
}
|
||||
if (queue->queueTail == entry) {
|
||||
queue->queueTail = entry->prev;
|
||||
}
|
||||
|
||||
if (entry->prev != NULL) {
|
||||
entry->prev->next = entry->next;
|
||||
}
|
||||
if (entry->next != NULL) {
|
||||
entry->next->prev = entry->prev;
|
||||
}
|
||||
queue->queueSize--;
|
||||
if (entry->prev != NULL) {
|
||||
entry->prev->next = entry->next;
|
||||
}
|
||||
if (entry->next != NULL) {
|
||||
entry->next->prev = entry->prev;
|
||||
}
|
||||
queue->queueSize--;
|
||||
}
|
||||
|
||||
static PRTP_QUEUE_ENTRY validateQueueConstraints(PRTP_REORDER_QUEUE queue) {
|
||||
int needsUpdate = 0;
|
||||
int needsUpdate = 0;
|
||||
|
||||
// Empty queue is fine
|
||||
if (queue->queueHead == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// 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 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;
|
||||
}
|
||||
// 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);
|
||||
if (needsUpdate) {
|
||||
// Recalculate the oldest entry if needed
|
||||
updateOldestQueued(queue);
|
||||
|
||||
// Return the lowest seq queued
|
||||
return getEntryByLowestSeq(queue);
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
// Return the lowest seq queued
|
||||
return getEntryByLowestSeq(queue);
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int RtpqAddPacket(PRTP_REORDER_QUEUE queue, PRTP_PACKET packet, PRTP_QUEUE_ENTRY packetEntry) {
|
||||
if (queue->nextRtpSequenceNumber != UINT16_MAX &&
|
||||
isBeforeSignedInt(packet->sequenceNumber, queue->nextRtpSequenceNumber, 0)) {
|
||||
// Reject packets behind our current sequence number
|
||||
return RTPQ_RET_REJECTED;
|
||||
}
|
||||
if (queue->nextRtpSequenceNumber != UINT16_MAX &&
|
||||
isBeforeSignedInt(packet->sequenceNumber, queue->nextRtpSequenceNumber, 0)) {
|
||||
// Reject packets behind our current sequence number
|
||||
return RTPQ_RET_REJECTED;
|
||||
}
|
||||
|
||||
if (queue->queueHead == NULL) {
|
||||
// Return immediately for an exact match with an empty queue
|
||||
if (queue->nextRtpSequenceNumber == UINT16_MAX ||
|
||||
packet->sequenceNumber == queue->nextRtpSequenceNumber) {
|
||||
queue->nextRtpSequenceNumber = packet->sequenceNumber + 1;
|
||||
return RTPQ_RET_HANDLE_IMMEDIATELY;
|
||||
}
|
||||
else {
|
||||
// Queue is empty currently so we'll put this packet on there
|
||||
if (!queuePacket(queue, packetEntry, 0, packet)) {
|
||||
return RTPQ_RET_REJECTED;
|
||||
}
|
||||
else {
|
||||
return RTPQ_RET_QUEUED_NOTHING_READY;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
PRTP_QUEUE_ENTRY lowestEntry;
|
||||
if (queue->queueHead == NULL) {
|
||||
// Return immediately for an exact match with an empty queue
|
||||
if (queue->nextRtpSequenceNumber == UINT16_MAX ||
|
||||
packet->sequenceNumber == queue->nextRtpSequenceNumber) {
|
||||
queue->nextRtpSequenceNumber = packet->sequenceNumber + 1;
|
||||
return RTPQ_RET_HANDLE_IMMEDIATELY;
|
||||
}
|
||||
else {
|
||||
// Queue is empty currently so we'll put this packet on there
|
||||
if (!queuePacket(queue, packetEntry, 0, packet)) {
|
||||
return RTPQ_RET_REJECTED;
|
||||
}
|
||||
else {
|
||||
return RTPQ_RET_QUEUED_NOTHING_READY;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
PRTP_QUEUE_ENTRY lowestEntry;
|
||||
|
||||
// Validate that the queue remains within our contraints
|
||||
// and get the lowest element
|
||||
lowestEntry = validateQueueConstraints(queue);
|
||||
// Validate that the queue remains within our contraints
|
||||
// and get the lowest element
|
||||
lowestEntry = validateQueueConstraints(queue);
|
||||
|
||||
// If the queue is now empty after validating queue constraints,
|
||||
// this packet can be returned immediately
|
||||
if (lowestEntry == NULL && queue->queueHead == NULL) {
|
||||
queue->nextRtpSequenceNumber = packet->sequenceNumber + 1;
|
||||
return RTPQ_RET_HANDLE_IMMEDIATELY;
|
||||
}
|
||||
// If the queue is now empty after validating queue constraints,
|
||||
// this packet can be returned immediately
|
||||
if (lowestEntry == NULL && queue->queueHead == NULL) {
|
||||
queue->nextRtpSequenceNumber = packet->sequenceNumber + 1;
|
||||
return RTPQ_RET_HANDLE_IMMEDIATELY;
|
||||
}
|
||||
|
||||
// Queue has data inside, so we need to see where this packet fits
|
||||
if (packet->sequenceNumber == queue->nextRtpSequenceNumber) {
|
||||
// It fits in a hole where we need a packet, now we have some ready
|
||||
if (!queuePacket(queue, packetEntry, 0, packet)) {
|
||||
return RTPQ_RET_REJECTED;
|
||||
}
|
||||
else {
|
||||
return RTPQ_RET_QUEUED_PACKETS_READY;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!queuePacket(queue, packetEntry, 0, packet)) {
|
||||
return RTPQ_RET_REJECTED;
|
||||
}
|
||||
else {
|
||||
// Constraint validation may have changed the oldest packet to one that
|
||||
// matches the next sequence number
|
||||
return (lowestEntry != NULL) ? RTPQ_RET_QUEUED_PACKETS_READY :
|
||||
RTPQ_RET_QUEUED_NOTHING_READY;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Queue has data inside, so we need to see where this packet fits
|
||||
if (packet->sequenceNumber == queue->nextRtpSequenceNumber) {
|
||||
// It fits in a hole where we need a packet, now we have some ready
|
||||
if (!queuePacket(queue, packetEntry, 0, packet)) {
|
||||
return RTPQ_RET_REJECTED;
|
||||
}
|
||||
else {
|
||||
return RTPQ_RET_QUEUED_PACKETS_READY;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!queuePacket(queue, packetEntry, 0, packet)) {
|
||||
return RTPQ_RET_REJECTED;
|
||||
}
|
||||
else {
|
||||
// Constraint validation may have changed the oldest packet to one that
|
||||
// matches the next sequence number
|
||||
return (lowestEntry != NULL) ? RTPQ_RET_QUEUED_PACKETS_READY :
|
||||
RTPQ_RET_QUEUED_NOTHING_READY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PRTP_PACKET RtpqGetQueuedPacket(PRTP_REORDER_QUEUE queue) {
|
||||
PRTP_QUEUE_ENTRY queuedEntry, entry;
|
||||
PRTP_QUEUE_ENTRY queuedEntry, entry;
|
||||
|
||||
// Find the next queued packet
|
||||
queuedEntry = NULL;
|
||||
entry = queue->queueHead;
|
||||
while (entry != NULL) {
|
||||
if (entry->packet->sequenceNumber == queue->nextRtpSequenceNumber) {
|
||||
queue->nextRtpSequenceNumber++;
|
||||
queuedEntry = entry;
|
||||
removeEntry(queue, entry);
|
||||
break;
|
||||
}
|
||||
// Find the next queued packet
|
||||
queuedEntry = NULL;
|
||||
entry = queue->queueHead;
|
||||
while (entry != NULL) {
|
||||
if (entry->packet->sequenceNumber == queue->nextRtpSequenceNumber) {
|
||||
queue->nextRtpSequenceNumber++;
|
||||
queuedEntry = entry;
|
||||
removeEntry(queue, entry);
|
||||
break;
|
||||
}
|
||||
|
||||
entry = entry->next;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
// Bail if we found nothing
|
||||
if (queuedEntry == NULL) {
|
||||
// Update the oldest queued packet time
|
||||
updateOldestQueued(queue);
|
||||
// Bail if we found nothing
|
||||
if (queuedEntry == NULL) {
|
||||
// Update the oldest queued packet time
|
||||
updateOldestQueued(queue);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// We don't update the oldest queued entry here, because we know
|
||||
// the caller will call again until it receives null
|
||||
// We don't update the oldest queued entry here, because we know
|
||||
// the caller will call again until it receives null
|
||||
|
||||
return queuedEntry->packet;
|
||||
return queuedEntry->packet;
|
||||
}
|
@ -6,26 +6,26 @@
|
||||
#define RTPQ_DEFAULT_QUEUE_TIME 40
|
||||
|
||||
typedef struct _RTP_QUEUE_ENTRY {
|
||||
PRTP_PACKET packet;
|
||||
PRTP_PACKET packet;
|
||||
|
||||
uint64_t queueTimeMs;
|
||||
uint64_t queueTimeMs;
|
||||
|
||||
struct _RTP_QUEUE_ENTRY *next;
|
||||
struct _RTP_QUEUE_ENTRY *prev;
|
||||
struct _RTP_QUEUE_ENTRY *next;
|
||||
struct _RTP_QUEUE_ENTRY *prev;
|
||||
} RTP_QUEUE_ENTRY, *PRTP_QUEUE_ENTRY;
|
||||
|
||||
typedef struct _RTP_REORDER_QUEUE {
|
||||
int maxSize;
|
||||
int maxQueueTimeMs;
|
||||
int maxSize;
|
||||
int maxQueueTimeMs;
|
||||
|
||||
PRTP_QUEUE_ENTRY queueHead;
|
||||
PRTP_QUEUE_ENTRY queueTail;
|
||||
int queueSize;
|
||||
PRTP_QUEUE_ENTRY queueHead;
|
||||
PRTP_QUEUE_ENTRY queueTail;
|
||||
int queueSize;
|
||||
|
||||
unsigned short nextRtpSequenceNumber;
|
||||
unsigned short nextRtpSequenceNumber;
|
||||
|
||||
uint64_t oldestQueuedTimeMs;
|
||||
PRTP_QUEUE_ENTRY oldestQueuedEntry;
|
||||
uint64_t oldestQueuedTimeMs;
|
||||
PRTP_QUEUE_ENTRY oldestQueuedEntry;
|
||||
} RTP_REORDER_QUEUE, *PRTP_REORDER_QUEUE;
|
||||
|
||||
#define RTPQ_RET_HANDLE_IMMEDIATELY 0
|
||||
|
@ -20,10 +20,10 @@
|
||||
|
||||
/* Linked List to store the options */
|
||||
typedef struct _OPTION_ITEM {
|
||||
char flags;
|
||||
char *option;
|
||||
char *content;
|
||||
struct _OPTION_ITEM *next;
|
||||
char flags;
|
||||
char *option;
|
||||
char *content;
|
||||
struct _OPTION_ITEM *next;
|
||||
} OPTION_ITEM, *POPTION_ITEM;
|
||||
|
||||
/* RTSP Message *
|
||||
@ -31,28 +31,28 @@ typedef struct _OPTION_ITEM {
|
||||
* TYPE_REQUEST = 0
|
||||
* TYPE_RESPONSE = 1 */
|
||||
typedef struct _RTSP_MESSAGE {
|
||||
char type;
|
||||
char flags;
|
||||
int sequenceNumber;
|
||||
char *protocol;
|
||||
POPTION_ITEM options;
|
||||
char *payload;
|
||||
int payloadLength;
|
||||
char type;
|
||||
char flags;
|
||||
int sequenceNumber;
|
||||
char *protocol;
|
||||
POPTION_ITEM options;
|
||||
char *payload;
|
||||
int payloadLength;
|
||||
|
||||
char* messageBuffer;
|
||||
char* messageBuffer;
|
||||
|
||||
union {
|
||||
struct {
|
||||
/* Request fields */
|
||||
char *command;
|
||||
char *target;
|
||||
} request;
|
||||
struct {
|
||||
/* Response fields */
|
||||
char *statusString;
|
||||
int statusCode;
|
||||
} response;
|
||||
} message;
|
||||
union {
|
||||
struct {
|
||||
/* Request fields */
|
||||
char *command;
|
||||
char *target;
|
||||
} request;
|
||||
struct {
|
||||
/* Response fields */
|
||||
char *statusString;
|
||||
int statusCode;
|
||||
} response;
|
||||
} message;
|
||||
} RTSP_MESSAGE, *PRTSP_MESSAGE;
|
||||
|
||||
int parseRtspMessage(PRTSP_MESSAGE msg, char *rtspMessage, int length);
|
||||
|
@ -14,285 +14,285 @@ static int rtspClientVersion;
|
||||
/* Create RTSP Option */
|
||||
static POPTION_ITEM createOptionItem(char* option, char* content)
|
||||
{
|
||||
POPTION_ITEM item = malloc(sizeof(*item));
|
||||
if (item == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
POPTION_ITEM item = malloc(sizeof(*item));
|
||||
if (item == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
item->option = malloc(strlen(option) + 1);
|
||||
if (item->option == NULL) {
|
||||
free(item);
|
||||
return NULL;
|
||||
}
|
||||
item->option = malloc(strlen(option) + 1);
|
||||
if (item->option == NULL) {
|
||||
free(item);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(item->option, option);
|
||||
strcpy(item->option, option);
|
||||
|
||||
item->content = malloc(strlen(content) + 1);
|
||||
if (item->content == NULL) {
|
||||
free(item->option);
|
||||
free(item);
|
||||
return NULL;
|
||||
}
|
||||
item->content = malloc(strlen(content) + 1);
|
||||
if (item->content == NULL) {
|
||||
free(item->option);
|
||||
free(item);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(item->content, content);
|
||||
strcpy(item->content, content);
|
||||
|
||||
item->next = NULL;
|
||||
item->flags = FLAG_ALLOCATED_OPTION_FIELDS;
|
||||
item->next = NULL;
|
||||
item->flags = FLAG_ALLOCATED_OPTION_FIELDS;
|
||||
|
||||
return item;
|
||||
return item;
|
||||
}
|
||||
|
||||
/* Add an option to the RTSP Message */
|
||||
static int addOption(PRTSP_MESSAGE msg, char* option, char* content)
|
||||
{
|
||||
POPTION_ITEM item = createOptionItem(option, content);
|
||||
if (item == NULL) {
|
||||
return 0;
|
||||
}
|
||||
POPTION_ITEM item = createOptionItem(option, content);
|
||||
if (item == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
insertOption(&msg->options, item);
|
||||
msg->flags |= FLAG_ALLOCATED_OPTION_ITEMS;
|
||||
insertOption(&msg->options, item);
|
||||
msg->flags |= FLAG_ALLOCATED_OPTION_ITEMS;
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Create an RTSP Request */
|
||||
static int initializeRtspRequest(PRTSP_MESSAGE msg, char* command, char* target)
|
||||
{
|
||||
char sequenceNumberStr[16];
|
||||
char sequenceNumberStr[16];
|
||||
char clientVersionStr[16];
|
||||
|
||||
// FIXME: Hacked CSeq attribute due to RTSP parser bug
|
||||
createRtspRequest(msg, NULL, 0, command, target, "RTSP/1.0",
|
||||
0, NULL, NULL, 0);
|
||||
// FIXME: Hacked CSeq attribute due to RTSP parser bug
|
||||
createRtspRequest(msg, NULL, 0, command, target, "RTSP/1.0",
|
||||
0, NULL, NULL, 0);
|
||||
|
||||
sprintf(sequenceNumberStr, "%d", currentSeqNumber++);
|
||||
sprintf(sequenceNumberStr, "%d", currentSeqNumber++);
|
||||
sprintf(clientVersionStr, "%d", rtspClientVersion);
|
||||
if (!addOption(msg, "CSeq", sequenceNumberStr) ||
|
||||
!addOption(msg, "X-GS-ClientVersion", clientVersionStr)) {
|
||||
freeMessage(msg);
|
||||
return 0;
|
||||
}
|
||||
if (!addOption(msg, "CSeq", sequenceNumberStr) ||
|
||||
!addOption(msg, "X-GS-ClientVersion", clientVersionStr)) {
|
||||
freeMessage(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Send RTSP message and get response */
|
||||
static int transactRtspMessage(PRTSP_MESSAGE request, PRTSP_MESSAGE response, int* error) {
|
||||
SOCK_RET err;
|
||||
int ret = 0;
|
||||
int offset;
|
||||
char* serializedMessage = NULL;
|
||||
int messageLen;
|
||||
int offset;
|
||||
char* serializedMessage = NULL;
|
||||
int messageLen;
|
||||
|
||||
*error = -1;
|
||||
|
||||
sock = connectTcpSocket(&RemoteAddr, RemoteAddrLen, 48010);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
sock = connectTcpSocket(&RemoteAddr, RemoteAddrLen, 48010);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
*error = LastSocketError();
|
||||
return ret;
|
||||
}
|
||||
enableNoDelay(sock);
|
||||
return ret;
|
||||
}
|
||||
enableNoDelay(sock);
|
||||
|
||||
serializedMessage = serializeRtspMessage(request, &messageLen);
|
||||
if (serializedMessage == NULL) {
|
||||
closesocket(sock);
|
||||
serializedMessage = serializeRtspMessage(request, &messageLen);
|
||||
if (serializedMessage == NULL) {
|
||||
closesocket(sock);
|
||||
sock = INVALID_SOCKET;
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Send our message
|
||||
err = send(sock, serializedMessage, messageLen, 0);
|
||||
if (err == SOCKET_ERROR) {
|
||||
// Send our message
|
||||
err = send(sock, serializedMessage, messageLen, 0);
|
||||
if (err == SOCKET_ERROR) {
|
||||
*error = LastSocketError();
|
||||
Limelog("Failed to send RTSP message: %d\n", *error);
|
||||
goto Exit;
|
||||
}
|
||||
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;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
// 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");
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (serializedMessage != NULL) {
|
||||
free(serializedMessage);
|
||||
}
|
||||
|
||||
closesocket(sock);
|
||||
sock = INVALID_SOCKET;
|
||||
return ret;
|
||||
closesocket(sock);
|
||||
sock = INVALID_SOCKET;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Terminate the RTSP Handshake process by closing the socket */
|
||||
void terminateRtspHandshake(void) {
|
||||
if (sock != INVALID_SOCKET) {
|
||||
closesocket(sock);
|
||||
sock = INVALID_SOCKET;
|
||||
}
|
||||
if (sock != INVALID_SOCKET) {
|
||||
closesocket(sock);
|
||||
sock = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
/* Send RTSP OPTIONS request */
|
||||
static int requestOptions(PRTSP_MESSAGE response, int* error) {
|
||||
RTSP_MESSAGE request;
|
||||
int ret;
|
||||
RTSP_MESSAGE request;
|
||||
int ret;
|
||||
|
||||
*error = -1;
|
||||
|
||||
ret = initializeRtspRequest(&request, "OPTIONS", rtspTargetUrl);
|
||||
if (ret != 0) {
|
||||
ret = transactRtspMessage(&request, response, error);
|
||||
freeMessage(&request);
|
||||
}
|
||||
ret = initializeRtspRequest(&request, "OPTIONS", rtspTargetUrl);
|
||||
if (ret != 0) {
|
||||
ret = transactRtspMessage(&request, response, error);
|
||||
freeMessage(&request);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Send RTSP DESCRIBE request */
|
||||
static int requestDescribe(PRTSP_MESSAGE response, int* error) {
|
||||
RTSP_MESSAGE request;
|
||||
int ret;
|
||||
RTSP_MESSAGE request;
|
||||
int ret;
|
||||
|
||||
*error = -1;
|
||||
|
||||
ret = initializeRtspRequest(&request, "DESCRIBE", rtspTargetUrl);
|
||||
if (ret != 0) {
|
||||
if (addOption(&request, "Accept",
|
||||
"application/sdp") &&
|
||||
addOption(&request, "If-Modified-Since",
|
||||
"Thu, 01 Jan 1970 00:00:00 GMT")) {
|
||||
ret = transactRtspMessage(&request, response, error);
|
||||
}
|
||||
else {
|
||||
ret = 0;
|
||||
}
|
||||
freeMessage(&request);
|
||||
}
|
||||
ret = initializeRtspRequest(&request, "DESCRIBE", rtspTargetUrl);
|
||||
if (ret != 0) {
|
||||
if (addOption(&request, "Accept",
|
||||
"application/sdp") &&
|
||||
addOption(&request, "If-Modified-Since",
|
||||
"Thu, 01 Jan 1970 00:00:00 GMT")) {
|
||||
ret = transactRtspMessage(&request, response, error);
|
||||
}
|
||||
else {
|
||||
ret = 0;
|
||||
}
|
||||
freeMessage(&request);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Send RTSP SETUP request */
|
||||
static int setupStream(PRTSP_MESSAGE response, char* target, int* error) {
|
||||
RTSP_MESSAGE request;
|
||||
int ret;
|
||||
RTSP_MESSAGE request;
|
||||
int ret;
|
||||
|
||||
*error = -1;
|
||||
|
||||
ret = initializeRtspRequest(&request, "SETUP", target);
|
||||
if (ret != 0) {
|
||||
if (hasSessionId) {
|
||||
if (!addOption(&request, "Session", sessionIdString)) {
|
||||
ret = 0;
|
||||
goto FreeMessage;
|
||||
}
|
||||
}
|
||||
ret = initializeRtspRequest(&request, "SETUP", target);
|
||||
if (ret != 0) {
|
||||
if (hasSessionId) {
|
||||
if (!addOption(&request, "Session", sessionIdString)) {
|
||||
ret = 0;
|
||||
goto FreeMessage;
|
||||
}
|
||||
}
|
||||
|
||||
if (addOption(&request, "Transport", " ") &&
|
||||
addOption(&request, "If-Modified-Since",
|
||||
"Thu, 01 Jan 1970 00:00:00 GMT")) {
|
||||
ret = transactRtspMessage(&request, response, error);
|
||||
}
|
||||
else {
|
||||
ret = 0;
|
||||
}
|
||||
if (addOption(&request, "Transport", " ") &&
|
||||
addOption(&request, "If-Modified-Since",
|
||||
"Thu, 01 Jan 1970 00:00:00 GMT")) {
|
||||
ret = transactRtspMessage(&request, response, error);
|
||||
}
|
||||
else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
FreeMessage:
|
||||
freeMessage(&request);
|
||||
}
|
||||
FreeMessage:
|
||||
freeMessage(&request);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Send RTSP PLAY request*/
|
||||
static int playStream(PRTSP_MESSAGE response, char* target, int* error) {
|
||||
RTSP_MESSAGE request;
|
||||
int ret;
|
||||
RTSP_MESSAGE request;
|
||||
int ret;
|
||||
|
||||
*error = -1;
|
||||
|
||||
ret = initializeRtspRequest(&request, "PLAY", target);
|
||||
if (ret != 0) {
|
||||
if (addOption(&request, "Session", sessionIdString)) {
|
||||
ret = transactRtspMessage(&request, response, error);
|
||||
}
|
||||
else {
|
||||
ret = 0;
|
||||
}
|
||||
freeMessage(&request);
|
||||
}
|
||||
ret = initializeRtspRequest(&request, "PLAY", target);
|
||||
if (ret != 0) {
|
||||
if (addOption(&request, "Session", sessionIdString)) {
|
||||
ret = transactRtspMessage(&request, response, error);
|
||||
}
|
||||
else {
|
||||
ret = 0;
|
||||
}
|
||||
freeMessage(&request);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Send RTSP ANNOUNCE message */
|
||||
static int sendVideoAnnounce(PRTSP_MESSAGE response, int* error) {
|
||||
RTSP_MESSAGE request;
|
||||
int ret;
|
||||
int payloadLength;
|
||||
char payloadLengthStr[16];
|
||||
RTSP_MESSAGE request;
|
||||
int ret;
|
||||
int payloadLength;
|
||||
char payloadLengthStr[16];
|
||||
|
||||
*error = -1;
|
||||
|
||||
ret = initializeRtspRequest(&request, "ANNOUNCE", "streamid=video");
|
||||
if (ret != 0) {
|
||||
ret = 0;
|
||||
ret = initializeRtspRequest(&request, "ANNOUNCE", "streamid=video");
|
||||
if (ret != 0) {
|
||||
ret = 0;
|
||||
|
||||
if (!addOption(&request, "Session", sessionIdString) ||
|
||||
!addOption(&request, "Content-type", "application/sdp")) {
|
||||
goto FreeMessage;
|
||||
}
|
||||
if (!addOption(&request, "Session", sessionIdString) ||
|
||||
!addOption(&request, "Content-type", "application/sdp")) {
|
||||
goto FreeMessage;
|
||||
}
|
||||
|
||||
request.payload = getSdpPayloadForStreamConfig(rtspClientVersion, &payloadLength);
|
||||
if (request.payload == NULL) {
|
||||
goto FreeMessage;
|
||||
}
|
||||
request.flags |= FLAG_ALLOCATED_PAYLOAD;
|
||||
request.payloadLength = payloadLength;
|
||||
request.payload = getSdpPayloadForStreamConfig(rtspClientVersion, &payloadLength);
|
||||
if (request.payload == NULL) {
|
||||
goto FreeMessage;
|
||||
}
|
||||
request.flags |= FLAG_ALLOCATED_PAYLOAD;
|
||||
request.payloadLength = payloadLength;
|
||||
|
||||
sprintf(payloadLengthStr, "%d", payloadLength);
|
||||
if (!addOption(&request, "Content-length", payloadLengthStr)) {
|
||||
goto FreeMessage;
|
||||
}
|
||||
sprintf(payloadLengthStr, "%d", payloadLength);
|
||||
if (!addOption(&request, "Content-length", payloadLengthStr)) {
|
||||
goto FreeMessage;
|
||||
}
|
||||
|
||||
ret = transactRtspMessage(&request, response, error);
|
||||
ret = transactRtspMessage(&request, response, error);
|
||||
|
||||
FreeMessage:
|
||||
freeMessage(&request);
|
||||
}
|
||||
FreeMessage:
|
||||
freeMessage(&request);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Perform RTSP Handshake with the streaming server machine as part of the connection process */
|
||||
int performRtspHandshake(void) {
|
||||
char urlAddr[URLSAFESTRING_LEN];
|
||||
|
||||
// Initialize global state
|
||||
// Initialize global state
|
||||
addrToUrlSafeString(&RemoteAddr, urlAddr);
|
||||
sprintf(rtspTargetUrl, "rtsp://%s", urlAddr);
|
||||
currentSeqNumber = 1;
|
||||
hasSessionId = 0;
|
||||
sprintf(rtspTargetUrl, "rtsp://%s", urlAddr);
|
||||
currentSeqNumber = 1;
|
||||
hasSessionId = 0;
|
||||
|
||||
if (ServerMajorVersion == 3) {
|
||||
rtspClientVersion = 10;
|
||||
@ -301,141 +301,141 @@ int performRtspHandshake(void) {
|
||||
rtspClientVersion = 11;
|
||||
}
|
||||
|
||||
{
|
||||
RTSP_MESSAGE response;
|
||||
{
|
||||
RTSP_MESSAGE response;
|
||||
int error = -1;
|
||||
|
||||
if (!requestOptions(&response, &error)) {
|
||||
Limelog("RTSP OPTIONS request failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
if (!requestOptions(&response, &error)) {
|
||||
Limelog("RTSP OPTIONS request failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (response.message.response.statusCode != 200) {
|
||||
Limelog("RTSP OPTIONS request failed: %d\n",
|
||||
response.message.response.statusCode);
|
||||
return response.message.response.statusCode;
|
||||
}
|
||||
if (response.message.response.statusCode != 200) {
|
||||
Limelog("RTSP OPTIONS request failed: %d\n",
|
||||
response.message.response.statusCode);
|
||||
return response.message.response.statusCode;
|
||||
}
|
||||
|
||||
freeMessage(&response);
|
||||
}
|
||||
freeMessage(&response);
|
||||
}
|
||||
|
||||
{
|
||||
RTSP_MESSAGE response;
|
||||
{
|
||||
RTSP_MESSAGE response;
|
||||
int error = -1;
|
||||
|
||||
if (!requestDescribe(&response, &error)) {
|
||||
Limelog("RTSP DESCRIBE request failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
if (!requestDescribe(&response, &error)) {
|
||||
Limelog("RTSP DESCRIBE request failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (response.message.response.statusCode != 200) {
|
||||
Limelog("RTSP DESCRIBE request failed: %d\n",
|
||||
response.message.response.statusCode);
|
||||
return response.message.response.statusCode;
|
||||
}
|
||||
if (response.message.response.statusCode != 200) {
|
||||
Limelog("RTSP DESCRIBE request failed: %d\n",
|
||||
response.message.response.statusCode);
|
||||
return response.message.response.statusCode;
|
||||
}
|
||||
|
||||
freeMessage(&response);
|
||||
}
|
||||
freeMessage(&response);
|
||||
}
|
||||
|
||||
{
|
||||
RTSP_MESSAGE response;
|
||||
char* sessionId;
|
||||
{
|
||||
RTSP_MESSAGE response;
|
||||
char* sessionId;
|
||||
int error = -1;
|
||||
|
||||
if (!setupStream(&response, "streamid=audio", &error)) {
|
||||
Limelog("RTSP SETUP streamid=audio request failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
if (!setupStream(&response, "streamid=audio", &error)) {
|
||||
Limelog("RTSP SETUP streamid=audio request failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (response.message.response.statusCode != 200) {
|
||||
Limelog("RTSP SETUP streamid=audio request failed: %d\n",
|
||||
response.message.response.statusCode);
|
||||
return response.message.response.statusCode;
|
||||
}
|
||||
if (response.message.response.statusCode != 200) {
|
||||
Limelog("RTSP SETUP streamid=audio request failed: %d\n",
|
||||
response.message.response.statusCode);
|
||||
return response.message.response.statusCode;
|
||||
}
|
||||
|
||||
sessionId = getOptionContent(response.options, "Session");
|
||||
if (sessionId == NULL) {
|
||||
Limelog("RTSP SETUP streamid=audio is missing session attribute");
|
||||
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;
|
||||
strcpy(sessionIdString, sessionId);
|
||||
hasSessionId = 1;
|
||||
|
||||
freeMessage(&response);
|
||||
}
|
||||
freeMessage(&response);
|
||||
}
|
||||
|
||||
{
|
||||
RTSP_MESSAGE response;
|
||||
{
|
||||
RTSP_MESSAGE response;
|
||||
int error = -1;
|
||||
|
||||
if (!setupStream(&response, "streamid=video", &error)) {
|
||||
Limelog("RTSP SETUP streamid=video request failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
if (!setupStream(&response, "streamid=video", &error)) {
|
||||
Limelog("RTSP SETUP streamid=video request failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (response.message.response.statusCode != 200) {
|
||||
Limelog("RTSP SETUP streamid=video request failed: %d\n",
|
||||
response.message.response.statusCode);
|
||||
return response.message.response.statusCode;
|
||||
}
|
||||
if (response.message.response.statusCode != 200) {
|
||||
Limelog("RTSP SETUP streamid=video request failed: %d\n",
|
||||
response.message.response.statusCode);
|
||||
return response.message.response.statusCode;
|
||||
}
|
||||
|
||||
freeMessage(&response);
|
||||
}
|
||||
freeMessage(&response);
|
||||
}
|
||||
|
||||
{
|
||||
RTSP_MESSAGE response;
|
||||
{
|
||||
RTSP_MESSAGE response;
|
||||
int error = -1;
|
||||
|
||||
if (!sendVideoAnnounce(&response, &error)) {
|
||||
Limelog("RTSP ANNOUNCE request failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
if (!sendVideoAnnounce(&response, &error)) {
|
||||
Limelog("RTSP ANNOUNCE request failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (response.message.response.statusCode != 200) {
|
||||
Limelog("RTSP ANNOUNCE request failed: %d\n",
|
||||
response.message.response.statusCode);
|
||||
return response.message.response.statusCode;
|
||||
}
|
||||
if (response.message.response.statusCode != 200) {
|
||||
Limelog("RTSP ANNOUNCE request failed: %d\n",
|
||||
response.message.response.statusCode);
|
||||
return response.message.response.statusCode;
|
||||
}
|
||||
|
||||
freeMessage(&response);
|
||||
}
|
||||
freeMessage(&response);
|
||||
}
|
||||
|
||||
{
|
||||
RTSP_MESSAGE response;
|
||||
{
|
||||
RTSP_MESSAGE response;
|
||||
int error = -1;
|
||||
|
||||
if (!playStream(&response, "streamid=video", &error)) {
|
||||
Limelog("RTSP PLAY streamid=video request failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
if (!playStream(&response, "streamid=video", &error)) {
|
||||
Limelog("RTSP PLAY streamid=video request failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (response.message.response.statusCode != 200) {
|
||||
Limelog("RTSP PLAY streamid=video failed: %d\n",
|
||||
response.message.response.statusCode);
|
||||
return response.message.response.statusCode;
|
||||
}
|
||||
if (response.message.response.statusCode != 200) {
|
||||
Limelog("RTSP PLAY streamid=video failed: %d\n",
|
||||
response.message.response.statusCode);
|
||||
return response.message.response.statusCode;
|
||||
}
|
||||
|
||||
freeMessage(&response);
|
||||
}
|
||||
freeMessage(&response);
|
||||
}
|
||||
|
||||
{
|
||||
RTSP_MESSAGE response;
|
||||
{
|
||||
RTSP_MESSAGE response;
|
||||
int error = -1;
|
||||
|
||||
if (!playStream(&response, "streamid=audio", &error)) {
|
||||
Limelog("RTSP PLAY streamid=audio request failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
if (!playStream(&response, "streamid=audio", &error)) {
|
||||
Limelog("RTSP PLAY streamid=audio request failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (response.message.response.statusCode != 200) {
|
||||
Limelog("RTSP PLAY streamid=audio failed: %d\n",
|
||||
response.message.response.statusCode);
|
||||
return response.message.response.statusCode;
|
||||
}
|
||||
if (response.message.response.statusCode != 200) {
|
||||
Limelog("RTSP PLAY streamid=audio failed: %d\n",
|
||||
response.message.response.statusCode);
|
||||
return response.message.response.statusCode;
|
||||
}
|
||||
|
||||
freeMessage(&response);
|
||||
}
|
||||
freeMessage(&response);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
@ -2,382 +2,382 @@
|
||||
|
||||
/* Check if String s begins with the given prefix */
|
||||
static int startsWith(const char *s, const char *prefix) {
|
||||
if (strncmp(s, prefix, strlen(prefix)) == 0){
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
if (strncmp(s, prefix, strlen(prefix)) == 0){
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Gets the length of the message */
|
||||
static int getMessageLength(PRTSP_MESSAGE msg){
|
||||
POPTION_ITEM current;
|
||||
POPTION_ITEM current;
|
||||
|
||||
/* Initialize to 1 for null terminator */
|
||||
size_t count = 1;
|
||||
/* Add the length of the protocol */
|
||||
count += strlen(msg->protocol);
|
||||
/* Initialize to 1 for null terminator */
|
||||
size_t count = 1;
|
||||
/* Add the length of the protocol */
|
||||
count += strlen(msg->protocol);
|
||||
|
||||
/* Add length of request-specific strings */
|
||||
if (msg->type == TYPE_REQUEST){
|
||||
count += strlen(msg->message.request.command);
|
||||
count += strlen(msg->message.request.target);
|
||||
/* Add 4 for the two spaces and \r\n*/
|
||||
count += 4;
|
||||
}
|
||||
/* Add length of response-specific strings */
|
||||
else {
|
||||
char statusCodeStr[16];
|
||||
sprintf(statusCodeStr, "%d", msg->message.response.statusCode);
|
||||
count += strlen(statusCodeStr);
|
||||
count += strlen(msg->message.response.statusString);
|
||||
/* Add 4 for two spaces and \r\n */
|
||||
count += 4;
|
||||
}
|
||||
/* Count the size of the options */
|
||||
current = msg->options;
|
||||
while (current != NULL){
|
||||
count += strlen(current->option);
|
||||
count += strlen(current->content);
|
||||
/* Add 4 because of :[space] and \r\n */
|
||||
count += 4;
|
||||
current = current->next;
|
||||
}
|
||||
/* Add 2 more for extra /r/n ending */
|
||||
count += 2;
|
||||
/* Add length of request-specific strings */
|
||||
if (msg->type == TYPE_REQUEST){
|
||||
count += strlen(msg->message.request.command);
|
||||
count += strlen(msg->message.request.target);
|
||||
/* Add 4 for the two spaces and \r\n*/
|
||||
count += 4;
|
||||
}
|
||||
/* Add length of response-specific strings */
|
||||
else {
|
||||
char statusCodeStr[16];
|
||||
sprintf(statusCodeStr, "%d", msg->message.response.statusCode);
|
||||
count += strlen(statusCodeStr);
|
||||
count += strlen(msg->message.response.statusString);
|
||||
/* Add 4 for two spaces and \r\n */
|
||||
count += 4;
|
||||
}
|
||||
/* Count the size of the options */
|
||||
current = msg->options;
|
||||
while (current != NULL){
|
||||
count += strlen(current->option);
|
||||
count += strlen(current->content);
|
||||
/* Add 4 because of :[space] and \r\n */
|
||||
count += 4;
|
||||
current = current->next;
|
||||
}
|
||||
/* Add 2 more for extra /r/n ending */
|
||||
count += 2;
|
||||
|
||||
/* Add the length of the payload, if any */
|
||||
count += msg->payloadLength;
|
||||
/* Add the length of the payload, if any */
|
||||
count += msg->payloadLength;
|
||||
|
||||
return (int)count;
|
||||
return (int)count;
|
||||
}
|
||||
|
||||
/* Given an RTSP message string rtspMessage, parse it into an RTSP_MESSAGE struct msg */
|
||||
int parseRtspMessage(PRTSP_MESSAGE msg, char *rtspMessage, int length) {
|
||||
char *token, *protocol, *endCheck, *target, *statusStr, *command, *sequence, flag;
|
||||
char messageEnded = 0, *payload = NULL, *opt = NULL;
|
||||
int statusCode = 0, sequenceNum, exitCode;
|
||||
POPTION_ITEM options = NULL;
|
||||
POPTION_ITEM newOpt;
|
||||
char *token, *protocol, *endCheck, *target, *statusStr, *command, *sequence, flag;
|
||||
char messageEnded = 0, *payload = NULL, *opt = NULL;
|
||||
int statusCode = 0, sequenceNum, exitCode;
|
||||
POPTION_ITEM options = NULL;
|
||||
POPTION_ITEM newOpt;
|
||||
|
||||
/* Delimeter sets for strtok() */
|
||||
char *delim = " \r\n";
|
||||
char *end = "\r\n";
|
||||
char *optDelim = " :\r\n";
|
||||
char typeFlag = TOKEN_OPTION;
|
||||
/* Delimeter sets for strtok() */
|
||||
char *delim = " \r\n";
|
||||
char *end = "\r\n";
|
||||
char *optDelim = " :\r\n";
|
||||
char typeFlag = TOKEN_OPTION;
|
||||
|
||||
/* Put the raw message into a string we can use */
|
||||
char *messageBuffer = malloc(length + 1);
|
||||
if (messageBuffer == NULL) {
|
||||
exitCode = RTSP_ERROR_NO_MEMORY;
|
||||
goto ExitFailure;
|
||||
}
|
||||
memcpy(messageBuffer, rtspMessage, length);
|
||||
/* Put the raw message into a string we can use */
|
||||
char *messageBuffer = malloc(length + 1);
|
||||
if (messageBuffer == NULL) {
|
||||
exitCode = RTSP_ERROR_NO_MEMORY;
|
||||
goto ExitFailure;
|
||||
}
|
||||
memcpy(messageBuffer, rtspMessage, length);
|
||||
|
||||
// The payload logic depends on a null-terminator at the end
|
||||
messageBuffer[length] = 0;
|
||||
// The payload logic depends on a null-terminator at the end
|
||||
messageBuffer[length] = 0;
|
||||
|
||||
/* Get the first token of the message*/
|
||||
token = strtok(messageBuffer, delim);
|
||||
if (token == NULL){
|
||||
exitCode = RTSP_ERROR_MALFORMED;
|
||||
goto ExitFailure;
|
||||
}
|
||||
/* Get the first token of the message*/
|
||||
token = strtok(messageBuffer, delim);
|
||||
if (token == NULL){
|
||||
exitCode = RTSP_ERROR_MALFORMED;
|
||||
goto ExitFailure;
|
||||
}
|
||||
|
||||
/* The message is a response */
|
||||
if (startsWith(token, "RTSP")){
|
||||
flag = TYPE_RESPONSE;
|
||||
/* The current token is the protocol */
|
||||
protocol = token;
|
||||
/* The message is a response */
|
||||
if (startsWith(token, "RTSP")){
|
||||
flag = TYPE_RESPONSE;
|
||||
/* The current token is the protocol */
|
||||
protocol = token;
|
||||
|
||||
/* Get the status code */
|
||||
token = strtok(NULL, delim);
|
||||
statusCode = atoi(token);
|
||||
if (token == NULL){
|
||||
exitCode = RTSP_ERROR_MALFORMED;
|
||||
goto ExitFailure;
|
||||
}
|
||||
/* Get the status code */
|
||||
token = strtok(NULL, delim);
|
||||
statusCode = atoi(token);
|
||||
if (token == NULL){
|
||||
exitCode = RTSP_ERROR_MALFORMED;
|
||||
goto ExitFailure;
|
||||
}
|
||||
|
||||
/* Get the status string */
|
||||
statusStr = strtok(NULL, end);
|
||||
if (statusStr == NULL){
|
||||
exitCode = RTSP_ERROR_MALFORMED;
|
||||
goto ExitFailure;
|
||||
}
|
||||
/* Get the status string */
|
||||
statusStr = strtok(NULL, end);
|
||||
if (statusStr == NULL){
|
||||
exitCode = RTSP_ERROR_MALFORMED;
|
||||
goto ExitFailure;
|
||||
}
|
||||
|
||||
/* Request fields - we don't care about them here */
|
||||
target = NULL;
|
||||
command = NULL;
|
||||
}
|
||||
/* The message is a request */
|
||||
else {
|
||||
flag = TYPE_REQUEST;
|
||||
/* The current token is the command */
|
||||
command = token;
|
||||
/* Get the target */
|
||||
target = strtok(NULL, delim);
|
||||
if (target == NULL){
|
||||
exitCode = RTSP_ERROR_MALFORMED;
|
||||
goto ExitFailure;
|
||||
}
|
||||
/* Get the protocol */
|
||||
protocol = strtok(NULL, delim);
|
||||
if (protocol == NULL){
|
||||
exitCode = RTSP_ERROR_MALFORMED;
|
||||
goto ExitFailure;
|
||||
}
|
||||
/* Response field - we don't care about it here */
|
||||
statusStr = NULL;
|
||||
}
|
||||
/* Check that the protocol is valid */
|
||||
if (strcmp(protocol, "RTSP/1.0")){
|
||||
exitCode = RTSP_ERROR_MALFORMED;
|
||||
goto ExitFailure;
|
||||
}
|
||||
/* Parse remaining options */
|
||||
while (token != NULL){
|
||||
token = strtok(NULL, typeFlag == TOKEN_OPTION ? optDelim : end);
|
||||
if (token != NULL){
|
||||
/* Request fields - we don't care about them here */
|
||||
target = NULL;
|
||||
command = NULL;
|
||||
}
|
||||
/* The message is a request */
|
||||
else {
|
||||
flag = TYPE_REQUEST;
|
||||
/* The current token is the command */
|
||||
command = token;
|
||||
/* Get the target */
|
||||
target = strtok(NULL, delim);
|
||||
if (target == NULL){
|
||||
exitCode = RTSP_ERROR_MALFORMED;
|
||||
goto ExitFailure;
|
||||
}
|
||||
/* Get the protocol */
|
||||
protocol = strtok(NULL, delim);
|
||||
if (protocol == NULL){
|
||||
exitCode = RTSP_ERROR_MALFORMED;
|
||||
goto ExitFailure;
|
||||
}
|
||||
/* Response field - we don't care about it here */
|
||||
statusStr = NULL;
|
||||
}
|
||||
/* Check that the protocol is valid */
|
||||
if (strcmp(protocol, "RTSP/1.0")){
|
||||
exitCode = RTSP_ERROR_MALFORMED;
|
||||
goto ExitFailure;
|
||||
}
|
||||
/* Parse remaining options */
|
||||
while (token != NULL){
|
||||
token = strtok(NULL, typeFlag == TOKEN_OPTION ? optDelim : end);
|
||||
if (token != NULL){
|
||||
|
||||
/* The token is an option */
|
||||
if (typeFlag == TOKEN_OPTION){
|
||||
opt = token;
|
||||
}
|
||||
/* The token is content */
|
||||
else {
|
||||
/* Create a new node containing the option and content */
|
||||
newOpt = (POPTION_ITEM)malloc(sizeof(OPTION_ITEM));
|
||||
if (newOpt == NULL){
|
||||
freeOptionList(options);
|
||||
exitCode = RTSP_ERROR_NO_MEMORY;
|
||||
goto ExitFailure;
|
||||
}
|
||||
newOpt->flags = 0;
|
||||
newOpt->option = opt;
|
||||
newOpt->content = token;
|
||||
newOpt->next = NULL;
|
||||
insertOption(&options, newOpt);
|
||||
/* The token is an option */
|
||||
if (typeFlag == TOKEN_OPTION){
|
||||
opt = token;
|
||||
}
|
||||
/* The token is content */
|
||||
else {
|
||||
/* Create a new node containing the option and content */
|
||||
newOpt = (POPTION_ITEM)malloc(sizeof(OPTION_ITEM));
|
||||
if (newOpt == NULL){
|
||||
freeOptionList(options);
|
||||
exitCode = RTSP_ERROR_NO_MEMORY;
|
||||
goto ExitFailure;
|
||||
}
|
||||
newOpt->flags = 0;
|
||||
newOpt->option = opt;
|
||||
newOpt->content = token;
|
||||
newOpt->next = NULL;
|
||||
insertOption(&options, newOpt);
|
||||
|
||||
/* Check if we're at the end of the message portion marked by \r\n\r\n
|
||||
* endCheck points to the remainder of messageBuffer after the token */
|
||||
endCheck = &token[0] + strlen(token) + 1;
|
||||
/* Check if we're at the end of the message portion marked by \r\n\r\n
|
||||
* endCheck points to the remainder of messageBuffer after the token */
|
||||
endCheck = &token[0] + strlen(token) + 1;
|
||||
|
||||
/* See if we've hit the end of the message. The first \r is missing because it's been tokenized */
|
||||
if (startsWith(endCheck, "\n\r\n")){
|
||||
/* See if we've hit the end of the message. The first \r is missing because it's been tokenized */
|
||||
if (startsWith(endCheck, "\n\r\n")){
|
||||
|
||||
/* We've encountered the end of the message - mark it thus */
|
||||
messageEnded = 1;
|
||||
/* We've encountered the end of the message - mark it thus */
|
||||
messageEnded = 1;
|
||||
|
||||
/* The payload is the remainder of messageBuffer. If none, then payload = null */
|
||||
if (endCheck[3] != '\0')
|
||||
payload = &endCheck[3];
|
||||
/* The payload is the remainder of messageBuffer. If none, then payload = null */
|
||||
if (endCheck[3] != '\0')
|
||||
payload = &endCheck[3];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
typeFlag ^= 1; // flip the flag
|
||||
}
|
||||
/* If we never encountered the double CRLF, then the message is malformed! */
|
||||
if (!messageEnded){
|
||||
exitCode = RTSP_ERROR_MALFORMED;
|
||||
goto ExitFailure;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
typeFlag ^= 1; // flip the flag
|
||||
}
|
||||
/* If we never encountered the double CRLF, then the message is malformed! */
|
||||
if (!messageEnded){
|
||||
exitCode = RTSP_ERROR_MALFORMED;
|
||||
goto ExitFailure;
|
||||
}
|
||||
|
||||
/* Get sequence number as an integer */
|
||||
sequence = getOptionContent(options, "CSeq");
|
||||
if (sequence != NULL) {
|
||||
sequenceNum = atoi(sequence);
|
||||
}
|
||||
else {
|
||||
sequenceNum = SEQ_INVALID;
|
||||
}
|
||||
/* Package the new parsed message into the struct */
|
||||
if (flag == TYPE_REQUEST){
|
||||
createRtspRequest(msg, messageBuffer, FLAG_ALLOCATED_MESSAGE_BUFFER | FLAG_ALLOCATED_OPTION_ITEMS, command, target,
|
||||
protocol, sequenceNum, options, payload, payload ? length - (int)(messageBuffer - payload) : 0);
|
||||
}
|
||||
else {
|
||||
createRtspResponse(msg, messageBuffer, FLAG_ALLOCATED_MESSAGE_BUFFER | FLAG_ALLOCATED_OPTION_ITEMS, protocol, statusCode,
|
||||
statusStr, sequenceNum, options, payload, payload ? length - (int)(messageBuffer - payload) : 0);
|
||||
}
|
||||
return RTSP_ERROR_SUCCESS;
|
||||
/* Get sequence number as an integer */
|
||||
sequence = getOptionContent(options, "CSeq");
|
||||
if (sequence != NULL) {
|
||||
sequenceNum = atoi(sequence);
|
||||
}
|
||||
else {
|
||||
sequenceNum = SEQ_INVALID;
|
||||
}
|
||||
/* Package the new parsed message into the struct */
|
||||
if (flag == TYPE_REQUEST){
|
||||
createRtspRequest(msg, messageBuffer, FLAG_ALLOCATED_MESSAGE_BUFFER | FLAG_ALLOCATED_OPTION_ITEMS, command, target,
|
||||
protocol, sequenceNum, options, payload, payload ? length - (int)(messageBuffer - payload) : 0);
|
||||
}
|
||||
else {
|
||||
createRtspResponse(msg, messageBuffer, FLAG_ALLOCATED_MESSAGE_BUFFER | FLAG_ALLOCATED_OPTION_ITEMS, protocol, statusCode,
|
||||
statusStr, sequenceNum, options, payload, payload ? length - (int)(messageBuffer - payload) : 0);
|
||||
}
|
||||
return RTSP_ERROR_SUCCESS;
|
||||
|
||||
/* Cleanup in failure condition */
|
||||
/* Cleanup in failure condition */
|
||||
ExitFailure:
|
||||
if (options) {
|
||||
free(options);
|
||||
}
|
||||
if (messageBuffer) {
|
||||
free(messageBuffer);
|
||||
}
|
||||
return exitCode;
|
||||
if (options) {
|
||||
free(options);
|
||||
}
|
||||
if (messageBuffer) {
|
||||
free(messageBuffer);
|
||||
}
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
/* Create new RTSP message struct with response data */
|
||||
void createRtspResponse(PRTSP_MESSAGE msg, char *message, int flags, char *protocol,
|
||||
int statusCode, char *statusString, int sequenceNumber, POPTION_ITEM optionsHead, char *payload, int payloadLength) {
|
||||
msg->type = TYPE_RESPONSE;
|
||||
msg->flags = flags;
|
||||
msg->messageBuffer = message;
|
||||
msg->protocol = protocol;
|
||||
msg->options = optionsHead;
|
||||
msg->payload = payload;
|
||||
msg->payloadLength = payloadLength;
|
||||
msg->sequenceNumber = sequenceNumber;
|
||||
msg->message.response.statusString = statusString;
|
||||
msg->message.response.statusCode = statusCode;
|
||||
int statusCode, char *statusString, int sequenceNumber, POPTION_ITEM optionsHead, char *payload, int payloadLength) {
|
||||
msg->type = TYPE_RESPONSE;
|
||||
msg->flags = flags;
|
||||
msg->messageBuffer = message;
|
||||
msg->protocol = protocol;
|
||||
msg->options = optionsHead;
|
||||
msg->payload = payload;
|
||||
msg->payloadLength = payloadLength;
|
||||
msg->sequenceNumber = sequenceNumber;
|
||||
msg->message.response.statusString = statusString;
|
||||
msg->message.response.statusCode = statusCode;
|
||||
}
|
||||
|
||||
/* Create new RTSP message struct with request data */
|
||||
void createRtspRequest(PRTSP_MESSAGE msg, char *message, int flags,
|
||||
char *command, char *target, char *protocol, int sequenceNumber, POPTION_ITEM optionsHead, char *payload, int payloadLength) {
|
||||
msg->type = TYPE_REQUEST;
|
||||
msg->flags = flags;
|
||||
msg->protocol = protocol;
|
||||
msg->messageBuffer = message;
|
||||
msg->options = optionsHead;
|
||||
msg->payload = payload;
|
||||
msg->payloadLength = payloadLength;
|
||||
msg->sequenceNumber = sequenceNumber;
|
||||
msg->message.request.command = command;
|
||||
msg->message.request.target = target;
|
||||
char *command, char *target, char *protocol, int sequenceNumber, POPTION_ITEM optionsHead, char *payload, int payloadLength) {
|
||||
msg->type = TYPE_REQUEST;
|
||||
msg->flags = flags;
|
||||
msg->protocol = protocol;
|
||||
msg->messageBuffer = message;
|
||||
msg->options = optionsHead;
|
||||
msg->payload = payload;
|
||||
msg->payloadLength = payloadLength;
|
||||
msg->sequenceNumber = sequenceNumber;
|
||||
msg->message.request.command = command;
|
||||
msg->message.request.target = target;
|
||||
}
|
||||
|
||||
/* Retrieves option content from the linked list given the option title */
|
||||
char *getOptionContent(POPTION_ITEM optionsHead, char *option){
|
||||
OPTION_ITEM *current = optionsHead;
|
||||
while (current != NULL){
|
||||
/* Check if current node is what we're looking for */
|
||||
if (!strcmp(current->option, option)){
|
||||
return current->content;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
/* Not found */
|
||||
return NULL;
|
||||
OPTION_ITEM *current = optionsHead;
|
||||
while (current != NULL){
|
||||
/* Check if current node is what we're looking for */
|
||||
if (!strcmp(current->option, option)){
|
||||
return current->content;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
/* Not found */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Adds new option opt to the struct's option list */
|
||||
void insertOption(POPTION_ITEM *optionsHead, POPTION_ITEM opt){
|
||||
OPTION_ITEM *current = *optionsHead;
|
||||
opt->next = NULL;
|
||||
OPTION_ITEM *current = *optionsHead;
|
||||
opt->next = NULL;
|
||||
|
||||
/* Empty options list */
|
||||
if (*optionsHead == NULL){
|
||||
*optionsHead = opt;
|
||||
return;
|
||||
}
|
||||
/* Traverse the list and insert the new option at the end */
|
||||
while (current != NULL){
|
||||
/* Check for duplicate option; if so, replace the option currently there */
|
||||
if (!strcmp(current->option, opt->option)){
|
||||
current->content = opt->content;
|
||||
return;
|
||||
}
|
||||
if (current->next == NULL){
|
||||
current->next = opt;
|
||||
return;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
/* Empty options list */
|
||||
if (*optionsHead == NULL){
|
||||
*optionsHead = opt;
|
||||
return;
|
||||
}
|
||||
/* Traverse the list and insert the new option at the end */
|
||||
while (current != NULL){
|
||||
/* Check for duplicate option; if so, replace the option currently there */
|
||||
if (!strcmp(current->option, opt->option)){
|
||||
current->content = opt->content;
|
||||
return;
|
||||
}
|
||||
if (current->next == NULL){
|
||||
current->next = opt;
|
||||
return;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free every node in the message's option list */
|
||||
void freeOptionList(POPTION_ITEM optionsHead){
|
||||
POPTION_ITEM current = optionsHead;
|
||||
POPTION_ITEM temp;
|
||||
while (current != NULL){
|
||||
temp = current;
|
||||
current = current->next;
|
||||
if (temp->flags & FLAG_ALLOCATED_OPTION_FIELDS){
|
||||
free(temp->option);
|
||||
free(temp->content);
|
||||
}
|
||||
free(temp);
|
||||
}
|
||||
POPTION_ITEM current = optionsHead;
|
||||
POPTION_ITEM temp;
|
||||
while (current != NULL){
|
||||
temp = current;
|
||||
current = current->next;
|
||||
if (temp->flags & FLAG_ALLOCATED_OPTION_FIELDS){
|
||||
free(temp->option);
|
||||
free(temp->content);
|
||||
}
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Serialize the message struct into a string containing the RTSP message */
|
||||
char *serializeRtspMessage(PRTSP_MESSAGE msg, int *serializedLength){
|
||||
int size = getMessageLength(msg);
|
||||
char *serializedMessage;
|
||||
POPTION_ITEM current = msg->options;
|
||||
char statusCodeStr[16];
|
||||
int size = getMessageLength(msg);
|
||||
char *serializedMessage;
|
||||
POPTION_ITEM current = msg->options;
|
||||
char statusCodeStr[16];
|
||||
|
||||
serializedMessage = malloc(size);
|
||||
if (serializedMessage == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
serializedMessage = malloc(size);
|
||||
if (serializedMessage == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (msg->type == TYPE_REQUEST){
|
||||
/* command [space] */
|
||||
strcpy(serializedMessage, msg->message.request.command);
|
||||
strcat(serializedMessage, " ");
|
||||
/* target [space] */
|
||||
strcat(serializedMessage, msg->message.request.target);
|
||||
strcat(serializedMessage, " ");
|
||||
/* protocol \r\n */
|
||||
strcat(serializedMessage, msg->protocol);
|
||||
strcat(serializedMessage, "\r\n");
|
||||
}
|
||||
else {
|
||||
/* protocol [space] */
|
||||
strcpy(serializedMessage, msg->protocol);
|
||||
strcat(serializedMessage, " ");
|
||||
/* status code [space] */
|
||||
sprintf(statusCodeStr, "%d", msg->message.response.statusCode);
|
||||
strcat(serializedMessage, statusCodeStr);
|
||||
strcat(serializedMessage, " ");
|
||||
/* status str\r\n */
|
||||
strcat(serializedMessage, msg->message.response.statusString);
|
||||
strcat(serializedMessage, "\r\n");
|
||||
}
|
||||
/* option content\r\n */
|
||||
while (current != NULL){
|
||||
strcat(serializedMessage, current->option);
|
||||
strcat(serializedMessage, ": ");
|
||||
strcat(serializedMessage, current->content);
|
||||
strcat(serializedMessage, "\r\n");
|
||||
current = current->next;
|
||||
}
|
||||
/* Final \r\n */
|
||||
strcat(serializedMessage, "\r\n");
|
||||
if (msg->type == TYPE_REQUEST){
|
||||
/* command [space] */
|
||||
strcpy(serializedMessage, msg->message.request.command);
|
||||
strcat(serializedMessage, " ");
|
||||
/* target [space] */
|
||||
strcat(serializedMessage, msg->message.request.target);
|
||||
strcat(serializedMessage, " ");
|
||||
/* protocol \r\n */
|
||||
strcat(serializedMessage, msg->protocol);
|
||||
strcat(serializedMessage, "\r\n");
|
||||
}
|
||||
else {
|
||||
/* protocol [space] */
|
||||
strcpy(serializedMessage, msg->protocol);
|
||||
strcat(serializedMessage, " ");
|
||||
/* status code [space] */
|
||||
sprintf(statusCodeStr, "%d", msg->message.response.statusCode);
|
||||
strcat(serializedMessage, statusCodeStr);
|
||||
strcat(serializedMessage, " ");
|
||||
/* status str\r\n */
|
||||
strcat(serializedMessage, msg->message.response.statusString);
|
||||
strcat(serializedMessage, "\r\n");
|
||||
}
|
||||
/* option content\r\n */
|
||||
while (current != NULL){
|
||||
strcat(serializedMessage, current->option);
|
||||
strcat(serializedMessage, ": ");
|
||||
strcat(serializedMessage, current->content);
|
||||
strcat(serializedMessage, "\r\n");
|
||||
current = current->next;
|
||||
}
|
||||
/* Final \r\n */
|
||||
strcat(serializedMessage, "\r\n");
|
||||
|
||||
/* payload */
|
||||
if (msg->payload != NULL) {
|
||||
int offset;
|
||||
/* payload */
|
||||
if (msg->payload != NULL) {
|
||||
int offset;
|
||||
|
||||
// Find end of the RTSP message header
|
||||
for (offset = 0; serializedMessage[offset] != 0; offset++);
|
||||
// Find end of the RTSP message header
|
||||
for (offset = 0; serializedMessage[offset] != 0; offset++);
|
||||
|
||||
// Add the payload after
|
||||
memcpy(&serializedMessage[offset], msg->payload, msg->payloadLength);
|
||||
// Add the payload after
|
||||
memcpy(&serializedMessage[offset], msg->payload, msg->payloadLength);
|
||||
|
||||
*serializedLength = offset + msg->payloadLength;
|
||||
}
|
||||
else {
|
||||
*serializedLength = (int)strlen(serializedMessage);
|
||||
}
|
||||
*serializedLength = offset + msg->payloadLength;
|
||||
}
|
||||
else {
|
||||
*serializedLength = (int)strlen(serializedMessage);
|
||||
}
|
||||
|
||||
return serializedMessage;
|
||||
return serializedMessage;
|
||||
}
|
||||
|
||||
/* Free everything in a msg struct */
|
||||
void freeMessage(PRTSP_MESSAGE msg){
|
||||
/* If we've allocated the message buffer */
|
||||
if (msg->flags & FLAG_ALLOCATED_MESSAGE_BUFFER){
|
||||
free(msg->messageBuffer);
|
||||
}
|
||||
/* If we've allocated the message buffer */
|
||||
if (msg->flags & FLAG_ALLOCATED_MESSAGE_BUFFER){
|
||||
free(msg->messageBuffer);
|
||||
}
|
||||
|
||||
/* If we've allocated any option items */
|
||||
if (msg->flags & FLAG_ALLOCATED_OPTION_ITEMS){
|
||||
freeOptionList(msg->options);
|
||||
}
|
||||
/* If we've allocated any option items */
|
||||
if (msg->flags & FLAG_ALLOCATED_OPTION_ITEMS){
|
||||
freeOptionList(msg->options);
|
||||
}
|
||||
|
||||
/* If we've allocated the payload */
|
||||
if (msg->flags & FLAG_ALLOCATED_PAYLOAD) {
|
||||
free(msg->payload);
|
||||
}
|
||||
/* If we've allocated the payload */
|
||||
if (msg->flags & FLAG_ALLOCATED_PAYLOAD) {
|
||||
free(msg->payload);
|
||||
}
|
||||
}
|
@ -12,86 +12,86 @@
|
||||
#define CHANNEL_MASK_51_SURROUND 0xFC
|
||||
|
||||
typedef struct _SDP_OPTION {
|
||||
char name[MAX_OPTION_NAME_LEN+1];
|
||||
void* payload;
|
||||
int payloadLen;
|
||||
struct _SDP_OPTION *next;
|
||||
char name[MAX_OPTION_NAME_LEN+1];
|
||||
void* payload;
|
||||
int payloadLen;
|
||||
struct _SDP_OPTION *next;
|
||||
} SDP_OPTION, *PSDP_OPTION;
|
||||
|
||||
/* Cleanup the attribute list */
|
||||
static void freeAttributeList(PSDP_OPTION head) {
|
||||
PSDP_OPTION next;
|
||||
while (head != NULL) {
|
||||
next = head->next;
|
||||
free(head);
|
||||
head = next;
|
||||
}
|
||||
PSDP_OPTION next;
|
||||
while (head != NULL) {
|
||||
next = head->next;
|
||||
free(head);
|
||||
head = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the size of the attribute list */
|
||||
static int getSerializedAttributeListSize(PSDP_OPTION head) {
|
||||
PSDP_OPTION currentEntry = head;
|
||||
size_t size = 0;
|
||||
while (currentEntry != NULL) {
|
||||
size += strlen("a=");
|
||||
size += strlen(currentEntry->name);
|
||||
size += strlen(":");
|
||||
size += currentEntry->payloadLen;
|
||||
size += strlen(" \r\n");
|
||||
PSDP_OPTION currentEntry = head;
|
||||
size_t size = 0;
|
||||
while (currentEntry != NULL) {
|
||||
size += strlen("a=");
|
||||
size += strlen(currentEntry->name);
|
||||
size += strlen(":");
|
||||
size += currentEntry->payloadLen;
|
||||
size += strlen(" \r\n");
|
||||
|
||||
currentEntry = currentEntry->next;
|
||||
}
|
||||
return (int)size;
|
||||
currentEntry = currentEntry->next;
|
||||
}
|
||||
return (int)size;
|
||||
}
|
||||
|
||||
/* Populate the serialized attribute list into a string */
|
||||
static int fillSerializedAttributeList(char* buffer, PSDP_OPTION head) {
|
||||
PSDP_OPTION currentEntry = head;
|
||||
int offset = 0;
|
||||
while (currentEntry != NULL) {
|
||||
offset += sprintf(&buffer[offset], "a=%s:", currentEntry->name);
|
||||
memcpy(&buffer[offset], currentEntry->payload, currentEntry->payloadLen);
|
||||
offset += currentEntry->payloadLen;
|
||||
offset += sprintf(&buffer[offset], " \r\n");
|
||||
PSDP_OPTION currentEntry = head;
|
||||
int offset = 0;
|
||||
while (currentEntry != NULL) {
|
||||
offset += sprintf(&buffer[offset], "a=%s:", currentEntry->name);
|
||||
memcpy(&buffer[offset], currentEntry->payload, currentEntry->payloadLen);
|
||||
offset += currentEntry->payloadLen;
|
||||
offset += sprintf(&buffer[offset], " \r\n");
|
||||
|
||||
currentEntry = currentEntry->next;
|
||||
}
|
||||
return offset;
|
||||
currentEntry = currentEntry->next;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
/* Add an attribute */
|
||||
static int addAttributeBinary(PSDP_OPTION *head, char* name, const void* payload, int payloadLen) {
|
||||
PSDP_OPTION option, currentOption;
|
||||
PSDP_OPTION option, currentOption;
|
||||
|
||||
option = malloc(sizeof(*option) + payloadLen);
|
||||
if (option == NULL) {
|
||||
return -1;
|
||||
}
|
||||
option = malloc(sizeof(*option) + payloadLen);
|
||||
if (option == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
option->next = NULL;
|
||||
option->payloadLen = payloadLen;
|
||||
strcpy(option->name, name);
|
||||
option->payload = (void*)(option + 1);
|
||||
memcpy(option->payload, payload, payloadLen);
|
||||
option->next = NULL;
|
||||
option->payloadLen = payloadLen;
|
||||
strcpy(option->name, name);
|
||||
option->payload = (void*)(option + 1);
|
||||
memcpy(option->payload, payload, payloadLen);
|
||||
|
||||
if (*head == NULL) {
|
||||
*head = option;
|
||||
}
|
||||
else {
|
||||
currentOption = *head;
|
||||
while (currentOption->next != NULL) {
|
||||
currentOption = currentOption->next;
|
||||
}
|
||||
currentOption->next = option;
|
||||
}
|
||||
if (*head == NULL) {
|
||||
*head = option;
|
||||
}
|
||||
else {
|
||||
currentOption = *head;
|
||||
while (currentOption->next != NULL) {
|
||||
currentOption = currentOption->next;
|
||||
}
|
||||
currentOption->next = option;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add an attribute string */
|
||||
static int addAttributeString(PSDP_OPTION *head, char* name, const char* payload) {
|
||||
// We purposefully omit the null terminating character
|
||||
return addAttributeBinary(head, name, payload, (int)strlen(payload));
|
||||
// We purposefully omit the null terminating character
|
||||
return addAttributeBinary(head, name, payload, (int)strlen(payload));
|
||||
}
|
||||
|
||||
static int addGen3Options(PSDP_OPTION *head, char* addrStr) {
|
||||
@ -181,40 +181,40 @@ static int addGen4Options(PSDP_OPTION *head, char* addrStr) {
|
||||
}
|
||||
|
||||
static PSDP_OPTION getAttributesList(char *urlSafeAddr) {
|
||||
PSDP_OPTION optionHead;
|
||||
char payloadStr[92];
|
||||
int err;
|
||||
PSDP_OPTION optionHead;
|
||||
char payloadStr[92];
|
||||
int err;
|
||||
|
||||
optionHead = NULL;
|
||||
err = 0;
|
||||
optionHead = NULL;
|
||||
err = 0;
|
||||
|
||||
sprintf(payloadStr, "%d", StreamConfig.width);
|
||||
err |= addAttributeString(&optionHead, "x-nv-video[0].clientViewportWd", payloadStr);
|
||||
sprintf(payloadStr, "%d", StreamConfig.height);
|
||||
err |= addAttributeString(&optionHead, "x-nv-video[0].clientViewportHt", payloadStr);
|
||||
sprintf(payloadStr, "%d", StreamConfig.width);
|
||||
err |= addAttributeString(&optionHead, "x-nv-video[0].clientViewportWd", payloadStr);
|
||||
sprintf(payloadStr, "%d", StreamConfig.height);
|
||||
err |= addAttributeString(&optionHead, "x-nv-video[0].clientViewportHt", payloadStr);
|
||||
|
||||
sprintf(payloadStr, "%d", StreamConfig.fps);
|
||||
err |= addAttributeString(&optionHead, "x-nv-video[0].maxFPS", payloadStr);
|
||||
sprintf(payloadStr, "%d", StreamConfig.fps);
|
||||
err |= addAttributeString(&optionHead, "x-nv-video[0].maxFPS", payloadStr);
|
||||
|
||||
sprintf(payloadStr, "%d", StreamConfig.packetSize);
|
||||
err |= addAttributeString(&optionHead, "x-nv-video[0].packetSize", payloadStr);
|
||||
|
||||
err |= addAttributeString(&optionHead, "x-nv-video[0].rateControlMode", "4");
|
||||
err |= addAttributeString(&optionHead, "x-nv-video[0].rateControlMode", "4");
|
||||
|
||||
if (StreamConfig.streamingRemotely) {
|
||||
err |= addAttributeString(&optionHead, "x-nv-video[0].averageBitrate", "4");
|
||||
err |= addAttributeString(&optionHead, "x-nv-video[0].peakBitrate", "4");
|
||||
}
|
||||
|
||||
err |= addAttributeString(&optionHead, "x-nv-video[0].timeoutLengthMs", "7000");
|
||||
err |= addAttributeString(&optionHead, "x-nv-video[0].framesWithInvalidRefThreshold", "0");
|
||||
err |= addAttributeString(&optionHead, "x-nv-video[0].timeoutLengthMs", "7000");
|
||||
err |= addAttributeString(&optionHead, "x-nv-video[0].framesWithInvalidRefThreshold", "0");
|
||||
|
||||
// We don't support dynamic bitrate scaling properly (it tends to bounce between min and max and never
|
||||
// settle on the optimal bitrate if it's somewhere in the middle), so we'll just latch the bitrate
|
||||
// to the requested value.
|
||||
sprintf(payloadStr, "%d", StreamConfig.bitrate);
|
||||
sprintf(payloadStr, "%d", StreamConfig.bitrate);
|
||||
err |= addAttributeString(&optionHead, "x-nv-vqos[0].bw.minimumBitrate", payloadStr);
|
||||
err |= addAttributeString(&optionHead, "x-nv-vqos[0].bw.maximumBitrate", payloadStr);
|
||||
err |= addAttributeString(&optionHead, "x-nv-vqos[0].bw.maximumBitrate", payloadStr);
|
||||
|
||||
// Using FEC turns padding on which makes us have to take the slow path
|
||||
// in the depacketizer, not to mention exposing some ambiguous cases with
|
||||
@ -222,7 +222,7 @@ static PSDP_OPTION getAttributesList(char *urlSafeAddr) {
|
||||
// execute an FEC recovery on a 1 packet frame, we'll just turn it off completely.
|
||||
err |= addAttributeString(&optionHead, "x-nv-vqos[0].fec.enable", "0");
|
||||
|
||||
err |= addAttributeString(&optionHead, "x-nv-vqos[0].videoQualityScoreUpdateTime", "5000");
|
||||
err |= addAttributeString(&optionHead, "x-nv-vqos[0].videoQualityScoreUpdateTime", "5000");
|
||||
|
||||
if (StreamConfig.streamingRemotely) {
|
||||
err |= addAttributeString(&optionHead, "x-nv-vqos[0].qosTrafficType", "0");
|
||||
@ -239,20 +239,20 @@ static PSDP_OPTION getAttributesList(char *urlSafeAddr) {
|
||||
err |= addGen4Options(&optionHead, urlSafeAddr);
|
||||
}
|
||||
|
||||
if (err == 0) {
|
||||
return optionHead;
|
||||
}
|
||||
if (err == 0) {
|
||||
return optionHead;
|
||||
}
|
||||
|
||||
freeAttributeList(optionHead);
|
||||
return NULL;
|
||||
freeAttributeList(optionHead);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Populate the SDP header with required information */
|
||||
static int fillSdpHeader(char* buffer, int rtspClientVersion, char *urlSafeAddr) {
|
||||
return sprintf(buffer,
|
||||
"v=0\r\n"
|
||||
"o=android 0 %d IN %s %s\r\n"
|
||||
"s=NVIDIA Streaming Client\r\n",
|
||||
return sprintf(buffer,
|
||||
"v=0\r\n"
|
||||
"o=android 0 %d IN %s %s\r\n"
|
||||
"s=NVIDIA Streaming Client\r\n",
|
||||
rtspClientVersion,
|
||||
RemoteAddr.ss_family == AF_INET ? "IPv4" : "IPv6",
|
||||
urlSafeAddr);
|
||||
@ -260,38 +260,38 @@ static int fillSdpHeader(char* buffer, int rtspClientVersion, char *urlSafeAddr)
|
||||
|
||||
/* Populate the SDP tail with required information */
|
||||
static int fillSdpTail(char* buffer) {
|
||||
return sprintf(buffer,
|
||||
"t=0 0\r\n"
|
||||
"m=video %d \r\n",
|
||||
return sprintf(buffer,
|
||||
"t=0 0\r\n"
|
||||
"m=video %d \r\n",
|
||||
ServerMajorVersion < 4 ? 47996 : 47998);
|
||||
}
|
||||
|
||||
/* Get the SDP attributes for the stream config */
|
||||
char* getSdpPayloadForStreamConfig(int rtspClientVersion, int *length) {
|
||||
PSDP_OPTION attributeList;
|
||||
int offset;
|
||||
char* payload;
|
||||
PSDP_OPTION attributeList;
|
||||
int offset;
|
||||
char* payload;
|
||||
char urlSafeAddr[URLSAFESTRING_LEN];
|
||||
|
||||
addrToUrlSafeString(&RemoteAddr, urlSafeAddr);
|
||||
|
||||
attributeList = getAttributesList(urlSafeAddr);
|
||||
if (attributeList == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
attributeList = getAttributesList(urlSafeAddr);
|
||||
if (attributeList == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
payload = malloc(MAX_SDP_HEADER_LEN + MAX_SDP_TAIL_LEN +
|
||||
getSerializedAttributeListSize(attributeList));
|
||||
if (payload == NULL) {
|
||||
freeAttributeList(attributeList);
|
||||
return NULL;
|
||||
}
|
||||
payload = malloc(MAX_SDP_HEADER_LEN + MAX_SDP_TAIL_LEN +
|
||||
getSerializedAttributeListSize(attributeList));
|
||||
if (payload == NULL) {
|
||||
freeAttributeList(attributeList);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
offset = fillSdpHeader(payload, rtspClientVersion, urlSafeAddr);
|
||||
offset += fillSerializedAttributeList(&payload[offset], attributeList);
|
||||
offset += fillSdpTail(&payload[offset]);
|
||||
offset = fillSdpHeader(payload, rtspClientVersion, urlSafeAddr);
|
||||
offset += fillSerializedAttributeList(&payload[offset], attributeList);
|
||||
offset += fillSdpTail(&payload[offset]);
|
||||
|
||||
freeAttributeList(attributeList);
|
||||
*length = offset;
|
||||
return payload;
|
||||
freeAttributeList(attributeList);
|
||||
*length = offset;
|
||||
return payload;
|
||||
}
|
@ -3,8 +3,8 @@
|
||||
#include "LinkedBlockingQueue.h"
|
||||
|
||||
typedef struct _QUEUED_DECODE_UNIT {
|
||||
DECODE_UNIT decodeUnit;
|
||||
LINKED_BLOCKING_QUEUE_ENTRY entry;
|
||||
DECODE_UNIT decodeUnit;
|
||||
LINKED_BLOCKING_QUEUE_ENTRY entry;
|
||||
} QUEUED_DECODE_UNIT, *PQUEUED_DECODE_UNIT;
|
||||
|
||||
void freeQueuedDecodeUnit(PQUEUED_DECODE_UNIT qdu);
|
||||
@ -17,11 +17,11 @@ int getNextQueuedDecodeUnit(PQUEUED_DECODE_UNIT *qdu);
|
||||
#define FLAG_SOF 0x4
|
||||
|
||||
typedef struct _NV_VIDEO_PACKET {
|
||||
int streamPacketIndex;
|
||||
int frameIndex;
|
||||
char flags;
|
||||
char reserved[3];
|
||||
int reserved2;
|
||||
int streamPacketIndex;
|
||||
int frameIndex;
|
||||
char flags;
|
||||
char reserved[3];
|
||||
int reserved2;
|
||||
} NV_VIDEO_PACKET, *PNV_VIDEO_PACKET;
|
||||
|
||||
#define FLAG_EXTENSION 0x10
|
||||
@ -30,10 +30,10 @@ typedef struct _NV_VIDEO_PACKET {
|
||||
#define MAX_RTP_HEADER_SIZE 16
|
||||
|
||||
typedef struct _RTP_PACKET {
|
||||
char header;
|
||||
char packetType;
|
||||
unsigned short sequenceNumber;
|
||||
char reserved[8];
|
||||
char header;
|
||||
char packetType;
|
||||
unsigned short sequenceNumber;
|
||||
char reserved[8];
|
||||
} RTP_PACKET, *PRTP_PACKET;
|
||||
|
||||
#pragma pack(pop)
|
@ -23,40 +23,40 @@ static LINKED_BLOCKING_QUEUE decodeUnitQueue;
|
||||
static unsigned int nominalPacketDataLength;
|
||||
|
||||
typedef struct _BUFFER_DESC {
|
||||
char* data;
|
||||
unsigned int offset;
|
||||
unsigned int length;
|
||||
char* data;
|
||||
unsigned int offset;
|
||||
unsigned int length;
|
||||
} BUFFER_DESC, *PBUFFER_DESC;
|
||||
|
||||
/* Init */
|
||||
void initializeVideoDepacketizer(int pktSize) {
|
||||
if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
LbqInitializeLinkedBlockingQueue(&decodeUnitQueue, 15);
|
||||
}
|
||||
nominalPacketDataLength = pktSize - sizeof(NV_VIDEO_PACKET);
|
||||
if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
LbqInitializeLinkedBlockingQueue(&decodeUnitQueue, 15);
|
||||
}
|
||||
nominalPacketDataLength = pktSize - sizeof(NV_VIDEO_PACKET);
|
||||
|
||||
nextFrameNumber = 1;
|
||||
nextPacketNumber = 0;
|
||||
startFrameNumber = 0;
|
||||
waitingForNextSuccessfulFrame = 0;
|
||||
waitingForIdrFrame = 1;
|
||||
gotNextFrameStart = 0;
|
||||
lastPacketInStream = -1;
|
||||
decodingFrame = 0;
|
||||
nextFrameNumber = 1;
|
||||
nextPacketNumber = 0;
|
||||
startFrameNumber = 0;
|
||||
waitingForNextSuccessfulFrame = 0;
|
||||
waitingForIdrFrame = 1;
|
||||
gotNextFrameStart = 0;
|
||||
lastPacketInStream = -1;
|
||||
decodingFrame = 0;
|
||||
strictIdrFrameWait = !(VideoCallbacks.capabilities & CAPABILITY_REFERENCE_FRAME_INVALIDATION);
|
||||
}
|
||||
|
||||
/* Free malloced memory in AvcFrameState*/
|
||||
static void cleanupAvcFrameState(void) {
|
||||
PLENTRY lastEntry;
|
||||
PLENTRY lastEntry;
|
||||
|
||||
while (nalChainHead != NULL) {
|
||||
lastEntry = nalChainHead;
|
||||
nalChainHead = lastEntry->next;
|
||||
free(lastEntry);
|
||||
}
|
||||
while (nalChainHead != NULL) {
|
||||
lastEntry = nalChainHead;
|
||||
nalChainHead = lastEntry->next;
|
||||
free(lastEntry);
|
||||
}
|
||||
|
||||
nalChainDataLength = 0;
|
||||
nalChainDataLength = 0;
|
||||
}
|
||||
|
||||
/* Cleanup AVC frame state and set that we're waiting for an IDR Frame*/
|
||||
@ -81,419 +81,419 @@ static void dropAvcFrameState(void) {
|
||||
requestIdrOnDemand();
|
||||
}
|
||||
|
||||
cleanupAvcFrameState();
|
||||
cleanupAvcFrameState();
|
||||
}
|
||||
|
||||
/* Cleanup the list of decode units */
|
||||
static void freeDecodeUnitList(PLINKED_BLOCKING_QUEUE_ENTRY entry) {
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY nextEntry;
|
||||
PLINKED_BLOCKING_QUEUE_ENTRY nextEntry;
|
||||
|
||||
while (entry != NULL) {
|
||||
nextEntry = entry->flink;
|
||||
while (entry != NULL) {
|
||||
nextEntry = entry->flink;
|
||||
|
||||
freeQueuedDecodeUnit((PQUEUED_DECODE_UNIT) entry->data);
|
||||
freeQueuedDecodeUnit((PQUEUED_DECODE_UNIT) entry->data);
|
||||
|
||||
entry = nextEntry;
|
||||
}
|
||||
entry = nextEntry;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleanup video depacketizer and free malloced memory */
|
||||
void destroyVideoDepacketizer(void) {
|
||||
if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
freeDecodeUnitList(LbqDestroyLinkedBlockingQueue(&decodeUnitQueue));
|
||||
}
|
||||
if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
freeDecodeUnitList(LbqDestroyLinkedBlockingQueue(&decodeUnitQueue));
|
||||
}
|
||||
|
||||
cleanupAvcFrameState();
|
||||
cleanupAvcFrameState();
|
||||
}
|
||||
|
||||
/* Returns 1 if candidate is a frame start and 0 otherwise */
|
||||
static int isSeqFrameStart(PBUFFER_DESC candidate) {
|
||||
return (candidate->length == 4 && candidate->data[candidate->offset + candidate->length - 1] == 1);
|
||||
return (candidate->length == 4 && candidate->data[candidate->offset + candidate->length - 1] == 1);
|
||||
}
|
||||
|
||||
/* Returns 1 if candidate an AVC start and 0 otherwise */
|
||||
static int isSeqAvcStart(PBUFFER_DESC candidate) {
|
||||
return (candidate->data[candidate->offset + candidate->length - 1] == 1);
|
||||
return (candidate->data[candidate->offset + candidate->length - 1] == 1);
|
||||
}
|
||||
|
||||
/* Returns 1 if candidate is padding and 0 otherwise */
|
||||
static int isSeqPadding(PBUFFER_DESC candidate) {
|
||||
return (candidate->data[candidate->offset + candidate->length - 1] == 0);
|
||||
return (candidate->data[candidate->offset + candidate->length - 1] == 0);
|
||||
}
|
||||
|
||||
/* Returns 1 on success, 0 otherwise */
|
||||
static int getSpecialSeq(PBUFFER_DESC current, PBUFFER_DESC candidate) {
|
||||
if (current->length < 3) {
|
||||
return 0;
|
||||
}
|
||||
if (current->length < 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (current->data[current->offset] == 0 &&
|
||||
current->data[current->offset + 1] == 0) {
|
||||
// Padding or frame start
|
||||
if (current->data[current->offset + 2] == 0) {
|
||||
if (current->length >= 4 && current->data[current->offset + 3] == 1) {
|
||||
// Frame start
|
||||
candidate->data = current->data;
|
||||
candidate->offset = current->offset;
|
||||
candidate->length = 4;
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
// Padding
|
||||
candidate->data = current->data;
|
||||
candidate->offset = current->offset;
|
||||
candidate->length = 3;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (current->data[current->offset + 2] == 1) {
|
||||
// NAL start
|
||||
candidate->data = current->data;
|
||||
candidate->offset = current->offset;
|
||||
candidate->length = 3;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (current->data[current->offset] == 0 &&
|
||||
current->data[current->offset + 1] == 0) {
|
||||
// Padding or frame start
|
||||
if (current->data[current->offset + 2] == 0) {
|
||||
if (current->length >= 4 && current->data[current->offset + 3] == 1) {
|
||||
// Frame start
|
||||
candidate->data = current->data;
|
||||
candidate->offset = current->offset;
|
||||
candidate->length = 4;
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
// Padding
|
||||
candidate->data = current->data;
|
||||
candidate->offset = current->offset;
|
||||
candidate->length = 3;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (current->data[current->offset + 2] == 1) {
|
||||
// NAL start
|
||||
candidate->data = current->data;
|
||||
candidate->offset = current->offset;
|
||||
candidate->length = 3;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the first decode unit available */
|
||||
int getNextQueuedDecodeUnit(PQUEUED_DECODE_UNIT *qdu) {
|
||||
int err = LbqWaitForQueueElement(&decodeUnitQueue, (void**) qdu);
|
||||
if (err == LBQ_SUCCESS) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
int err = LbqWaitForQueueElement(&decodeUnitQueue, (void**) qdu);
|
||||
if (err == LBQ_SUCCESS) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleanup a decode unit by freeing the buffer chain and the holder */
|
||||
void freeQueuedDecodeUnit(PQUEUED_DECODE_UNIT qdu) {
|
||||
PLENTRY lastEntry;
|
||||
PLENTRY lastEntry;
|
||||
|
||||
while (qdu->decodeUnit.bufferList != NULL) {
|
||||
lastEntry = qdu->decodeUnit.bufferList;
|
||||
qdu->decodeUnit.bufferList = lastEntry->next;
|
||||
free(lastEntry);
|
||||
}
|
||||
while (qdu->decodeUnit.bufferList != NULL) {
|
||||
lastEntry = qdu->decodeUnit.bufferList;
|
||||
qdu->decodeUnit.bufferList = lastEntry->next;
|
||||
free(lastEntry);
|
||||
}
|
||||
|
||||
free(qdu);
|
||||
free(qdu);
|
||||
}
|
||||
|
||||
/* Reassemble the frame with the given frame number */
|
||||
static void reassembleAvcFrame(int frameNumber) {
|
||||
if (nalChainHead != NULL) {
|
||||
PQUEUED_DECODE_UNIT qdu = (PQUEUED_DECODE_UNIT) malloc(sizeof(*qdu));
|
||||
if (qdu != NULL) {
|
||||
qdu->decodeUnit.bufferList = nalChainHead;
|
||||
qdu->decodeUnit.fullLength = nalChainDataLength;
|
||||
if (nalChainHead != NULL) {
|
||||
PQUEUED_DECODE_UNIT qdu = (PQUEUED_DECODE_UNIT) malloc(sizeof(*qdu));
|
||||
if (qdu != NULL) {
|
||||
qdu->decodeUnit.bufferList = nalChainHead;
|
||||
qdu->decodeUnit.fullLength = nalChainDataLength;
|
||||
|
||||
nalChainHead = NULL;
|
||||
nalChainDataLength = 0;
|
||||
nalChainHead = NULL;
|
||||
nalChainDataLength = 0;
|
||||
|
||||
if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
if (LbqOfferQueueItem(&decodeUnitQueue, qdu, &qdu->entry) == LBQ_BOUND_EXCEEDED) {
|
||||
Limelog("Video decode unit queue overflow\n");
|
||||
if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
if (LbqOfferQueueItem(&decodeUnitQueue, qdu, &qdu->entry) == LBQ_BOUND_EXCEEDED) {
|
||||
Limelog("Video decode unit queue overflow\n");
|
||||
|
||||
// Clear frame state and wait for an IDR
|
||||
nalChainHead = qdu->decodeUnit.bufferList;
|
||||
nalChainDataLength = qdu->decodeUnit.fullLength;
|
||||
dropAvcFrameState();
|
||||
// Clear frame state and wait for an IDR
|
||||
nalChainHead = qdu->decodeUnit.bufferList;
|
||||
nalChainDataLength = qdu->decodeUnit.fullLength;
|
||||
dropAvcFrameState();
|
||||
|
||||
// Free the DU
|
||||
free(qdu);
|
||||
// Free the DU
|
||||
free(qdu);
|
||||
|
||||
// Flush the decode unit queue
|
||||
freeDecodeUnitList(LbqFlushQueueItems(&decodeUnitQueue));
|
||||
// Flush the decode unit queue
|
||||
freeDecodeUnitList(LbqFlushQueueItems(&decodeUnitQueue));
|
||||
|
||||
// FIXME: Get proper lower bound
|
||||
connectionSinkTooSlow(0, frameNumber);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
int ret = VideoCallbacks.submitDecodeUnit(&qdu->decodeUnit);
|
||||
// FIXME: Get proper lower bound
|
||||
connectionSinkTooSlow(0, frameNumber);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
int ret = VideoCallbacks.submitDecodeUnit(&qdu->decodeUnit);
|
||||
|
||||
freeQueuedDecodeUnit(qdu);
|
||||
freeQueuedDecodeUnit(qdu);
|
||||
|
||||
if (ret == DR_NEED_IDR) {
|
||||
Limelog("Request IDR frame on behalf of DR\n");
|
||||
requestIdrOnDemand();
|
||||
}
|
||||
}
|
||||
if (ret == DR_NEED_IDR) {
|
||||
Limelog("Request IDR frame on behalf of DR\n");
|
||||
requestIdrOnDemand();
|
||||
}
|
||||
}
|
||||
|
||||
// Notify the control connection
|
||||
connectionReceivedFrame(frameNumber);
|
||||
// Notify the control connection
|
||||
connectionReceivedFrame(frameNumber);
|
||||
|
||||
// Clear frame drops
|
||||
consecutiveFrameDrops = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void queueFragment(char *data, int offset, int length) {
|
||||
PLENTRY entry = (PLENTRY) malloc(sizeof(*entry) + length);
|
||||
if (entry != NULL) {
|
||||
entry->next = NULL;
|
||||
entry->length = length;
|
||||
entry->data = (char*) (entry + 1);
|
||||
PLENTRY entry = (PLENTRY) malloc(sizeof(*entry) + length);
|
||||
if (entry != NULL) {
|
||||
entry->next = NULL;
|
||||
entry->length = length;
|
||||
entry->data = (char*) (entry + 1);
|
||||
|
||||
memcpy(entry->data, &data[offset], entry->length);
|
||||
memcpy(entry->data, &data[offset], entry->length);
|
||||
|
||||
nalChainDataLength += entry->length;
|
||||
nalChainDataLength += entry->length;
|
||||
|
||||
if (nalChainHead == NULL) {
|
||||
nalChainHead = entry;
|
||||
}
|
||||
else {
|
||||
PLENTRY currentEntry = nalChainHead;
|
||||
if (nalChainHead == NULL) {
|
||||
nalChainHead = entry;
|
||||
}
|
||||
else {
|
||||
PLENTRY currentEntry = nalChainHead;
|
||||
|
||||
while (currentEntry->next != NULL) {
|
||||
currentEntry = currentEntry->next;
|
||||
}
|
||||
while (currentEntry->next != NULL) {
|
||||
currentEntry = currentEntry->next;
|
||||
}
|
||||
|
||||
currentEntry->next = entry;
|
||||
}
|
||||
}
|
||||
currentEntry->next = entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Process an RTP Payload */
|
||||
static void processRtpPayloadSlow(PNV_VIDEO_PACKET videoPacket, PBUFFER_DESC currentPos) {
|
||||
BUFFER_DESC specialSeq;
|
||||
int decodingAvc = 0;
|
||||
BUFFER_DESC specialSeq;
|
||||
int decodingAvc = 0;
|
||||
|
||||
while (currentPos->length != 0) {
|
||||
int start = currentPos->offset;
|
||||
while (currentPos->length != 0) {
|
||||
int start = currentPos->offset;
|
||||
|
||||
if (getSpecialSeq(currentPos, &specialSeq)) {
|
||||
if (isSeqAvcStart(&specialSeq)) {
|
||||
// Now we're decoding AVC
|
||||
decodingAvc = 1;
|
||||
if (getSpecialSeq(currentPos, &specialSeq)) {
|
||||
if (isSeqAvcStart(&specialSeq)) {
|
||||
// Now we're decoding AVC
|
||||
decodingAvc = 1;
|
||||
|
||||
if (isSeqFrameStart(&specialSeq)) {
|
||||
// Now we're working on a frame
|
||||
decodingFrame = 1;
|
||||
if (isSeqFrameStart(&specialSeq)) {
|
||||
// Now we're working on a frame
|
||||
decodingFrame = 1;
|
||||
|
||||
// Reassemble any pending frame
|
||||
reassembleAvcFrame(videoPacket->frameIndex);
|
||||
// Reassemble any pending frame
|
||||
reassembleAvcFrame(videoPacket->frameIndex);
|
||||
|
||||
if (specialSeq.data[specialSeq.offset + specialSeq.length] == 0x65) {
|
||||
// This is the NALU code for I-frame data
|
||||
waitingForIdrFrame = 0;
|
||||
}
|
||||
}
|
||||
if (specialSeq.data[specialSeq.offset + specialSeq.length] == 0x65) {
|
||||
// This is the NALU code for I-frame data
|
||||
waitingForIdrFrame = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip the start sequence
|
||||
currentPos->length -= specialSeq.length;
|
||||
currentPos->offset += specialSeq.length;
|
||||
}
|
||||
else {
|
||||
// Check if this is padding after a full AVC frame
|
||||
if (decodingAvc && isSeqPadding(currentPos)) {
|
||||
reassembleAvcFrame(videoPacket->frameIndex);
|
||||
}
|
||||
// Skip the start sequence
|
||||
currentPos->length -= specialSeq.length;
|
||||
currentPos->offset += specialSeq.length;
|
||||
}
|
||||
else {
|
||||
// Check if this is padding after a full AVC frame
|
||||
if (decodingAvc && isSeqPadding(currentPos)) {
|
||||
reassembleAvcFrame(videoPacket->frameIndex);
|
||||
}
|
||||
|
||||
// Not decoding AVC
|
||||
decodingAvc = 0;
|
||||
// Not decoding AVC
|
||||
decodingAvc = 0;
|
||||
|
||||
// Just skip this byte
|
||||
currentPos->length--;
|
||||
currentPos->offset++;
|
||||
}
|
||||
}
|
||||
// Just skip this byte
|
||||
currentPos->length--;
|
||||
currentPos->offset++;
|
||||
}
|
||||
}
|
||||
|
||||
// Move to the next special sequence
|
||||
while (currentPos->length != 0) {
|
||||
// Check if this should end the current NAL
|
||||
if (getSpecialSeq(currentPos, &specialSeq)) {
|
||||
if (decodingAvc || !isSeqPadding(&specialSeq)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Move to the next special sequence
|
||||
while (currentPos->length != 0) {
|
||||
// Check if this should end the current NAL
|
||||
if (getSpecialSeq(currentPos, &specialSeq)) {
|
||||
if (decodingAvc || !isSeqPadding(&specialSeq)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// This byte is part of the NAL data
|
||||
currentPos->offset++;
|
||||
currentPos->length--;
|
||||
}
|
||||
// This byte is part of the NAL data
|
||||
currentPos->offset++;
|
||||
currentPos->length--;
|
||||
}
|
||||
|
||||
if (decodingAvc) {
|
||||
queueFragment(currentPos->data, start, currentPos->offset - start);
|
||||
}
|
||||
}
|
||||
if (decodingAvc) {
|
||||
queueFragment(currentPos->data, start, currentPos->offset - start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return 1 if packet is the first one in the frame */
|
||||
static int isFirstPacket(char flags) {
|
||||
// Clear the picture data flag
|
||||
flags &= ~FLAG_CONTAINS_PIC_DATA;
|
||||
// Clear the picture data flag
|
||||
flags &= ~FLAG_CONTAINS_PIC_DATA;
|
||||
|
||||
// Check if it's just the start or both start and end of a frame
|
||||
return (flags == (FLAG_SOF | FLAG_EOF) ||
|
||||
flags == FLAG_SOF);
|
||||
// Check if it's just the start or both start and end of a frame
|
||||
return (flags == (FLAG_SOF | FLAG_EOF) ||
|
||||
flags == FLAG_SOF);
|
||||
}
|
||||
|
||||
/* Adds a fragment directly to the queue */
|
||||
static void processRtpPayloadFast(BUFFER_DESC location) {
|
||||
queueFragment(location.data, location.offset, location.length);
|
||||
queueFragment(location.data, location.offset, location.length);
|
||||
}
|
||||
|
||||
/* Process an RTP Payload */
|
||||
void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length) {
|
||||
BUFFER_DESC currentPos, specialSeq;
|
||||
int frameIndex;
|
||||
char flags;
|
||||
int firstPacket;
|
||||
int streamPacketIndex;
|
||||
BUFFER_DESC currentPos, specialSeq;
|
||||
int frameIndex;
|
||||
char flags;
|
||||
int firstPacket;
|
||||
int streamPacketIndex;
|
||||
|
||||
// Mask the top 8 bits from the SPI
|
||||
videoPacket->streamPacketIndex >>= 8;
|
||||
videoPacket->streamPacketIndex &= 0xFFFFFF;
|
||||
// Mask the top 8 bits from the SPI
|
||||
videoPacket->streamPacketIndex >>= 8;
|
||||
videoPacket->streamPacketIndex &= 0xFFFFFF;
|
||||
|
||||
currentPos.data = (char*) (videoPacket + 1);
|
||||
currentPos.offset = 0;
|
||||
currentPos.length = length - sizeof(*videoPacket);
|
||||
currentPos.data = (char*) (videoPacket + 1);
|
||||
currentPos.offset = 0;
|
||||
currentPos.length = length - sizeof(*videoPacket);
|
||||
|
||||
frameIndex = videoPacket->frameIndex;
|
||||
flags = videoPacket->flags;
|
||||
firstPacket = isFirstPacket(flags);
|
||||
frameIndex = videoPacket->frameIndex;
|
||||
flags = videoPacket->flags;
|
||||
firstPacket = isFirstPacket(flags);
|
||||
|
||||
// Drop duplicates or re-ordered packets
|
||||
streamPacketIndex = videoPacket->streamPacketIndex;
|
||||
if (isBeforeSignedInt((short) streamPacketIndex, (short) (lastPacketInStream + 1), 0)) {
|
||||
return;
|
||||
}
|
||||
// Drop duplicates or re-ordered packets
|
||||
streamPacketIndex = videoPacket->streamPacketIndex;
|
||||
if (isBeforeSignedInt((short) streamPacketIndex, (short) (lastPacketInStream + 1), 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Drop packets from a previously completed frame
|
||||
if (isBeforeSignedInt(frameIndex, nextFrameNumber, 0)) {
|
||||
return;
|
||||
}
|
||||
// Drop packets from a previously completed frame
|
||||
if (isBeforeSignedInt(frameIndex, nextFrameNumber, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for a frame start before receiving a frame end
|
||||
if (firstPacket && decodingFrame)
|
||||
{
|
||||
Limelog("Network dropped end of a frame\n");
|
||||
nextFrameNumber = frameIndex;
|
||||
// Look for a frame start before receiving a frame end
|
||||
if (firstPacket && decodingFrame)
|
||||
{
|
||||
Limelog("Network dropped end of a frame\n");
|
||||
nextFrameNumber = frameIndex;
|
||||
|
||||
// Unexpected start of next frame before terminating the last
|
||||
waitingForNextSuccessfulFrame = 1;
|
||||
dropAvcFrameState();
|
||||
}
|
||||
// Look for a non-frame start before a frame start
|
||||
else if (!firstPacket && !decodingFrame) {
|
||||
// Check if this looks like a real frame
|
||||
if (flags == FLAG_CONTAINS_PIC_DATA ||
|
||||
flags == FLAG_EOF ||
|
||||
currentPos.length < nominalPacketDataLength)
|
||||
{
|
||||
Limelog("Network dropped beginning of a frame\n");
|
||||
nextFrameNumber = frameIndex + 1;
|
||||
// Unexpected start of next frame before terminating the last
|
||||
waitingForNextSuccessfulFrame = 1;
|
||||
dropAvcFrameState();
|
||||
}
|
||||
// Look for a non-frame start before a frame start
|
||||
else if (!firstPacket && !decodingFrame) {
|
||||
// Check if this looks like a real frame
|
||||
if (flags == FLAG_CONTAINS_PIC_DATA ||
|
||||
flags == FLAG_EOF ||
|
||||
currentPos.length < nominalPacketDataLength)
|
||||
{
|
||||
Limelog("Network dropped beginning of a frame\n");
|
||||
nextFrameNumber = frameIndex + 1;
|
||||
|
||||
waitingForNextSuccessfulFrame = 1;
|
||||
waitingForNextSuccessfulFrame = 1;
|
||||
|
||||
dropAvcFrameState();
|
||||
decodingFrame = 0;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// FEC data
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Check sequencing of this frame to ensure we didn't
|
||||
// miss one in between
|
||||
else if (firstPacket) {
|
||||
// Make sure this is the next consecutive frame
|
||||
if (isBeforeSignedInt(nextFrameNumber, frameIndex, 1)) {
|
||||
Limelog("Network dropped an entire frame\n");
|
||||
nextFrameNumber = frameIndex;
|
||||
dropAvcFrameState();
|
||||
decodingFrame = 0;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// FEC data
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Check sequencing of this frame to ensure we didn't
|
||||
// miss one in between
|
||||
else if (firstPacket) {
|
||||
// Make sure this is the next consecutive frame
|
||||
if (isBeforeSignedInt(nextFrameNumber, frameIndex, 1)) {
|
||||
Limelog("Network dropped an entire frame\n");
|
||||
nextFrameNumber = frameIndex;
|
||||
|
||||
// Wait until next complete frame
|
||||
waitingForNextSuccessfulFrame = 1;
|
||||
dropAvcFrameState();
|
||||
}
|
||||
else if (nextFrameNumber != frameIndex) {
|
||||
// Duplicate packet or FEC dup
|
||||
decodingFrame = 0;
|
||||
return;
|
||||
}
|
||||
// Wait until next complete frame
|
||||
waitingForNextSuccessfulFrame = 1;
|
||||
dropAvcFrameState();
|
||||
}
|
||||
else if (nextFrameNumber != frameIndex) {
|
||||
// Duplicate packet or FEC dup
|
||||
decodingFrame = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// We're now decoding a frame
|
||||
decodingFrame = 1;
|
||||
}
|
||||
// We're now decoding a frame
|
||||
decodingFrame = 1;
|
||||
}
|
||||
|
||||
// If it's not the first packet of a frame
|
||||
// we need to drop it if the stream packet index
|
||||
// doesn't match
|
||||
if (!firstPacket && decodingFrame) {
|
||||
if (streamPacketIndex != (int) (lastPacketInStream + 1)) {
|
||||
Limelog("Network dropped middle of a frame\n");
|
||||
nextFrameNumber = frameIndex + 1;
|
||||
// If it's not the first packet of a frame
|
||||
// we need to drop it if the stream packet index
|
||||
// doesn't match
|
||||
if (!firstPacket && decodingFrame) {
|
||||
if (streamPacketIndex != (int) (lastPacketInStream + 1)) {
|
||||
Limelog("Network dropped middle of a frame\n");
|
||||
nextFrameNumber = frameIndex + 1;
|
||||
|
||||
waitingForNextSuccessfulFrame = 1;
|
||||
waitingForNextSuccessfulFrame = 1;
|
||||
|
||||
dropAvcFrameState();
|
||||
decodingFrame = 0;
|
||||
dropAvcFrameState();
|
||||
decodingFrame = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Notify the server of any packet losses
|
||||
if (streamPacketIndex != (int) (lastPacketInStream + 1)) {
|
||||
// Packets were lost so report this to the server
|
||||
connectionLostPackets(lastPacketInStream, streamPacketIndex);
|
||||
}
|
||||
lastPacketInStream = streamPacketIndex;
|
||||
// Notify the server of any packet losses
|
||||
if (streamPacketIndex != (int) (lastPacketInStream + 1)) {
|
||||
// Packets were lost so report this to the server
|
||||
connectionLostPackets(lastPacketInStream, streamPacketIndex);
|
||||
}
|
||||
lastPacketInStream = streamPacketIndex;
|
||||
|
||||
if (firstPacket &&
|
||||
getSpecialSeq(¤tPos, &specialSeq) &&
|
||||
isSeqFrameStart(&specialSeq) &&
|
||||
specialSeq.data[specialSeq.offset + specialSeq.length] == 0x67)
|
||||
{
|
||||
// SPS and PPS prefix is padded between NALs, so we must decode it with the slow path
|
||||
processRtpPayloadSlow(videoPacket, ¤tPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
processRtpPayloadFast(currentPos);
|
||||
}
|
||||
if (firstPacket &&
|
||||
getSpecialSeq(¤tPos, &specialSeq) &&
|
||||
isSeqFrameStart(&specialSeq) &&
|
||||
specialSeq.data[specialSeq.offset + specialSeq.length] == 0x67)
|
||||
{
|
||||
// SPS and PPS prefix is padded between NALs, so we must decode it with the slow path
|
||||
processRtpPayloadSlow(videoPacket, ¤tPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
processRtpPayloadFast(currentPos);
|
||||
}
|
||||
|
||||
if (flags & FLAG_EOF) {
|
||||
// Move on to the next frame
|
||||
decodingFrame = 0;
|
||||
nextFrameNumber = frameIndex + 1;
|
||||
if (flags & FLAG_EOF) {
|
||||
// Move on to the next frame
|
||||
decodingFrame = 0;
|
||||
nextFrameNumber = frameIndex + 1;
|
||||
|
||||
// If waiting for next successful frame and we got here
|
||||
// with an end flag, we can send a message to the server
|
||||
if (waitingForNextSuccessfulFrame) {
|
||||
// This is the next successful frame after a loss event
|
||||
connectionDetectedFrameLoss(startFrameNumber, nextFrameNumber - 1);
|
||||
waitingForNextSuccessfulFrame = 0;
|
||||
}
|
||||
// If waiting for next successful frame and we got here
|
||||
// with an end flag, we can send a message to the server
|
||||
if (waitingForNextSuccessfulFrame) {
|
||||
// This is the next successful frame after a loss event
|
||||
connectionDetectedFrameLoss(startFrameNumber, nextFrameNumber - 1);
|
||||
waitingForNextSuccessfulFrame = 0;
|
||||
}
|
||||
|
||||
// If we need an IDR frame first, then drop this frame
|
||||
if (waitingForIdrFrame) {
|
||||
Limelog("Waiting for IDR frame\n");
|
||||
// If we need an IDR frame first, then drop this frame
|
||||
if (waitingForIdrFrame) {
|
||||
Limelog("Waiting for IDR frame\n");
|
||||
|
||||
dropAvcFrameState();
|
||||
return;
|
||||
}
|
||||
dropAvcFrameState();
|
||||
return;
|
||||
}
|
||||
|
||||
reassembleAvcFrame(frameIndex);
|
||||
reassembleAvcFrame(frameIndex);
|
||||
|
||||
startFrameNumber = nextFrameNumber;
|
||||
}
|
||||
startFrameNumber = nextFrameNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add an RTP Packet to the queue */
|
||||
void queueRtpPacket(PRTP_PACKET rtpPacket, int length) {
|
||||
int dataOffset;
|
||||
int dataOffset;
|
||||
|
||||
dataOffset = sizeof(*rtpPacket);
|
||||
if (rtpPacket->header & FLAG_EXTENSION) {
|
||||
dataOffset += 4; // 2 additional fields
|
||||
}
|
||||
dataOffset = sizeof(*rtpPacket);
|
||||
if (rtpPacket->header & FLAG_EXTENSION) {
|
||||
dataOffset += 4; // 2 additional fields
|
||||
}
|
||||
|
||||
processRtpPayload((PNV_VIDEO_PACKET)(((char*)rtpPacket) + dataOffset), length - dataOffset);
|
||||
processRtpPayload((PNV_VIDEO_PACKET)(((char*)rtpPacket) + dataOffset), length - dataOffset);
|
||||
}
|
||||
|
@ -27,114 +27,114 @@ static PLT_THREAD decoderThread;
|
||||
|
||||
/* Initialize the video stream */
|
||||
void initializeVideoStream(void) {
|
||||
initializeVideoDepacketizer(StreamConfig.packetSize);
|
||||
RtpqInitializeQueue(&rtpQueue, RTPQ_DEFAULT_MAX_SIZE, RTP_QUEUE_DELAY);
|
||||
initializeVideoDepacketizer(StreamConfig.packetSize);
|
||||
RtpqInitializeQueue(&rtpQueue, RTPQ_DEFAULT_MAX_SIZE, RTP_QUEUE_DELAY);
|
||||
}
|
||||
|
||||
/* Clean up the video stream */
|
||||
void destroyVideoStream(void) {
|
||||
destroyVideoDepacketizer();
|
||||
RtpqCleanupQueue(&rtpQueue);
|
||||
destroyVideoDepacketizer();
|
||||
RtpqCleanupQueue(&rtpQueue);
|
||||
}
|
||||
|
||||
/* UDP Ping proc */
|
||||
static void UdpPingThreadProc(void *context) {
|
||||
char pingData [] = { 0x50, 0x49, 0x4E, 0x47 };
|
||||
struct sockaddr_in6 saddr;
|
||||
SOCK_RET err;
|
||||
char pingData [] = { 0x50, 0x49, 0x4E, 0x47 };
|
||||
struct sockaddr_in6 saddr;
|
||||
SOCK_RET err;
|
||||
|
||||
memcpy(&saddr, &RemoteAddr, sizeof(saddr));
|
||||
saddr.sin6_port = htons(RTP_PORT);
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
PltSleepMs(500);
|
||||
}
|
||||
}
|
||||
|
||||
/* Receive thread proc */
|
||||
static void ReceiveThreadProc(void* context) {
|
||||
int err;
|
||||
int bufferSize, receiveSize;
|
||||
char* buffer;
|
||||
int queueStatus;
|
||||
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;
|
||||
receiveSize = StreamConfig.packetSize + MAX_RTP_HEADER_SIZE;
|
||||
bufferSize = receiveSize + sizeof(int) + sizeof(RTP_QUEUE_ENTRY);
|
||||
buffer = NULL;
|
||||
|
||||
while (!PltIsThreadInterrupted(&receiveThread)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (buffer == NULL) {
|
||||
buffer = (char*) malloc(bufferSize);
|
||||
if (buffer == NULL) {
|
||||
Limelog("Video Receive: malloc() failed\n");
|
||||
ListenerCallbacks.connectionTerminated(-1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
err = (int) recv(rtpSocket, buffer, receiveSize, 0);
|
||||
if (err <= 0) {
|
||||
Limelog("Video Receive: recv() failed: %d\n", (int)LastSocketError());
|
||||
ListenerCallbacks.connectionTerminated(LastSocketError());
|
||||
break;
|
||||
}
|
||||
err = (int) recv(rtpSocket, buffer, receiveSize, 0);
|
||||
if (err <= 0) {
|
||||
Limelog("Video Receive: recv() failed: %d\n", (int)LastSocketError());
|
||||
ListenerCallbacks.connectionTerminated(LastSocketError());
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(&buffer[receiveSize], &err, sizeof(int));
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (buffer != NULL) {
|
||||
free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/* Decoder thread proc */
|
||||
static void DecoderThreadProc(void* context) {
|
||||
PQUEUED_DECODE_UNIT qdu;
|
||||
while (!PltIsThreadInterrupted(&decoderThread)) {
|
||||
if (!getNextQueuedDecodeUnit(&qdu)) {
|
||||
return;
|
||||
}
|
||||
PQUEUED_DECODE_UNIT qdu;
|
||||
while (!PltIsThreadInterrupted(&decoderThread)) {
|
||||
if (!getNextQueuedDecodeUnit(&qdu)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int ret = VideoCallbacks.submitDecodeUnit(&qdu->decodeUnit);
|
||||
int ret = VideoCallbacks.submitDecodeUnit(&qdu->decodeUnit);
|
||||
|
||||
freeQueuedDecodeUnit(qdu);
|
||||
freeQueuedDecodeUnit(qdu);
|
||||
|
||||
if (ret == DR_NEED_IDR) {
|
||||
Limelog("Requesting IDR frame on behalf of DR\n");
|
||||
requestIdrOnDemand();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the first frame of the video stream */
|
||||
@ -145,66 +145,66 @@ int readFirstFrame(void) {
|
||||
closesocket(firstFrameSocket);
|
||||
firstFrameSocket = INVALID_SOCKET;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Terminate the video stream */
|
||||
void stopVideoStream(void) {
|
||||
PltInterruptThread(&udpPingThread);
|
||||
PltInterruptThread(&receiveThread);
|
||||
if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
PltInterruptThread(&decoderThread);
|
||||
}
|
||||
PltInterruptThread(&udpPingThread);
|
||||
PltInterruptThread(&receiveThread);
|
||||
if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
PltInterruptThread(&decoderThread);
|
||||
}
|
||||
|
||||
if (firstFrameSocket != INVALID_SOCKET) {
|
||||
closesocket(firstFrameSocket);
|
||||
firstFrameSocket = INVALID_SOCKET;
|
||||
}
|
||||
if (rtpSocket != INVALID_SOCKET) {
|
||||
closesocket(rtpSocket);
|
||||
rtpSocket = INVALID_SOCKET;
|
||||
}
|
||||
if (firstFrameSocket != INVALID_SOCKET) {
|
||||
closesocket(firstFrameSocket);
|
||||
firstFrameSocket = INVALID_SOCKET;
|
||||
}
|
||||
if (rtpSocket != INVALID_SOCKET) {
|
||||
closesocket(rtpSocket);
|
||||
rtpSocket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
PltJoinThread(&udpPingThread);
|
||||
PltJoinThread(&receiveThread);
|
||||
if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
PltJoinThread(&decoderThread);
|
||||
}
|
||||
PltJoinThread(&udpPingThread);
|
||||
PltJoinThread(&receiveThread);
|
||||
if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
PltJoinThread(&decoderThread);
|
||||
}
|
||||
|
||||
PltCloseThread(&udpPingThread);
|
||||
PltCloseThread(&receiveThread);
|
||||
if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
PltCloseThread(&decoderThread);
|
||||
}
|
||||
PltCloseThread(&udpPingThread);
|
||||
PltCloseThread(&receiveThread);
|
||||
if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
PltCloseThread(&decoderThread);
|
||||
}
|
||||
|
||||
VideoCallbacks.cleanup();
|
||||
}
|
||||
|
||||
/* Start the video stream */
|
||||
int startVideoStream(void* rendererContext, int drFlags) {
|
||||
int err;
|
||||
int err;
|
||||
|
||||
// This must be called before the decoder thread starts submitting
|
||||
// decode units
|
||||
VideoCallbacks.setup(StreamConfig.width,
|
||||
StreamConfig.height, StreamConfig.fps, rendererContext, drFlags);
|
||||
VideoCallbacks.setup(StreamConfig.width,
|
||||
StreamConfig.height, StreamConfig.fps, rendererContext, drFlags);
|
||||
|
||||
rtpSocket = bindUdpSocket(RemoteAddr.ss_family, RTP_RECV_BUFFER);
|
||||
if (rtpSocket == INVALID_SOCKET) {
|
||||
return LastSocketError();
|
||||
}
|
||||
rtpSocket = bindUdpSocket(RemoteAddr.ss_family, RTP_RECV_BUFFER);
|
||||
if (rtpSocket == INVALID_SOCKET) {
|
||||
return LastSocketError();
|
||||
}
|
||||
|
||||
err = PltCreateThread(ReceiveThreadProc, NULL, &receiveThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
err = PltCreateThread(ReceiveThreadProc, NULL, &receiveThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
err = PltCreateThread(DecoderThreadProc, NULL, &decoderThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||
err = PltCreateThread(DecoderThreadProc, NULL, &decoderThread);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (ServerMajorVersion == 3) {
|
||||
// Connect this socket to open port 47998 for our ping thread
|
||||
@ -229,5 +229,5 @@ int startVideoStream(void* rendererContext, int drFlags) {
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user