mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2025-08-18 01:15:46 +00:00
310 lines
8.9 KiB
C
310 lines
8.9 KiB
C
#include "Limelight-internal.h"
|
|
#include "Platform.h"
|
|
|
|
static int stage = STAGE_NONE;
|
|
static ConnListenerConnectionTerminated originalTerminationCallback;
|
|
static int alreadyTerminated;
|
|
|
|
// Common globals
|
|
struct sockaddr_storage RemoteAddr;
|
|
SOCKADDR_LEN RemoteAddrLen;
|
|
int ServerMajorVersion;
|
|
STREAM_CONFIGURATION StreamConfig;
|
|
PLATFORM_CALLBACKS PlatformCallbacks;
|
|
CONNECTION_LISTENER_CALLBACKS ListenerCallbacks;
|
|
DECODER_RENDERER_CALLBACKS VideoCallbacks;
|
|
AUDIO_RENDERER_CALLBACKS AudioCallbacks;
|
|
|
|
/* Connection stages */
|
|
static const char* stageNames[STAGE_MAX] = {
|
|
"none",
|
|
"platform initialization",
|
|
"name resolution",
|
|
"RTSP handshake",
|
|
"control stream initialization",
|
|
"video stream initialization",
|
|
"audio stream initialization",
|
|
"input stream initialization",
|
|
"control stream establishment",
|
|
"video stream establishment",
|
|
"audio stream establishment",
|
|
"input stream establishment"
|
|
};
|
|
|
|
/* Get the name of the current stage based on its number */
|
|
const char* LiGetStageName(int stage) {
|
|
return stageNames[stage];
|
|
}
|
|
|
|
/* Stop the connection by undoing the step at the current stage and those before it */
|
|
void LiStopConnection(void) {
|
|
// Disable termination callbacks now
|
|
alreadyTerminated = 1;
|
|
|
|
if (stage == STAGE_INPUT_STREAM_START) {
|
|
Limelog("Stopping input stream...");
|
|
stopInputStream();
|
|
stage--;
|
|
Limelog("done\n");
|
|
}
|
|
if (stage == STAGE_AUDIO_STREAM_START) {
|
|
Limelog("Stopping audio stream...");
|
|
stopAudioStream();
|
|
stage--;
|
|
Limelog("done\n");
|
|
}
|
|
if (stage == STAGE_VIDEO_STREAM_START) {
|
|
Limelog("Stopping video stream...");
|
|
stopVideoStream();
|
|
stage--;
|
|
Limelog("done\n");
|
|
}
|
|
if (stage == STAGE_CONTROL_STREAM_START) {
|
|
Limelog("Stopping control stream...");
|
|
stopControlStream();
|
|
stage--;
|
|
Limelog("done\n");
|
|
}
|
|
if (stage == STAGE_INPUT_STREAM_INIT) {
|
|
Limelog("Cleaning up input stream...");
|
|
destroyInputStream();
|
|
stage--;
|
|
Limelog("done\n");
|
|
}
|
|
if (stage == STAGE_AUDIO_STREAM_INIT) {
|
|
Limelog("Cleaning up audio stream...");
|
|
destroyAudioStream();
|
|
stage--;
|
|
Limelog("done\n");
|
|
}
|
|
if (stage == STAGE_VIDEO_STREAM_INIT) {
|
|
Limelog("Cleaning up video stream...");
|
|
destroyVideoStream();
|
|
stage--;
|
|
Limelog("done\n");
|
|
}
|
|
if (stage == STAGE_CONTROL_STREAM_INIT) {
|
|
Limelog("Cleaning up control stream...");
|
|
destroyControlStream();
|
|
stage--;
|
|
Limelog("done\n");
|
|
}
|
|
if (stage == STAGE_RTSP_HANDSHAKE) {
|
|
Limelog("Terminating RTSP handshake...");
|
|
terminateRtspHandshake();
|
|
stage--;
|
|
Limelog("done\n");
|
|
}
|
|
if (stage == STAGE_NAME_RESOLUTION) {
|
|
// Nothing to do
|
|
stage--;
|
|
}
|
|
if (stage == STAGE_PLATFORM_INIT) {
|
|
Limelog("Cleaning up platform...");
|
|
cleanupPlatform();
|
|
stage--;
|
|
Limelog("done\n");
|
|
}
|
|
LC_ASSERT(stage == STAGE_NONE);
|
|
}
|
|
|
|
static void ClInternalConnectionTerminated(long errorCode)
|
|
{
|
|
// Avoid recursion and issuing multiple callbacks
|
|
if (alreadyTerminated) {
|
|
return;
|
|
}
|
|
|
|
alreadyTerminated = 1;
|
|
originalTerminationCallback(errorCode);
|
|
}
|
|
|
|
void LiCompleteThreadStart(void)
|
|
{
|
|
PltRunThreadProc();
|
|
}
|
|
|
|
static int resolveHostName(char *host)
|
|
{
|
|
struct addrinfo hints, *res;
|
|
int err;
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_flags = AI_ADDRCONFIG;
|
|
err = getaddrinfo(host, NULL, &hints, &res);
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
if (res == NULL) {
|
|
Limelog("getaddrinfo() returned success without addresses\n");
|
|
return -1;
|
|
}
|
|
|
|
// Use the first address in the list
|
|
memcpy(&RemoteAddr, res->ai_addr, res->ai_addrlen);
|
|
RemoteAddrLen = res->ai_addrlen;
|
|
|
|
freeaddrinfo(res);
|
|
return 0;
|
|
}
|
|
|
|
/* Starts the connection to the streaming machine */
|
|
int LiStartConnection(char* host, PSTREAM_CONFIGURATION streamConfig, PCONNECTION_LISTENER_CALLBACKS clCallbacks,
|
|
PDECODER_RENDERER_CALLBACKS drCallbacks, PAUDIO_RENDERER_CALLBACKS arCallbacks, PPLATFORM_CALLBACKS plCallbacks,
|
|
void* renderContext, int drFlags, int _serverMajorVersion) {
|
|
int err;
|
|
|
|
ServerMajorVersion = _serverMajorVersion;
|
|
memcpy(&StreamConfig, streamConfig, sizeof(StreamConfig));
|
|
|
|
// Replace missing callbacks with placeholders
|
|
fixupMissingCallbacks(&drCallbacks, &arCallbacks, &clCallbacks, &plCallbacks);
|
|
memcpy(&PlatformCallbacks, plCallbacks, sizeof(PlatformCallbacks));
|
|
memcpy(&VideoCallbacks, drCallbacks, sizeof(VideoCallbacks));
|
|
memcpy(&AudioCallbacks, arCallbacks, sizeof(AudioCallbacks));
|
|
|
|
// Hook the termination callback so we can avoid issuing a termination callback
|
|
// after LiStopConnection() is called
|
|
originalTerminationCallback = clCallbacks->connectionTerminated;
|
|
memcpy(&ListenerCallbacks, clCallbacks, sizeof(ListenerCallbacks));
|
|
ListenerCallbacks.connectionTerminated = ClInternalConnectionTerminated;
|
|
|
|
alreadyTerminated = 0;
|
|
|
|
Limelog("Initializing platform...");
|
|
ListenerCallbacks.stageStarting(STAGE_PLATFORM_INIT);
|
|
err = initializePlatform();
|
|
if (err != 0) {
|
|
Limelog("failed: %d\n", err);
|
|
ListenerCallbacks.stageFailed(STAGE_PLATFORM_INIT, err);
|
|
goto Cleanup;
|
|
}
|
|
stage++;
|
|
LC_ASSERT(stage == STAGE_PLATFORM_INIT);
|
|
ListenerCallbacks.stageComplete(STAGE_PLATFORM_INIT);
|
|
Limelog("done\n");
|
|
|
|
Limelog("Resolving host name...");
|
|
LC_ASSERT(stage == STAGE_NAME_RESOLUTION);
|
|
ListenerCallbacks.stageStarting(STAGE_NAME_RESOLUTION);
|
|
err = resolveHostName(host);
|
|
if (err != 0) {
|
|
Limelog("failed: %d\n", err);
|
|
ListenerCallbacks.stageFailed(STAGE_NAME_RESOLUTION, err);
|
|
goto Cleanup;
|
|
}
|
|
stage++;
|
|
LC_ASSERT(stage == STAGE_NAME_RESOLUTION);
|
|
ListenerCallbacks.stageComplete(STAGE_NAME_RESOLUTION);
|
|
Limelog("done\n");
|
|
|
|
Limelog("Starting RTSP handshake...");
|
|
ListenerCallbacks.stageStarting(STAGE_RTSP_HANDSHAKE);
|
|
err = performRtspHandshake();
|
|
if (err != 0) {
|
|
Limelog("failed: %d\n", err);
|
|
ListenerCallbacks.stageFailed(STAGE_RTSP_HANDSHAKE, err);
|
|
goto Cleanup;
|
|
}
|
|
stage++;
|
|
LC_ASSERT(stage == STAGE_RTSP_HANDSHAKE);
|
|
ListenerCallbacks.stageComplete(STAGE_RTSP_HANDSHAKE);
|
|
Limelog("done\n");
|
|
|
|
Limelog("Initializing control stream...");
|
|
ListenerCallbacks.stageStarting(STAGE_CONTROL_STREAM_INIT);
|
|
err = initializeControlStream();
|
|
if (err != 0) {
|
|
Limelog("failed: %d\n", err);
|
|
ListenerCallbacks.stageFailed(STAGE_CONTROL_STREAM_INIT, err);
|
|
goto Cleanup;
|
|
}
|
|
stage++;
|
|
LC_ASSERT(stage == STAGE_CONTROL_STREAM_INIT);
|
|
ListenerCallbacks.stageComplete(STAGE_CONTROL_STREAM_INIT);
|
|
Limelog("done\n");
|
|
|
|
Limelog("Initializing video stream...");
|
|
ListenerCallbacks.stageStarting(STAGE_VIDEO_STREAM_INIT);
|
|
initializeVideoStream();
|
|
stage++;
|
|
LC_ASSERT(stage == STAGE_VIDEO_STREAM_INIT);
|
|
ListenerCallbacks.stageComplete(STAGE_VIDEO_STREAM_INIT);
|
|
Limelog("done\n");
|
|
|
|
Limelog("Initializing audio stream...");
|
|
ListenerCallbacks.stageStarting(STAGE_AUDIO_STREAM_INIT);
|
|
initializeAudioStream();
|
|
stage++;
|
|
LC_ASSERT(stage == STAGE_AUDIO_STREAM_INIT);
|
|
ListenerCallbacks.stageComplete(STAGE_AUDIO_STREAM_INIT);
|
|
Limelog("done\n");
|
|
|
|
Limelog("Initializing input stream...");
|
|
ListenerCallbacks.stageStarting(STAGE_INPUT_STREAM_INIT);
|
|
initializeInputStream(streamConfig->remoteInputAesKey, sizeof(streamConfig->remoteInputAesKey),
|
|
streamConfig->remoteInputAesIv, sizeof(streamConfig->remoteInputAesIv));
|
|
stage++;
|
|
LC_ASSERT(stage == STAGE_INPUT_STREAM_INIT);
|
|
ListenerCallbacks.stageComplete(STAGE_INPUT_STREAM_INIT);
|
|
Limelog("done\n");
|
|
|
|
Limelog("Starting control stream...");
|
|
ListenerCallbacks.stageStarting(STAGE_CONTROL_STREAM_START);
|
|
err = startControlStream();
|
|
if (err != 0) {
|
|
Limelog("failed: %d\n", err);
|
|
ListenerCallbacks.stageFailed(STAGE_CONTROL_STREAM_START, err);
|
|
goto Cleanup;
|
|
}
|
|
stage++;
|
|
LC_ASSERT(stage == STAGE_CONTROL_STREAM_START);
|
|
ListenerCallbacks.stageComplete(STAGE_CONTROL_STREAM_START);
|
|
Limelog("done\n");
|
|
|
|
Limelog("Starting video stream...");
|
|
ListenerCallbacks.stageStarting(STAGE_VIDEO_STREAM_START);
|
|
err = startVideoStream(renderContext, drFlags);
|
|
if (err != 0) {
|
|
Limelog("Video stream start failed: %d\n", err);
|
|
ListenerCallbacks.stageFailed(STAGE_VIDEO_STREAM_START, err);
|
|
goto Cleanup;
|
|
}
|
|
stage++;
|
|
LC_ASSERT(stage == STAGE_VIDEO_STREAM_START);
|
|
ListenerCallbacks.stageComplete(STAGE_VIDEO_STREAM_START);
|
|
Limelog("done\n");
|
|
|
|
Limelog("Starting audio stream...");
|
|
ListenerCallbacks.stageStarting(STAGE_AUDIO_STREAM_START);
|
|
err = startAudioStream();
|
|
if (err != 0) {
|
|
Limelog("Audio stream start failed: %d\n", err);
|
|
ListenerCallbacks.stageFailed(STAGE_AUDIO_STREAM_START, err);
|
|
goto Cleanup;
|
|
}
|
|
stage++;
|
|
LC_ASSERT(stage == STAGE_AUDIO_STREAM_START);
|
|
ListenerCallbacks.stageComplete(STAGE_AUDIO_STREAM_START);
|
|
Limelog("done\n");
|
|
|
|
Limelog("Starting input stream...");
|
|
ListenerCallbacks.stageStarting(STAGE_INPUT_STREAM_START);
|
|
err = startInputStream();
|
|
if (err != 0) {
|
|
Limelog("Input stream start failed: %d\n", err);
|
|
ListenerCallbacks.stageFailed(STAGE_INPUT_STREAM_START, err);
|
|
goto Cleanup;
|
|
}
|
|
stage++;
|
|
LC_ASSERT(stage == STAGE_INPUT_STREAM_START);
|
|
ListenerCallbacks.stageComplete(STAGE_INPUT_STREAM_START);
|
|
Limelog("done\n");
|
|
|
|
ListenerCallbacks.connectionStarted();
|
|
|
|
Cleanup:
|
|
return err;
|
|
} |