### Install Dependencies using apt Source: https://github.com/microsoft/ringmaster/blob/main/README.md Installs necessary packages for building Ringmaster on Ubuntu. Requires sudo privileges. ```shell sudo apt install autoconf libvpx-dev libsdl2-dev ``` -------------------------------- ### Bash Script for Building Ringmaster Source: https://context7.com/microsoft/ringmaster/llms.txt Instructions to build the Ringmaster project using autotools on Ubuntu. Includes installing dependencies, configuring, and compiling the project. ```bash # Install dependencies on Ubuntu 18.04+ sudo apt install autoconf libvpx-dev libsdl2-dev # Build the project ./autogen.sh ./configure make -j$(nproc) # Binaries are created in src/app/ ls src/app/ # video_sender video_receiver # Download sample video (704x576 @ 30 FPS) wget https://media.xiph.org/video/derf/y4m/ice_4cif_30fps.y4m ``` -------------------------------- ### Video Sender - C++ Command Line Usage Source: https://context7.com/microsoft/ringmaster/llms.txt This C++ code snippet demonstrates how to start a video sender. It reads raw YUV4MPEG video files, encodes frames using VP9, fragments them into UDP datagrams, and manages retransmissions based on ACK feedback. It supports custom MTU and verbose logging. ```cpp // Start a video sender on port 12345 with a raw video file // Usage: ./video_sender [options] port y4m_file // Command line example: ./video_sender 12345 ice_4cif_30fps.y4m // With custom MTU and verbose logging: ./video_sender --mtu 1400 --verbose --output sender_stats.txt 12345 video.y4m // The sender binds to 0.0.0.0:12345 and waits for a ConfigMsg from receiver // Once connected, it: // 1. Reads raw frames at specified FPS (e.g., 30 fps) // 2. Compresses frames using VP9 encoder // 3. Packetizes into datagrams with max payload of ~1440 bytes // 4. Sends datagrams over UDP // 5. Tracks unacked packets and retransmits up to 3 times // 6. Maintains RTT estimation using ACK timestamps // Expected output: // Local address: 0.0.0.0:12345 // Waiting for receiver... // Peer address: 127.0.0.1:54321 // Received config: width=704 height=576 FPS=30 bitrate=500 // Initialized VP9 encoder (CPU used: 4) // Sent datagram: frame_id=0 frag_id=0 frag_cnt=12 rtx=0 // Sent datagram: frame_id=0 frag_id=1 frag_cnt=12 rtx=0 ``` -------------------------------- ### C++ Non-blocking UDP Socket Operations Source: https://context7.com/microsoft/ringmaster/llms.txt Provides examples of using a non-blocking UDP socket wrapper for sending and receiving data. It covers binding the socket to an address, connecting to a peer, sending and receiving data in both blocking and non-blocking modes, and sending data to a specific address without establishing a connection. Dependencies include 'udp_socket.hh' and 'address.hh'. ```cpp #include "udp_socket.hh" #include "address.hh" // Server (sender) usage: bind to port and wait for client UDPSocket sender_sock; sender_sock.bind({"0", 12345}); // Bind to 0.0.0.0:12345 std::cout << "Listening on: " << sender_sock.local_address().str() << std::endl; // Receive from any address auto [peer_addr, data] = sender_sock.recvfrom(); if (data) { std::cout << "Received from " << peer_addr.str() << std::endl; // Now connect to this peer for subsequent send/recv sender_sock.connect(peer_addr); sender_sock.set_blocking(false); // Enable non-blocking mode } // Client (receiver) usage: connect to sender UDPSocket receiver_sock; Address sender_addr{"127.0.0.1", 12345}; receiver_sock.connect(sender_addr); std::cout << "Connected to: " << sender_addr.str() << std::endl; // Send to connected peer std::string message = "Hello"; if (receiver_sock.send(message)) { std::cout << "Sent " << message.size() << " bytes" << std::endl; } else { // EWOULDBLOCK in non-blocking mode, try again later } // Receive from connected peer (blocking mode) auto received = receiver_sock.recv(); if (received) { std::cout << "Received: " << *received << std::endl; } // Non-blocking receive returns nullopt if no data available receiver_sock.set_blocking(false); auto maybe_data = receiver_sock.recv(); if (!maybe_data) { // EWOULDBLOCK, no data available } // Send to specific address without connecting UDPSocket bcast_sock; bcast_sock.sendto({"192.168.1.255", 9999}, "broadcast message"); ``` -------------------------------- ### Bash Script for Ringmaster Sender Usage Source: https://context7.com/microsoft/ringmaster/llms.txt Demonstrates how to run the Ringmaster video sender. Includes basic usage, downloading sample video, and advanced options for MTU, output logging, and verbosity. ```bash # Terminal 1: Start sender on port 12345 cd src/app ./video_sender 12345 ../../ice_4cif_30fps.y4m # Output: # Local address: 0.0.0.0:12345 # Waiting for receiver... # Advanced sender options ./video_sender --mtu 1400 --output sender.log --verbose 12345 video.y4m # --mtu: Set MTU (512-1500 bytes, affects fragmentation) # --output: Write statistics to file # --verbose: Enable detailed packet logging ``` -------------------------------- ### Bash Script for Ringmaster Receiver Usage Source: https://context7.com/microsoft/ringmaster/llms.txt Demonstrates how to run the Ringmaster video receiver. Includes basic usage connecting to a sender and advanced options for lazy decoding, output logging, verbosity, FPS, and CBR. ```bash # Terminal 2: Start receiver connecting to sender cd src/app ./video_receiver 127.0.0.1 12345 704 576 --fps 30 --cbr 500 # Output: # Peer address: 127.0.0.1:12345 # Local address: 0.0.0.0:54321 # [SDL window opens displaying video at 30 FPS] # Advanced receiver options ./video_receiver --lazy 1 --output receiver.log --verbose 127.0.0.1 12345 704 576 --fps 30 --cbr 1000 # --lazy 0: Decode and display (default) # --lazy 1: Decode only, no display (performance testing) # --lazy 2: Neither decode nor display (bandwidth testing) # --fps: Request frame rate from sender # --cbr: Request constant bitrate in kbps ``` -------------------------------- ### Emulate 1:1 Video Call using video_sender and video_receiver Source: https://github.com/microsoft/ringmaster/blob/main/README.md Demonstrates initiating a 1:1 video call by running the video sender and receiver applications. The sender compresses raw video into VP9 and transmits it over UDP, while the receiver decodes and displays the video. ```shell ./video_sender 12345 ice_4cif_30fps.y4m ./video_receiver 127.0.0.1 12345 704 576 --fps 30 --cbr 500 ``` -------------------------------- ### Video Receiver - C++ Command Line Usage Source: https://context7.com/microsoft/ringmaster/llms.txt This C++ code snippet illustrates how to run a video receiver. It connects to a sender, requests video configuration, receives and reassembles fragmented frames, and decodes VP9 frames. It can optionally display frames using SDL2 and supports lazy decoding and verbose logging. ```cpp // Start a video receiver connecting to sender at 127.0.0.1:12345 // Specify video dimensions and request 30 FPS at 500 kbps CBR // Usage: ./video_receiver [options] host port width height ./video_receiver 127.0.0.1 12345 704 576 --fps 30 --cbr 500 // With lazy decoding (decode but don't display) and verbose logging: ./video_receiver --lazy 1 --verbose --output receiver_stats.txt 127.0.0.1 12345 704 576 --fps 30 --cbr 500 // With no decoding or display (for bandwidth testing): ./video_receiver --lazy 2 127.0.0.1 12345 704 576 --fps 30 --cbr 1000 // The receiver: // 1. Creates UDP socket and connects to sender // 2. Sends ConfigMsg with requested width, height, fps, bitrate // 3. Receives datagrams in main loop // 4. Sends immediate ACK for each datagram // 5. Reassembles fragments into complete frames // 6. Decodes complete frames using VP9 decoder (lazy_level 0 or 1) // 7. Displays decoded frames in SDL window (lazy_level 0 only) // Expected output: // Peer address: 127.0.0.1:12345 // Local address: 0.0.0.0:54321 // Acked datagram: frame_id=0 frag_id=0 // Acked datagram: frame_id=0 frag_id=1 ``` -------------------------------- ### Build Ringmaster using autogen, configure, and make Source: https://github.com/microsoft/ringmaster/blob/main/README.md Compiles the Ringmaster platform from source. This process involves generating build scripts, configuring the build environment, and then compiling the code. ```shell ./autogen.sh ./configure make -j ``` -------------------------------- ### C++ Protocol Message Serialization and Deserialization Source: https://context7.com/microsoft/ringmaster/llms.txt Demonstrates how to serialize and deserialize control messages (ConfigMsg, AckMsg) and video data datagrams using the project's binary protocol. It covers creating messages, converting them to binary strings, and parsing received binary data back into message objects. Dependencies include the 'protocol.hh' header. ```cpp #include "protocol.hh" // ConfigMsg: Receiver requests video configuration from sender ConfigMsg config(704, 576, 30, 500); // width=704, height=576, frame_rate=30 fps, target_bitrate=500 kbps std::string config_binary = config.serialize_to_string(); // Binary format: [TYPE=2][width][height][frame_rate][target_bitrate] // Send over UDP socket // Parse received ConfigMsg std::shared_ptr msg = Msg::parse_from_string(config_binary); if (msg && msg->type == Msg::Type::CONFIG) { auto cfg = std::dynamic_pointer_cast(msg); std::cout << "Width: " << cfg->width << std::endl; std::cout << "Height: " << cfg->height << std::endl; std::cout << "FPS: " << cfg->frame_rate << std::endl; std::cout << "Bitrate: " << cfg->target_bitrate << " kbps" << std::endl; } // Datagram: Carries video frame fragments Datagram datagram(42, FrameType::KEY, 3, 8, "payload_data"); // frame_id=42, frame_type=KEY, frag_id=3, frag_cnt=8, payload="payload_data" datagram.send_ts = 1234567890; // Microsecond timestamp std::string dgram_binary = datagram.serialize_to_string(); // Binary format: [frame_id][frame_type][frag_id][frag_cnt][send_ts][payload] // Header is 15 bytes, max payload is 1444 bytes (default MTU 1500 - 28 - 15) // Set custom MTU Datagram::set_mtu(1400); // Now max_payload = 1400 - 28 - 15 = 1357 bytes // Parse received Datagram Datagram received; if (received.parse_from_string(dgram_binary)) { std::cout << "Frame ID: " << received.frame_id << std::endl; std::cout << "Fragment: " << received.frag_id << "/" << received.frag_cnt << std::endl; } // AckMsg: Receiver acknowledges received datagram AckMsg ack(datagram); // Construct from datagram // ack.frame_id = 42, ack.frag_id = 3, ack.send_ts = 1234567890 std::string ack_binary = ack.serialize_to_string(); // Binary format: [TYPE=1][frame_id][frag_id][send_ts] // Send ACK back to sender for RTT calculation ``` -------------------------------- ### C++ YUV4MPEG Video Input Reader Source: https://context7.com/microsoft/ringmaster/llms.txt Reads YUV4MPEG (.y4m) video files and provides raw I420 frames. It allows specifying resolution and loop behavior. Dependencies include 'yuv4mpeg.hh' and 'image.hh'. Outputs raw YUV frame data suitable for encoding. ```cpp #include "yuv4mpeg.hh" #include "image.hh" // Open YUV4MPEG video file with loop enabled YUV4MPEG video_input("ice_4cif_30fps.y4m", 704, 576, true); // File contains raw YUV frames at 704x576 resolution // loop=true restarts from beginning when reaching end of file // Get frame size information size_t frame_size = video_input.frame_size(); // 704*576*3/2 = 608256 bytes size_t y_size = video_input.y_size(); // 704*576 = 405504 bytes (Y plane) size_t uv_size = video_input.uv_size(); // 704*576/4 = 101376 bytes (U/V planes) // Allocate raw image buffer (I420 format) RawImage raw_img(704, 576); // Read frames in a loop while (true) { if (!video_input.read_frame(raw_img)) { std::cout << "Reached end of video file" << std::endl; break; // Only happens if loop=false } // raw_img now contains YUV data in I420 format // - Y plane: 704x576 luminance values // - U plane: 352x288 chrominance values (subsampled) // - V plane: 352x288 chrominance values (subsampled) // Access plane data uint8_t* y_plane = raw_img.y_plane(); uint8_t* u_plane = raw_img.u_plane(); uint8_t* v_plane = raw_img.v_plane(); // Process frame (e.g., encode with VP9) // encoder.compress_frame(raw_img); } // Video file automatically closed when YUV4MPEG destructor is called ``` -------------------------------- ### C++ RawImage for I420 Video Frames Source: https://context7.com/microsoft/ringmaster/llms.txt Manages I420 format image data, providing access to planes, dimensions, and conversion from YUYV. It wraps the libvpx vpx_image structure and can optionally provide access to the underlying vpx_image. ```cpp #include "image.hh" // Allocate new raw image (I420 format: YUV 4:2:0 planar) RawImage raw_img(704, 576); // Get dimensions uint16_t width = raw_img.display_width(); // 704 uint16_t height = raw_img.display_height(); // 576 // Get plane sizes size_t y_size = raw_img.y_size(); // 704 * 576 = 405504 bytes size_t uv_size = raw_img.uv_size(); // 704 * 576 / 4 = 101376 bytes // Access plane pointers uint8_t* y_plane = raw_img.y_plane(); // Luminance (Y) plane uint8_t* u_plane = raw_img.u_plane(); // Chrominance (U) plane uint8_t* v_plane = raw_img.v_plane(); // Chrominance (V) plane // Get strides (may differ from width due to alignment) int y_stride = raw_img.y_stride(); // Usually equals width int u_stride = raw_img.u_stride(); // Usually equals width/2 int v_stride = raw_img.v_stride(); // Usually equals width/2 // Copy plane data from buffers std::string y_data(y_size, 0); std::string u_data(uv_size, 0); std::string v_data(uv_size, 0); // ... fill with image data ... raw_img.copy_y_from(y_data); raw_img.copy_u_from(u_data); raw_img.copy_v_from(v_data); // Copy from YUYV format (common in webcams) std::string yuyv_buffer(width * height * 2, 0); // ... fill with YUYV data from V4L2 camera ... raw_img.copy_from_yuyv(yuyv_buffer); // Converts YUYV (YUY2) to I420 format automatically // Access underlying vpx_image for encoder/decoder vpx_image* vpx_img = raw_img.get_vpx_image(); // Pass to vpx_codec_encode() or use in encoder // Wrap existing vpx_image (non-owning) vpx_image_t existing_img; // ... initialize from decoder output ... RawImage wrapped_img(&existing_img); // Does not free vpx_image on destruction ``` -------------------------------- ### VP9 Encoder: Compress and Packetize Raw Frames (C++) Source: https://context7.com/microsoft/ringmaster/llms.txt This C++ snippet demonstrates initializing and using the VP9 Encoder class to compress raw video frames. It covers setting target bitrate, compressing a frame, and managing sent datagrams for acknowledgment tracking. The output is fragmented datagrams ready for transmission. ```cpp #include "encoder.hh" #include "image.hh" // Initialize encoder for 704x576 video at 30 FPS Encoder encoder(704, 576, 30, "encoder_stats.txt"); // Set target bitrate to 500 kbps encoder.set_target_bitrate(500); encoder.set_verbose(true); // Compress a raw frame (I420 format) RawImage raw_img(704, 576); // ... populate raw_img with YUV data ... encoder.compress_frame(raw_img); // After compression, datagrams are in send buffer std::deque& send_buf = encoder.send_buf(); // send_buf now contains fragmented datagrams ready to transmit // When datagram is sent (excluding retransmissions), track it as unacked Datagram datagram = send_buf.front(); datagram.send_ts = timestamp_us(); // Set send timestamp encoder.add_unacked(std::move(datagram)); // When ACK received from receiver std::shared_ptr ack = std::make_shared(); ack->frame_id = 42; ack->frag_id = 3; ack->send_ts = datagram.send_ts; // Original send timestamp from datagram encoder.handle_ack(ack); // handle_ack() updates RTT estimation and removes from unacked map // May schedule retransmissions for lost packets // Output periodic statistics (call every second) encoder.output_periodic_stats(); // Outputs encoding time, frame rate, bitrate to stdout and optional file ``` -------------------------------- ### C++ Event Loop with Poller Source: https://context7.com/microsoft/ringmaster/llms.txt Implements an event-driven I/O multiplexer using poll() in C++. It monitors file descriptors for events (readable, writable) and invokes registered callback functions. Dependencies include 'poller.hh', 'udp_socket.hh', and 'timerfd.hh'. It handles socket communication and timer events. ```cpp #include "poller.hh" #include "udp_socket.hh" #include "timerfd.hh" Poller poller; UDPSocket udp_sock; udp_sock.set_blocking(false); // Register socket for readable events poller.register_event(udp_sock, Poller::In, [&udp_sock]() { auto data = udp_sock.recv(); if (data) { std::cout << "Received: " << *data << std::endl; } } ); // Register socket for writable events (initially inactive) std::deque send_queue; poller.register_event(udp_sock, Poller::Out, [&]() { while (!send_queue.empty()) { if (udp_sock.send(send_queue.front())) { send_queue.pop_front(); } else { break; // EWOULDBLOCK, try again later } } // Deactivate when queue is empty if (send_queue.empty()) { poller.deactivate(udp_sock, Poller::Out); } } ); // Create periodic timer (e.g., 30 FPS = 33.33ms interval) Timerfd fps_timer; timespec interval{0, 33333333}; // 33.33 ms in nanoseconds fps_timer.set_time(interval, interval); poller.register_event(fps_timer, Poller::In, [&]() { uint64_t expirations = fps_timer.read_expirations(); std::cout << "Timer fired " << expirations << " times" << std::endl; // Generate and queue frame data send_queue.push_back("frame_data"); // Activate socket for writing poller.activate(udp_sock, Poller::Out); } ); // Main event loop while (true) { poller.poll(-1); // Block indefinitely until events occur // Registered callbacks are invoked automatically } // Cleanup: deregister file descriptors poller.deregister(udp_sock); poller.deregister(fps_timer); ``` -------------------------------- ### VP9 Decoder: Reassemble and Decode Frames (C++) Source: https://context7.com/microsoft/ringmaster/llms.txt This C++ snippet illustrates the VP9 Decoder class's functionality for receiving and processing VP9-encoded video data. It covers adding received datagrams, checking for frame completeness, consuming and decoding frames, and optionally displaying them. The decoder manages frame buffering and can automatically skip to complete keyframes. ```cpp #include "decoder.hh" #include "protocol.hh" // Initialize decoder for 704x576 video // lazy_level: 0=decode+display, 1=decode only, 2=neither Decoder decoder(704, 576, 0, "decoder_stats.txt"); decoder.set_verbose(true); // When datagram arrives from sender Datagram datagram; // ... receive and parse datagram from UDP socket ... datagram.frame_id = 42; datagram.frame_type = FrameType::NONKEY; datagram.frag_id = 3; datagram.frag_cnt = 8; datagram.payload = "...encoded video data..."; // Add received datagram to decoder decoder.add_datagram(std::move(datagram)); // Check if next expected frame is complete while (decoder.next_frame_complete()) { // Next frame has all fragments, consume it decoder.consume_next_frame(); // consume_next_frame() will: // - Decode frame using VP9 decoder (if lazy_level <= 1) // - Display frame in SDL window (if lazy_level == 0) // - Advance to next frame // - Clean up frame buffer } // If current frame is incomplete but later keyframe is complete, // next_frame_complete() skips to the keyframe automatically // Get current expected frame ID uint32_t next = decoder.next_frame(); // Output periodic statistics (call every second) decoder.output_periodic_stats(); // Outputs received frame rate, bitrate, decode time ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.