### Build and run the generic libpeer example Source: https://github.com/sepfy/libpeer/blob/main/README.md Commands to install dependencies, clone the repository, build the project, and execute the sample application with a signaling URL. ```bash $ sudo apt -y install git cmake $ git clone --recursive https://github.com/sepfy/libpeer $ cd libpeer $ cmake -S . -B build && cmake --build build $ wget http://www.live555.com/liveMedia/public/264/test.264 # Download test video file $ wget https://mauvecloud.net/sounds/alaw08m.wav # Download test audio file $ ./examples/generic/sample -u ``` -------------------------------- ### Build the Project Source: https://github.com/sepfy/libpeer/blob/main/examples/pico/README.md Navigate to the PICO example directory and generate build files using CMake. ```bash $ cd libpeer/examples/pico $ mkdir build $ cd build $ cmake .. ``` -------------------------------- ### Download Required Libraries Source: https://github.com/sepfy/libpeer/blob/main/examples/esp32/README.md Clones the libpeer library and navigates to the ESP32 examples directory. This is necessary for setting up the WebRTC streaming functionality. ```bash git clone https://github.com/sepfy/libpeer cd libpeer/examples/esp32 ``` -------------------------------- ### Install esp-idf Source: https://github.com/sepfy/libpeer/blob/main/examples/esp32/README.md Installs the ESP-IDF development framework, version 5.2 or higher. Ensure you source the export script after installation. ```bash git clone -b v5.2.2 https://github.com/espressif/esp-idf.git cd esp-idf source install.sh source export.sh ``` -------------------------------- ### Build libpeer on Raspberry Pi Source: https://github.com/sepfy/libpeer/blob/main/examples/raspberrypi/README.md Clones the libpeer repository recursively, navigates to the Raspberry Pi examples directory, creates a build directory, configures with CMake, and compiles the project. ```bash $ git clone --recursive https://github.com/sepfy/libpeer $ cd libpeer/examples/raspberrypi $ mkdir build && cd build $ cmake .. $ make ``` -------------------------------- ### ESP32 WebRTC Camera Streaming Example Source: https://context7.com/sepfy/libpeer/llms.txt This C code implements a full example for ESP32 microcontrollers, demonstrating camera streaming over a WebRTC DataChannel with MQTT signaling. It requires ESP-IDF framework and FreeRTOS tasks for managing peer connections and signaling. ```c #include #include "esp_log.h" #include "esp_system.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" #include "nvs_flash.h" #include "protocol_examples_common.h" #include "peer.h" static const char* TAG = "webrtc-camera"; PeerConnection* g_pc = NULL; PeerConnectionState g_state = PEER_CONNECTION_CLOSED; SemaphoreHandle_t g_mutex = NULL; int g_datachannel_ready = 0; void on_state_change(PeerConnectionState state, void* userdata) { ESP_LOGI(TAG, "State: %d", state); g_state = state; } void on_message(char* msg, size_t len, void* userdata, uint16_t sid) { ESP_LOGI(TAG, "Message: %.*s", (int)len, msg); } void on_open(void* userdata) { ESP_LOGI(TAG, "DataChannel opened"); g_datachannel_ready = 1; } void on_close(void* userdata) { ESP_LOGI(TAG, "DataChannel closed"); g_datachannel_ready = 0; } void peer_connection_task(void* arg) { while (1) { if (xSemaphoreTake(g_mutex, portMAX_DELAY)) { peer_connection_loop(g_pc); xSemaphoreGive(g_mutex); } vTaskDelay(pdMS_TO_TICKS(1)); } } void camera_task(void* arg) { uint8_t* jpeg_buffer; size_t jpeg_size; while (1) { if (g_datachannel_ready) { // Capture JPEG frame from camera // jpeg_size = camera_capture(&jpeg_buffer); jpeg_size = 0; // placeholder if (jpeg_size > 0) { if (xSemaphoreTake(g_mutex, pdMS_TO_TICKS(100))) { peer_connection_datachannel_send(g_pc, (char*)jpeg_buffer, jpeg_size); xSemaphoreGive(g_mutex); } } } vTaskDelay(pdMS_TO_TICKS(33)); // ~30 FPS } } void app_main(void) { ESP_LOGI(TAG, "Starting WebRTC Camera"); ESP_LOGI(TAG, "Free heap: %d bytes", esp_get_free_heap_size()); // Initialize NVS and network ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); ESP_ERROR_CHECK(example_connect()); g_mutex = xSemaphoreCreateMutex(); // Initialize libpeer peer_init(); // Configure peer connection PeerConfiguration config = { .ice_servers = {{.urls = "stun:stun.l.google.com:19302"}}, .audio_codec = CODEC_PCMA, .datachannel = DATA_CHANNEL_BINARY }; g_pc = peer_connection_create(&config); peer_connection_oniceconnectionstatechange(g_pc, on_state_change); peer_connection_ondatachannel(g_pc, on_message, on_open, on_close); // Connect to signaling server (configure via menuconfig) peer_signaling_connect( "mqtts://broker.example.com/devices/esp32-cam", "base64_encoded_credentials", g_pc ); // Start tasks xTaskCreatePinnedToCore(peer_connection_task, "peer", 8192, NULL, 5, NULL, 1); xTaskCreatePinnedToCore(camera_task, "camera", 4096, NULL, 8, NULL, 1); // Main signaling loop while (1) { peer_signaling_loop(); vTaskDelay(pdMS_TO_TICKS(10)); } } ``` -------------------------------- ### Install Dependencies for Raspberry Pi OS Source: https://github.com/sepfy/libpeer/blob/main/examples/raspberrypi/README.md Installs necessary GStreamer and development packages on Raspberry Pi OS Lite. Ensure your system is updated before running. ```bash $ sudo apt update $ sudo apt install -y gstreamer1.0-libcamera gstreamer1.0-alsa gstreamer1.0-tools gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-plugins-good libgstreamer1.0-dev git cmake ``` -------------------------------- ### Configure and Connect to WHIP Server Source: https://github.com/sepfy/libpeer/wiki/WebRTC-Server Modify service_config with the WHIP server URL and use peer_signaling_whip_connect to establish the connection. This example disables the keep-alive check, which is necessary for servers like ZLMediaKit that do not send STUN packets. ```c #define KEEPALIVE_CONNCHECK 0 ``` ```c service_config.http_url = "127.0.0.1/index/api/whip?app=live&stream=test"; peer_signaling_set_config(&service_config); peer_signaling_whip_connect(); ``` -------------------------------- ### Configure Sample Project Build with CMake Source: https://github.com/sepfy/libpeer/blob/main/examples/generic/CMakeLists.txt Defines the project name, finds C source files, includes header directories, creates an executable, and links necessary libraries. ```cmake project(sample) file(GLOB SRCS "*.c") include_directories(${CMAKE_SOURCE_DIR}/src) add_executable(sample ${SRCS}) target_link_libraries(sample peer pthread) ``` -------------------------------- ### Flash the Firmware Source: https://github.com/sepfy/libpeer/blob/main/examples/pico/README.md Use picotool to load the generated UF2 file onto the PICO W device. ```bash $ sudo picotool load -f pico_peer.uf2 ``` -------------------------------- ### Create a PeerConnection with Configuration Source: https://context7.com/sepfy/libpeer/llms.txt Configure ICE servers, codecs, and callbacks before creating a PeerConnection. Ensure to destroy the connection with peer_connection_destroy when finished. ```c #include "peer.h" // Callback for receiving audio data from remote peer void on_audio_received(uint8_t* data, size_t size, void* userdata) { // Process received audio data printf("Received %zu bytes of audio\n", size); } // Callback for receiving video data from remote peer void on_video_received(uint8_t* data, size_t size, void* userdata) { // Process received video data printf("Received %zu bytes of video\n", size); } // Callback when remote peer requests a keyframe void on_keyframe_request(void* userdata) { printf("Remote peer requested keyframe\n"); // Generate and send a keyframe } int main() { peer_init(); // Configure the peer connection PeerConfiguration config = { .ice_servers = { {.urls = "stun:stun.l.google.com:19302"}, {.urls = "turn:turn.example.com:3478", .username = "user", .credential = "password"} }, .datachannel = DATA_CHANNEL_STRING, // Enable string datachannel .video_codec = CODEC_H264, // Use H.264 video .audio_codec = CODEC_PCMA, // Use G.711 A-law audio .onaudiotrack = on_audio_received, .onvideotrack = on_video_received, .on_request_keyframe = on_keyframe_request, .user_data = NULL }; PeerConnection* pc = peer_connection_create(&config); if (pc == NULL) { printf("Failed to create peer connection\n"); return -1; } // Use peer connection... peer_connection_destroy(pc); peer_deinit(); return 0; } ``` -------------------------------- ### Configure Build Environment Source: https://github.com/sepfy/libpeer/blob/main/examples/pico/README.md Set the necessary environment variables for the PICO SDK, FreeRTOS kernel, and Wi-Fi credentials. ```bash $ export PICO_SDK_PATH=/pico-sdk $ export FREERTOS_KERNEL_PATH=/FreeRTOS-Kernel $ export WIFI_SSID= $ export WIFI_PASSWORD= ``` -------------------------------- ### Initialize and Deinitialize libpeer Source: https://context7.com/sepfy/libpeer/llms.txt Call peer_init() before creating any peer connections and peer_deinit() after destroying all connections to manage global state. ```c #include "peer.h" int main() { // Initialize libpeer library peer_init(); // ... create peer connections and use WebRTC features ... // Cleanup when done peer_deinit(); return 0; } ``` -------------------------------- ### Define Project and Source Files Source: https://github.com/sepfy/libpeer/blob/main/tests/CMakeLists.txt Initializes the CMake project and discovers all C source files in the current directory. ```cmake project(tests) file(GLOB SRCS "*.c") ``` -------------------------------- ### Clone Required Repositories Source: https://github.com/sepfy/libpeer/blob/main/examples/pico/README.md Download the libpeer library, PICO SDK, and FreeRTOS kernel using recursive cloning. ```bash $ git clone --recursive https://github.com/sepfy/libpeer $ git clone --recurisve https://github.com/raspberrypi/pico-sdk $ git clone --recursive https://github.com/FreeRTOS/FreeRTOS-Kernel ``` -------------------------------- ### Generate SDP Offers and Answers Source: https://context7.com/sepfy/libpeer/llms.txt Demonstrates manual SDP negotiation using peer_connection_create_offer and peer_connection_create_answer. Requires a signaling mechanism to exchange the generated SDP strings with the remote peer. ```c #include "peer.h" PeerConnection* g_pc = NULL; void on_ice_candidate(char* sdp, void* userdata) { printf("Local SDP generated:\n%s\n", sdp); // Send this SDP to remote peer via your signaling mechanism // send_sdp_to_remote(sdp); } void handle_remote_offer(const char* remote_sdp) { // Set the remote offer peer_connection_set_remote_description(g_pc, remote_sdp, SDP_TYPE_OFFER); // Create and send answer const char* answer = peer_connection_create_answer(g_pc); printf("Created answer:\n%s\n", answer); // send_answer_to_remote(answer); } void initiate_connection() { // Create offer to start the connection const char* offer = peer_connection_create_offer(g_pc); printf("Created offer:\n%s\n", offer); // send_offer_to_remote(offer); } int main() { peer_init(); PeerConfiguration config = { .ice_servers = {{.urls = "stun:stun.l.google.com:19302"}}, .video_codec = CODEC_H264, .datachannel = DATA_CHANNEL_STRING }; g_pc = peer_connection_create(&config); // Register callback for when local SDP is ready peer_connection_onicecandidate(g_pc, on_ice_candidate); // Either initiate or respond to connection // initiate_connection(); // If we're the caller // handle_remote_offer(received_sdp); // If we're the callee peer_connection_destroy(g_pc); peer_deinit(); return 0; } ``` -------------------------------- ### Configure ESP32 Build Source: https://github.com/sepfy/libpeer/blob/main/examples/esp32/README.md Configures the ESP32 project using `idf.py menuconfig`. Set the Signaling URL and WiFi credentials for connection. ```bash idf.py menuconfig # Peer Connection Configuration -> Signaling URL # Example Connection Configuration -> WiFi SSID and WiFi Password ``` -------------------------------- ### Manage Peer Connection Signaling and ICE Candidates in C Source: https://context7.com/sepfy/libpeer/llms.txt Demonstrates handling incoming SDP offers/answers and adding remote ICE candidates using the libpeer library. ```c #include "peer.h" PeerConnection* g_pc = NULL; // Example of handling incoming SDP from signaling void on_signaling_message(const char* type, const char* sdp) { if (strcmp(type, "offer") == 0) { // Received offer from remote peer peer_connection_set_remote_description(g_pc, sdp, SDP_TYPE_OFFER); // Generate and send answer const char* answer = peer_connection_create_answer(g_pc); // send_to_signaling("answer", answer); } else if (strcmp(type, "answer") == 0) { // Received answer to our offer peer_connection_set_remote_description(g_pc, sdp, SDP_TYPE_ANSWER); // ICE checking starts automatically after setting answer } } // Example of trickle ICE candidate handling void on_ice_candidate_received(const char* candidate) { // Add remote ICE candidate int result = peer_connection_add_ice_candidate(g_pc, (char*)candidate); if (result == 0) { printf("Added remote ICE candidate\n"); } else { printf("Failed to parse ICE candidate\n"); } } int main() { peer_init(); PeerConfiguration config = { .ice_servers = {{.urls = "stun:stun.l.google.com:19302"}}, .video_codec = CODEC_H264 }; g_pc = peer_connection_create(&config); // Simulate receiving an offer const char* remote_offer = "v=0\r\n" "o=- 123456789 2 IN IP4 127.0.0.1\r\n" "s=-\r\n" "t=0 0\r\n" "a=ice-ufrag:abc123\r\n" "a=ice-pwd:verylongpassword\r\n" "a=fingerprint:sha-256 AA:BB:CC:...\r\n" "a=setup:actpass\r\n" "m=video 9 UDP/TLS/RTP/SAVPF 96\r\n" "a=rtpmap:96 H264/90000\r\n"; on_signaling_message("offer", remote_offer); peer_connection_destroy(g_pc); peer_deinit(); return 0; } ``` -------------------------------- ### Configure CMakeLists.txt for libpeer Source: https://github.com/sepfy/libpeer/blob/main/examples/esp32/CMakeLists.txt These lines must appear in the project's CMakeLists.txt file in the specified order to ensure correct project initialization. ```cmake cmake_minimum_required(VERSION 3.16) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(esp32-peer) # Uncomment if using usrsctp. #idf_build_set_property(COMPILE_OPTIONS "-DHAVE_USRSCTP" APPEND) ``` -------------------------------- ### Build ESP32 Project Source: https://github.com/sepfy/libpeer/blob/main/examples/esp32/README.md Builds the ESP32 project using the ESP-IDF build system. This step compiles the code and prepares it for flashing. ```bash idf.py build ``` -------------------------------- ### Create Data Channels Source: https://context7.com/sepfy/libpeer/llms.txt Demonstrates creating different types of data channels: reliable ordered, reliable unordered, and partial reliable with retransmissions. Ensure the PeerConnection object is initialized before calling these functions. ```c #include "peer.h" PeerConnection* g_pc = NULL; void create_datachannels() { // Create a reliable ordered datachannel peer_connection_create_datachannel( g_pc, DATA_CHANNEL_RELIABLE, // Reliable and ordered 0, // Priority 0, // Reliability parameter (unused for reliable) "control", // Label "" // Protocol ); // Create an unreliable unordered datachannel for real-time data peer_connection_create_datachannel( g_pc, DATA_CHANNEL_RELIABLE_UNORDERED, // Reliable but unordered 0, 0, "sensor-data", "" ); // Create a partial reliable datachannel with max retransmits peer_connection_create_datachannel_sid( g_pc, DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT, // Limited retransmissions 0, 3, // Max 3 retransmits "video-metadata", "", 1 // Stream ID ); } int main() { peer_init(); PeerConfiguration config = { .ice_servers = {{.urls = "stun:stun.l.google.com:19302"}}, .datachannel = DATA_CHANNEL_BINARY }; g_pc = peer_connection_create(&config); // ... after connection is established ... create_datachannels(); peer_connection_destroy(g_pc); peer_deinit(); return 0; } ``` -------------------------------- ### Raspberry Pi GStreamer Media Integration Source: https://context7.com/sepfy/libpeer/llms.txt This C code sets up GStreamer pipelines for camera capture, microphone input, and speaker output, and integrates them with libpeer for WebRTC signaling and media transport. ```c #include #include #include #include #include #include "peer.h" // GStreamer pipelines for Raspberry Pi with libcamera and ALSA const char CAMERA_PIPELINE[] = "libcamerasrc ! video/x-raw,format=NV12,width=1280,height=960,framerate=30/1 ! " "v4l2h264enc ! video/x-h264,stream-format=byte-stream ! " "h264parse config-interval=-1 ! appsink name=video-sink"; const char MIC_PIPELINE[] = "alsasrc device=plughw:0,0 ! audio/x-raw,format=S16LE,rate=8000,channels=1 ! " "alawenc ! appsink name=audio-sink"; const char SPEAKER_PIPELINE[] = "appsrc name=audio-src format=time ! alawdec ! " "audio/x-raw,format=S16LE,rate=8000,channels=1 ! alsasink sync=false"; typedef struct { GstElement *video_pipeline, *video_sink; GstElement *mic_pipeline, *mic_sink; GstElement *speaker_pipeline, *speaker_src; } MediaPipelines; PeerConnection* g_pc = NULL; MediaPipelines g_media; int g_running = 1; // Called when new video frame is available GstFlowReturn on_video_sample(GstElement* sink, void* data) { GstSample* sample; GstBuffer* buffer; GstMapInfo info; g_signal_emit_by_name(sink, "pull-sample", &sample); if (sample) { buffer = gst_sample_get_buffer(sample); gst_buffer_map(buffer, &info, GST_MAP_READ); peer_connection_send_video(g_pc, info.data, info.size); gst_buffer_unmap(buffer, &info); gst_sample_unref(sample); return GST_FLOW_OK; } return GST_FLOW_ERROR; } // Called when new audio frame is available GstFlowReturn on_audio_sample(GstElement* sink, void* data) { GstSample* sample; GstBuffer* buffer; GstMapInfo info; g_signal_emit_by_name(sink, "pull-sample", &sample); if (sample) { buffer = gst_sample_get_buffer(sample); gst_buffer_map(buffer, &info, GST_MAP_READ); peer_connection_send_audio(g_pc, info.data, info.size); gst_buffer_unmap(buffer, &info); gst_sample_unref(sample); return GST_FLOW_OK; } return GST_FLOW_ERROR; } // Callback for receiving audio from remote peer void on_remote_audio(uint8_t* data, size_t size, void* userdata) { GstBuffer* buffer = gst_buffer_new_allocate(NULL, size, NULL); gst_buffer_fill(buffer, 0, data, size); GstFlowReturn ret; g_signal_emit_by_name(g_media.speaker_src, "push-buffer", buffer, &ret); gst_buffer_unref(buffer); } void on_state_change(PeerConnectionState state, void* data) { printf("State: %s\n", peer_connection_state_to_string(state)); if (state == PEER_CONNECTION_COMPLETED) { // Start media pipelines when connected gst_element_set_state(g_media.video_pipeline, GST_STATE_PLAYING); gst_element_set_state(g_media.mic_pipeline, GST_STATE_PLAYING); gst_element_set_state(g_media.speaker_pipeline, GST_STATE_PLAYING); } } int main(int argc, char* argv[]) { const char* url = argv[1]; // MQTT or WHIP URL const char* token = argc > 2 ? argv[2] : NULL; pthread_t signaling_thread, connection_thread; signal(SIGINT, ^(int sig) { g_running = 0; }); // Initialize GStreamer gst_init(&argc, &argv); // Setup video pipeline g_media.video_pipeline = gst_parse_launch(CAMERA_PIPELINE, NULL); g_media.video_sink = gst_bin_get_by_name(GST_BIN(g_media.video_pipeline), "video-sink"); g_object_set(g_media.video_sink, "emit-signals", TRUE, NULL); g_signal_connect(g_media.video_sink, "new-sample", G_CALLBACK(on_video_sample), NULL); // Setup microphone pipeline g_media.mic_pipeline = gst_parse_launch(MIC_PIPELINE, NULL); g_media.mic_sink = gst_bin_get_by_name(GST_BIN(g_media.mic_pipeline), "audio-sink"); g_object_set(g_media.mic_sink, "emit-signals", TRUE, NULL); g_signal_connect(g_media.mic_sink, "new-sample", G_CALLBACK(on_audio_sample), NULL); // Setup speaker pipeline g_media.speaker_pipeline = gst_parse_launch(SPEAKER_PIPELINE, NULL); g_media.speaker_src = gst_bin_get_by_name(GST_BIN(g_media.speaker_pipeline), "audio-src"); // Initialize libpeer peer_init(); PeerConfiguration config = { .ice_servers = {{.urls = "stun:stun.l.google.com:19302"}}, .video_codec = CODEC_H264, .audio_codec = CODEC_PCMA, .onaudiotrack = on_remote_audio, .datachannel = DATA_CHANNEL_STRING }; g_pc = peer_connection_create(&config); peer_connection_oniceconnectionstatechange(g_pc, on_state_change); peer_signaling_connect(url, token, g_pc); // Run event loops in threads... // (similar to generic example) while (g_running) { peer_signaling_loop(); peer_connection_loop(g_pc); usleep(1000); } // Cleanup ``` -------------------------------- ### Build Executables and Link Libraries Source: https://github.com/sepfy/libpeer/blob/main/tests/CMakeLists.txt Iterates through discovered C source files, creates an executable for each, and links the 'peer' and 'pthread' libraries. It also links the 'cjson' library to the 'test_peer_connection' target. ```cmake foreach(sourcefile ${SRCS}) string(REPLACE ".c" "" appname ${sourcefile}) string(REPLACE "${PROJECT_SOURCE_DIR}/" "" appname ${appname}) add_executable(${appname} ${sourcefile}) target_link_libraries(${appname} peer pthread) endforeach(sourcefile ${TEST_SRCS}) target_link_libraries(test_peer_connection cjson) ``` -------------------------------- ### Initialization and Deinitialization Source: https://context7.com/sepfy/libpeer/llms.txt Functions to initialize and deinitialize the libpeer library. These must be called before and after creating any peer connections, respectively. ```APIDOC ## peer_init / peer_deinit ### Description Initializes and deinitializes the libpeer library. These functions must be called before creating any peer connections and after destroying all connections respectively. They handle global state initialization for SCTP and other subsystems. ### Method C Function Calls ### Endpoint N/A ### Parameters None ### Request Example ```c #include "peer.h" int main() { // Initialize libpeer library peer_init(); // ... create peer connections and use WebRTC features ... // Cleanup when done peer_deinit(); return 0; } ``` ### Response N/A ``` -------------------------------- ### Configure Raspberry Pi Project with CMake Source: https://github.com/sepfy/libpeer/blob/main/examples/raspberrypi/CMakeLists.txt This CMakeLists.txt file sets up the build environment for a Raspberry Pi project. It finds C source files, integrates the libpeer library, and links against GStreamer and pthread libraries. ```cmake cmake_minimum_required(VERSION 3.1) project(raspberrypi) file(GLOB SRCS "*.c") find_package(PkgConfig) pkg_check_modules(GST REQUIRED gstreamer-1.0>=1.4 gstreamer-base-1.0>=1.4) include(ExternalProject) ExternalProject_Add(libpeer SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../ CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/dist ) include_directories(${CMAKE_BINARY_DIR}/dist/include ${GST_INCLUDE_DIRS}) link_directories(${CMAKE_BINARY_DIR}/dist/lib) add_executable(raspberrypi ${SRCS}) target_link_libraries(raspberrypi peer pthread ${GST_LIBRARIES}) ``` -------------------------------- ### Test Raspberry Pi Camera Stream Source: https://github.com/sepfy/libpeer/blob/main/examples/raspberrypi/README.md Executes the compiled Raspberry Pi application, connecting to a specified URL obtained from the test website. Requires the URL to be provided as an argument. ```bash $ ./raspberrypi -u ``` -------------------------------- ### Connect to Signaling Server Source: https://context7.com/sepfy/libpeer/llms.txt Establishes a connection to a signaling server using MQTT or WHIP protocols. The URL specifies the protocol and endpoint, while the token provides authentication. Requires separate threads for signaling and connection loops. ```c #include "peer.h" #include PeerConnection* g_pc = NULL; int g_running = 1; void* signaling_task(void* data) { while (g_running) { peer_signaling_loop(); usleep(1000); } pthread_exit(NULL); } void* connection_task(void* data) { while (g_running) { peer_connection_loop(g_pc); usleep(1000); } pthread_exit(NULL); } int main(int argc, char* argv[]) { const char* url = "mqtts://broker.example.com/devices/camera001"; const char* token = "dXNlcm5hbWU6cGFzc3dvcmQ="; // base64(username:password) pthread_t signaling_thread, connection_thread; peer_init(); PeerConfiguration config = { .ice_servers = {{.urls = "stun:stun.l.google.com:19302"}}, .video_codec = CODEC_H264, .audio_codec = CODEC_PCMA, .datachannel = DATA_CHANNEL_STRING }; g_pc = peer_connection_create(&config); // Connect to MQTT signaling server peer_signaling_connect(url, token, g_pc); // Start processing loops pthread_create(&connection_thread, NULL, connection_task, NULL); pthread_create(&signaling_thread, NULL, signaling_task, NULL); // Run main application... sleep(300); g_running = 0; pthread_join(signaling_thread, NULL); pthread_join(connection_thread, NULL); peer_signaling_disconnect(); peer_connection_destroy(g_pc); peer_deinit(); return 0; } ``` -------------------------------- ### Flash ESP32 Device Source: https://github.com/sepfy/libpeer/blob/main/examples/esp32/README.md Flashes the compiled project onto the ESP32 device. This command uploads the firmware to the hardware. ```bash idf.py flash ``` -------------------------------- ### Register DataChannel Callbacks with peer_connection_ondatachannel Source: https://context7.com/sepfy/libpeer/llms.txt Registers callback functions for message reception, channel opening, and channel closure. Ensure the PeerConfiguration is set to enable DataChannels before creating the connection. ```c #include "peer.h" #include PeerConnection* g_pc = NULL; int g_datachannel_open = 0; void on_datachannel_message(char* msg, size_t len, void* userdata, uint16_t sid) { printf("Received message on stream %d: %.*s\n", sid, (int)len, msg); // Echo ping-pong example if (strncmp(msg, "ping", 4) == 0) { printf("Sending pong response\n"); peer_connection_datachannel_send(g_pc, "pong", 4); } } void on_datachannel_open(void* userdata) { printf("DataChannel opened - ready to send messages\n"); g_datachannel_open = 1; // Send initial message peer_connection_datachannel_send(g_pc, "Hello from libpeer!", 19); } void on_datachannel_close(void* userdata) { printf("DataChannel closed\n"); g_datachannel_open = 0; } int main() { peer_init(); PeerConfiguration config = { .ice_servers = {{.urls = "stun:stun.l.google.com:19302"}}, .datachannel = DATA_CHANNEL_STRING // Enable string datachannel }; g_pc = peer_connection_create(&config); // Register datachannel callbacks peer_connection_ondatachannel(g_pc, on_datachannel_message, on_datachannel_open, on_datachannel_close); // ... establish connection and run loop ... peer_connection_destroy(g_pc); peer_deinit(); return 0; } ``` -------------------------------- ### Peer Connection Creation Source: https://context7.com/sepfy/libpeer/llms.txt Creates a new PeerConnection instance with specified configuration, including ICE servers, codecs, and datachannel settings. The created PeerConnection must be destroyed using `peer_connection_destroy`. ```APIDOC ## peer_connection_create ### Description Creates a new PeerConnection instance with the specified configuration. The configuration includes ICE servers, audio/video codecs, and datachannel settings. Returns a pointer to the PeerConnection that must be destroyed with peer_connection_destroy when no longer needed. ### Method C Function Call ### Endpoint N/A ### Parameters #### Request Body - **config** (PeerConfiguration*) - Required - Pointer to a structure containing the PeerConnection configuration. - **ice_servers** (IceServer[]) - Optional - Array of ICE server configurations. - **urls** (const char*) - Required - URL of the ICE server (e.g., "stun:stun.l.google.com:19302"). - **username** (const char*) - Optional - Username for TURN authentication. - **credential** (const char*) - Optional - Password for TURN authentication. - **datachannel** (DataChannelType) - Optional - Enables datachannel support (e.g., DATA_CHANNEL_STRING). - **video_codec** (CodecType) - Optional - Specifies the video codec (e.g., CODEC_H264). - **audio_codec** (CodecType) - Optional - Specifies the audio codec (e.g., CODEC_PCMA). - **onaudiotrack** (AudioCallback) - Optional - Callback function for receiving audio data. - **onvideotrack** (VideoCallback) - Optional - Callback function for receiving video data. - **on_request_keyframe** (KeyframeCallback) - Optional - Callback function when remote peer requests a keyframe. - **user_data** (void*) - Optional - User-defined data pointer passed to callbacks. ### Request Example ```c #include "peer.h" // Callback for receiving audio data from remote peer void on_audio_received(uint8_t* data, size_t size, void* userdata) { // Process received audio data printf("Received %zu bytes of audio\n", size); } // Callback for receiving video data from remote peer void on_video_received(uint8_t* data, size_t size, void* userdata) { // Process received video data printf("Received %zu bytes of video\n", size); } // Callback when remote peer requests a keyframe void on_keyframe_request(void* userdata) { printf("Remote peer requested keyframe\n"); // Generate and send a keyframe } int main() { peer_init(); // Configure the peer connection PeerConfiguration config = { .ice_servers = { {.urls = "stun:stun.l.google.com:19302"}, {.urls = "turn:turn.example.com:3478", .username = "user", .credential = "password"} }, .datachannel = DATA_CHANNEL_STRING, // Enable string datachannel .video_codec = CODEC_H264, // Use H.264 video .audio_codec = CODEC_PCMA, // Use G.711 A-law audio .onaudiotrack = on_audio_received, .onvideotrack = on_video_received, .on_request_keyframe = on_keyframe_request, .user_data = NULL }; PeerConnection* pc = peer_connection_create(&config); if (pc == NULL) { printf("Failed to create peer connection\n"); return -1; } // Use peer connection... peer_connection_destroy(pc); peer_deinit(); return 0; } ``` ### Response #### Success Response (Pointer) - **pc** (PeerConnection*) - Pointer to the newly created PeerConnection instance. Returns NULL on failure. #### Response Example ```json { "pc": "0x12345678" } ``` ``` -------------------------------- ### Run PeerConnection Event Loop in a Thread Source: https://context7.com/sepfy/libpeer/llms.txt Call peer_connection_loop repeatedly in a loop to process events. Running this in a separate thread is recommended to avoid blocking the main application. Use usleep for a small delay between iterations. ```c #include "peer.h" #include #include PeerConnection* g_pc = NULL; int g_running = 1; // Run peer connection loop in a separate thread void* peer_connection_task(void* data) { while (g_running) { peer_connection_loop(g_pc); usleep(1000); // 1ms delay between iterations } pthread_exit(NULL); } int main() { pthread_t pc_thread; peer_init(); PeerConfiguration config = { .ice_servers = {{.urls = "stun:stun.l.google.com:19302"}}, .video_codec = CODEC_H264, .audio_codec = CODEC_PCMA }; g_pc = peer_connection_create(&config); // Start peer connection loop in background thread pthread_create(&pc_thread, NULL, peer_connection_task, NULL); // Main application logic... sleep(60); g_running = 0; pthread_join(pc_thread, NULL); peer_connection_destroy(g_pc); peer_deinit(); return 0; } ``` -------------------------------- ### Register ICE Connection State Callback Source: https://context7.com/sepfy/libpeer/llms.txt Registers a callback to monitor state transitions such as NEW, CONNECTED, or FAILED. Use peer_connection_state_to_string to convert states to human-readable format. ```c #include "peer.h" PeerConnectionState g_state = PEER_CONNECTION_CLOSED; void on_connection_state_change(PeerConnectionState state, void* userdata) { printf("Connection state changed: %s\n", peer_connection_state_to_string(state)); g_state = state; switch (state) { case PEER_CONNECTION_COMPLETED: printf("WebRTC connection established - ready to send/receive media\n"); break; case PEER_CONNECTION_FAILED: printf("Connection failed - check network/ICE configuration\n"); break; case PEER_CONNECTION_CLOSED: printf("Connection closed\n"); break; default: break; } } int main() { peer_init(); PeerConfiguration config = { .ice_servers = {{.urls = "stun:stun.l.google.com:19302"}}, .video_codec = CODEC_H264 }; PeerConnection* pc = peer_connection_create(&config); // Register state change callback peer_connection_oniceconnectionstatechange(pc, on_connection_state_change); // Connection loop... while (g_state != PEER_CONNECTION_COMPLETED && g_state != PEER_CONNECTION_FAILED) { peer_connection_loop(pc); usleep(1000); } peer_connection_destroy(pc); peer_deinit(); return 0; } ``` -------------------------------- ### Include Project Headers Source: https://github.com/sepfy/libpeer/blob/main/tests/CMakeLists.txt Specifies the directory where project header files are located. ```cmake include_directories(${PROJECT_SOURCE_DIR}/../src) ``` -------------------------------- ### peer_signaling_connect Source: https://context7.com/sepfy/libpeer/llms.txt Connects to a signaling server using MQTT or HTTPS (WHIP protocol). ```APIDOC ## peer_signaling_connect ### Description Connects to a signaling server using MQTT or HTTPS (WHIP protocol). The URL determines the protocol: mqtt:// or mqtts:// for MQTT, http:// or https:// for WHIP. The token provides authentication credentials. ### Parameters - **url** (const char*) - Required - Signaling server URL (mqtt://, mqtts://, http://, or https://). - **token** (const char*) - Required - Authentication token (base64 encoded username:password for MQTT, Bearer token for WHIP). - **pc** (PeerConnection*) - Required - The peer connection instance. ``` -------------------------------- ### Register Libpeer Component Source: https://github.com/sepfy/libpeer/blob/main/examples/esp32/main/CMakeLists.txt Registers the component source files and include directories for the build system. ```cmake idf_component_register(SRCS "app_main.c" "camera.c" "audio.c" INCLUDE_DIRS "." ) ``` -------------------------------- ### Connect to WHIP Signaling Server Source: https://context7.com/sepfy/libpeer/llms.txt Uses peer_signaling_connect to establish a WebRTC connection via WHIP. Requires a valid endpoint URL and bearer token for authentication. ```c #include "peer.h" #include PeerConnection* g_pc = NULL; PeerConnectionState g_state = PEER_CONNECTION_CLOSED; void on_state_change(PeerConnectionState state, void* data) { g_state = state; printf("State: %s\n", peer_connection_state_to_string(state)); } void* connection_task(void* data) { while (g_state != PEER_CONNECTION_CLOSED) { peer_connection_loop(g_pc); usleep(1000); } return NULL; } int main() { // WHIP endpoint URL (e.g., for ZLMediaKit, Janus, or other WHIP servers) const char* whip_url = "https://media.example.com/whip/stream/test"; const char* bearer_token = "your_api_token"; pthread_t connection_thread; peer_init(); PeerConfiguration config = { .ice_servers = {{.urls = "stun:stun.l.google.com:19302"}}, .video_codec = CODEC_H264, .audio_codec = CODEC_PCMA }; g_pc = peer_connection_create(&config); peer_connection_oniceconnectionstatechange(g_pc, on_state_change); // Connect using WHIP - automatically creates offer and sends to server peer_signaling_connect(whip_url, bearer_token, g_pc); pthread_create(&connection_thread, NULL, connection_task, NULL); // Send media when connected while (g_state != PEER_CONNECTION_FAILED) { if (g_state == PEER_CONNECTION_COMPLETED) { // Send video/audio frames... } usleep(10000); } pthread_join(connection_thread, NULL); peer_signaling_disconnect(); peer_connection_destroy(g_pc); peer_deinit(); return 0; } ``` -------------------------------- ### peer_connection_create_datachannel Source: https://context7.com/sepfy/libpeer/llms.txt Creates a new DataChannel with specified reliability parameters and ordering constraints. ```APIDOC ## peer_connection_create_datachannel ### Description Creates a new DataChannel with specified reliability parameters. The channel_type controls ordering and reliability (reliable, unordered, or partial reliable with retransmit/timed modes). Returns 0 on success. ### Parameters - **pc** (PeerConnection*) - Required - The peer connection instance. - **channel_type** (int) - Required - Reliability mode (e.g., DATA_CHANNEL_RELIABLE, DATA_CHANNEL_RELIABLE_UNORDERED). - **priority** (int) - Required - Channel priority. - **reliability_param** (int) - Required - Reliability parameter (e.g., max retransmits). - **label** (const char*) - Required - Channel label. - **protocol** (const char*) - Required - Channel protocol. ``` -------------------------------- ### peer_connection_set_remote_description Source: https://context7.com/sepfy/libpeer/llms.txt Sets the remote peer's SDP (offer or answer). This function parses the SDP to extract ICE credentials, DTLS fingerprint, and media SSRCs. When setting an answer, it triggers the ICE connectivity check process. ```APIDOC ## peer_connection_set_remote_description ### Description Sets the remote peer's SDP (offer or answer). This function parses the SDP to extract ICE credentials, DTLS fingerprint, and media SSRCs. When setting an answer, it triggers the ICE connectivity check process. ### Method Not applicable (C function) ### Endpoint Not applicable (C function) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```c // Example of handling incoming SDP from signaling void on_signaling_message(const char* type, const char* sdp) { if (strcmp(type, "offer") == 0) { // Received offer from remote peer peer_connection_set_remote_description(g_pc, sdp, SDP_TYPE_OFFER); // Generate and send answer const char* answer = peer_connection_create_answer(g_pc); // send_to_signaling("answer", answer); } else if (strcmp(type, "answer") == 0) { // Received answer to our offer peer_connection_set_remote_description(g_pc, sdp, SDP_TYPE_ANSWER); // ICE checking starts automatically after setting answer } } ``` ### Response #### Success Response (0) Indicates successful parsing and setting of the remote description. #### Response Example None (function returns an integer status code) ### Error Handling Returns a non-zero value on failure. ``` -------------------------------- ### Clean up WebRTC resources Source: https://context7.com/sepfy/libpeer/llms.txt This function is used to deinitialize the WebRTC connection and release associated resources. Ensure all media pipelines and peer connections are destroyed before calling this. ```c gst_element_set_state(g_media.video_pipeline, GST_STATE_NULL); gst_element_set_state(g_media.mic_pipeline, GST_STATE_NULL); gst_element_set_state(g_media.speaker_pipeline, GST_STATE_NULL); peer_signaling_disconnect(); peer_connection_destroy(g_pc); peer_deinit(); return 0; } ``` -------------------------------- ### Set Component Compile Options Source: https://github.com/sepfy/libpeer/blob/main/examples/esp32/main/CMakeLists.txt Applies specific compiler flags to the component library to suppress format warnings. ```cmake target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") ``` -------------------------------- ### peer_connection_oniceconnectionstatechange Source: https://context7.com/sepfy/libpeer/llms.txt Registers a callback function to monitor ICE connection state changes. The callback is invoked when the connection transitions between states: NEW, CHECKING, CONNECTED, COMPLETED, FAILED, DISCONNECTED, or CLOSED. ```APIDOC ## peer_connection_oniceconnectionstatechange ### Description Registers a callback function to monitor ICE connection state changes. The callback is invoked when the connection transitions between states: NEW, CHECKING, CONNECTED, COMPLETED, FAILED, DISCONNECTED, or CLOSED. Use peer_connection_state_to_string() to convert state to human-readable string. ### Method `peer_connection_oniceconnectionstatechange(PeerConnection* pc, void (*callback)(PeerConnectionState, void*))` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```c void on_connection_state_change(PeerConnectionState state, void* userdata) { printf("Connection state changed: %s\n", peer_connection_state_to_string(state)); // Handle state change logic here } peer_connection_oniceconnectionstatechange(pc, on_connection_state_change); ``` ### Response None (callback is invoked asynchronously) #### Success Response (200) N/A #### Response Example N/A ``` -------------------------------- ### Send Video and Audio Data Source: https://context7.com/sepfy/libpeer/llms.txt Sends media frames to a remote peer. Requires the connection to be in the PEER_CONNECTION_COMPLETED state. ```c #include "peer.h" #include PeerConnection* g_pc = NULL; PeerConnectionState g_state = PEER_CONNECTION_CLOSED; uint64_t get_timestamp_ms() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000 + tv.tv_usec / 1000; } void on_state_change(PeerConnectionState state, void* data) { g_state = state; } int main() { uint64_t curr_time, video_time = 0, audio_time = 0; uint8_t video_frame[65536]; uint8_t audio_frame[320]; size_t video_size, audio_size; peer_init(); PeerConfiguration config = { .ice_servers = {{.urls = "stun:stun.l.google.com:19302"}}, .video_codec = CODEC_H264, .audio_codec = CODEC_PCMA }; g_pc = peer_connection_create(&config); peer_connection_oniceconnectionstatechange(g_pc, on_state_change); // ... establish connection via signaling ... while (1) { peer_connection_loop(g_pc); if (g_state == PEER_CONNECTION_COMPLETED) { curr_time = get_timestamp_ms(); // Send video at 25 FPS (40ms interval) if (curr_time - video_time > 40) { video_time = curr_time; // Read H.264 frame from camera/file // video_size = read_h264_frame(video_frame, sizeof(video_frame)); video_size = 0; // placeholder if (video_size > 0) { peer_connection_send_video(g_pc, video_frame, video_size); } } // Send audio at 50 packets/sec (20ms interval for G.711) if (curr_time - audio_time > 20) { audio_time = curr_time; // Read audio samples // audio_size = read_audio_frame(audio_frame, sizeof(audio_frame)); audio_size = 0; // placeholder if (audio_size > 0) { peer_connection_send_audio(g_pc, audio_frame, audio_size); } } } usleep(1000); } peer_connection_destroy(g_pc); peer_deinit(); return 0; } ```