### Installing Elixir Project Dependencies Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/examples/chat/README.md This command fetches and installs all necessary dependencies for the Elixir project, ensuring all required libraries are available before running the application. It must be executed from within the project's root directory. ```Shell mix deps.get ``` -------------------------------- ### Installing Elixir Project Dependencies Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/examples/whip_whep/README.md This command fetches and installs all necessary dependencies for the Elixir project. It is a prerequisite step to ensure all required libraries are available before the application can be started. ```Elixir mix deps.get ``` -------------------------------- ### Running Elixir WebRTC Application Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/examples/send_from_file/README.md These commands prepare and start the Elixir WebRTC application. `mix deps.get` fetches project dependencies, and `mix run --no-halt` executes the application without exiting, allowing it to serve content continuously. This sets up the server to stream the generated media files to a connected browser. ```Shell mix deps.get mix run --no-halt ``` -------------------------------- ### Running Elixir Application in Development Mode Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/examples/whip_whep/README.md This command starts the Elixir application in a non-halting mode, which is ideal for development and continuous operation. It keeps the server running in the foreground, allowing for real-time interaction and logging. ```Elixir mix run --no-halt ``` -------------------------------- ### Installing Elixir WebRTC Dependency Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/README.md This snippet demonstrates how to add the `ex_webrtc` library as a dependency to an Elixir project. It specifies the package name and a compatible version range, ensuring the project can utilize the core WebRTC functionalities. ```Elixir def deps do [ {:ex_webrtc, "~> 0.14.0"} ] end ``` -------------------------------- ### Establishing WebRTC Peer Connection with Audio Transceivers in Elixir Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/mastering_transceivers.md This snippet demonstrates the full WebRTC signaling process in Elixir, including starting two peer connections, adding audio transceivers to each, creating an offer, setting local and remote descriptions, creating an answer, and finally inspecting the transceivers on the second peer connection. It simulates a basic audio call setup. ```Elixir {:ok, pc1} = PeerConnection.start_link() {:ok, pc2} = PeerConnection.start_link() {:ok, pc1_tr1} = PeerConnection.add_transceiver(pc1, :audio) {:ok, pc2_tr1} = PeerConnection.add_transceiver(pc2, :audio) {:ok, offer} = PeerConnection.create_offer(pc1) :ok = PeerConnection.set_local_description(pc1, offer) :ok = PeerConnection.set_remote_description(pc2, offer) {:ok, answer} = PeerConnection.create_answer(pc2) :ok = PeerConnection.set_local_description(pc2, answer) :ok = PeerConnection.set_remote_description(pc1, answer) transceivers = PeerConnection.get_transceivers(pc2) 2 = Enum.count(transceivers) IO.inspect(transceivers) ``` -------------------------------- ### Generating Video and Audio Files with FFmpeg Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/examples/send_from_file/README.md This snippet provides FFmpeg commands to create a 5-second IVF video file (640x480, 30 FPS, GOP 60) and an Ogg audio file with an Opus stream (420 Hz, 5 seconds duration). These files are used as input for the Elixir WebRTC example, ensuring compatibility with the application's requirements. ```Shell ffmpeg -f lavfi -i testsrc=duration=5:size=640x480:rate=30 -g 60 video.ivf ffmpeg -f lavfi -i sine=frequency=420:duration=5 -c:a libopus audio.ogg ``` -------------------------------- ### Running Elixir Application with No Halt Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/examples/chat/README.md This command starts the Elixir application in the current directory. The `--no-halt` flag prevents the Erlang VM from shutting down after the initial script execution, keeping the application running indefinitely to serve requests. ```Shell mix run --no-halt ``` -------------------------------- ### Installing Elixir Project Dependencies Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/examples/dtmf/README.md This command fetches and installs all necessary dependencies for the Elixir project, ensuring all required libraries are available before running the application. ```Shell mix deps.get ``` -------------------------------- ### Enabling DataChannels with ex_sctp in Elixir WebRTC Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/README.md This code snippet shows how to include the optional `ex_sctp` dependency alongside `ex_webrtc` to enable DataChannel support. The `ex_sctp` library is required for this functionality and necessitates a Rust installation for compilation. ```Elixir def deps do [ {:ex_webrtc, "~> 0.14.0"}, {:ex_sctp, "~> 0.1.0"} ] end ``` -------------------------------- ### Initializing ExWebRTC PeerConnection in Elixir Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/introduction/negotiation.md Initializes an `ExWebRTC.PeerConnection` process in Elixir, analogous to `RTCPeerConnection` in JavaScript. It starts a new process for the peer connection, configured with STUN servers for ICE negotiation, and returns the process identifier. ```Elixir {:ok, pc} = ExWebRTC.PeerConnection.start_link(ice_servers: [%{urls: "stun:stun.l.google.com:19302"}]) ``` -------------------------------- ### Establishing Bidirectional WebRTC Connection with Single Negotiation - JavaScript Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/mastering_transceivers.md This JavaScript example illustrates how to set up a bidirectional WebRTC connection using a single negotiation and the *Warmup* technique. It initializes two RTCPeerConnection instances, adds an audio transceiver, creates an offer, and then manipulates the transceiver direction to sendrecv before creating and applying the answer. ```js pc1 = new RTCPeerConnection(); pc2 = new RTCPeerConnection(); pc1.ontrack = ev => console.log("pc1 ontrack"); pc2.ontrack = ev => console.log("pc2 ontrack"); tr = pc1.addTransceiver("audio"); offer = await pc1.createOffer(); await pc1.setLocalDescription(offer); await pc2.setRemoteDescription(offer); // change direction from default "recvonly" to "sendrecv" pc2.getTransceivers()[0].direction = "sendrecv"; answer = await pc2.createAnswer(); await pc2.setLocalDescription(answer); await pc1.setRemoteDescription(answer); ``` -------------------------------- ### Setting Up WebRTC Peer Connection with Audio Stream in JavaScript Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/mastering_transceivers.md This JavaScript example illustrates how to establish a WebRTC peer connection. It obtains a local audio stream, initializes two RTCPeerConnection objects, adds an audio transceiver to the first peer, adds the local audio track to the second peer, and then performs the offer/answer exchange to establish the connection. Finally, it logs the transceivers of the second peer. ```JavaScript const localStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false}); pc1 = new RTCPeerConnection(); pc2 = new RTCPeerConnection(); pc1Tr1 = pc1.addTransceiver("audio"); pc2Sender = pc2.addTrack(localStream.getTracks()[0]); offer = await pc1.createOffer(); await pc1.setLocalDescription(offer); await pc2.setRemoteDescription(offer); answer = await pc2.createAnswer(); await pc2.setLocalDescription(answer); await pc1.setRemoteDescription(answer); // observe that pc2 has one transceiver console.log(pc2.getTransceivers()); ``` -------------------------------- ### WebRTC SDP Offer/Answer Example Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/test/fixtures/sdp/firefox_audio_video_sdp.txt This SDP snippet describes a WebRTC session with both audio and video streams. It includes details such as ICE credentials (ufrag, pwd), RTP/RTCP multiplexing, supported codecs (Opus, G722, PCMU, PCMA for audio; VP8, VP9, H264 for video), and various RTP header extensions and feedback mechanisms. The 'sendrecv' attribute indicates that both sending and receiving are enabled for the media streams. ```SDP v=0 o=mozilla...THIS_IS_SDPARTA-99.0 4408209549129620304 0 IN IP4 0.0.0.0 s=- t=0 0 a=fingerprint:sha-256 75:4F:CA:8F:4F:E4:CE:5E:67:42:D3:DA:0E:E1:87:2A:E5:E3:02:76:30:FC:AB:F0:B4:7E:24:71:18:38:9C:6E a=group:BUNDLE 0 1 a=ice-options:trickle a=msid-semantic:WMS * m=audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8 101 c=IN IP4 0.0.0.0 a=sendrecv a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level a=extmap:2/recvonly urn:ietf:params:rtp-hdrext:csrc-audio-level a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid a=fmtp:109 maxplaybackrate=48000;stereo=1;useinbandfec=1 a=fmtp:101 0-15 a=ice-pwd:59efe498b762e472067fa5e134df8722 a=ice-ufrag:6c0579ea a=mid:0 a=msid:- {ba72adfe-d5b5-42bd-bf15-e8fcdcbb2ee1} a=rtcp-mux a=rtpmap:109 opus/48000/2 a=rtpmap:9 G722/8000/1 a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000/1 a=setup:actpass a=ssrc:3772375965 cname:{b11da7a1-56f4-4fc1-a969-6d82236f494d} m=video 9 UDP/TLS/RTP/SAVPF 120 124 121 125 126 127 97 98 c=IN IP4 0.0.0.0 a=sendrecv a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid a=extmap:4 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time a=extmap:5 urn:ietf:params:rtp-hdrext:toffset a=extmap:6/recvonly http://www.webrtc.org/experiments/rtp-hdrext/playout-delay a=extmap:7 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 a=fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1 a=fmtp:97 profile-level-id=42e01f;level-asymmetry-allowed=1 a=fmtp:120 max-fs=12288;max-fr=60 a=fmtp:124 apt=120 a=fmtp:121 max-fs=12288;max-fr=60 a=fmtp:125 apt=121 a=fmtp:127 apt=126 a=fmtp:98 apt=97 a=ice-pwd:59efe498b762e472067fa5e134df8722 a=ice-ufrag:6c0579ea a=mid:1 a=msid:- {73b368b6-4a66-409b-87ea-0ed7919bce82} a=rtcp-fb:120 nack a=rtcp-fb:120 nack pli a=rtcp-fb:120 ccm fir a=rtcp-fb:120 goog-remb a=rtcp-fb:120 transport-cc a=rtcp-fb:121 nack a=rtcp-fb:121 nack pli a=rtcp-fb:121 ccm fir a=rtcp-fb:121 goog-remb a=rtcp-fb:121 transport-cc a=rtcp-fb:126 nack a=rtcp-fb:126 nack pli a=rtcp-fb:126 ccm fir a=rtcp-fb:126 goog-remb a=rtcp-fb:126 transport-cc a=rtcp-fb:97 nack a=rtcp-fb:97 nack pli a=rtcp-fb:97 ccm fir a=rtcp-fb:97 goog-remb a=rtcp-fb:97 transport-cc a=rtcp-mux a=rtcp-rsize a=rtpmap:120 VP8/90000 a=rtpmap:124 rtx/90000 a=rtpmap:121 VP9/90000 a=rtpmap:125 rtx/90000 a=rtpmap:126 H264/90000 a=rtpmap:127 rtx/90000 a=rtpmap:97 H264/90000 a=rtpmap:98 rtx/90000 a=setup:actpass a=ssrc:7463831 cname:{b11da7a1-56f4-4fc1-a969-6d82236f494d} a=ssrc:2513307155 cname:{b11da7a1-56f4-4fc1-a969-6d82236f494d} a=ssrc-group:FID 7463831 2513307155 ``` -------------------------------- ### Example WebRTC SDP Offer Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/test/fixtures/sdp/obs_audio_video_sdp.txt This snippet presents a complete WebRTC Session Description Protocol (SDP) offer. It includes details for an audio stream (Opus) and a video stream (H.264), specifying ICE candidates, RTP/RTCP multiplexing, security fingerprints (SHA-256), and various media attributes necessary for establishing a WebRTC peer connection. ```SDP v=0 o=rtc 2113209719 0 IN IP4 127.0.0.1 s=- t=0 0 a=group:BUNDLE 0 1 a=group:LS 0 1 a=msid-semantic:WMS * a=setup:actpass a=ice-ufrag:v3f/ a=ice-pwd:4b+Ogzsq+Xx0AEkYvTvG/n a=ice-options:ice2,trickle a=fingerprint:sha-256 82:06:23:C6:08:6C:47:62:82:22:90:F8:A5:4A:FF:F9:61:A6:5B:BF:B3:06:4E:D9:F5:39:8B:0B:2D:E5:0B:AB m=audio 58712 UDP/TLS/RTP/SAVPF 111 c=IN IP4 192.168.68.60 a=mid:0 a=sendonly a=ssrc:2168349679 cname:OTU9sDg2gfT6S7D7 a=ssrc:2168349679 msid:Uvjgw5v3KVIiH64D Uvjgw5v3KVIiH64D-audio a=msid:Uvjgw5v3KVIiH64D Uvjgw5v3KVIiH64D-audio a=rtcp-mux a=rtpmap:111 OPUS/48000/2 a=fmtp:111 minptime=10;maxaveragebitrate=96000;stereo=1;sprop-stereo=1;useinbandfec=1 a=candidate:3 1 UDP 2130705919 fd00::3f:ad47 58712 typ host a=candidate:1 1 UDP 2122317823 192.168.68.60 58712 typ host a=candidate:2 1 UDP 2122317567 100.86.241.178 58712 typ host a=end-of-candidates m=video 58712 UDP/TLS/RTP/SAVPF 96 c=IN IP4 192.168.68.60 a=mid:1 a=sendonly a=ssrc:2168349680 cname:OTU9sDg2gfT6S7D7 a=ssrc:2168349680 msid:Uvjgw5v3KVIiH64D Uvjgw5v3KVIiH64D-video a=msid:Uvjgw5v3KVIiH64D Uvjgw5v3KVIiH64D-video a=rtcp-mux a=rtpmap:96 H264/90000 a=rtcp-fb:96 nack a=rtcp-fb:96 nack pli a=rtcp-fb:96 goog-remb a=fmtp:96 profile-level-id=42e01f;packetization-mode=1;level-asymmetry-allowed=1 ``` -------------------------------- ### Playing Saved Audio and Video Files with ffplay Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/examples/save_to_file/README.md These commands use `ffplay` (part of `ffmpeg`) to play the saved audio and video files. `audio.ogg` contains the recorded audio, and `video.ivf` contains the recorded video. Ensure `ffmpeg` is installed on your system to use `ffplay`. ```Shell ffplay audio.ogg ffplay video.ivf ``` -------------------------------- ### Initializing PeerConnection and Tracks for Media Forwarding in Elixir Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/introduction/forwarding.md This init/1 function for a GenServer sets up an Elixir WebRTC PeerConnection. It starts the connection with a STUN server, generates a stream ID, and adds two new MediaStreamTrack instances (audio and video) to the PeerConnection for sending data. The function initializes the state with the PeerConnection process, and maps for incoming and outgoing track IDs, preparing for media forwarding. ```Elixir def init(_) do {:ok, pc} = PeerConnection.start_link(ice_servers: [%{urls: "stun:stun.l.google.com:19302"}]) # we expect to receive two tracks from the web browser - audio and video # so we also need to add two tracks here, we will use them to loop media back to the other browser # from each of the web browser tracks stream_id = MediaStreamTrack.generate_stream_id() audio_track = MediaStreamTrack.new(:audio, [stream_id]) video_track = MediaStreamTrack.new(:video, [stream_id]) {:ok, _sender} = PeerConnection.add_track(pc, audio_track) {:ok, _sender} = PeerConnection.add_track(pc, video_track) # in_tracks (tracks we will receive from the browser) = %{id => kind} # out_tracks (tracks we will send to the browser) = %{kind => id} in_tracks = %{} out_tracks = %{audio: audio_track.id, video: video_track.id} {:ok, %{pc: pc, out_tracks: out_tracks, in_tracks: in_tracks}} end ``` -------------------------------- ### Initializing Peer Connections and Setting Transceiver Direction in Elixir Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/mastering_transceivers.md This Elixir snippet demonstrates the initial setup of two WebRTC peer connections (`pc1`, `pc2`), adding an audio transceiver to `pc1`, and performing a full SDP offer/answer exchange. It then shows how to retrieve a transceiver from `pc2` and set its direction to `:inactive`, followed by another SDP exchange to reflect this change. It also includes `receive` blocks to observe track and track_muted events. ```Elixir {:ok, pc1} = PeerConnection.start_link() {:ok, pc2} = PeerConnection.start_link() {:ok, _tr} = PeerConnection.add_transceiver(pc1, :audio) {:ok, offer} = PeerConnection.create_offer(pc1) :ok = PeerConnection.set_local_description(pc1, offer) :ok = PeerConnection.set_remote_description(pc2, offer) receive do {:ex_webrtc, _pc, {:track, _track}} = msg -> IO.inspect(msg) end [pc2_tr] = PeerConnection.get_transceivers(pc2) :ok = PeerConnection.set_transceiver_direction(pc2, pc2_tr.id, :inactive) {:ok, answer} = PeerConnection.create_answer(pc2) IO.inspect("Setting local description on pc2"); :ok = PeerConnection.set_local_description(pc2, answer) receive do {:ex_webrtc, _pc, {:track_muted, _track_id}} = msg -> IO.inspect(msg) end :ok = PeerConnection.set_remote_description(pc1, answer) ``` -------------------------------- ### Observing Transceiver Stealing with RTCPeerConnection in JavaScript Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/mastering_transceivers.md This snippet demonstrates the initial setup of two peer connections and their transceivers. It sets the stage to observe how a remote offer containing a new m-line might lead to a peer connection 'stealing' an existing transceiver, particularly when `addTrack` is used to create the transceiver implicitly. ```JavaScript pc1 = new RTCPeerConnection(); pc2 = new RTCPeerConnection(); pc1Tr1 = pc1.addTransceiver("audio"); pc2Tr1 = pc2.addTransceiver("audio"); offer = await pc1.createOffer(); await pc1.setLocalDescription(offer); await pc2.setRemoteDescription(offer); answer = await pc2.createAnswer(); await pc2.setLocalDescription(answer); await pc1.setRemoteDescription(answer); // observe that pc2 has two transceivers console.log(pc2.getTransceivers()); ``` -------------------------------- ### Establishing Bidirectional WebRTC Connection with Single Negotiation - Elixir Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/mastering_transceivers.md This Elixir example demonstrates establishing a bidirectional WebRTC connection using a single negotiation. It initializes two PeerConnection instances, adds an audio transceiver, creates an offer, and then explicitly sets the transceiver direction to :sendrecv on the answering peer before creating and applying the answer. ```elixir {:ok, pc1} = PeerConnection.start_link() {:ok, pc2} = PeerConnection.start_link() {:ok, _tr} = PeerConnection.add_transceiver(pc1, :audio) {:ok, offer} = PeerConnection.create_offer(pc1) :ok = PeerConnection.set_local_description(pc1, offer) :ok = PeerConnection.set_remote_description(pc2, offer) receive do {:ex_webrtc, ^pc2, {:track, _track}} = msg -> IO.inspect(msg) end [pc2_tr] = PeerConnection.get_transceivers(pc2) :ok = PeerConnection.set_transceiver_direction(pc2, pc2_tr.id, :sendrecv) {:ok, answer} = PeerConnection.create_answer(pc2) :ok = PeerConnection.set_local_description(pc2, answer) :ok = PeerConnection.set_remote_description(pc1, answer) receive do {:ex_webrtc, ^pc1, {:track, _track}} = msg -> IO.inspect(msg) end ``` -------------------------------- ### Modifying Dockerfile for Elixir WebRTC on Fly.io Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/deploying/fly.md This `diff` snippet shows necessary modifications to the auto-generated Dockerfile for deploying Elixir WebRTC applications on Fly.io. It updates Elixir, OTP, and Debian versions, and adds `pkg-config` and `libssl-dev` to the `apt-get install` command, which are crucial dependencies for building WebRTC components. ```Dockerfile - ARG ELIXIR_VERSION=1.16.0 - ARG OTP_VERSION=26.2.1 - ARG DEBIAN_VERSION=bullseye-20231009-slim + ARG ELIXIR_VERSION=1.17.2 + ARG OTP_VERSION=27.0.1 + ARG DEBIAN_VERSION=bookworm-20240701-slim # when building on arm64, you will also need to add libsrtp2-dev - RUN apt-get update -y && apt-get install -y build-essential git \ - && apt-get clean && rm -f /var/lib/apt/lists/*_* + RUN apt-get update -y && apt-get install -y build-essential git pkg-config libssl-dev \ + && apt-get clean && rm -f /var/lib/apt/lists/*_* ``` -------------------------------- ### Offering to Receive Data in WebRTC - Elixir Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/mastering_transceivers.md This Elixir example demonstrates how to handle offering to receive media tracks when the remote side might not send them by default. It initializes two PeerConnection instances, adds a :recvonly audio transceiver, creates an offer, and then explicitly sets the transceiver direction to :sendonly on the answering peer to ensure the track becomes active. ```elixir {:ok, pc1} = PeerConnection.start_link() {:ok, pc2} = PeerConnection.start_link() {:ok, _tr} = PeerConnection.add_transceiver(pc1, :audio, direction: :recvonly) {:ok, offer} = PeerConnection.create_offer(pc1) :ok = PeerConnection.set_local_description(pc1, offer) :ok = PeerConnection.set_remote_description(pc2, offer) [pc2_tr] = PeerConnection.get_transceivers(pc2) :ok = PeerConnection.set_transceiver_direction(pc2, pc2_tr.id, :sendonly) {:ok, answer} = PeerConnection.create_answer(pc2) :ok = PeerConnection.set_local_description(pc2, answer) :ok = PeerConnection.set_remote_description(pc1, answer) [pc2_tr] = PeerConnection.get_transceivers(pc2) IO.inspect(pc2_tr.direction) IO.inspect(pc2_tr.current_direction); ``` -------------------------------- ### Recycling m-lines with PeerConnection in Elixir Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/mastering_transceivers.md This Elixir snippet mirrors the JavaScript example, illustrating m-line recycling in `ex_webrtc`. It shows how a stopped audio transceiver's m-line can be reused by a new video transceiver, demonstrating the framework's behavior in managing SDP m-lines and transceiver states. ```Elixir {:ok, pc1} = PeerConnection.start_link() {:ok, pc2} = PeerConnection.start_link() {:ok, tr1} = PeerConnection.add_transceiver(pc1, :audio) {:ok, tr2} = PeerConnection.add_transceiver(pc1, :video) {:ok, offer} = PeerConnection.create_offer(pc1) :ok = PeerConnection.set_local_description(pc1, offer) :ok = PeerConnection.set_remote_description(pc2, offer) {:ok, answer} = PeerConnection.create_answer(pc2) :ok = PeerConnection.set_local_description(pc2, answer) :ok = PeerConnection.set_remote_description(pc1, answer) :ok = PeerConnection.stop_transceiver(pc1, tr1.id) {:ok, offer} = PeerConnection.create_offer(pc1) :ok = PeerConnection.set_local_description(pc1, offer) :ok = PeerConnection.set_remote_description(pc2, offer) {:ok, answer} = PeerConnection.create_answer(pc2) :ok = PeerConnection.set_local_description(pc2, answer) :ok = PeerConnection.set_remote_description(pc1, answer) {:ok, tr3} = PeerConnection.add_transceiver(pc1, :video) {:ok, offer} = PeerConnection.create_offer(pc1) :ok = PeerConnection.set_local_description(pc1, offer) :ok = PeerConnection.set_remote_description(pc2, offer) {:ok, answer} = PeerConnection.create_answer(pc2) :ok = PeerConnection.set_local_description(pc2, answer) :ok = PeerConnection.set_remote_description(pc1, answer) [%{kind: :video}, %{kind: :video}] = PeerConnection.get_transceivers(pc1) ``` -------------------------------- ### Running Elixir Application in Development Mode Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/examples/dtmf/README.md This command starts the Elixir application in the current directory. The `--no-halt` flag prevents the Erlang VM from shutting down, keeping the application running continuously for development and testing. ```Shell mix run --no-halt ``` -------------------------------- ### WebRTC Peer Connection with MediaStreamTrack and Transceiver in Elixir Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/mastering_transceivers.md This Elixir snippet demonstrates an alternative WebRTC setup where a MediaStreamTrack is explicitly created and added to one peer, while a transceiver is added to the other. It then proceeds with the standard offer/answer signaling process and inspects the transceivers on the receiving peer. Note the comment indicating a feature might not be fully supported yet. ```Elixir # TODO not supported yet {:ok, pc1} = PeerConnection.start_link() {:ok, pc2} = PeerConnection.start_link() track = MediaStreamTrack.new(:audio) {:ok, pc1_tr1} = PeerConnection.add_transceiver(pc1, :audio) {:ok, _pc2_sender} = PeerConnection.add_track(pc2, track) {:ok, offer} = PeerConnection.create_offer(pc1) :ok = PeerConnection.set_local_description(pc1, offer) :ok = PeerConnection.set_remote_description(pc2, offer) {:ok, answer} = PeerConnection.create_answer(pc2) :ok = PeerConnection.set_local_description(pc2, answer) :ok = PeerConnection.set_remote_description(pc1, answer) IO.inspect(PeerConnection.get_transceivers(pc2)) ``` -------------------------------- ### Retrieving Remote Tracks from Transceivers in Elixir Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/introduction/forwarding.md This Elixir snippet demonstrates how to obtain all remote tracks associated with a PeerConnection by iterating over its transceivers. It uses PeerConnection.get_transceivers/1 to get the list of transceivers and then maps over them to extract the receiver.track for each, providing a way to access the remote media tracks. ```Elixir tracks = peer_connection |> PeerConnection.get_transceivers() |> Enum.map(&(&1.receiver.track)) ``` -------------------------------- ### Illustrating Multi-Peer Connection Flow with Mermaid Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/modifying.md This Mermaid flowchart visualizes the architecture for connecting multiple WebRTC peers through an Elixir application. It shows three PeerConnections (PC1, PC2, PC3) within the Elixir subgraph, all connected to a central 'Forwarder' component, which then routes data to and from individual web browsers (WB1, WB2, WB3). This setup enables data forwarding between any connected peers. ```Mermaid flowchart BR subgraph Elixir PC1[PeerConnection 1] <--> Forwarder <--> PC2[PeerConnection 2] Forwarder <--> PC3[PeerConnection 3] end WB1((Web Browser 1)) <-.-> PC1 WB2((Web Browser 2)) <-.-> PC2 WB3((Web Browser 3)) <-.-> PC3 ``` -------------------------------- ### Initializing WebRTC Warmup Connection - JavaScript Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/mastering_transceivers.md This JavaScript snippet demonstrates the 'Warmup' technique for WebRTC. It initializes two peer connections, adds an audio transceiver to the first, and completes the SDP offer/answer exchange. After the connection is established, it uses `navigator.mediaDevices.getUserMedia` to obtain an audio track and then attaches it to the transceiver's sender using `replaceTrack`, allowing media to flow. ```JavaScript pc1 = new RTCPeerConnection(); pc2 = new RTCPeerConnection(); tr = pc1.addTransceiver("audio"); offer = await pc1.createOffer(); await pc1.setLocalDescription(offer); await pc2.setRemoteDescription(offer); answer = await pc2.createAnswer(); await pc2.setLocalDescription(answer); await pc1.setRemoteDescription(answer); // once MediaStreamTrack is ready, // start sending it with replaceTrack const localStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false}); await tr.sender.replaceTrack(localStream.getTracks()[0]); ``` -------------------------------- ### Creating and Setting Local SDP Offer in JavaScript Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/introduction/negotiation.md Generates an SDP (Session Description Protocol) offer using `pc.createOffer()`, which describes the local peer's capabilities and desired media. The generated offer is then set as the local description using `pc.setLocalDescription()`, making it available for exchange with the remote peer. ```JavaScript const offer = await pc.createOffer(); // offer == { type: "offer", sdp: ""} await pc.setLocalDescription(offer); ``` -------------------------------- ### Initializing WebRTC Warmup Connection - Elixir Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/mastering_transceivers.md This Elixir snippet illustrates the 'Warmup' technique for WebRTC using the `ex_webrtc` library. It initializes two peer connections and adds an audio transceiver to the first. It then performs the SDP offer/answer exchange to establish the connection. While the Elixir WebRTC library requires manual media sending via `send_rtp`, it also provides a `replace_track` function for similar functionality. ```Elixir {:ok, pc1} = PeerConnection.start_link() {:ok, pc2} = PeerConnection.start_link() {:ok, tr} = PeerConnection.add_transceiver(pc1, :audio) {:ok, offer} = PeerConnection.create_offer(pc1) :ok = PeerConnection.set_local_description(pc1, offer) :ok = PeerConnection.set_remote_description(pc2, offer) {:ok, answer} = PeerConnection.create_answer(pc2) :ok = PeerConnection.set_local_description(pc2, answer) :ok = PeerConnection.set_remote_description(pc1, answer) # although in Elixir WebRTC user has to send media on their own, # using send_rtp function, we also added replace_track function ``` -------------------------------- ### Generating WebRTC SDP Offer in JavaScript Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/test/fixtures/sdp/README.md This JavaScript code snippet demonstrates how to create a basic WebRTC SDP offer. It initializes an RTCPeerConnection, adds audio and video transceivers to enable media exchange, and then asynchronously generates an offer. This offer is a crucial first step in establishing a WebRTC peer-to-peer connection. ```JavaScript pc = new RTCPeerConnection(); pc.addTransceiver("audio"); pc.addTransceiver("video"); await pc.createOffer(); ``` -------------------------------- ### Configuring ICE Port Range in Elixir WebRTC Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/debugging.md This Elixir snippet shows how to configure a specific UDP port range for ICE (Interactive Connectivity Establishment) when starting an `ExWebRTC.PeerConnection`. By setting `ice_port_range`, users can restrict WebRTC to use ports within the specified range (e.g., 50000 to 50100), which is crucial for firewall configurations and network deployments. ```Elixir {:ok, pc} = ExWebRTC.PeerConnection.start(ice_port_range: 50_000..50_100) ``` -------------------------------- ### Initializing Elixir WebRTC PeerConnection with STUN and IP Filter Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/deploying/fly.md This Elixir code initializes a `PeerConnection` with a specified STUN server and an `ice_ip_filter`. The `ice_ip_filter` is dynamically retrieved from application environment, allowing for custom IP filtering logic, crucial for deployments on platforms like Fly.io. ```Elixir ip_filter = Application.get_env(:your_app, :ice_ip_filter) {:ok, pc} = PeerConnection.start_link( ice_ip_filter: ip_filter, ice_servers: [%{urls: "stun:stun.l.google.com:19302"}] ) ``` -------------------------------- ### Configuring STUN Servers for Elixir WebRTC PeerConnection Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/deploying/bare.md This Elixir code demonstrates how to configure a STUN (Session Traversal Utilities for NAT) server when initializing a PeerConnection. A STUN server helps the application discover its public IP address when deployed behind a NAT, enabling direct peer-to-peer connections for media exchange. ```elixir PeerConnection.start_link(ice_servers: [%{urls: "stun:stun.l.google.com:19302"}]) ``` -------------------------------- ### Creating, Setting, and Sending Local Answer in Elixir Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/introduction/negotiation.md This Elixir code creates a WebRTC answer using `ExWebRTC.PeerConnection.create_answer`, sets it as the local description, and then encodes the answer to JSON using `Jason` before sending it back to the web browser via a `web_socket_send_answer` function. ```elixir {:ok, answer} = ExWebRTC.PeerConnection.create_answer(pc) :ok = PeerConnection.set_local_description(pc, answer) answer |> ExWebRTC.SessionDescription.to_json() |> Jason.encode!() |> web_socket_send_answer() ``` -------------------------------- ### Initializing RTCPeerConnection in JavaScript Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/introduction/negotiation.md Initializes a new `RTCPeerConnection` object, which serves as the primary interface for WebRTC communication. The `iceServers` option is provided for STUN server configuration, essential for NAT traversal and establishing connectivity. ```JavaScript const pc = new RTCPeerConnection({ iceServers: "stun:stun.l.google.com:19302" }) ``` -------------------------------- ### Configuring RTCPeerConnection with ICE Servers - JavaScript Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/introduction/negotiation.md This JavaScript snippet shows the configuration of an `RTCPeerConnection` object with `iceServers`. The `iceServers` option specifies a list of STUN/TURN servers (e.g., `stun:stun.l.google.com:19302`) that assist the PeerConnection in generating various ICE candidates, significantly improving the chances of successful peer-to-peer connection establishment. ```JavaScript const pc = new RTCPeerConnection({ iceServers: "stun:stun.l.google.com:19302" }) ``` -------------------------------- ### Client-Initiated Negotiation for First Peer (Mermaid) Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/modifying.md This diagram illustrates the negotiation flow when the first peer (Web Browser) joins. The client initiates the negotiation by adding a track, leading to an offer/answer exchange with the Elixir `PeerConnection` and subsequent track forwarding. ```mermaid flowchart LR subgraph P1["Peer 1 (Web Browser)"] User-- "1. addTrack(track11)" -->PCW1[PeerConnection] end subgraph elixir [Elixir App] PCE1[PeerConnection 1]-- "3. {:track, track11}" -->Forwarder end PCW1-. "2. offer/answer" .->PCE1 ``` -------------------------------- ### Receiving and Setting Remote Offer in Elixir Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/introduction/negotiation.md This Elixir snippet demonstrates how to receive a WebRTC offer via a WebSocket message. It decodes the JSON offer using the `Jason` library, converts it into an `ExWebRTC.SessionDescription`, and then sets it as the remote description on the `PeerConnection` object. ```elixir receive do {:web_socket, {:offer, json}} -> offer = json |> Jason.decode!() |> ExWebRTC.SessionDescription.from_json() ExWebRTC.PeerConnection.set_remote_description(pc, offer) end ``` -------------------------------- ### Adding Media Tracks to RTCPeerConnection in JavaScript Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/introduction/negotiation.md Iterates through the tracks within the `localStream` (obtained from `getUserMedia`) and adds each track to the `RTCPeerConnection` instance. This prepares the media for transmission to the remote peer, indicating what media will be sent. ```JavaScript for (const track of localStream.getTracks()) { pc.addTrack(track, localStream); } ``` -------------------------------- ### Configuring Simulcast Layers in JavaScript Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/debugging.md This JavaScript snippet demonstrates how to configure Simulcast layers for a WebRTC PeerConnection. It sets up media constraints for video and audio, obtains a local media stream, and then adds a video transceiver with multiple `sendEncodings` to define different resolution and bitrate layers for Simulcast. This helps manage bandwidth and CPU load by allowing the browser to selectively send layers. ```JavaScript mediaConstraints = {video: {width: { ideal: 1280 }, height: { ideal: 720 } }, audio: true}; const localStream = await navigator.mediaDevices.getUserMedia(mediaConstraints); const pc = new RTCPeerConnection(); pc.addTransceivers(localStream.getVideoTracks()[0], { sendEncodings: [ { rid: "h", maxBitrate: 1200 * 1024}, { rid: "m", scaleResolutionDownBy: 2, maxBitrate: 600 * 1024}, { rid: "l", scaleResolutionDownBy: 4, maxBitrate: 300 * 1024 } ] }); ``` -------------------------------- ### Exposing UDP Ports in Docker Container for WebRTC Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/deploying/bare.md This Docker command illustrates how to manually expose a range of UDP ports (50000-50010) when running a Docker container. This is necessary for WebRTC applications when the --network host option is not feasible, allowing external access to the UDP ports used by ICE for media traffic. ```sh docker run -p 50000-50010/udp myapp ``` -------------------------------- ### Obtaining Local Media Stream in JavaScript Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/introduction/negotiation.md Acquires local audio and video tracks from the user's webcam and microphone using the `navigator.mediaDevices.getUserMedia` API. This function prompts the user for media permissions and returns a `MediaStream` object containing the tracks. ```JavaScript const localStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true }); ``` -------------------------------- ### Configuring STUN Server in JavaScript for WebRTC Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/deploying/fly.md This snippet demonstrates how to configure a STUN server for an RTCPeerConnection in JavaScript. The `iceServers` array specifies the URL of the STUN server, which helps in discovering public IP addresses and NAT traversal for WebRTC connections. ```JavaScript pc = new RTCPeerConnection({ iceServers: [{ urls: "stun:stun.l.google.com:19302" }], }); ``` -------------------------------- ### Displaying Remote WebRTC Tracks in JavaScript Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/introduction/forwarding.md This JavaScript snippet sets up an `ontrack` event handler for a `PeerConnection` (`pc`). When a remote track is received, the event listener assigns the first stream from the `event.stream` array to the `srcObject` of a `videoPlayer` element, making the incoming video and audio visible and audible to the user. ```JavaScript // these will be the tracks that we added using `PeerConnection.add_track` in Elixir // but be careful! event for the same track, the ids might be different for each of the peers pc.ontrack = event => videoPlayer.srcObject = event.stream[0]; ``` -------------------------------- ### Generating Opus Audio with FFmpeg (Console) Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/test/fixtures/ogg/README.md This console command utilizes FFmpeg to generate a 1-second sine wave audio file. The audio is encoded using the libopus codec at a bitrate of 32kbps and saved as 'opus_correct.ogg'. This file serves as a test fixture. ```console ffmpeg -f lavfi -i "sine=frequency=440:duration=1" -c:a libopus -b:a 32k opus_correct.ogg ``` -------------------------------- ### Offering to Receive Data in WebRTC - JavaScript Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/mastering_transceivers.md This JavaScript snippet shows how to offer to receive media tracks when the other side might not send them by default. It initializes two RTCPeerConnection instances, adds a recvonly audio transceiver, creates an offer, and then explicitly changes the transceiver direction to sendonly on the answering peer to ensure the track becomes active. ```js pc1 = new RTCPeerConnection(); pc2 = new RTCPeerConnection(); tr = pc1.addTransceiver("audio", { direction: "recvonly" }); offer = await pc1.createOffer(); await pc1.setLocalDescription(offer); await pc2.setRemoteDescription(offer); // change direction from default "recvonly" to "sendonly" // in other case, when negotiation finishes, // currentDirection of this transceiver will be inactive pc2.getTransceivers()[0].direction = "sendonly"; answer = await pc2.createAnswer(); await pc2.setLocalDescription(answer); await pc1.setRemoteDescription(answer); console.log(pc2.getTransceivers()[0].direction); console.log(pc2.getTransceivers()[0].currentDirection); ``` -------------------------------- ### Handling and Echoing WebRTC RTP Packets in Elixir Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/introduction/forwarding.md This Elixir `handle_info` function processes incoming RTP packets received via `ex_webrtc`. It retrieves the track `kind` from `state.in_tracks` and the corresponding output `id` from `state.out_tracks`. The packet is then immediately sent back using `PeerConnection.send_rtp`, effectively echoing the media. ```Elixir def handle_info({:ex_webrtc, _from, {:rtp, track_id, nil, packet}}, state) do kind = Map.fetch!(state.in_tracks, track_id) id = Map.fetch!(state.out_tracks, kind) :ok = PeerConnection.send_rtp(state.pc, id, packet) {:noreply, state} end ``` -------------------------------- ### Recycling m-lines with RTCPeerConnection in JavaScript Source: https://github.com/elixir-webrtc/ex_webrtc/blob/master/guides/advanced/mastering_transceivers.md This snippet demonstrates how `RTCPeerConnection` reuses (recycles) m-lines in SDP offers after a transceiver has been stopped. It shows that a new transceiver can occupy an m-line previously used by a stopped transceiver, even if their media kinds differ, and highlights that the order of `getTransceivers()` may not match SDP m-line order. ```JavaScript pc1 = new RTCPeerConnection(); pc2 = new RTCPeerConnection(); tr1 = pc1.addTransceiver("audio"); tr2 = pc1.addTransceiver("video"); offer = await pc1.createOffer(); await pc1.setLocalDescription(offer); await pc2.setRemoteDescription(offer); answer = await pc2.createAnswer(); await pc2.setLocalDescription(answer); await pc1.setRemoteDescription(answer); tr1.stop(); offer = await pc1.createOffer(); await pc1.setLocalDescription(offer); await pc2.setRemoteDescription(offer); answer = await pc2.createAnswer(); await pc2.setLocalDescription(answer); await pc1.setRemoteDescription(answer); tr3 = pc1.addTransceiver("video"); // Notice that createOffer will reuse (recycle) // free m-line, even though its initial type was audio. // However, pc1.getTransceivers() will return [tr1, tr3]. // That's important as the order of transceivers doesn't // have to match the order of m-lines i.e. tr3 maps to m-line // with index 0 and tr1 maps to m-line with index 1. offer = await pc1.createOffer(); await pc1.setLocalDescription(offer); await pc2.setRemoteDescription(offer); answer = await pc2.createAnswer(); await pc2.setLocalDescription(answer); await pc1.setRemoteDescription(answer); // notice that after renegotiation // pc1.getTransceivers() will only // return two (video) transceivers console.log(pc1.getTransceivers()); ```