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