### Run Basic Room Connection Example Source: https://github.com/livekit/python-sdks/blob/main/examples/README.md Execute the basic room connection example script. This demonstrates event handling and participant tracking within a LiveKit room. ```bash python room_example.py ``` -------------------------------- ### Run End-to-End Encryption Example Source: https://github.com/livekit/python-sdks/blob/main/examples/README.md Execute the end-to-end encryption example script. This example includes a rotating 3D cube visualization alongside E2EE. ```bash python e2ee.py ``` -------------------------------- ### Install and Run Audio Stream Player Source: https://github.com/livekit/python-sdks/blob/main/examples/README.md Installs the sounddevice library and runs the play_audio_stream.py script to play incoming audio from remote participants. ```bash pip install sounddevice python play_audio_stream.py ``` -------------------------------- ### Run Publish Color Cycling Video Example Source: https://github.com/livekit/python-sdks/blob/main/examples/README.md Execute the publish hue video example script. This script publishes an animated video track where the colors cycle. ```bash python publish_hue.py ``` -------------------------------- ### Run Publish Microphone Audio Example Source: https://github.com/livekit/python-sdks/blob/main/examples/README.md Execute the publish microphone audio example script. This script captures microphone audio with AEC enabled and visualizes the dB levels. ```bash cd local_audio python publish_mic.py ``` -------------------------------- ### Run LiveKit API Example Source: https://github.com/livekit/python-sdks/blob/main/examples/README.md Execute the LiveKit API example script. This script shows how to manage rooms, such as creating and listing rooms, using the LiveKit API. ```bash python api.py ``` -------------------------------- ### Run Multiple Connections Example Source: https://github.com/livekit/python-sdks/blob/main/examples/README.md Execute the multiple connections example script. This script shows how to establish sequential room connections within a single thread, suitable for integration with frameworks like Django or Flask. ```bash python multiple_connections.py ``` -------------------------------- ### Run Publish Sine Wave Audio Example Source: https://github.com/livekit/python-sdks/blob/main/examples/README.md Execute the publish sine wave audio example script. This script publishes an audio track consisting of a sine wave at a specified frequency. ```bash python publish_wave.py ``` -------------------------------- ### Run Full Duplex Audio Example Source: https://github.com/livekit/python-sdks/blob/main/examples/README.md Execute the full duplex audio example script. This script demonstrates microphone capture and speaker playout for real-time audio communication. ```bash cd local_audio python full_duplex.py ``` -------------------------------- ### Run Participant Attributes Example Source: https://github.com/livekit/python-sdks/blob/main/examples/README.md Execute the participant attributes example script. This script demonstrates how to dynamically set, update, and delete participant attributes. ```bash python participant_attributes.py ``` -------------------------------- ### Run Remote Procedure Call (RPC) Example Source: https://github.com/livekit/python-sdks/blob/main/examples/README.md Execute the RPC example script. This demonstrates making remote procedure calls between participants for actions like greetings and math operations, including timeout handling. ```bash python rpc.py ``` -------------------------------- ### Setup Engine in Python Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Sets up the engine and subscribes to various room events. This method is called during initialization. ```python setupEngine(e) { this.engine = e, this.engine.on(ne.RemoteMute, (n, r) => { const o = this.trackPublications.get(n); !o || !o.track || (r ? o.mute() : o.unmute()); }), this.engine.on(ne.Connected, this.handleReconnected).on(ne.SignalRestarted, this.handleReconnected).on(ne.SignalResumed, this.handleReconnected).on(ne.Restarting, this.handleReconnecting).on(ne.Resuming, this.handleReconnecting).on(ne.LocalTrackUnpublished, this.handleLocalTrackUnpublished).on(ne.SubscribedQualityUpdate, this.handleSubscribedQualityUpdate).on(ne.Disconnected, this.handleDisconnected).on(ne.SignalRequestResponse, this.handleSignalRequestResponse).on(ne.DataPacketReceived, this.handleDataPacket); } ``` -------------------------------- ### Set LiveKit Connection Environment Variables Source: https://github.com/livekit/python-sdks/blob/main/examples/local_video/README.md Set the LiveKit server URL and API credentials as environment variables before running the examples. ```bash export LIVEKIT_URL=https://your-livekit-host export LIVEKIT_API_KEY=your-api-key export LIVEKIT_API_SECRET=your-api-secret ``` -------------------------------- ### Handle Video Playback Started Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Sets the video playback status to not blocked and emits a VideoPlaybackStatusChanged event. ```typescript this.handleVideoPlaybackStarted=()=>{this.isVideoPlaybackBlocked&&(this.isVideoPlaybackBlocked=!1,this.emit(B.VideoPlaybackStatusChanged,!0))} ``` -------------------------------- ### Start Video Playback Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Allows the user to initiate video playback for the room. This is often required due to browser autoplay policies. ```typescript function nw(){const a=e=>Ta(this,null,function*(){Ae.info("Start Video for room: ",e),yield e.startVideo()});return{className:ka("start-audio-button"),roomVideoPlaybackAllowedObservable:K_,handleStartVideoPlayback:a}} ``` -------------------------------- ### Start Audio Playback Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Allows the user to initiate audio playback for the room. This is often required due to browser autoplay policies. ```typescript function tw(){const a=e=>Ta(this,null,function*(){Ae.info("Start Audio for room: ",e),yield e.startAudio()});return{className:ka("start-audio-button"),roomAudioPlaybackAllowedObservable:Y_,handleStartAudioPlayback:a}} ``` -------------------------------- ### Initialize Audio Playback Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Starts audio playback, including a workaround for iOS to ensure audio elements are properly initialized and resumed. ```javascript this.startAudio=()=>R(this,void 0,void 0,function*(){const u=[],d=Dn();if(d&&d.os==="iOS"){const h="livekit-dummy-audio-el";let m=document.getElementById(h);if(!m){m=document.createElement("audio"),m.id=h,m.autoplay=!0,m.hidden=!0;const g=Dd();g.enabled=!0;const y=new MediaStream([g]);m.srcObject=y,document.addEventListener("visibilitychange",()=>{m&&(m.srcObject=document.hidden?null:y,document.hidden||(this.log.debug("page visible again, triggering startAudio to resume playback and update playback status",this.logContext),this.startAudio()))}),document.body.append(m),this.once(B.Disconnected,()=>{m==null||m.remove(),m=null})}u.push(m)}this.remoteParticipants.forEach(h=>{h.audioTrackPublications.forEach(m=>{m.track&&m.track.attachedElements.forEach(g=>{u.push(g)})})});try{yield Promise.all([this.acquireAudioContext(),...u.map(h=>(h.muted=!1,h.play()))]),this.handleAudioPlaybackStarted()}catch(h){throw this.handleAudioPlaybackFailed(h),h}}) ``` -------------------------------- ### Get Element Dimensions Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Retrieves the width and height of an element. This is a simple utility function for getting basic dimensions. ```javascript function DR(a){const{width:e,height:n}=fb(a);return{width:e,height:n}} ``` -------------------------------- ### Run Local Video Subscriber Example Source: https://github.com/livekit/python-sdks/blob/main/examples/local_video/README.md Execute the subscriber script to connect to a room and render the first video track. Specify the room name and identity. ```bash uv run --project examples/local_video python examples/local_video/subscriber.py \ --room-name demo \ --identity py-viewer ``` -------------------------------- ### Get Media Stream Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Gets a media stream with specified audio and video constraints. Handles user media promises and processor settings. ```javascript function ab(a){ return R(this,void 0,void 0,function*(){ var e,n; a??(a={}),(e=a.audio)!==null&&e!==void 0||(a.audio={deviceId:"default"}),(n=a.video)!==null&&n!==void 0||(a.video={deviceId:"default"}); const{audioProcessor:r,videoProcessor:o}=Ff(a), l=Hy(a,Fy,Yy), u=Qc(l), d=navigator.mediaDevices.getUserMedia(u); a.audio&&(gt.userMediaPromiseMap.set("audioinput",d),d.catch(()=>gt.userMediaPromiseMap.delete("audioinput"))), a.video&&(gt.userMediaPromiseMap.set("videoinput",d),d.catch(()=>gt.userMediaPromiseMap.delete("videoinput"))); const h=yield d; return Promise.all(h.getTracks().map(m=>R(this,void 0,void 0,function*(){ const g=m.kind==="audio"; g?l.audio:l.video; let y; const b=g?u.audio:u.video; typeof b!="boolean"&&(y=b); const T=m.getSettings().deviceId; y!=null&&y.deviceId&&Ii(y.deviceId)!==T?y.deviceId=T:y||(y={deviceId:T}); const C=Xy(m,y); return C.kind===j.Kind.Video?C.source=j.Source.Camera:C.kind===j.Kind.Audio&&(C.source=j.Source.Microphone), C.mediaStream=h, ti(C)&&r?yield C.setProcessor(r):Er(C)&&o&&(yield C.setProcessor(o)), C }))) }) } ``` -------------------------------- ### Initialize a LiveKit Client Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Demonstrates how to initialize the LiveKit client with connection details. Ensure you have the necessary credentials. ```python from livekit import Room, ConnectOptions async def main(): url = "ws://localhost:7880" token = "YOUR_ACCESS_TOKEN" options = ConnectOptions(token=token) room = await Room.connect(url, options) print(f"Connected to room: {room.name}") # Add your room logic here await room.disconnect() if __name__ == "__main__": import asyncio asyncio.run(main()) ``` -------------------------------- ### Run Local Video Publisher Example Source: https://github.com/livekit/python-sdks/blob/main/examples/local_video/README.md Execute the publisher script to stream a local camera feed. Specify the room name, identity, and camera index. ```bash uv run --project examples/local_video python examples/local_video/publisher.py \ --room-name demo \ --identity py-cam \ --camera-index 0 ``` -------------------------------- ### Get Media Devices Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Retrieves a list of available media devices. Optionally filters by kind and can request to get a new device if none are available. ```typescript getDevices(e){return R(this,arguments,void 0,function(n){var r=this;let o=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0;return function*(){var l;if(((l=gt.userMediaPromiseMap)===null||l===void 0?void 0:l.size)>0){ke.debug("awaiting getUserMedia promise");try{n?yield gt.userMediaPromiseMap.get(n):yield Promise.all(gt.userMediaPromiseMap.values())}catch{ke.warn("error waiting for media permissons")}}let u=yield navigator.mediaDevices.enumerateDevices();if(o&&!(Bi()&&r.hasDeviceInUse(n))&&(u.filter(h=>h.kind===n).length===0||u.some(h=>{const m=h.label==="",g=n?h.kind===n:!0;return m&&g}))){const h={video:n!=="audioinput"&&n!=="audiooutput",audio:n!=="videoinput"&&{deviceId:"default"}},m=yield navigator.mediaDevices.getUserMedia(h);u=yield navigator.mediaDevices.enumerateDevices(),m.getTracks().forEach(g=>{g.stop()})}return r._previousDevices=u,n&&(u=u.filter(d=>d.kind===n)),u}()})} ``` -------------------------------- ### Get Playout Delay Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Gets the current playout delay hint from the receiver. Returns 0 and logs a warning if playout delay is not supported or if the track has ended. ```typescript getPlayoutDelay(){if(this.receiver){if("playoutDelayHint"in this.receiver)return this.receiver.playoutDelayHint;this.log.warn("Playout delay not supported in this browser")}else this.log.warn("Cannot get playout delay, track already ended");return 0} ``` -------------------------------- ### Start Monitoring Receiver Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Starts a monitoring interval for the receiver to periodically check its status and potentially update time synchronization. Also registers for time sync updates if available. ```typescript startMonitor(){this.monitorInterval||(this.monitorInterval=setInterval(()=>this.monitorReceiver(),Jf)),a1()&&this.registerTimeSyncUpdate()} ``` -------------------------------- ### Create Simulcast Sender in Python Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Sets up a simulcast sender for publishing media tracks with multiple quality levels. It utilizes insertable streams for efficient simulcasting or falls back to a basic sender if necessary. Requires a pcManager and track information. ```python createSimulcastSender(e,n,r,o){ return R(this,void 0,void 0,function*(){ if(uf())return this.createSimulcastTransceiverSender(e,n,r,o); if(df())return this.log.debug("using add-track fallback",this.logContext),this.createRTCRtpSender(e.mediaStreamTrack); throw new ut("Cannot stream on this device") }) } ``` -------------------------------- ### Create SDK Client Info Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Creates a UC client info object with SDK details. Includes OS information if available. ```javascript function KE(){var a; const e=new UC({sdk:Y0.JS,protocol:xE,version:PE}); return An()&&(e.os=(a=qy())!==null&&a!==void 0?a:"",e) } ``` -------------------------------- ### Create and List Rooms Source: https://github.com/livekit/python-sdks/blob/main/README.md Demonstrates how to create a new room and list existing rooms using the LiveKit API. Requires an asyncio event loop. ```APIDOC ## Create and List Rooms ### Description Demonstrates how to create a new room and list existing rooms using the LiveKit API. Requires an asyncio event loop. ### Method ```python from livekit import api import asyncio async def main(): lkapi = api.LiveKitAPI("https://my-project.livekit.cloud") room_info = await lkapi.room.create_room( api.CreateRoomRequest(name="my-room"), ) print(room_info) results = await lkapi.room.list_rooms(api.ListRoomsRequest()) print(results) await lkapi.aclose() asyncio.run(main()) ``` ``` -------------------------------- ### Get Video Track Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Retrieves only the video track from a media stream. ```javascript function Z1(a){ return R(this,void 0,void 0,function*(){ return(yield ab({audio:!1,video:a}))[0] }) } ``` -------------------------------- ### Create and List Rooms with LiveKit API Source: https://github.com/livekit/python-sdks/blob/main/README.md Demonstrates creating a new room and listing existing rooms using the LiveKit API. Requires an asyncio event loop to run. ```python from livekit import api import asyncio async def main(): lkapi = api.LiveKitAPI("https://my-project.livekit.cloud") room_info = await lkapi.room.create_room( api.CreateRoomRequest(name="my-room"), ) print(room_info) results = await lkapi.room.list_rooms(api.ListRoomsRequest()) print(results) await lkapi.aclose() asyncio.run(main()) ``` -------------------------------- ### Get Audio Track Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Retrieves only the audio track from a media stream. ```javascript function J1(a){ return R(this,void 0,void 0,function*(){ return(yield ab({audio:a,video:!1}))[0] }) } ``` -------------------------------- ### Initialize LiveKit Room in Python SDK Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Initializes the LiveKit room with provided options, setting up default configurations for audio/video capture and publishing. It also handles initial device settings and E2EE setup if enabled. ```typescript this.setMaxListeners(100), this.remoteParticipants = new Map, this.sidToIdentity = new Map, this.options = Object.assign(Object.assign({}, b1), e), this.log = ei((r = this.options.loggerName) !== null && r !== void 0 ? r : gn.Room), this.transcriptionReceivedTimes = new Map, this.options.audioCaptureDefaults = Object.assign(Object.assign({}, Fy), e == null ? void 0 : e.audioCaptureDefaults), this.options.videoCaptureDefaults = Object.assign(Object.assign({}, Yy), e == null ? void 0 : e.videoCaptureDefaults), this.options.publishDefaults = Object.assign(Object.assign({}, y1), e == null ? void 0 : e.publishDefaults), this.maybeCreateEngine(), this.disconnectLock = new Ht, this.localParticipant = new Y1("", "", this.engine, this.options, this.rpcHandlers), this.options.videoCaptureDefaults.deviceId && this.localParticipant.activeDeviceMap.set("videoinput", Ii(this.options.videoCaptureDefaults.deviceId)), this.options.audioCaptureDefaults.deviceId && this.localParticipant.activeDeviceMap.set("audioinput", Ii(this.options.audioCaptureDefaults.deviceId)), !((o = this.options.audioOutput) === null || o === void 0) && o.deviceId && this.switchActiveDevice("audiooutput", Ii(this.options.audioOutput.deviceId)).catch(u => this.log.warn("Could not set audio output: ".concat(u.message), this.logContext)), this.options.e2ee && this.setupE2EE(), xt() && (() => { const u = new AbortController; (l = navigator.mediaDevices) === null || l === void 0 || l.addEventListener("devicechange", this.handleDeviceChange, { signal: u.signal }), ni.cleanupRegistry && ni.cleanupRegistry.register(this, () => { u.abort() }) })() ``` -------------------------------- ### Get Room Engine Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Retrieves the underlying engine of the room, if available. ```python get engine(){var e;return(e=this.room)===null||e===void 0?void 0:e.engine} ``` -------------------------------- ### Setup Event Listeners for Room and Key Provider Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Sets up various event listeners for room and key provider events. This includes handling track publishing, connection state changes, track unsubscriptions, track subscriptions, signal connection, and key setting/ratcheting. ```typescript setupEventListeners(e,n){e.on(B.TrackPublished,(r,o)=>this.setParticipantCryptorEnabled(r.trackInfo.encryption!==Vt.NONE,o.identity)),e.on(B.ConnectionStateChanged,r=>{r===Te.Connected&&e.remoteParticipants.forEach(o=>{o.trackPublications.forEach(l=>{this.setParticipantCryptorEnabled(l.trackInfo.encryption!==Vt.NONE,o.identity)})})}).on(B.TrackUnsubscribed,(r,o,l)=>{var u;const d={kind:"removeTransform",data:{participantIdentity:l.identity,trackId:r.mediaStreamID}};(u=this.worker)===null||u===void 0||u.postMessage(d)}).on(B.TrackSubscribed,(r,o,l)=>{this.setupE2EEReceiver(r,l.identity,o.trackInfo)}).on(B.SignalConnected,()=>{if(!this.room)throw new TypeError("expected room to be present on signal connect");n.getKeys().forEach(r=>{this.postKey(r)}),this.setParticipantCryptorEnabled(this.room.localParticipant.isE2EEEnabled,this.room.localParticipant.identity)}),e.localParticipant.on(F.LocalTrackPublished,r=>R(this,void 0,void 0,function*(){this.setupE2EESender(r.track,r.track.sender)})),n.on(Li.SetKey,r=>this.postKey(r)).on(Li.RatchetRequest,(r,o)=>this.postRatchetRequest(r,o))} ``` -------------------------------- ### Get React Native Platform Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Returns the platform ('ios' or 'android') if running in React Native. ```typescript function qy(){if(!An())return;let a=zy();if(a)return a.platform} ``` -------------------------------- ### Initialize and Use AVSynchronizer Source: https://github.com/livekit/python-sdks/blob/main/examples/video-stream/README.md Demonstrates the basic usage of AVSynchronizer by initializing it with audio and video sources and pushing initial synchronized frames. Subsequent frames are automatically synchronized. ```python av_sync = AVSynchronizer( audio_source=audio_source, video_source=video_source, video_fps=30.0, video_queue_size_ms=100 ) # Push frames to synchronizer await av_sync.push(video_frame) await av_sync.push(audio_frame) ``` -------------------------------- ### Get Event Names Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Returns an array of all event names that have listeners registered. Useful for introspection. ```javascript l.prototype.eventNames=function(){return this._eventsCount>0?n(this._events):[]} ``` -------------------------------- ### Get Track Publication by SID Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Retrieves a track publication using its unique session ID (SID). ```typescript getTrackPublicationBySid(e){return this.trackPublications.get(e)} ``` -------------------------------- ### List Available Audio Devices in Python Source: https://github.com/livekit/python-sdks/blob/main/README.md Lists all available input and output audio devices on the system, and retrieves the default input and output device indices. Requires the 'sounddevice' library. ```python devices = rtc.MediaDevices() # List input devices input_devices = devices.list_input_devices() for device in input_devices: print(f"{device['index']}: {device['name']}") # List output devices output_devices = devices.list_output_devices() for device in output_devices: print(f"{device['index']}: {device['name']}") # Get default device indices default_input = devices.default_input_device() default_output = devices.default_output_device() ``` -------------------------------- ### Get Device Pixel Ratio Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Returns the device's pixel ratio, defaulting to 1 if not available. ```typescript function Pv(){if(xt())return window.devicePixelRatio;if(An()){let a=zy();if(a)return a.devicePixelRatio}return 1} ``` -------------------------------- ### Accessing Server API Services Source: https://github.com/livekit/python-sdks/blob/main/README.md Shows how to instantiate the LiveKitAPI client and access various service clients like Room, Egress, Ingress, Sip, Agent Dispatch, and Connector. ```APIDOC ## Accessing Server API Services ### Description Shows how to instantiate the LiveKitAPI client and access various service clients like Room, Egress, Ingress, Sip, Agent Dispatch, and Connector. ### Method ```python lkapi = api.LiveKitAPI("https://my-project.livekit.cloud") # Room Service room_svc = lkapi.room # Egress Service egress_svc = lkapi.egress # Ingress Service ingress_svc = lkapi.ingress # Sip Service sip_svc = lkapi.sip # Agent Dispatch dispatch_svc = lkapi.agent_dispatch # Connector Service connector_svc = lkapi.connector ``` ``` -------------------------------- ### Get LiveKit React Native Global Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Retrieves the global LiveKit object if running in React Native. ```typescript function zy(){if(global&&global.LiveKitReactNativeGlobal)return global.LiveKitReactNativeGlobal} ``` -------------------------------- ### Get Current Cache Value Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Retrieves the current value from the cache. Returns null if no cache is available. ```javascript function Vl(){var t=ea.current;return t!==null?t:Be.pooledCache} ``` -------------------------------- ### Initialize and Manage Engine Events Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Creates the engine instance and attaches event listeners for connection state changes and media updates. ```javascript maybeCreateEngine(){this.engine&&!this.engine.isClosed||(this.engine=new N1(this.options),this.engine.on(ne.ParticipantUpdate,this.handleParticipantUpdates).on(ne.RoomUpdate,this.handleRoomUpdate).on(ne.SpeakersChanged,this.handleSpeakersChanged).on(ne.StreamStateChanged,this.handleStreamStateUpdate).on(ne.ConnectionQualityUpdate,this.handleConnectionQualityUpdate).on(ne.SubscriptionError,this.handleSubscriptionError).on(ne.SubscriptionPermissionUpdate,this.handleSubscriptionPermissionUpdate).on(ne.MediaTrackAdded,(e,n,r)=>{this.onTrackAdded(e,n,r)}).on(ne.Disconnected,e=>{this.handleDisconnect(this.options.stopLocalTrackOnUnpublish,e)}).on(ne.ActiveSpeakersUpdate,this.handleActiveSpeakersUpdate).on(ne.DataPacketReceived,this.handleDataPacket).on(ne.Resuming,()=>{this.clearConnectionReconcile(),this.isResuming=!0,this.log.info("Resuming signal connection",this.logContext),this.setAndEmitConnectionState(Te.SignalReconnecting)&&this.emit(B.SignalReconnecting)}).on(ne.Resumed,()=>{this.registerConnectionReconcile(),this.isResuming=!1,this.log.info("Resumed signal connection",this.logContext),this.updateSubscriptions(),this.emitBufferedEvents(),this.setAndEmitConnectionState(Te.Connected)&&this.emit(B.Reconnected)}).on(ne.SignalResumed,()=>{this.bufferedEvents=[],(this.state===Te.Reconnecting||this.isResuming)&&this.sendSyncState()}).on(ne.Restarting,this.handleRestarting).on(ne.SignalRestarted,this.handleSignalRestarted).on(ne.Offline,()=>{this.setAndEmitConnectionState(Te.Reconnecting)&&this.emit(B.Reconnecting)}).on(ne.DCBufferStatusChanged,(e,n)=>{this.emit(B.DCBufferStatusChanged,e,n) ``` -------------------------------- ### Publish Audio and Video Track Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Shows how to publish audio and video tracks to a LiveKit room. This requires access to local media devices. ```python from livekit import Room, ConnectOptions, Track, LocalParticipant import asyncio async def publish_media(room: Room): # Assume you have a local audio track and a local video track # For demonstration, we'll create dummy tracks audio_track = LocalParticipant.create_audio_track("local-audio") video_track = LocalParticipant.create_video_track("local-video") await room.local_participant.publish_track(audio_track) await room.local_participant.publish_track(video_track) print("Published audio and video tracks.") async def main(): url = "ws://localhost:7880" token = "YOUR_ACCESS_TOKEN" options = ConnectOptions(token=token) room = await Room.connect(url, options) await publish_media(room) # Keep the connection alive for a while to allow others to subscribe await asyncio.sleep(30) await room.disconnect() if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Get Suspended Lanes Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Calculates the lanes that are currently suspended. This is useful for determining the state of concurrent operations. ```javascript function to(t,i){var s=t.pendingLanes;if(s===0)return 0;var c=0,f=t.suspendedLanes,p=t.pingedLanes,v=t.warmLanes;t=t.finishedLanes!==0;var S=s&134217727;return S!==0?(s=S&~f,s!==0?c=Gi(s):(p&=S,p!==0?c=Gi(p):t||(v=S&~v,v!==0&&(c=Gi(v))))):(S=s&~f,S!==0?c=Gi(S):p!==0?c=Gi(p):t||(v=s&~v,v!==0&&(c=Gi(v)))),c===0?0:i!==0&&i!==c&&(i&f)===0&&(f=c&-c,v=i&-i,f>=v||f===32&&(v&4194176)!==0)?i:c} ``` -------------------------------- ### Configuration Constants Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Default configuration objects for audio, video, and peer connection settings. ```javascript const gf="vp8",y1={audioPreset:cf.music,dtx:!0,red:!0,forceStereo:!1,simulcast:!0,screenShareEncoding:Hf.h1080fps15.encoding,stopMicTrackOnMute:!1,videoCodec:gf,backupCodec:!0},Fy={deviceId:"default",autoGainControl:!0,echoCancellation:!0,noiseSuppression:!0,voiceIsolation:!0},Yy={deviceId:"default",resolution:Hs.h720.resolution},b1={adaptiveStream:!1,dynacast:!1,stopLocalTrackOnUnpublish:!0,reconnectPolicy:new uE,disconnectOnPageLeave:!0,webAudioMix:!1},Xf={autoSubscribe:!0,maxRetries:1,peerConnectionTimeout:15e3,websocketTimeout:15e3}; ``` -------------------------------- ### Connect to LiveKit Room and Handle Events Source: https://github.com/livekit/python-sdks/blob/main/README.md Connects to a LiveKit room using the real-time SDK and sets up event handlers for participant connections and track subscriptions. Automatically subscribes to all published tracks. ```python from livekit import rtc async def main(): room = rtc.Room() @room.on("participant_connected") def on_participant_connected(participant: rtc.RemoteParticipant): logging.info( "participant connected: %s %s", participant.sid, participant.identity) async def receive_frames(stream: rtc.VideoStream): async for frame in stream: # received a video frame from the track, process it here pass # track_subscribed is emitted whenever the local participant is subscribed to a new track @room.on("track_subscribed") def on_track_subscribed(track: rtc.Track, publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant): logging.info("track subscribed: %s", publication.sid) if track.kind == rtc.TrackKind.KIND_VIDEO: video_stream = rtc.VideoStream(track) asyncio.ensure_future(receive_frames(video_stream)) # By default, autosubscribe is enabled. The participant will be subscribed to # all published tracks in the room await room.connect(URL, TOKEN) logging.info("connected to room %s", room.name) # participants and tracks that are already available in the room # participant_connected and track_published events will *not* be emitted for them for identity, participant in room.remote_participants.items(): print(f"identity: {identity}") print(f"participant: {participant}") for tid, publication in participant.track_publications.items(): print(f"\ttrack id: {publication}") ``` -------------------------------- ### List available audio devices Source: https://github.com/livekit/python-sdks/blob/main/examples/README.md Lists all available audio input and output devices on the system. Use this to identify device IDs for custom configurations. ```bash python basic_room.py --list-devices ``` -------------------------------- ### Get Max Event Listeners Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Retrieves the maximum number of listeners allowed for events on this emitter. Defaults to EventEmitter.defaultMaxListeners. ```javascript l.prototype.getMaxListeners=function(){return h(this)} ``` -------------------------------- ### Handle Room Events Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Illustrates how to register event listeners for various room events such as participant connection/disconnection and track subscription. ```python from livekit import Room, ConnectOptions import asyncio def on_participant_connected(participant): print(f"Participant connected: {participant.identity}") def on_participant_disconnected(participant): print(f"Participant disconnected: {participant.identity}") def on_track_subscribed(track, participant): print(f"Track subscribed: {track.sid} by {participant.identity}") def on_track_unpublished(track, participant): print(f"Track unpublished: {track.sid} by {participant.identity}") async def main(): url = "ws://localhost:7880" token = "YOUR_ACCESS_TOKEN" options = ConnectOptions(token=token) room = await Room.connect(url, options) room.on(Room.event_participant_connected, on_participant_connected) room.on(Room.event_participant_disconnected, on_participant_disconnected) room.on(Room.event_track_subscribed, on_track_subscribed) room.on(Room.event_track_unpublished, on_track_unpublished) print(f"Connected to room: {room.name}") # Keep the connection alive await asyncio.Future() await room.disconnect() if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Get Contextual State Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Returns the current context's parent and pool. Returns null if no context is available. ```javascript function Tm(){var t=Vl();return t===null?null:{parent:lt._currentValue,pool:t}} ``` -------------------------------- ### Delay the subscription to an Observable Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Delays the subscription to an Observable by a specified time. The Observable will not start emitting until the delay has passed. ```typescript function Ub(a,e){return e===void 0&&(e=0),Sa(function(n,r){r.add(a.schedule(function(){return n.subscribe(r)},e))})} ``` -------------------------------- ### Capture Microphone Input in Python Source: https://github.com/livekit/python-sdks/blob/main/README.md Opens the default microphone with audio processing enabled (AEC, noise suppression, etc.) and publishes it as an audio track. Requires the 'sounddevice' library. ```python from livekit import rtc # Create a MediaDevices instance devices = rtc.MediaDevices() # Open the default microphone with audio processing enabled mic = devices.open_input( enable_aec=True, # Acoustic Echo Cancellation noise_suppression=True, # Noise suppression high_pass_filter=True, # High-pass filter auto_gain_control=True # Automatic gain control ) # Use the audio source to create a track and publish it track = rtc.LocalAudioTrack.create_audio_track("microphone", mic.source) await room.local_participant.publish_track(track) # Clean up when done await mic.aclose() ``` -------------------------------- ### Start Track Monitoring Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Initiates the monitoring of sender statistics at a regular interval. Requires a signal client to be set. ```typescript startMonitor(e) { var n; if (this.signalClient = e, !xt()) return; const r = (n = this.sender) === null || n === void 0 ? void 0 : n.getParameters(); r && (this.encodings = r.encodings), !this.monitorInterval && (this.monitorInterval = setInterval(() => { this.monitorSender() }, Jf)) } ``` -------------------------------- ### Get Main Axis Offset Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Calculates the offset for the main axis based on the determined axis. This is used in positioning calculations. ```typescript function ob(a){return rb(Wc(a))} ``` -------------------------------- ### Initialize Engine with Options Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Initializes the Engine with custom options, including logger name and experimental signal latency. The logger is configured with a name and a context callback for dynamic logging information. ```typescript this.log=ei((n=e.loggerName)!==null&&n!==void 0?n:gn.Engine),this.loggerOptions={loggerName:e.loggerName,loggerContextCb:()=>this.logContext},this.client=new Yf(void 0,this.loggerOptions),this.client.signalLatency=this.options.expSignalLatency,this.reconnectPolicy=this.options.reconnectPolicy,this.registerOnLineListener(),this.closingLock=new Ht,this.dataProcessLock=new Ht,this.dcBufferStatus=new Map([[_e.LOSSY,!0],[_e.RELIABLE,!0]]) ``` -------------------------------- ### Create Data Channels in Python Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Initializes reliable and lossy data channels for publisher communication. Sets up message and error handlers, and configures buffered amount low thresholds. Ensure the pcManager is available before calling this method. ```python createDataChannels(){ this.pcManager&&(this.lossyDC&&(this.lossyDC.onmessage=null,this.lossyDC.onerror=null), this.reliableDC&&(this.reliableDC.onmessage=null,this.reliableDC.onerror=null), this.lossyDC=this.pcManager.createPublisherDataChannel(Wv,{ordered:!0,maxRetransmits:0}), this.reliableDC=this.pcManager.createPublisherDataChannel($v,{ordered:!0}), this.lossyDC.onmessage=this.handleDataMessage, this.reliableDC.onmessage=this.handleDataMessage, this.lossyDC.onerror=this.handleDataError, this.reliableDC.onerror=this.handleDataError, this.lossyDC.bufferedAmountLowThreshold=65535, this.reliableDC.bufferedAmountLowThreshold=65535, this.lossyDC.onbufferedamountlow=this.handleBufferedAmountLow, this.reliableDC.onbufferedamountlow=this.handleBufferedAmountLow) } ``` -------------------------------- ### Get Main Axis Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Determines the main axis ('x' or 'y') based on the placement. This is crucial for understanding the primary direction of alignment. ```typescript function Wc(a){return["top","bottom"].includes(ya(a))?"y":"x"} ``` -------------------------------- ### Handle Client Event Callbacks in Python Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Sets up event handlers for various client events such as track published, unpublished, subscribed, token refresh, mute changes, quality updates, close, and leave. Ensure the client is properly initialized before setting these callbacks. ```python this.client.onLocalTrackPublished=e=>{ var n; if(this.log.debug("received trackPublishedResponse",Object.assign(Object.assign({},this.logContext),{cid:e.cid,track:(n=e.track)===null||n===void 0?void 0:n.sid})),!this.pendingTrackResolvers[e.cid]){this.log.error("missing track resolver for ".concat(e.cid),Object.assign(Object.assign({},this.logContext),{cid:e.cid}));return} const{resolve:r}=this.pendingTrackResolvers[e.cid]; delete this.pendingTrackResolvers[e.cid], r(e.track) }, this.client.onLocalTrackUnpublished=e=>{ this.emit(ne.LocalTrackUnpublished,e) }, this.client.onLocalTrackSubscribed=e=>{ this.emit(ne.LocalTrackSubscribed,e) }, this.client.onTokenRefresh=e=>{ this.token=e }, this.client.onRemoteMuteChanged=(e,n)=>{ this.emit(ne.RemoteMute,e,n) }, this.client.onSubscribedQualityUpdate=e=>{ this.emit(ne.SubscribedQualityUpdate,e) }, this.client.onClose=()=>{ this.handleDisconnect("signal",sr.RR_SIGNAL_DISCONNECTED) }, this.client.onLeave=e=>{ switch(this.log.debug("client leave request",Object.assign(Object.assign({},this.logContext),{reason:e==null?void 0:e.reason})),e.regions&&this.regionUrlProvider&&(this.log.debug("updating regions",this.logContext),this.regionUrlProvider.setServerReportedRegions(e.regions))), e.action){ case ur.DISCONNECT: this.emit(ne.Disconnected,e==null?void 0:e.reason); this.close(); break; case ur.RECONNECT: this.fullReconnectOnNext=!0; this.handleDisconnect(Ld); break; case ur.RESUME: this.handleDisconnect(Ld) } } ``` -------------------------------- ### Get Alignment Axis Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Determines the alignment axis ('x' or 'y') based on the provided placement. Useful for calculating offsets and positioning. ```typescript function sb(a){return a==="y"?"height":"width"} ``` -------------------------------- ### Get Data Channel Information Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Collects information about all configured data channels, including their labels, IDs, and targets (publisher/subscriber). ```typescript dataChannelsInfo(){const e=[],n=(r,o)=>{(r==null?void 0:r.id)!==void 0&&r.id!==null&&e.push(new ry({label:r.label,id:r.id,target:o}))};return n(this.dataChannelForKind(_e.LOSSY),mn.PUBLISHER),n(this.dataChannelForKind(_e.RELIABLE),mn.PUBLISHER),n(this.dataChannelForKind(_e.LOSSY,!0),mn.SUBSCRIBER),n(this.dataChannelForKind(_e.RELIABLE,!0),mn.SUBSCRIBER),e} ``` -------------------------------- ### Prepare Connection Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Prepares a connection to a LiveKit server, optionally checking region URLs. Use this before establishing a full connection to ensure optimal server selection. ```typescript prepareConnection(e,n){return R(this,void 0,void 0,function*(){if(this.state===Te.Disconnected){this.log.debug("prepareConnection to ".concat(e),this.logContext);try{if(hf(new URL(e))&&n){this.regionUrlProvider=new bf(e,n);const r=yield this.regionUrlProvider.getNextBestRegionUrl();r&&this.state===Te.Disconnected&&(this.regionUrl=r,yield fetch(Lv(r),{method:"HEAD"}),this.log.debug("prepared connection to ".concat(r),this.logContext))}else yield fetch(Lv(e),{method:"HEAD"})}catch(r){this.log.warn("could not prepare connection",Object.assign(Object.assign({},this.logContext),{error:r}))}}})} ``` -------------------------------- ### Get Transceivers Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Returns a list of all transceivers associated with the WebRTC peer connection. Transceivers manage the sending and receiving of media. ```typescript getTransceivers(){var e,n;return(n=(e=this._pc)===null||e===void 0?void 0:e.getTransceivers())!==null&&n!==void 0?n:[]} ``` -------------------------------- ### Publishing a Track with Options Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Initiates the publishing of a track with specified options, including codec, encryption, and stereo settings. Handles default publish settings and codec compatibility. ```python if (!this.hasPermissionsToPublish(e)) throw new wE("failed to publish track, insufficient permissions", 403); Array.from(this.trackPublications.values()).find(_ => hr(e) && _.source === e.source) && e.source !== j.Source.Unknown && this.log.info("publishing a second track with the same source: ".concat(e.source), Object.assign(Object.assign({}, this.logContext), Me(e))); n.stopMicTrackOnMute && ti(e) && (e.stopOnMute = !0); e.source === j.Source.ScreenShare && br() && (n.simulcast = !1); n.videoCodec === "av1" && !zE() && (n.videoCodec = void 0); n.videoCodec === "vp9" && !qE() && (n.videoCodec = void 0); n.videoCodec === void 0 && (n.videoCodec = gf); this.enabledPublishVideoCodecs.length > 0 && (this.enabledPublishVideoCodecs.some(_ => n.videoCodec === Dc(_.mime)) || (n.videoCodec = Dc(this.enabledPublishVideoCodecs[0].mime))); const k = n.videoCodec; e.on(W.Muted, this.onTrackMuted), e.on(W.Unmuted, this.onTrackUnmuted), e.on(W.Ended, this.handleTrackEnded), e.on(W.UpstreamPaused, this.onTrackUpstreamPaused), e.on(W.UpstreamResumed, this.onTrackUpstreamResumed), e.on(W.AudioTrackFeatureUpdate, this.onTrackFeatureUpdate); const w = new Jd({ cid: e.mediaStreamTrack.id, name: n.name, type: j.kindToProto(e.kind), muted: e.isMuted, source: j.sourceToProto(e.source), disableDtx: !(!((o = n.dtx) !== null && o !== void 0) || o), encryption: this.encryptionType, stereo: r, disableRed: this.isE2EEEnabled || !(!((l = n.red) !== null && l !== void 0) || l), stream: n == null ? void 0 : n.stream, backupCodecPolicy: n == null ? void 0 : n.backupCodecPolicy }); let O; if (e.kind === j.Kind.Video) { let _ = { width: 0, height: 0 }; try { _ = yield e.waitForDimensions(); } catch {} const $ = (d = (u = this.roomOptions.videoCaptureDefaults) === null || u === void 0 ? void 0 : u.resolution) !== null && d !== void 0 ? d : Hs.h720.resolution; _ = { width: $.width ``` -------------------------------- ### Set LiveKit Environment Variables Source: https://github.com/livekit/python-sdks/blob/main/examples/README.md Set the required environment variables before running the examples. These include the LiveKit server URL, API key, and API secret. ```bash export LIVEKIT_URL=ws://localhost:7880 export LIVEKIT_API_KEY=devkey export LIVEKIT_API_SECRET=secret ``` -------------------------------- ### Get Signalling State Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Retrieves the signaling state of the WebRTC peer connection. This state reflects the progress of the session negotiation. ```typescript getSignallingState(){var e,n;return(n=(e=this._pc)===null||e===void 0?void 0:e.signalingState)!==null&&n!==void 0?n:"closed"} ``` -------------------------------- ### Get IntersectionObserver Instance Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Returns an IntersectionObserver instance, creating one if it doesn't exist. Used for observing element visibility. ```javascript let Od=null; const Nv=()=>(Od||(Od=new IntersectionObserver(YE,{root:null,rootMargin:"0px"})),Od); ``` -------------------------------- ### ICE Parameters Parsing and Writing Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Utilities for parsing and constructing ICE (Interactive Connectivity Establishment) parameters from SDP. ```APIDOC ## ICE Parameters Parsing and Writing ### Description Functions to parse ICE ufrag and password from SDP and to construct SDP lines for ICE parameters. ### Functions #### `getIceParameters(sdp1, sdp2)` Retrieves ICE parameters (username fragment and password) from two SDP strings. - **sdp1** (string) - The first SDP string. - **sdp2** (string) - The second SDP string. - **Returns**: An object with `usernameFragment` and `password`, or null if not found. ```javascript e.getIceParameters = function(n, r) { const o = e.matchPrefix(n + r, "a=ice-ufrag:")[0]; const l = e.matchPrefix(n + r, "a=ice-pwd:")[0]; if (o && l) { return { usernameFragment: o.substring(12), password: l.substring(10) }; } else { return null; } }; ``` #### `writeIceParameters(iceParams)` Constructs SDP lines for ICE parameters. - **iceParams** (object) - An object containing ICE parameters (`usernameFragment`, `password`, and optional `iceLite`). - **Returns**: A string of SDP lines for ICE parameters. ```javascript e.writeIceParameters = function(n) { let r = "a=ice-ufrag:" + n.usernameFragment + `\r a=ice-pwd:` + n.password + `\r `; n.iceLite && (r += `a=ice-lite\r `); return r; }; ``` ``` -------------------------------- ### Get Target Element Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Retrieves the target DOM element from an event, handling cases where the target might be a text node. ```javascript function vl(t){return t=t.target||t.srcElement||window,t.correspondingUseElement&&(t=t.correspondingUseElement),t.nodeType===3?t.parentNode:t} ``` -------------------------------- ### Create audio source with PlatformAudio Source: https://github.com/livekit/python-sdks/blob/main/examples/README.md Initializes PlatformAudio for microphone capture with echo cancellation, noise suppression, and auto gain control enabled. This is the recommended mode for most applications. ```python platform_audio = rtc.PlatformAudio() source = platform_audio.create_audio_source( rtc.PlatformAudioOptions( echo_cancellation=True, noise_suppression=True, auto_gain_control=True, ) ) track = rtc.LocalAudioTrack.create_audio_track("microphone", source) ``` -------------------------------- ### Create and Send Offer Source: https://github.com/livekit/python-sdks/blob/main/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html Initiates the creation of a WebRTC offer and sends it. Handles ICE restarts and renegotiation. Use when initiating a new connection or re-establishing an existing one. ```typescript createAndSendOffer(e){return R(this,void 0,void 0,function*(){var n;if(this.onOffer===void 0)return;if(e!=null&&e.iceRestart&&(this.log.debug("restarting ICE",this.logContext),this.restartingIce=!0),this._pc&&this._pc.signalingState==="have-local-offer"){const l=this._pc.remoteDescription;if(e!=null&&e.iceRestart&&l)yield this._pc.setRemoteDescription(l);else{this.renegotiate=!0;return}}else if(!this._pc||this._pc.signalingState==="closed"){this.log.warn("could not createOffer with closed peer connection",this.logContext);return}this.log.debug("starting to negotiate",this.logContext);const r=yield this.pc.createOffer(e);this.log.debug("original offer",Object.assign({sdp:r.sdp},this.logContext));const o=Mi.parse((n=r.sdp)!==null&&n!==void 0?n:""));o.media.forEach(l=>{Yv(l),l.type==="audio"?Fv(l,[],[]):l.type==="video"&&this.trackBitrates.some(u=>{if(!l.msid||!u.cid||!l.msid.includes(u.cid))return!1;let d=0;if(l.rtp.some(m=>m.codec.toUpperCase()===u.codec.toUpperCase()?(d=m.payload,!0):!1),d===0||(Is(u.codec)&&this.ensureVideoDDExtensionForSVC(l,o),u.codec!=="av1"))return!0;const h=Math.round(u.maxbr*p1);for(const m of l.fmtp)if(m.payload===d){m.config.includes("x-google-start-bitrate")||(m.config+=";x-google-start-bitrate=".concat(h));break}return!0})}),yield this.setMungedSDP(r,Mi.write(o)),this.onOffer(r)})} ```