Fix inconsistent whitespace

This commit is contained in:
Cameron Gutman 2016-02-06 23:46:47 -05:00
parent 349d1baa53
commit dfdfe4f0f6
30 changed files with 3674 additions and 3674 deletions

View File

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

View File

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

View File

@ -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);

View File

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

View File

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

View File

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

View File

@ -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)

View File

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

View File

@ -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);

View File

@ -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

View File

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

View File

@ -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);

View File

@ -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) {

View File

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

View File

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

View File

@ -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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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

View File

@ -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);

View File

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

View File

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

View File

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

View File

@ -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)

View File

@ -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(&currentPos, &specialSeq) && getSpecialSeq(&currentPos, &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, &currentPos); processRtpPayloadSlow(videoPacket, &currentPos);
} }
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);
} }

View File

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