diff --git a/src/Connection.c b/src/Connection.c index e0287fc..ab5a6f7 100644 --- a/src/Connection.c +++ b/src/Connection.c @@ -190,6 +190,11 @@ int LiStartConnection(PSERVER_INFORMATION serverInfo, PSTREAM_CONFIGURATION stre memcpy(&VideoCallbacks, drCallbacks, sizeof(VideoCallbacks)); memcpy(&AudioCallbacks, arCallbacks, sizeof(AudioCallbacks)); +#ifdef LC_DEBUG_RECORD_MODE + // Install the pass-through recorder callbacks + setRecorderCallbacks(&VideoCallbacks, &AudioCallbacks); +#endif + // Hook the termination callback so we can avoid issuing a termination callback // after LiStopConnection() is called. // diff --git a/src/Limelight-internal.h b/src/Limelight-internal.h index eb6649d..d177d91 100644 --- a/src/Limelight-internal.h +++ b/src/Limelight-internal.h @@ -70,6 +70,7 @@ void* extendBuffer(void* ptr, size_t newSize); void fixupMissingCallbacks(PDECODER_RENDERER_CALLBACKS* drCallbacks, PAUDIO_RENDERER_CALLBACKS* arCallbacks, PCONNECTION_LISTENER_CALLBACKS* clCallbacks); +void setRecorderCallbacks(PDECODER_RENDERER_CALLBACKS drCallbacks, PAUDIO_RENDERER_CALLBACKS arCallbacks); char* getSdpPayloadForStreamConfig(int rtspClientVersion, int* length); diff --git a/src/RecorderCallbacks.c b/src/RecorderCallbacks.c new file mode 100644 index 0000000..fc9a8fa --- /dev/null +++ b/src/RecorderCallbacks.c @@ -0,0 +1,97 @@ +#include "Limelight-internal.h" + +static FILE* videoFile; +static FILE* audioFile; + +static DECODER_RENDERER_CALLBACKS realDrCallbacks; +static AUDIO_RENDERER_CALLBACKS realArCallbacks; + +static int recDrSetup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) +{ + const char* path = context; + + if (path != NULL) { + videoFile = fopen(path, "wb"); + if (videoFile == NULL) { + return -1; + } + } + else { + Limelog("Video recording will not be enabled - file path not specified in drContext!\n"); + } + + return realDrCallbacks.setup(videoFormat, width, height, redrawRate, NULL, drFlags); +} + +static void recDrCleanup(void) +{ + if (videoFile != NULL) { + fclose(videoFile); + videoFile = NULL; + } + + realDrCallbacks.cleanup(); +} + +static int recDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) +{ + if (videoFile != NULL) { + PLENTRY entry = decodeUnit->bufferList; + while (entry != NULL) { + fwrite(entry->data, 1, entry->length, videoFile); + entry = entry->next; + } + } + + return realDrCallbacks.submitDecodeUnit(decodeUnit); +} + +static int recArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) +{ + const char* path = context; + + if (path != NULL) { + audioFile = fopen(path, "wb"); + if (audioFile == NULL) { + return -1; + } + } + else { + Limelog("Audio recording will not be enabled - file path not specified in arContext!\n"); + } + + return realArCallbacks.init(audioConfiguration, opusConfig, NULL, arFlags); +} + +static void recArCleanup(void) +{ + if (audioFile != NULL) { + fclose(audioFile); + audioFile = NULL; + } + + realArCallbacks.cleanup(); +} + +static void recArDecodeAndPlaySample(char* sampleData, int sampleLength) +{ + if (audioFile != NULL) { + fwrite(sampleData, 1, sampleLength, audioFile); + } + + realArCallbacks.decodeAndPlaySample(sampleData, sampleLength); +} + +void setRecorderCallbacks(PDECODER_RENDERER_CALLBACKS drCallbacks, PAUDIO_RENDERER_CALLBACKS arCallbacks) +{ + realDrCallbacks = *drCallbacks; + realArCallbacks = *arCallbacks; + + drCallbacks->setup = recDrSetup; + drCallbacks->cleanup = recDrCleanup; + drCallbacks->submitDecodeUnit = recDrSubmitDecodeUnit; + + arCallbacks->init = recArInit; + arCallbacks->cleanup = recArCleanup; + arCallbacks->decodeAndPlaySample = recArDecodeAndPlaySample; +}