void stopAudioStream(void) { AudioCallbacks.stop(); PltInterruptThread(&udpPingThread); PltInterruptThread(&receiveThread); if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) { // Signal threads waiting on the LBQ LbqSignalQueueShutdown(&packetQueue); PltInterruptThread(&decoderThread); } PltJoinThread(&udpPingThread); PltJoinThread(&receiveThread); if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) { PltJoinThread(&decoderThread); } PltCloseThread(&udpPingThread); PltCloseThread(&receiveThread); if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) { PltCloseThread(&decoderThread); } if (rtpSocket != INVALID_SOCKET) { closeSocket(rtpSocket); rtpSocket = INVALID_SOCKET; } AudioCallbacks.cleanup(); }
/* Stops the control stream */ int stopControlStream(void) { PltInterruptThread(&lossStatsThread); PltInterruptThread(&resyncThread); if (ctlSock != INVALID_SOCKET) { closesocket(ctlSock); ctlSock = INVALID_SOCKET; } PltJoinThread(&lossStatsThread); PltJoinThread(&resyncThread); PltCloseThread(&lossStatsThread); PltCloseThread(&resyncThread); return 0; }
void stopAudioStream(void) { callbacks.stop(); PltInterruptThread(&udpPingThread); PltInterruptThread(&receiveThread); PltInterruptThread(&decoderThread); if (rtpSocket != INVALID_SOCKET) { closesocket(rtpSocket); rtpSocket = INVALID_SOCKET; } PltJoinThread(&udpPingThread); PltJoinThread(&receiveThread); PltJoinThread(&decoderThread); PltCloseThread(&udpPingThread); PltCloseThread(&receiveThread); PltCloseThread(&decoderThread); }
/* Stops the input stream */ int stopInputStream(void) { PltInterruptThread(&inputSendThread); if (inputSock != INVALID_SOCKET) { closesocket(inputSock); inputSock = INVALID_SOCKET; } PltJoinThread(&inputSendThread); PltCloseThread(&inputSendThread); return 0; }
// Terminate the video stream void stopVideoStream(void) { // Wake up client code that may be waiting on the decode unit queue stopVideoDepacketizer(); PltInterruptThread(&udpPingThread); PltInterruptThread(&receiveThread); if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) { PltInterruptThread(&decoderThread); } if (firstFrameSocket != INVALID_SOCKET) { shutdownTcpSocket(firstFrameSocket); } PltJoinThread(&udpPingThread); PltJoinThread(&receiveThread); if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) { PltJoinThread(&decoderThread); } PltCloseThread(&udpPingThread); PltCloseThread(&receiveThread); if ((VideoCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) { PltCloseThread(&decoderThread); } if (firstFrameSocket != INVALID_SOCKET) { closeSocket(firstFrameSocket); firstFrameSocket = INVALID_SOCKET; } if (rtpSocket != INVALID_SOCKET) { closeSocket(rtpSocket); rtpSocket = INVALID_SOCKET; } VideoCallbacks.cleanup(); }
// This shim callback runs the client's connectionTerminated() callback on a // separate thread. This is neccessary because other internal threads directly // invoke this callback. That can result in a deadlock if the client // calls LiStopConnection() in the callback when the cleanup code // attempts to join the thread that the termination callback (and LiStopConnection) // is running on. static void ClInternalConnectionTerminated(long errorCode) { int err; // Avoid recursion and issuing multiple callbacks if (alreadyTerminated) { return; } alreadyTerminated = 1; // Invoke the termination callback on a separate thread err = PltCreateThread(terminationCallbackThreadFunc, NULL, &terminationCallbackThread); if (err != 0) { // Nothing we can safely do here, so we'll just assert on debug builds Limelog("Failed to create termination thread: %d\n", err); LC_ASSERT(err == 0); } // Close the thread handle since we can never wait on it PltCloseThread(&terminationCallbackThread); }
int startAudioStream(void* audioContext, int arFlags) { int err; POPUS_MULTISTREAM_CONFIGURATION chosenConfig; if (StreamConfig.audioConfiguration == AUDIO_CONFIGURATION_STEREO) { chosenConfig = &opusStereoConfig; } else if (StreamConfig.audioConfiguration == AUDIO_CONFIGURATION_51_SURROUND) { if (HighQualitySurroundEnabled) { chosenConfig = &opus51HighSurroundConfig; } else { chosenConfig = &opus51SurroundConfig; } } else { Limelog("Invalid audio configuration: %d\n", StreamConfig.audioConfiguration); return -1; } err = AudioCallbacks.init(StreamConfig.audioConfiguration, chosenConfig, audioContext, arFlags); if (err != 0) { return err; } rtpSocket = bindUdpSocket(RemoteAddr.ss_family, RTP_RECV_BUFFER); if (rtpSocket == INVALID_SOCKET) { err = LastSocketFail(); AudioCallbacks.cleanup(); return err; } AudioCallbacks.start(); err = PltCreateThread(ReceiveThreadProc, NULL, &receiveThread); if (err != 0) { AudioCallbacks.stop(); closeSocket(rtpSocket); AudioCallbacks.cleanup(); return err; } if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) { err = PltCreateThread(DecoderThreadProc, NULL, &decoderThread); if (err != 0) { AudioCallbacks.stop(); PltInterruptThread(&receiveThread); PltJoinThread(&receiveThread); PltCloseThread(&receiveThread); closeSocket(rtpSocket); AudioCallbacks.cleanup(); return err; } } // Don't start pinging (which will cause GFE to start sending us traffic) // until everything else is started. Otherwise we could accumulate a // bunch of audio packets in the socket receive buffer while our audio // backend is starting up and create audio latency. err = PltCreateThread(UdpPingThreadProc, NULL, &udpPingThread); if (err != 0) { AudioCallbacks.stop(); PltInterruptThread(&receiveThread); if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) { // Signal threads waiting on the LBQ LbqSignalQueueShutdown(&packetQueue); PltInterruptThread(&decoderThread); } PltJoinThread(&receiveThread); if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) { PltJoinThread(&decoderThread); } PltCloseThread(&receiveThread); if ((AudioCallbacks.capabilities & CAPABILITY_DIRECT_SUBMIT) == 0) { PltCloseThread(&decoderThread); } closeSocket(rtpSocket); AudioCallbacks.cleanup(); return err; } return 0; }