#include "Samples.h" #ifdef KVS_WITH_WEBRTC #include "frame_ring_buffer/frame_ring_buffer.h" #endif /* #ifdef KVS_WITH_WEBRTC */ extern PSampleConfiguration gSampleConfiguration; // #define VERBOSE INT32 webrtc_main(INT32 argc, CHAR* argv[]) { STATUS retStatus = STATUS_SUCCESS; UINT32 frameSize; PSampleConfiguration pSampleConfiguration = NULL; SignalingClientMetrics signalingClientMetrics; PCHAR pChannelName; signalingClientMetrics.version = 0; SET_INSTRUMENTED_ALLOCATORS(); #ifdef KVS_WITH_WEBRTC /* SIGINT is handled in "kvs_with_webrtc.c". */ #else #ifndef _WIN32 signal(SIGINT, sigintHandler); #endif #endif /* #ifdef KVS_WITH_WEBRTC */ // do trickleIce by default printf("[KVS Master] Using trickleICE by default\n"); #ifdef IOT_CORE_ENABLE_CREDENTIALS CHK_ERR((pChannelName = getenv(IOT_CORE_THING_NAME)) != NULL, STATUS_INVALID_OPERATION, "AWS_IOT_CORE_THING_NAME must be set"); #else pChannelName = argc > 1 ? argv[1] : SAMPLE_CHANNEL_NAME; #endif retStatus = createSampleConfiguration(pChannelName, SIGNALING_CHANNEL_ROLE_TYPE_MASTER, TRUE, TRUE, &pSampleConfiguration); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] createSampleConfiguration(): operation returned status code: 0x%08x \n", retStatus); goto CleanUp; } printf("[KVS Master] Created signaling channel %s\n", pChannelName); if (pSampleConfiguration->enableFileLogging) { retStatus = createFileLogger(FILE_LOGGING_BUFFER_SIZE, MAX_NUMBER_OF_LOG_FILES, (PCHAR) FILE_LOGGER_LOG_FILE_DIRECTORY_PATH, TRUE, TRUE, NULL); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] createFileLogger(): operation returned status code: 0x%08x \n", retStatus); pSampleConfiguration->enableFileLogging = FALSE; } } // Set the audio and video handlers #ifdef KVS_WITH_WEBRTC /* Media source is fed from "kvs_with_webrtc.c". */ #else pSampleConfiguration->audioSource = sendAudioPackets; pSampleConfiguration->videoSource = sendVideoPackets; #endif /* #ifdef KVS_WITH_WEBRTC */ pSampleConfiguration->receiveAudioVideoSource = sampleReceiveVideoFrame; pSampleConfiguration->onDataChannel = onDataChannel; #ifdef KVS_WITH_WEBRTC pSampleConfiguration->mediaType = SAMPLE_STREAMING_VIDEO_ONLY; #else pSampleConfiguration->mediaType = SAMPLE_STREAMING_AUDIO_VIDEO; #endif printf("[KVS Master] Finished setting audio and video handlers\n"); // Check if the samples are present #ifdef KVS_WITH_WEBRTC /* Media source is fed from "kvs_with_webrtc.c", so we don't need to check the media source here. */ #else retStatus = readFrameFromDisk(NULL, &frameSize, "./h264SampleFrames/frame-0001.h264"); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] readFrameFromDisk(): operation returned status code: 0x%08x \n", retStatus); goto CleanUp; } printf("[KVS Master] Checked sample video frame availability....available\n"); retStatus = readFrameFromDisk(NULL, &frameSize, "./opusSampleFrames/sample-001.opus"); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] readFrameFromDisk(): operation returned status code: 0x%08x \n", retStatus); goto CleanUp; } printf("[KVS Master] Checked sample audio frame availability....available\n"); #endif // Initialize KVS WebRTC. This must be done before anything else, and must only be done once. retStatus = initKvsWebRtc(); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] initKvsWebRtc(): operation returned status code: 0x%08x \n", retStatus); goto CleanUp; } printf("[KVS Master] KVS WebRTC initialization completed successfully\n"); pSampleConfiguration->signalingClientCallbacks.messageReceivedFn = signalingMessageReceived; strcpy(pSampleConfiguration->clientInfo.clientId, SAMPLE_MASTER_CLIENT_ID); retStatus = createSignalingClientSync(&pSampleConfiguration->clientInfo, &pSampleConfiguration->channelInfo, &pSampleConfiguration->signalingClientCallbacks, pSampleConfiguration->pCredentialProvider, &pSampleConfiguration->signalingClientHandle); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] createSignalingClientSync(): operation returned status code: 0x%08x \n", retStatus); goto CleanUp; } printf("[KVS Master] Signaling client created successfully\n"); // Enable the processing of the messages retStatus = signalingClientConnectSync(pSampleConfiguration->signalingClientHandle); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] signalingClientConnectSync(): operation returned status code: 0x%08x \n", retStatus); goto CleanUp; } printf("[KVS Master] Signaling client connection to socket established\n"); gSampleConfiguration = pSampleConfiguration; printf("[KVS Master] Channel %s set up done \n", pChannelName); // Checking for termination retStatus = sessionCleanupWait(pSampleConfiguration); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] sessionCleanupWait(): operation returned status code: 0x%08x \n", retStatus); goto CleanUp; } printf("[KVS Master] Streaming session terminated\n"); CleanUp: if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] Terminated with status code 0x%08x", retStatus); } printf("[KVS Master] Cleaning up....\n"); if (pSampleConfiguration != NULL) { // Kick of the termination sequence ATOMIC_STORE_BOOL(&pSampleConfiguration->appTerminateFlag, TRUE); if (pSampleConfiguration->mediaSenderTid != INVALID_TID_VALUE) { THREAD_JOIN(pSampleConfiguration->mediaSenderTid, NULL); } if (pSampleConfiguration->enableFileLogging) { freeFileLogger(); } retStatus = signalingClientGetMetrics(pSampleConfiguration->signalingClientHandle, &signalingClientMetrics); if (retStatus == STATUS_SUCCESS) { logSignalingClientStats(&signalingClientMetrics); } else { printf("[KVS Master] signalingClientGetMetrics() operation returned status code: 0x%08x", retStatus); } retStatus = freeSignalingClient(&pSampleConfiguration->signalingClientHandle); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] freeSignalingClient(): operation returned status code: 0x%08x", retStatus); } retStatus = freeSampleConfiguration(&pSampleConfiguration); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] freeSampleConfiguration(): operation returned status code: 0x%08x", retStatus); } gSampleConfiguration = NULL; } printf("[KVS Master] Cleanup done\n"); RESET_INSTRUMENTED_ALLOCATORS(); // https://www.gnu.org/software/libc/manual/html_node/Exit-Status.html // We can only return with 0 - 127. Some platforms treat exit code >= 128 // to be a success code, which might give an unintended behaviour. // Some platforms also treat 1 or 0 differently, so it's better to use // EXIT_FAILURE and EXIT_SUCCESS macros for portability. return STATUS_FAILED(retStatus) ? EXIT_FAILURE : EXIT_SUCCESS; } STATUS readFrameFromDisk(PBYTE pFrame, PUINT32 pSize, PCHAR frameFilePath) { STATUS retStatus = STATUS_SUCCESS; UINT64 size = 0; if (pSize == NULL) { printf("[KVS Master] readFrameFromDisk(): operation returned status code: 0x%08x \n", STATUS_NULL_ARG); goto CleanUp; } size = *pSize; // Get the size and read into frame retStatus = readFile(frameFilePath, TRUE, pFrame, &size); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] readFile(): operation returned status code: 0x%08x \n", retStatus); goto CleanUp; } CleanUp: if (pSize != NULL) { *pSize = (UINT32) size; } return retStatus; } #ifdef KVS_WITH_WEBRTC /* Media source is fed from "kvs_with_webrtc.c", so we don't need to the source feeder here. */ #else PVOID sendVideoPackets(PVOID args) { STATUS retStatus = STATUS_SUCCESS; PSampleConfiguration pSampleConfiguration = (PSampleConfiguration) args; RtcEncoderStats encoderStats; Frame frame; UINT32 fileIndex = 0, frameSize; CHAR filePath[MAX_PATH_LEN + 1]; STATUS status; UINT32 i; UINT64 startTime, lastFrameTime, elapsed; MEMSET(&encoderStats, 0x00, SIZEOF(RtcEncoderStats)); if (pSampleConfiguration == NULL) { printf("[KVS Master] sendVideoPackets(): operation returned status code: 0x%08x \n", STATUS_NULL_ARG); goto CleanUp; } frame.presentationTs = 0; startTime = GETTIME(); lastFrameTime = startTime; while (!ATOMIC_LOAD_BOOL(&pSampleConfiguration->appTerminateFlag)) { fileIndex = fileIndex % NUMBER_OF_H264_FRAME_FILES + 1; snprintf(filePath, MAX_PATH_LEN, "./h264SampleFrames/frame-%04d.h264", fileIndex); retStatus = readFrameFromDisk(NULL, &frameSize, filePath); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] readFrameFromDisk(): operation returned status code: 0x%08x \n", retStatus); goto CleanUp; } // Re-alloc if needed if (frameSize > pSampleConfiguration->videoBufferSize) { pSampleConfiguration->pVideoFrameBuffer = (PBYTE) MEMREALLOC(pSampleConfiguration->pVideoFrameBuffer, frameSize); if (pSampleConfiguration->pVideoFrameBuffer == NULL) { printf("[KVS Master] Video frame Buffer reallocation failed...%s (code %d)\n", strerror(errno), errno); printf("[KVS Master] MEMREALLOC(): operation returned status code: 0x%08x \n", STATUS_NOT_ENOUGH_MEMORY); goto CleanUp; } pSampleConfiguration->videoBufferSize = frameSize; } frame.frameData = pSampleConfiguration->pVideoFrameBuffer; frame.size = frameSize; retStatus = readFrameFromDisk(frame.frameData, &frameSize, filePath); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] readFrameFromDisk(): operation returned status code: 0x%08x \n", retStatus); goto CleanUp; } // based on bitrate of samples/h264SampleFrames/frame-* encoderStats.width = 640; encoderStats.height = 480; encoderStats.targetBitrate = 262000; frame.presentationTs += SAMPLE_VIDEO_FRAME_DURATION; MUTEX_LOCK(pSampleConfiguration->streamingSessionListReadLock); for (i = 0; i < pSampleConfiguration->streamingSessionCount; ++i) { status = writeFrame(pSampleConfiguration->sampleStreamingSessionList[i]->pVideoRtcRtpTransceiver, &frame); encoderStats.encodeTimeMsec = 4; // update encode time to an arbitrary number to demonstrate stats update updateEncoderStats(pSampleConfiguration->sampleStreamingSessionList[i]->pVideoRtcRtpTransceiver, &encoderStats); if (status != STATUS_SRTP_NOT_READY_YET) { if (status != STATUS_SUCCESS) { #ifdef VERBOSE printf("writeFrame() failed with 0x%08x\n", status); #endif } } } MUTEX_UNLOCK(pSampleConfiguration->streamingSessionListReadLock); // Adjust sleep in the case the sleep itself and writeFrame take longer than expected. Since sleep makes sure that the thread // will be paused at least until the given amount, we can assume that there's no too early frame scenario. // Also, it's very unlikely to have a delay greater than SAMPLE_VIDEO_FRAME_DURATION, so the logic assumes that this is always // true for simplicity. elapsed = lastFrameTime - startTime; THREAD_SLEEP(SAMPLE_VIDEO_FRAME_DURATION - elapsed % SAMPLE_VIDEO_FRAME_DURATION); lastFrameTime = GETTIME(); } CleanUp: CHK_LOG_ERR(retStatus); return (PVOID) (ULONG_PTR) retStatus; } PVOID sendAudioPackets(PVOID args) { STATUS retStatus = STATUS_SUCCESS; PSampleConfiguration pSampleConfiguration = (PSampleConfiguration) args; Frame frame; UINT32 fileIndex = 0, frameSize; CHAR filePath[MAX_PATH_LEN + 1]; UINT32 i; STATUS status; if (pSampleConfiguration == NULL) { printf("[KVS Master] sendAudioPackets(): operation returned status code: 0x%08x \n", STATUS_NULL_ARG); goto CleanUp; } frame.presentationTs = 0; while (!ATOMIC_LOAD_BOOL(&pSampleConfiguration->appTerminateFlag)) { fileIndex = fileIndex % NUMBER_OF_OPUS_FRAME_FILES + 1; snprintf(filePath, MAX_PATH_LEN, "./opusSampleFrames/sample-%03d.opus", fileIndex); retStatus = readFrameFromDisk(NULL, &frameSize, filePath); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] readFrameFromDisk(): operation returned status code: 0x%08x \n", retStatus); goto CleanUp; } // Re-alloc if needed if (frameSize > pSampleConfiguration->audioBufferSize) { pSampleConfiguration->pAudioFrameBuffer = (UINT8*) MEMREALLOC(pSampleConfiguration->pAudioFrameBuffer, frameSize); if (pSampleConfiguration->pAudioFrameBuffer == NULL) { printf("[KVS Master] Audio frame Buffer reallocation failed...%s (code %d)\n", strerror(errno), errno); printf("[KVS Master] MEMREALLOC(): operation returned status code: 0x%08x \n", STATUS_NOT_ENOUGH_MEMORY); goto CleanUp; } } frame.frameData = pSampleConfiguration->pAudioFrameBuffer; frame.size = frameSize; retStatus = readFrameFromDisk(frame.frameData, &frameSize, filePath); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] readFrameFromDisk(): operation returned status code: 0x%08x \n", retStatus); goto CleanUp; } frame.presentationTs += SAMPLE_AUDIO_FRAME_DURATION; MUTEX_LOCK(pSampleConfiguration->streamingSessionListReadLock); for (i = 0; i < pSampleConfiguration->streamingSessionCount; ++i) { status = writeFrame(pSampleConfiguration->sampleStreamingSessionList[i]->pAudioRtcRtpTransceiver, &frame); if (status != STATUS_SRTP_NOT_READY_YET) { if (status != STATUS_SUCCESS) { #ifdef VERBOSE printf("writeFrame() failed with 0x%08x\n", status); #endif } } } MUTEX_UNLOCK(pSampleConfiguration->streamingSessionListReadLock); THREAD_SLEEP(SAMPLE_AUDIO_FRAME_DURATION); } CleanUp: return (PVOID) (ULONG_PTR) retStatus; } #endif PVOID sampleReceiveVideoFrame(PVOID args) { STATUS retStatus = STATUS_SUCCESS; PSampleStreamingSession pSampleStreamingSession = (PSampleStreamingSession) args; if (pSampleStreamingSession == NULL) { printf("[KVS Master] sampleReceiveVideoFrame(): operation returned status code: 0x%08x \n", STATUS_NULL_ARG); goto CleanUp; } retStatus = transceiverOnFrame(pSampleStreamingSession->pVideoRtcRtpTransceiver, (UINT64) pSampleStreamingSession, sampleFrameHandler); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] transceiverOnFrame(): operation returned status code: 0x%08x \n", retStatus); goto CleanUp; } CleanUp: return (PVOID) (ULONG_PTR) retStatus; } #ifdef KVS_WITH_WEBRTC static int webrtc_onRtpResend(uint8_t **ppData, size_t *puLen, void *pAppData) { int res = 0; FrameKeyHandle keyHandle = (FrameKeyHandle)pAppData; res = FrameRingBuffer_getFrame(keyHandle, ppData, puLen); return res; } int webrtc_taskAddVideoFrame(uint8_t *pData, size_t uLen, uint64_t uTimestamp, FrameKeyHandle keyHandle) { PSampleConfiguration pSampleConfiguration = gSampleConfiguration; Frame frame = {0}; FrameEx frameEx = {0}; STATUS status; UINT32 i; if (pSampleConfiguration != NULL) { frame.frameData = pData; frame.size = uLen; frame.presentationTs = GETTIME(); frameEx.onRtpResend = webrtc_onRtpResend; frameEx.pAppData = keyHandle; MUTEX_LOCK(pSampleConfiguration->streamingSessionListReadLock); for (i = 0; i < pSampleConfiguration->streamingSessionCount; ++i) { status = writeFrameEx(pSampleConfiguration->sampleStreamingSessionList[i]->pVideoRtcRtpTransceiver, &frame, &frameEx); } MUTEX_UNLOCK(pSampleConfiguration->streamingSessionListReadLock); } return 0; } #endif /* #ifdef KVS_WITH_WEBRTC */