### Install @antmedia/webrtc_adaptor Source: https://context7.com/ant-media/streamapp/llms.txt Install the SDK using npm or yarn. This is the first step before importing and using the WebRTCAdaptor class. ```bash npm install @antmedia/webrtc_adaptor # or yarn add @antmedia/webrtc_adaptor ``` ```javascript import { WebRTCAdaptor } from '@antmedia/webrtc_adaptor'; ``` -------------------------------- ### Install npm Packages (Root Directory) Source: https://github.com/ant-media/streamapp/blob/master/README.md Run this command in the main project directory to install all necessary npm packages for the project. ```shell npm install ``` -------------------------------- ### Start Publishing Animation and Playback Link Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/samples/publish_audio.html Handles the UI transition when publishing starts, hiding the offline status and showing the broadcasting status. It also constructs and displays a link to the audio player page for the current stream. ```javascript function startAnimation() { $("#offlineInfo").hide(); $("#broadcastingInfo").show(); $("#playlink").attr("href", "../audio_player.html?id=" + streamId) $("#playlink").show(); setTimeout(function () { var state = webRTCAdaptor.signallingState(streamId); if (state != null && state != "closed") { var iceState = webRTCAdaptor.iceConnectionState(streamId); if (iceState != null && iceState != "failed" && iceState != "disconnected") { startAnimation(); } else { $("#playlink").hide(); $("#broadcastingInfo").hide(); $("#offlineInfo").show(); } } else { $("#playlink").hide(); $("#broadcastingInfo").hide(); $("#offlineInfo").show(); } }, 200); } ``` -------------------------------- ### Install npm Packages (Embedded Player) Source: https://github.com/ant-media/streamapp/blob/master/README.md Navigate to the 'embedded-player' directory and run this command to install its specific npm packages. ```shell cd embedded-player npm install ``` -------------------------------- ### Start Publishing Animation Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/samples/publish_webrtc.html Manages UI updates and stream status checks after publishing starts. It recursively calls itself to monitor the ICE connection state. ```javascript function startAnimation() { $("#offlineInfo").hide(); $("#broadcastingInfo").show(); $("#playlink").attr("href", "../play.html?id=" + streamId+"&playOrder="+playType); $("#playlink").text("Play with " + playTitle); $("#playlink").show(); setTimeout(function () { var state = webRTCAdaptor.signallingState(streamId); if (state != null && state != "closed") { var iceState = webRTCAdaptor.iceConnectionState(streamId); if (iceState != null && iceState != "new" && iceState != "closed" && iceState != "failed" && iceState != "disconnected") { startAnimation(); } else { $("#playlink").hide(); $("#broadcastingInfo").hide(); $("#offlineInfo").show(); } } else { $("#playlink").hide(); $("#broadcastingInfo").hide(); $("#offlineInfo").show(); } }, 200); } ``` -------------------------------- ### Start Publishing Stream Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/samples/publish_webrtc.html Initiates the WebRTC publishing process. Sets the bitrate and calls the `publish` method with stream details. ```javascript function startPublishing() { setBitrateValue(); webRTCAdaptor.bandwidth = maxVideoBitrateKbps; streamId = streamIdBox.value; webRTCAdaptor.publish(streamId, token, subscriberId, subscriberCode, streamName, mainTrack, metaData); } ``` -------------------------------- ### Install WebRTC Adaptor via npm Source: https://github.com/ant-media/streamapp/blob/master/README.md Use this command to add the WebRTC adaptor to your project dependencies using npm. ```shell npm install @antmedia/webrtc_adaptor ``` -------------------------------- ### Initialize WebRTC Adaptor Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/canvas-player.html Initializes the WebRTCAdaptor globally. This is a common setup step for WebRTC functionality. ```javascript window.webRTCAdaptor = webRTCAdaptor; ``` -------------------------------- ### Initialize WebRTC Adaptor and Get URL Parameters Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/conference-room.html Imports necessary modules and retrieves URL parameters to configure the conference room. Handles default values for room and stream names. ```javascript import {WebRTCAdaptor} from "./js/webrtc_adaptor.js" import {getUrlParameter} from "./js/fetch.stream.js" /** * This page accepts 7 arguments through url parameter * 1. "streamId": the stream id to publish stream. It's optional. ?streamId=stream1 * 2. "playOnly": If it's true, user does not publish stream. It only play streams in the room. * 3. "token": It's experimental. * 4. "roomName": The id of the conference room which requested streams belongs to * 5. "streamName": Stream name of stream * 6. "subscriberId": It's experimental. * 7. "subscriberCode": It's experimental. */ var token = getUrlParameter("token"); var publishStreamId = getUrlParameter("streamId"); var streamName = getUrlParameter("streamName"); var playOnly = getUrlParameter("playOnly"); var roomName = getUrlParameter("roomName"); var subscriberId = getUrlParameter("subscriberId"); var subscriberCode = getUrlParameter("subscriberCode"); var isChatActive = false; var isPlaying = false; var fullScreenId = -1; if(roomName == null){ roomName="room1"; } if(streamName == null) { streamName="Guest"; } document.getElementById("streamNameInput").value = streamName; if(playOnly == null) { playOnly = false; } ``` -------------------------------- ### Start Publishing Audio Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/samples/publish_audio.html Initiates the audio publishing process by calling the publish method on the WebRTCAdaptor instance with the stream ID. This function is triggered by the 'Start Publishing' button. ```javascript function startPublishing() { streamId = streamNameBox.value; webRTCAdaptor.publish(streamId); } ``` -------------------------------- ### Install WebRTC Adaptor via yarn Source: https://github.com/ant-media/streamapp/blob/master/README.md Use this command to add the WebRTC adaptor to your project dependencies using yarn. ```shell yarn add @antmedia/webrtc_adaptor ``` -------------------------------- ### Handle Button Click for Starting/Stopping Stream Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/whip.html Toggles the stream between starting and stopping. If the client is not initialized, it calls `init()`. It also sets up an event listener for ICE connection state changes to update the UI and display a play link when connected. The `ingest` method is called to start sending media. ```javascript function buttonClicked() { console.log("button clicked"); if (client && client.peer && client.peer.iceConnectionState != "new") { stop(); } else { if (client == null) { init(); } document.getElementById("status").innerHTML = "starting"; button.innerHTML = "Stop"; client.peer.addEventListener('iceconnectionstatechange', function () { if (client.peer) { console.log('iceconnectionstatechange', client.peer.iceConnectionState); document.getElementById("status").innerHTML = client.peer.iceConnectionState; if (client.peer.iceConnectionState == "connected") { document.getElementById("playLink").style.display = "block"; document.getElementById("playLink").href = "play.html?id=" + streamId + "&playOrder=webrtc"; } else { document.getElementById("playLink").style.display = "none"; } } else { document.getElementById("playLink").style.display = "none"; } }); client.ingest(mediaStream).then(function () { console.log("Ingesting"); }) .catch(function (err) { console.error(err); }); } } ``` -------------------------------- ### Initialize and Start WebRTC Test Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/webrtc-test-tool.html Initiates the WebRTC test by setting up publishing and playing adaptors. It checks if the WebSocket URL has changed and reinitializes if necessary, otherwise it proceeds with publishing. ```javascript tartPublish = false; function startWebRTCTest() { start_webrtc_test_button.disabled = true; if(websocketURL != test_websocket_url.value){ websocketURL = test_websocket_url.value; startPublish = true; initPublishWebRTCAdaptor(false,null); initPlayWebRTCAdaptor(null,null); } else { publishWebRTCAdaptor.publish(streamId, token); } } ``` -------------------------------- ### Play a Stream with Adaptor Source: https://context7.com/ant-media/streamapp/llms.txt Sends the 'play' command to start streaming. Handles single-stream or multitrack playback. For multitrack, specify tracks to enable or disable. ```javascript adaptor.play('stream-001'); ``` ```javascript adaptor.play('stream-001', 'eyJhbGciOiJIUzI1NiJ9...'); ``` ```javascript adaptor.play('myRoom', null, null, ['userStream-alice', 'userStream-bob', '!userStream-charlie'], null, null, null, 'viewer' ); ``` ```javascript adaptor.play({ streamId: 'myRoom', token: null, enableTracks: ['userStream-alice'], disableTracksByDefault: true, // all other tracks muted until explicitly enabled metaData: JSON.stringify({ viewer: 'Dave' }) }); ``` -------------------------------- ### Initialize WebRTC Adaptor with Local Stream Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/publish_with_timestamp.html Initializes the WebRTC adaptor after obtaining user media (camera and audio). It sets up the local camera view and starts a timer to update the canvas. ```javascript $(function() { var id = getUrlParameter("id"); if(typeof id != "undefined") { $("#streamName").val(id); } else { id = getUrlParameter("name"); if (typeof id != "undefined") { $("#streamName").val(id); } else { $("#streamName").val("stream1"); } } //get audio with getUserMedia navigator.mediaDevices.getUserMedia({video: true, audio:true}).then(function (stream) { //add audio track to the localstream which is captured from canvas window.stream = stream; localCanvasStream.addTrack(stream.getAudioTracks()[0]); localCameraView = document.getElementById("localCameraView"); localCameraView.srcObject = stream; localCameraView.play(); //update canvas for every 40ms setInterval(function() { draw(); }, 40); initWebRTCAdaptor(localCanvasStream); }); }); window.localCanvasStream = localCanvasStream; ``` -------------------------------- ### Start Screen Share - JavaScript Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/conference-room.html Initiates screen sharing by calling the webRTCAdaptor's switchDesktopCapture method. Handles UI updates for screen share buttons. ```javascript function startScreenShare() { isScreenSharing = true; webRTCAdaptor.switchDesktopCapture(publishStreamId); handleScreenShareButtons(); } ``` -------------------------------- ### WebRTC Configuration and URL Setup Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/canvas-publish.html Defines WebRTC configuration objects for ICE servers and SDP constraints, and constructs the WebSocket URL based on the current protocol, hostname, and path. ```javascript var pc_config = { 'iceServers': [ { 'urls': 'stun:stun1.l.google.com:19302' } ] }; /* //sample turn configuration { iceServers: [ { urls: "", username: "", credential: "", } ] }; */ var sdpConstraints = { OfferToReceiveAudio: false, OfferToReceiveVideo: false }; var mediaConstraints = { video: true, audio: true }; var appName = location.pathname.substring(0, location.pathname.lastIndexOf("/") + 1); var path = location.hostname + ":" + location.port + appName + "websocket?rtmpForward=" + rtmpForward; var websocketURL = "ws://" + path; if (location.protocol.startsWith("https")) { websocketURL = "wss://" + path; } ``` -------------------------------- ### Initialize WebRTC Adaptor and Get URL Parameters Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/conference-deprecated.html Initializes the WebRTCAdaptor and retrieves streamId, playOnly, and token from URL parameters. Handles default values for playOnly. ```javascript import {WebRTCAdaptor} from "./js/webrtc_adaptor.js" import {getUrlParameter} from "./js/fetch.stream.js" /** * This page accepts 3 arguments through url parameter * 1. "streamId": the stream id to publish stream. It's optional. ?streamId=stream1 * 2. "playOnly": If it's true, user does not publish stream. It only play streams in the room. * 3. "token": It's experimental. */ var token = getUrlParameter("token"); var streamId = getUrlParameter("streamId"); var playOnly = getUrlParameter("playOnly"); if(playOnly == null) { playOnly = false; } ``` -------------------------------- ### Initialize WebRTCAdaptor and Publish Stream Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/publish_with_timestamp.html Initializes the WebRTCAdaptor with WebSocket URL, media constraints, and callback functions. It handles the publishing process, including starting and stopping the stream, and provides feedback on connection states. ```javascript import {WebRTCAdaptor} from "./js/webrtc_adaptor.js" import {getUrlParameter} from "./js/fetch.stream.js" var canvas = document.getElementById('canvas'); var localCameraView = null; function draw() { if (canvas.getContext && localCameraView != null) { var ctx = canvas.getContext('2d'); canvas.width = localCameraView.videoWidth; canvas.height = localCameraView.videoHeight; ctx.drawImage(localCameraView, 0, 0, canvas.width, canvas.height); ctx.fillStyle = 'rgba(255, 255, 255, 0.9)'; ctx.font = "20px Arial"; var text = "Publish: " + Date.now(); var textMetrics = ctx.measureText(text); ctx.fillRect(5, 20, textMetrics.width+ 10, 30); ctx.fillStyle = 'rgba(0, 0, 255, 1.0)'; ctx.fillText(text, 10, 40); } } //capture stream from canvas var localCanvasStream = canvas.captureStream(25); var token = getUrlParameter("token"); var camera_checkbox = document.getElementById("camera_checkbox"); var screen_share_checkbox = document.getElementById("screen_share_checkbox"); var screen_share_with_camera_checkbox = document.getElementById("screen_share_with_camera_checkbox"); var start_publish_button = document.getElementById("start_publish_button"); var stop_publish_button = document.getElementById("stop_publish_button"); var install_extension_link = document.getElementById("install_chrome_extension_link"); var streamNameBox = document.getElementById("streamName"); var streamId; var name = getUrlParameter("name"); if(name !== "undefined") { streamNameBox.value = name; } // It should be true var rtmpForward = getUrlParameter("rtmpForward"); function startPublishing() { streamId = streamNameBox.value; webRTCAdaptor.publish(streamId, token); } start_publish_button.addEventListener("click", startPublishing, false); function stopPublishing() { webRTCAdaptor.stop(streamId); } stop_publish_button.addEventListener("click", stopPublishing, false); function startAnimation() { $("#broadcastingInfo").fadeIn(800, function () { $("#broadcastingInfo").fadeOut(800, function () { var state = webRTCAdaptor.signallingState(streamId); if (state != null && state != "closed") { var iceState = webRTCAdaptor.iceConnectionState(streamId); if (iceState != null && iceState != "failed" && iceState != "disconnected") { startAnimation(); } } }); }); } var pc_config = null; var sdpConstraints = { OfferToReceiveAudio : false, OfferToReceiveVideo : false }; var mediaConstraints = { video : true, audio : true }; var appName = location.pathname.substring(0, location.pathname.lastIndexOf("/")+1); var path = location.hostname + ":" + location.port + appName + "websocket?rtmpForward=" + rtmpForward; var websocketURL = "ws://" + path; if (location.protocol.startsWith("https")) { websocketURL = "wss://" + path; } var webRTCAdaptor; function initWebRTCAdaptor(stream) { webRTCAdaptor = new WebRTCAdaptor({ websocket_url : websocketURL, mediaConstraints : mediaConstraints, peerconnection_config : pc_config, sdp_constraints : sdpConstraints, localVideoId : "localVideo", localStream: stream, debug:true, callback : function(info, obj) { if (info == "initialized") { console.log("initialized"); start_publish_button.disabled = false; stop_publish_button.disabled = true; } else if (info == "publish_started") { //stream is being published console.log("publish started"); start_publish_button.disabled = true; stop_publish_button.disabled = false; startAnimation(); } else if (info == "publish_finished") { //stream is being finished console.log("publish finished"); start_publish_button.disabled = false; stop_publish_button.disabled = true; } else if (info == "browser_screen_share_supported") { } else if (info == "screen_share_stopped") { } else if (info == "closed") { //console.log("Connection closed"); if (typeof obj != "undefined") { console.log("Connecton closed: " + JSON.stringify(obj)); } } else if (info == "pong") { //ping/pong message are sent to and received from server to make the connection alive all the time //It's especially useful when load balancer or firewalls close the websocket connection due to inactivity } else if (info == "refreshConnection") { startPublishing(); } else if (info == "ice_connection_state_changed") { console.log("iceConnectionState Changed: ",JSON.stringify(obj)); } else if (info == "updated_stats") { //obj is the PeerStats which has fields //averageOutgoingBitrate - kbits/sec //currentOutgoingBitrate - kbits/sec console.log("Average outgoing bitrate " + obj.averageOutgoingBitrate + " kbits/sec" + " Current outgoing bitrate: " + obj.currentOutgoingBitrate + " kbits/sec"); } }, callbackError : function(error, message) { //some of the possible errors, NotFoundError, Se } }); } ``` -------------------------------- ### Initialize WebRTC Adaptor for Publishing Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/webrtc-test-tool.html Initializes the WebRTCAdaptor for publishing a stream. It configures websocket URL, media constraints, and callbacks for various events like initialization, publish start, and connection closed. ```javascript function initPublishWebRTCAdaptor(publishImmediately) { publishWebRTCAdaptor = new WebRTCAdaptor({ websocket_url : websocketURL, mediaConstraints : publishMediaConstraints, peerconnection_config : pc_config, sdp_constraints : publishSdpConstraints, localVideoId : "localVideo", debug:true, bandwidth:"unlimited", callback : (info, obj) => { if (info == "initialized") { console.log("initialized"); websocketSucceed.style.display = "inline"; websocketFailed.style.display = "none"; start_webrtc_test_button.disabled = false; if(startPublish){ publishWebRTCAdaptor.publish(streamId, token); } } else if (info == "publish_started") { webRTCPlayAdaptor.play(streamId, token); resetPublishWarnStatistics(); resetPublishInfoStatistics(); resetPlayInfoStatistics(); resetPlayWarnStatistics(); resetParameters(); //stream is being published console.log("publish started"); publish_statistics.style.display = "block"; play_statistics.style.display = "block"; test_result_text.textContent = "Test Started"; start_webrtc_test_button.disabled = true; progress.style.display = "block"; $(".progress-bar").css("width", statsTime + "%").text(statsTime + " %"); test_result_text.style.display = "block"; publishWebRTCAdaptor.enableStats(obj.streamId); } else if (info == "publish_finished") { //stream is being finished console.log("publish finished"); start_webrtc_test_button.disabled = false; test_result_text.textContent = "Test Finished"; } else if (info == "closed") { //console.log("Connection closed"); if (typeof obj != "undefined") { websocketSucceed.style.display = "none"; websocketFailed.style.display = "inline"; console.log("Connecton closed: " + JSON.stringify(obj)); } } else if (info == "pong") { //ping/pong message are sent to and received from server to make the connection alive all the time //It's especially useful when load balancer or firewalls close the websocket connection due to inactivity } else if (info == "refreshConnection") { checkAndRepublishIfRequired(); } else if (info == "ice_connection_state_changed") { console.log("iceConnectionState Changed: ",JSON.stringify(obj)); } else if (info == "updated_stats") { statsTime++; var percentValue = 100/test_duration_text.value; $(".progress-bar").css("width", statsTime*percentValue + "%").text((statsTime*percentValue).toPrecision(3) + " %"); if(statsTime == test_duration_text.value){ isFinished = true; publishWebRTCAdaptor.stop(streamId); webRTCPlayAdaptor.stop(streamId); statsTime=0; if(publishMinBitrateValue < 500){ publish_min_bit_rate_warn.style.display = "inline"; } else{ publish_min_bit_rate_info.style.display = "inline"; } if(publishAverageBitrateValue < 800){ publish_average_bit_rate_warn.style.display = "inline"; } else{ publish_average_bit_rate_info.style.display = "inline"; } if(publishMaxBitrateValue < 1200){ publish_max_bit_rate_warn.style.display = "inline"; } else{ publish_max_bit_rate_info.style.display = "inline"; } if(publishPacketLostValue < 20){ publish_packet_lost_info.style.display = "inline"; } else{ publish_packet_lost_warn.style.display = "inline"; } if(publishJitterValue < 0.1){ publish_jitter_info.style.display = "inline"; } else{ publish_jitter_warn.style.display = "inline"; } if(publishRTTValue < 0.1){ round_trip_time_info.style.display = "inline"; } else{ round_trip_time_warn.style.display = "inline"; } source_resolution_info.style.display = "inline"; ongoing_resolution_info.style.display = "inline"; on_going_fps_info.style.display = "inline"; } //obj is the PeerStats which has fields //averageOutgoingBitrate - kbits/sec //currentOutgoingBitrate - kbits/sec console.log("Average outgoing bitrate " + obj.averageOutgoingBitrate + " kbits/sec" + " Current outgoing bitrate: " + obj.currentOutgoingBitrate + " kbits/sec" + " video source width: " + obj.resWidth + " video source height: " + obj.resHeight + "frame width: " + obj.frameWidth + " frame height: " + obj.frameHeight + " video packetLost: " + obj.videoPacketsLost + " audio packetsLost: " + obj.audioPacketsLost + " video RTT: " + obj.videoRoundTripTime + " audio RTT: " + obj.audioRoundTripTime + " video jitter: " + obj.videoJitter + " audio jitter: " + obj.audioJitter ); if(publishMinBitrateValue == 0 || publishMinBitrateValue > obj.currentOutgoingBitrate){ publishMinBitrateValue = obj.currentOutgoingBitrate; } if(publishAverageBitrateValue == 0 || publishAverageBitrateValue > obj.averageOutgoingBitrate){ publishAverageBitrateValue = obj.averageOutgoingBitrate; } if(obj.currentOutgoingBitrate != 0 && publishMaxBitrateValue < obj.currentOutgoingBitrate ){ publishMaxBitrateValue = obj.currentOutgoingBitrate; } $("#publish_min_bit_rate").text(publishMinBitrateValue); $("#publish_average_bit_rate").text(obj.averageOutg ``` -------------------------------- ### Enable and Get WebRTC Statistics Source: https://context7.com/ant-media/streamapp/llms.txt Use `enableStats` to start periodic statistics updates and `getStats` for a one-time retrieval. `disableStats` stops the periodic updates. The callback receives `PeerStats` objects containing detailed metrics. ```javascript // Periodic stats adaptor.enableStats('stream-001', 3000); // every 3 s // In callback: // info === 'updated_stats', obj is a PeerStats instance function handleStats(info, obj) { if (info !== 'updated_stats') return; console.log('Outgoing bitrate (current):', obj.currentOutgoingBitrate, 'kbps'); console.log('Outgoing bitrate (avg):', obj.averageOutgoingBitrate, 'kbps'); console.log('Incoming bitrate (current):', obj.currentIncomingBitrate, 'kbps'); console.log('Video FPS:', obj.currentFPS); console.log('Video packets lost:', obj.videoPacketsLost); console.log('Audio jitter (s):', obj.audioJitter); console.log('Frame size:', obj.frameWidth + 'x' + obj.frameHeight); console.log('Quality limit reason:', obj.qualityLimitationReason); // Per-track inbound stats (multitrack play): obj.inboundRtpList.forEach(t => console.log('Track', t.trackIdentifier, 'bytes:', t.bytesReceived) ); } // One-shot adaptor.getStats('stream-001').then(stats => { console.log('RTT video:', stats.videoRoundTripTime, 's'); }); adaptor.disableStats('stream-001'); ``` -------------------------------- ### Initialize Playback Controls and Event Handlers Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/multitrackplayer.html Sets the default stream name and attaches click event listeners to the start, stop, and get tracks buttons using jQuery. Ensure jQuery is loaded before this script. ```javascript $(function() { $("#streamName").val("stream_multi_track"); $("#start_play_button").click(function() { startPlaying(); }); $("#stop_play_button").click(function() { stopPlaying(); }); $("#get_tracks_button").click(function() { getTracks(); }); }); ``` -------------------------------- ### Global Initialization and Event Binding Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/whip.html Calls the `init` function to set up the client and media stream on page load. It also exposes `buttonClicked` and `client` to the global scope for debugging or external interaction. ```javascript init(); window.buttonClicked = buttonClicked; window.client = client; ``` -------------------------------- ### Initialize WebRTCAdaptor and Event Listeners Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/player.html Sets up the WebRTCAdaptor, retrieves URL parameters, and attaches event listeners to UI elements for player control. Ensure necessary JavaScript files are imported. ```javascript import {WebRTCAdaptor} from "./js/webrtc_adaptor.js" import {getUrlParameter} from "./js/fetch.stream.js" import {errorHandler} from "./js/utility.js" import "./js/loglevel.min.js"; const Logger = window.log; Logger.setLevel("debug"); var token = getUrlParameter("token"); $(function() { var id = getUrlParameter("id"); if(typeof id != "undefined") { $("#streamName").val(id); } else { id = getUrlParameter("name"); if (typeof id != "undefined") { $("#streamName").val(id); } else { $("#streamName").val("stream1"); } } }); var signaling = getUrlParameter("signaling"); if (signaling == "true") { signaling = true; } else { signaling = false; } var subscriberId = getUrlParameter("subscriberId"); var subscriberCode = getUrlParameter("subscriberCode"); var metaData = getUrlParameter("metaData"); var start_play_button = document.getElementById("start_play_button"); start_play_button.addEventListener("click", startPlaying, false) var stop_play_button = document.getElementById("stop_play_button"); stop_play_button.addEventListener("click",stopPlaying,false); var options = document.getElementById("options"); options.addEventListener("click", toggleOptions, false); var send = document.getElementById("send"); send.addEventListener("click", sendData, false); var streamNameBox = document.getElementById("streamName") streamNameBox.defaultValue = "stream1"; var remoteVideo = document.getElementById("remoteVideo"); var lastFrameReceivedCount = 0; var lastFrameDecodedCount = 0; var lastStatsReceiveTime = 0; var streamId; ``` -------------------------------- ### Initialize WebRTCAdaptor and Utility Functions Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/multitrack-conference.html Imports necessary modules for WebRTC functionality, including the WebRTCAdaptor, utility functions for URL parameters, sound measurement, and logging. Sets up initial logging level. ```javascript import { WebRTCAdaptor } from "./js/webrtc_adaptor.js" import { getUrlParameter } from "./js/fetch.stream.js" import { SoundMeter } from "./js/soundmeter.js" import { generateRandomString, getWebSocketURL, errorHandler, updateBroadcastStatusInfo } from "./js/utility.js" import "./js/loglevel.min.js"; const Logger = window.log; Logger.setLevel("debug"); ``` -------------------------------- ### Initialize WebRTCAdaptor Source: https://context7.com/ant-media/streamapp/llms.txt Create a new WebRTCAdaptor instance with configuration options. The constructor opens the local media stream and connects the WebSocket. Ensure websocketURL or httpEndpointUrl is provided. ```javascript import { WebRTCAdaptor } from '@antmedia/webrtc_adaptor'; const adaptor = new WebRTCAdaptor({ websocketURL: 'wss://your-server:5443/WebRTCAppEE/websocket', mediaConstraints: { video: { width: { ideal: 1280 }, height: { ideal: 720 }, frameRate: { ideal: 30 } }, audio: { noiseSuppression: true, echoCancellation: true } }, peerconnection_config: { iceServers: [{ urls: 'stun:stun1.l.google.com:19302' }], sdpSemantics: 'unified-plan' }, sdp_constraints: { OfferToReceiveAudio: false, OfferToReceiveVideo: false }, localVideoId: 'localVideo', // bandwidth: 1500, // kbps; pass "unlimited" to remove cap dataChannelEnabled: true, debug: false, reconnectIfRequiredFlag: true, // auto-reconnect on ICE failure degradationPreference: 'maintain-resolution', // or 'maintain-framerate' / 'balanced' callback(info, obj) { if (info === 'initialized') console.log('Ready to publish/play'); if (info === 'publish_started') console.log('Publishing stream', obj); if (info === 'play_started') console.log('Playback started', obj); if (info === 'newTrackAvailable') { // obj = { stream, track, streamId, trackId } document.getElementById('remoteVideo').srcObject = obj.stream; } if (info === 'data_received') console.log('Data:', obj.data); if (info === 'updated_stats') console.log('Bitrate out:', obj.currentOutgoingBitrate, 'kbps'); }, callbackError(error, message) { // error codes: 'NotAllowedError', 'no_stream_exist', 'WebSocketNotConnected', etc. console.error('[AMS Error]', error, message); } }); ``` -------------------------------- ### Start and Stop Streaming Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/merge_streams.html Functions to initiate and terminate the WebRTC stream merging process. `start` calls the `startStreaming` method on the merger object, and `stop` calls `stopStreaming`. ```javascript function start() { merger.startStreaming(); } function stop() { merger.stopStreaming(); } ``` -------------------------------- ### Initialize WebRTC Adaptor and Play Stream Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/multitrack-play.html Sets up the WebRTC adaptor with the provided WebSocket URL and configuration. It then initiates playback for a specific stream ID and token. Handles initialization, playback start/finish, new tracks, and connection closure events. ```javascript var appName = getUrlParameter("app"); if (!appName) { appName = location.pathname.substring(1, location.pathname.indexOf("/", 1) + 1); } if (!appName.endsWith("/")) { appName += "/"; } var path = location.hostname + ":" + location.port + "/" + appName + "websocket"; var websocketURL = "ws://" + path; if (location.protocol.startsWith("https")) { websocketURL = "wss://" + path; } if (streamId == null || streamId == "") { alert("Stream Id is not provided. Please give ?id={STREAM_ID} as query parameter in the URL"); } else { var webRTCAdaptor = new WebRTCAdaptor({ websocket_url: websocketURL, isPlayMode: true, callback: function (info, obj) { if (info === "initialized") { clearPlayers(); webRTCAdaptor.play(streamId, token, "", [], subscriberId, subscriberCode); } else if (info === "play_started") { setPlaceHolderVisibility(false); } else if (info === "play_finished") { clearPlayers(); setPlaceHolderVisibility(true); } else if (info == "newTrackAvailable") { playVideo(obj); } else if (info === "closed") { clearPlayers(); setPlaceHolderVisibility(true); } }, callbackError: function (error) { console.log("error callback: " + JSON.stringify(error)); } }); } ``` -------------------------------- ### Handle Session Restored Notification Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/samples/publish_webrtc.html This code snippet handles the 'session_restored' notification. It enables the stop publish button, disables the start publish button, and starts an animation, indicating that a session has been successfully restored. ```javascript else if (info == "session_restored") { start_publish_button.disabled = true; stop_publish_button.disabled = false; startAnimation(); console.log(info + " notification received"); } ``` -------------------------------- ### Initialize WebRTC Adaptor and UI Elements Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/samples/publish_webrtc.html Sets up the WebRTC adaptor, initializes UI elements, and retrieves URL parameters for stream configuration. Handles stream ID generation and event listeners for UI controls. ```javascript import { WebRTCAdaptor } from "../js/webrtc_adaptor.js" import { getUrlParameter } from "../js/fetch.stream.js" import { SoundMeter } from "../js/soundmeter.js" import { generateRandomString, getWebSocketURL, errorHandler } from "../js/utility.js" import "../js/loglevel.min.js"; const Logger = window.log; Logger.setLevel("debug"); var debug = getUrlParameter("debug"); if (debug == null) { debug = false; } var mediaConstraints = { audio: { noiseSuppression: true, echoCancellation: true }, video: true }; function init() { $(' a[data-toggle="tooltip"]').tooltip() var id = getUrlParameter("id"); if (typeof id != "undefined") { $("#streamId").val(id); } else { id = getUrlParameter("name"); if (typeof id != "undefined") { $("#streamId").val(id); } else { $("#streamId").val("streamId_" + generateRandomString(9)); } } } var max_bandwidth_input = document.getElementById("maxBandwidthTextBox"); var max_bandwidth_apply = document.getElementById("max_bandwidth_apply"); max_bandwidth_apply.addEventListener("click", setBitrateValue, false); var maxVideoBitrateKbps = 1200; var subscriberId = getUrlParameter("subscriberId"); var subscriberCode = getUrlParameter("subscriberCode"); var streamName = getUrlParameter("streamName"); var mainTrack = getUrlParameter("mainTrack"); var playType = getUrlParameter("play"); var metaData = getUrlParameter("metaData"); var playTitle; if (playType == "hls") { playTitle = "HLS"; } else if (playType == "dash") { playTitle = "Dash"; } else { playTitle = "WebRTC"; playType = "webrtc"; } //TODO: Migrate these methods to Jquery var start_publish_button = document.getElementById("start_publish_button"); start_publish_button.addEventListener("click", startPublishing, false); var stop_publish_button = document.getElementById("stop_publish_button"); stop_publish_button.addEventListener("click", stopPublishing, false); var options = document.getElementById("options"); options.addEventListener("click", toggleOptions, false); var send = document.getElementById("send"); send.addEventListener("click", sendData, false); var streamIdBox = document.getElementById("streamId"); var toggleNoiseSuppression = document.getElementById("noiseSuppression"); toggleNoiseSuppression.addEventListener("click", changeAudioConstraints, false); var toggleEchoCancellation = document.getElementById("echoCancellation"); toggleEchoCancellation.addEventListener("click", changeAudioConstraints, false); var streamId; var token = getUrlParameter("token"); // It should be true var rtmpForward = getUrlParameter("rtmpForward"); var volume_change_input = document.getElementById("volume_change_input"); volume_change_input.addEventListener("change", changeVolume); function changeVolume() { /** * Change the gain levels on the input selector. */ if (document.getElementById('volume_change_input') != null) { webRTCAdaptor.setVolumeLevel(this.value); } } function setBitrateValue() { var bitrateBoxValue = max_bandwidth_input.value; if (bitrateBoxValue == "unlimited" || bitrateBoxValue == NaN) { maxVideoBitrateKbps = "unlimited" console.log("input bitrate: " + maxVideoBitrateKbps); } else { var bitrate = parseInt(bitrateBoxValue); if (bitrate == NaN) { maxVideoBitrateKbps = 1200; } else if (bitrate < 100) { maxVideoBitrateKbps = 100; } else { maxVideoBitrateKbps = bitrate; } console.log("input bitrate: " + maxVideoBitrateKbps); } // Check stream is publishing // If it's publishing then call changeBandwidth function if (start_publish_button.disabled) { webRTCAdaptor.changeBandwidth(maxVideoBitrateKbps, $("#streamId").val()); } } let meterRefresh = null; var audioLevelTimerId = -1; const instantMeter = document.querySelector('#audio_level_text_container'); const instantValueDisplay = document.querySelector('#audio_level_text'); function disableAudioLevel() { if (audioLevelTimerId != -1) { clearInterval(audioLevelTimerId); audioLevelTimerId = -1; } } function enableAudioLevel() { //ATTENTION: Using sound meter in order to get audio level may cause audio distortion in Windows browsers //webRTCAdaptor.enableAudioLevelForLocalStream((value) => { // instantMeter.value = instantValueDisplay.innerText = value; //}, 200); audioLevelTimerId = setInterval( ``` -------------------------------- ### Stream Event Callbacks Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/merge_streams.html Callback functions executed when a stream starts or finishes. `streamStarted` enables the stop button and shows broadcasting info with animation, while `streamFinished` enables the start button and hides the info. ```javascript function streamStarted() { $("#start_button").prop("disabled", true); $("#stop_button").prop("disabled", false); $("#broadcastingInfo").show(); startAnimation(); } function streamFinished() { $("#start_button").prop("disabled", false); $("#stop_button").prop("disabled", true); $("#broadcastingInfo").hide(); } ``` -------------------------------- ### Initialize WebRTCAdaptor Source: https://github.com/ant-media/streamapp/blob/master/README.md Instantiate the WebRTCAdaptor with configuration options including WebSocket URL, media constraints, peer connection configuration, and callback functions for handling information and errors. Ensure the local video element ID is correctly set. ```javascript import { WebRTCAdaptor } from '@antmedia/webrtc_adaptor'; const webRTCAdaptor = new WebRTCAdaptor({ websocket_url: "wss://your-domain.tld:5443/WebRTCAppEE/websocket", mediaConstraints: { video: true, audio: true, }, peerconnection_config: { 'iceServers': [{'urls': 'stun:stun1.l.google.com:19302'}] }, sdp_constraints: { OfferToReceiveAudio : false, OfferToReceiveVideo : false, }, localVideoId: "id-of-video-element", // bandwidth: int|string, // default is 900 kbps, string can be 'unlimited' dataChannelEnabled: true|false, // enable or disable data channel callback: (info, obj) => {}, // check info callbacks bellow callbackError: function(error, message) {}, // check error callbacks bellow }); ``` -------------------------------- ### Initialize WebRTCAdaptor Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/samples/publish_audio.html Initializes the WebRTCAdaptor with configuration for audio publishing. This includes websocket URL, media constraints, peer connection configuration, and SDP constraints. The callback function handles initialization status and errors. ```javascript var pc_config = { 'iceServers': [{ 'urls': 'stun:stun1.l.google.com:19302' }] }; var sdpConstraints = { OfferToReceiveAudio: false, OfferToReceiveVideo: false }; var mediaConstraints = { video: false, audio: true }; var websocketURL = getWebSocketURL(location); var webRTCAdaptor = new WebRTCAdaptor({ websocket_url: websocketURL, mediaConstraints: mediaConstraints, peerconnection_config: pc_config, sdp_constraints: sdpConstraints, localVideoId: "localVideo", debug: true, callback: function (info, description) { if (info == "initialized") { console.log("initialized"); start_publish_button.disabled = false; stop_publish_button.disabled = true; } else if (info == "publish_started") { //stream is being published console.log("publish started"); start_publish_button.disabled = true; stop_publish_button.disabled = false; startAnimation(); } else if (info == "publish_finished") { //stream is being finished console.log("publish finished"); start_publish_button.disabled = false; stop_publish_button.disabled = true; } else if (info == "closed") { //console.log("Connection closed"); if (typeof description != "undefined") { console.log("Connecton closed: " + JSON.stringify(description)); } } }, callbackError: function (error, message) { //some of the possible errors, NotFoundError, SecurityError,PermissionDeniedError $.notify("Warning: " + errorHandler(error, message), { autoHideDelay:5000, className:'error', position:'top center' }); } }); ``` -------------------------------- ### Start Publishing WebRTC Stream Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/samples/publish_webrtc_deep_ar_effects_frame.html Starts the WebRTC publishing process. Sets the player iframe source and calls the publish method of the WebRTCAdaptor with stream ID, token, subscriber ID, subscriber code, stream name, and main track. ```javascript var streamIdBox = document.getElementById("streamId"); var streamId; var token = getUrlParameter("token"); function startPublishing() { streamId = streamIdBox.value; playerIframe.src = "../play.html?id="+streamId; webRTCAdaptor.publish(streamId, token, subscriberId, subscriberCode, streamName, mainTrack); } ``` -------------------------------- ### Publish Source: https://github.com/ant-media/streamapp/blob/master/README.md Starts streaming by calling the publish method with a stream ID. ```APIDOC ## Publish ### Description Starts streaming by calling the publish method with a stream ID. ### Method `publish(streamId)` ### Parameters - **streamId** (string) - Required - The unique identifier for the stream to be published. ### Request Example ```javascript webRTCAdaptor.publish(streamId); ``` ``` -------------------------------- ### Play Source: https://github.com/ant-media/streamapp/blob/master/README.md Starts playing a stream by calling the play method with a stream ID. ```APIDOC ## Play ### Description Starts playing a stream by calling the play method with a stream ID. ### Method `play(streamId)` ### Parameters - **streamId** (string) - Required - The unique identifier for the stream to be played. ### Request Example ```javascript webRTCAdaptor.play(streamId); ``` ``` -------------------------------- ### Initialize WHIP Client and Media Stream Source: https://github.com/ant-media/streamapp/blob/master/src/main/webapp/whip.html Initializes the WHIP client with the server endpoint and captures user media (video and audio). The stream ID is either provided via URL or generated randomly. Debugging is enabled, and STUN servers are configured for ICE. The captured media is then attached to a video element. ```javascript import { WHIPClient } from "https://esm.sh/@eyevinn/whip-web-client@1.1.2/dist/whip-client.modern.js"; import {getQueryParameter, generateRandomString} from "./js/utility.js" import {getUrlParameter} from "./js/fetch.stream.js" var appName = location.pathname.substring(1, location.pathname.indexOf("/", 1) + 1); var path = location.hostname + ":" + location.port + "/" + appName + "whip/"; var whipEndpoint = "http"; if (location.protocol.startsWith("https")) { whipEndpoint += "s"; } whipEndpoint += "://" + path; let constantStreamId = getUrlParameter("id"); var streamId; var client; var mediaStream; var button = document.getElementById("button"); async function init() { if (constantStreamId) { streamId = constantStreamId; } else { streamId = "stream" + generateRandomString(6); } client = new WHIPClient({ endpoint: whipEndpoint + streamId, opts: { debug: true, iceServers: [{"urls": "stun:stun1.l.google.com:19302"}] } }); const videoIngest = document.querySelector("video#ingest"); mediaStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true }); videoIngest.srcObject = mediaStream; button.disabled = false; } ```