diff --git a/src/AudioStream.c b/src/AudioStream.c index 074a58d..578d353 100644 --- a/src/AudioStream.c +++ b/src/AudioStream.c @@ -250,6 +250,8 @@ static void DecoderThreadProc(void* context) { } void stopAudioStream(void) { + AudioCallbacks.stop(); + PltInterruptThread(&udpPingThread); PltInterruptThread(&receiveThread); if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) { @@ -301,8 +303,11 @@ int startAudioStream(void) { return err; } + AudioCallbacks.start(); + err = PltCreateThread(ReceiveThreadProc, NULL, &receiveThread); if (err != 0) { + AudioCallbacks.stop(); PltInterruptThread(&udpPingThread); PltJoinThread(&udpPingThread); PltCloseThread(&udpPingThread); @@ -314,6 +319,7 @@ int startAudioStream(void) { if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) { err = PltCreateThread(DecoderThreadProc, NULL, &decoderThread); if (err != 0) { + AudioCallbacks.stop(); PltInterruptThread(&udpPingThread); PltInterruptThread(&receiveThread); PltJoinThread(&udpPingThread); diff --git a/src/FakeCallbacks.c b/src/FakeCallbacks.c index 9b25bf5..d999432 100644 --- a/src/FakeCallbacks.c +++ b/src/FakeCallbacks.c @@ -1,21 +1,29 @@ #include "Limelight-internal.h" static int fakeDrSetup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { return 0; } +static void fakeDrStart(void) {} +static void fakeDrStop(void) {} static void fakeDrCleanup(void) {} static int fakeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) { return DR_OK; } static DECODER_RENDERER_CALLBACKS fakeDrCallbacks = { .setup = fakeDrSetup, + .start = fakeDrStart, + .stop = fakeDrStop, .cleanup = fakeDrCleanup, .submitDecodeUnit = fakeDrSubmitDecodeUnit, }; static int fakeArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { return 0; } +static void fakeArStart(void) {} +static void fakeArStop(void) {} static void fakeArCleanup(void) {} static void fakeArDecodeAndPlaySample(char* sampleData, int sampleLength) {} AUDIO_RENDERER_CALLBACKS fakeArCallbacks = { .init = fakeArInit, + .start = fakeArStart, + .stop = fakeArStop, .cleanup = fakeArCleanup, .decodeAndPlaySample = fakeArDecodeAndPlaySample, }; @@ -48,6 +56,12 @@ void fixupMissingCallbacks(PDECODER_RENDERER_CALLBACKS* drCallbacks, PAUDIO_REND if ((*drCallbacks)->setup == NULL) { (*drCallbacks)->setup = fakeDrSetup; } + if ((*drCallbacks)->start == NULL) { + (*drCallbacks)->start = fakeDrStart; + } + if ((*drCallbacks)->stop == NULL) { + (*drCallbacks)->stop = fakeDrStop; + } if ((*drCallbacks)->cleanup == NULL) { (*drCallbacks)->cleanup = fakeDrCleanup; } @@ -63,6 +77,12 @@ void fixupMissingCallbacks(PDECODER_RENDERER_CALLBACKS* drCallbacks, PAUDIO_REND if ((*arCallbacks)->init == NULL) { (*arCallbacks)->init = fakeArInit; } + if ((*arCallbacks)->start == NULL) { + (*arCallbacks)->start = fakeArStart; + } + if ((*arCallbacks)->stop == NULL) { + (*arCallbacks)->stop = fakeArStop; + } if ((*arCallbacks)->cleanup == NULL) { (*arCallbacks)->cleanup = fakeArCleanup; } diff --git a/src/Limelight.h b/src/Limelight.h index f7cbf99..6c7a62f 100644 --- a/src/Limelight.h +++ b/src/Limelight.h @@ -105,9 +105,16 @@ typedef struct _DECODE_UNIT { // Returns 0 on success, non-zero on failure. typedef int(*DecoderRendererSetup)(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags); -// This callback performs the teardown of the video decoder +// This callback notifies the decoder that the stream is starting. No frames can be submitted before this callback returns. +typedef void(*DecoderRendererStart)(void); + +// This callback notifies the decoder that the stream is stopping. Frames may still be submitted but they may be safely discarded. +typedef void(*DecoderRendererStop)(void); + +// This callback performs the teardown of the video decoder. No more frames will be submitted when this callback is invoked. typedef void(*DecoderRendererCleanup)(void); + // This callback provides Annex B formatted elementary stream data to the // decoder. If the decoder is unable to process the submitted data for some reason, // it must return DR_NEED_IDR to generate a keyframe. @@ -117,6 +124,8 @@ typedef int(*DecoderRendererSubmitDecodeUnit)(PDECODE_UNIT decodeUnit); typedef struct _DECODER_RENDERER_CALLBACKS { DecoderRendererSetup setup; + DecoderRendererStart start; + DecoderRendererStop stop; DecoderRendererCleanup cleanup; DecoderRendererSubmitDecodeUnit submitDecodeUnit; int capabilities; @@ -152,7 +161,13 @@ typedef struct _OPUS_MULTISTREAM_CONFIGURATION { // specified in the stream configuration. Returns 0 on success, non-zero on failure. typedef int(*AudioRendererInit)(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig); -// This callback performs the final teardown of the audio decoder +// This callback notifies the decoder that the stream is starting. No audio can be submitted before this callback returns. +typedef void(*AudioRendererStart)(void); + +// This callback notifies the decoder that the stream is stopping. Audio samples may still be submitted but they may be safely discarded. +typedef void(*AudioRendererStop)(void); + +// This callback performs the final teardown of the audio decoder. No additional audio will be submitted when this callback is invoked. typedef void(*AudioRendererCleanup)(void); // This callback provides Opus audio data to be decoded and played. sampleLength is in bytes. @@ -160,6 +175,8 @@ typedef void(*AudioRendererDecodeAndPlaySample)(char* sampleData, int sampleLeng typedef struct _AUDIO_RENDERER_CALLBACKS { AudioRendererInit init; + AudioRendererStart start; + AudioRendererStop stop; AudioRendererCleanup cleanup; AudioRendererDecodeAndPlaySample decodeAndPlaySample; int capabilities; diff --git a/src/VideoStream.c b/src/VideoStream.c index 8bfef66..7235907 100644 --- a/src/VideoStream.c +++ b/src/VideoStream.c @@ -151,6 +151,8 @@ int readFirstFrame(void) { // Terminate the video stream void stopVideoStream(void) { + VideoCallbacks.stop(); + // Wake up client code that may be waiting on the decode unit queue stopVideoDepacketizer(); @@ -209,8 +211,11 @@ int startVideoStream(void* rendererContext, int drFlags) { return LastSocketError(); } + VideoCallbacks.start(); + err = PltCreateThread(ReceiveThreadProc, NULL, &receiveThread); if (err != 0) { + VideoCallbacks.stop(); closeSocket(rtpSocket); VideoCallbacks.cleanup(); return err; @@ -219,6 +224,7 @@ int startVideoStream(void* rendererContext, int drFlags) { if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) { err = PltCreateThread(DecoderThreadProc, NULL, &decoderThread); if (err != 0) { + VideoCallbacks.stop(); PltInterruptThread(&receiveThread); PltJoinThread(&receiveThread); PltCloseThread(&receiveThread); @@ -233,6 +239,7 @@ int startVideoStream(void* rendererContext, int drFlags) { firstFrameSocket = connectTcpSocket(&RemoteAddr, RemoteAddrLen, FIRST_FRAME_PORT, FIRST_FRAME_TIMEOUT_SEC); if (firstFrameSocket == INVALID_SOCKET) { + VideoCallbacks.stop(); stopVideoDepacketizer(); PltInterruptThread(&receiveThread); if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) { @@ -256,6 +263,7 @@ int startVideoStream(void* rendererContext, int drFlags) { // to send UDP data err = PltCreateThread(UdpPingThreadProc, NULL, &udpPingThread); if (err != 0) { + VideoCallbacks.stop(); stopVideoDepacketizer(); PltInterruptThread(&receiveThread); if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) {