callway 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -0
- package/dist/index.d.ts +241 -0
- package/dist/index.js +726 -0
- package/dist/index.js.map +1 -0
- package/dist/mediaUtils-5gVEq26H.d.ts +49 -0
- package/dist/react/index.d.ts +87 -0
- package/dist/react/index.js +186 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/PeerManager.ts","../src/core/MediaManager.ts","../src/core/mediaUtils.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * PeerManager - Manages RTCPeerConnection instances for WebRTC calls\n * \n * Handles:\n * - Creating and managing peer connections per remote participant\n * - Creating offers and answers\n * - Adding ICE candidates\n * - Connection state management\n * \n * Supports both 1:1 and multi-peer/group calls.\n * For 1:1 calls, use initialize() with a single remoteId.\n * For group calls, use addPeer() to add multiple peers dynamically.\n */\n\nexport interface PeerConnectionConfig {\n iceServers?: RTCConfiguration['iceServers'];\n}\n\nexport interface SignalingMessage {\n type: 'offer' | 'answer' | 'ice-candidate';\n from: string;\n to: string;\n data: RTCSessionDescriptionInit | RTCIceCandidateInit;\n}\n\nexport type SignalingHandler = (message: SignalingMessage) => void | Promise<void>;\n\ninterface PeerConnectionInfo {\n connection: RTCPeerConnection;\n remoteId: string;\n isInitiator: boolean; // true if we created the offer\n iceCandidateQueue: RTCIceCandidateInit[]; // Queue for ICE candidates received before remote description\n remoteDescriptionSet: boolean; // Track if remote description has been set\n makingOffer: boolean; // Track if we are in the middle of making an offer\n polite: boolean; // Perfect-negotiation role\n}\n\nexport class PeerManager {\n // Multi-peer support: Map of remote peer IDs to their connections\n private peerConnections: Map<string, PeerConnectionInfo> = new Map();\n \n // Legacy 1:1 support (for backward compatibility)\n private peerConnection: RTCPeerConnection | null = null;\n private remoteId: string | null = null;\n \n private localId: string;\n private signalingHandler: SignalingHandler | null = null;\n private signalingAdapter: import('../signaling/SignalingAdapter').SignalingAdapter | null = null;\n private registeredInAdapter: boolean = false;\n private config: PeerConnectionConfig;\n private remoteStreamCallback: ((stream: MediaStream, remoteId: string) => void) | null = null;\n private connectionStateCallback: ((state: RTCPeerConnectionState, remoteId: string) => void) | null = null;\n private iceConnectionStateCallback: ((state: RTCIceConnectionState, remoteId: string) => void) | null = null;\n\n constructor(localId: string, config: PeerConnectionConfig = {}) {\n this.localId = localId;\n this.config = config;\n }\n\n /**\n * Set the signaling handler for sending messages\n */\n setSignalingHandler(handler: SignalingHandler): void {\n this.signalingHandler = handler;\n }\n\n /**\n * Set a signaling adapter. This will register this peer and wire sending.\n */\n setSignalingAdapter(adapter: import('../signaling/SignalingAdapter').SignalingAdapter, roomId?: string): void {\n this.signalingAdapter = adapter;\n // Outgoing uses adapter.sendMessage\n this.signalingHandler = async (message: SignalingMessage) => {\n await adapter.sendMessage(message);\n };\n // Incoming registration\n adapter.registerPeer(this.localId, async (message: SignalingMessage) => {\n if (message.type === 'offer') {\n await this.handleOffer(message.data as RTCSessionDescriptionInit, message.from);\n } else if (message.type === 'answer') {\n await this.handleAnswer(message.data as RTCSessionDescriptionInit, message.from);\n } else if (message.type === 'ice-candidate') {\n await this.addIceCandidate(message.data as RTCIceCandidateInit, message.from);\n }\n }, roomId);\n this.registeredInAdapter = true;\n }\n\n /**\n * Create a new peer connection for a specific remote peer\n */\n private createPeerConnection(remoteId: string, polite: boolean): RTCPeerConnection {\n const pc = new RTCPeerConnection({\n iceServers: this.config.iceServers || [\n { urls: 'stun:stun.l.google.com:19302' }\n ]\n });\n\n // Handle ICE candidates\n pc.onicecandidate = (event) => {\n if (event.candidate && this.signalingHandler) {\n console.log(`[PeerManager ${this.localId}] ICE candidate for ${remoteId}:`, event.candidate);\n this.signalingHandler({\n type: 'ice-candidate',\n from: this.localId,\n to: remoteId,\n data: event.candidate.toJSON()\n });\n }\n };\n\n // Handle connection state changes\n pc.onconnectionstatechange = () => {\n console.log(`[PeerManager ${this.localId}] Connection state with ${remoteId}:`, pc.connectionState);\n if (this.connectionStateCallback) {\n this.connectionStateCallback(pc.connectionState, remoteId);\n }\n };\n\n // Handle ICE connection state changes\n pc.oniceconnectionstatechange = () => {\n console.log(`[PeerManager ${this.localId}] ICE connection state with ${remoteId}:`, pc.iceConnectionState);\n if (this.iceConnectionStateCallback) {\n this.iceConnectionStateCallback(pc.iceConnectionState, remoteId);\n }\n };\n\n // Set up remote stream handler\n pc.ontrack = (event) => {\n console.log(`[PeerManager ${this.localId}] Remote track received from ${remoteId}:`, event.track.kind);\n if (this.remoteStreamCallback) {\n if (event.streams && event.streams[0]) {\n this.remoteStreamCallback(event.streams[0], remoteId);\n } else {\n // Create a stream from the track if no stream is provided\n const stream = new MediaStream([event.track]);\n this.remoteStreamCallback(stream, remoteId);\n }\n }\n };\n\n // Perfect negotiation: drive offers on negotiationneeded\n pc.onnegotiationneeded = async () => {\n const peerInfo = this.peerConnections.get(remoteId);\n if (!peerInfo) return;\n if (peerInfo.makingOffer) return;\n peerInfo.makingOffer = true;\n try {\n if (pc.signalingState !== 'stable') {\n console.log(`[PeerManager ${this.localId}] negotiationneeded aborted (state ${pc.signalingState}) for ${remoteId}`);\n return;\n }\n console.log(`[PeerManager ${this.localId}] negotiationneeded -> creating offer for ${remoteId}`);\n const offer = await pc.createOffer();\n await pc.setLocalDescription(offer);\n peerInfo.isInitiator = true;\n if (this.signalingHandler) {\n await this.signalingHandler({\n type: 'offer',\n from: this.localId,\n to: remoteId,\n data: offer\n });\n }\n } catch (err) {\n console.warn(`[PeerManager ${this.localId}] negotiationneeded failed for ${remoteId}:`, err);\n } finally {\n peerInfo.makingOffer = false;\n }\n };\n\n return pc;\n }\n\n /**\n * Initialize peer connection for a call (1:1 compatibility)\n * For multi-peer calls, use addPeer() instead\n */\n async initialize(remoteId: string): Promise<void> {\n if (this.peerConnection || this.peerConnections.has(remoteId)) {\n throw new Error('Peer connection already initialized');\n }\n\n this.remoteId = remoteId;\n const polite = this.localId > remoteId;\n this.peerConnection = this.createPeerConnection(remoteId, polite);\n \n // Also add to multi-peer map for consistency\n this.peerConnections.set(remoteId, {\n connection: this.peerConnection,\n remoteId,\n isInitiator: false,\n iceCandidateQueue: [],\n remoteDescriptionSet: false,\n makingOffer: false,\n polite\n });\n \n console.log(`[PeerManager ${this.localId}] Initialized peer connection for remote: ${remoteId}`);\n }\n\n /**\n * Add a new peer to the room (multi-peer support)\n * Creates a new RTCPeerConnection for this peer\n */\n async addPeer(remoteId: string, isInitiator: boolean = false): Promise<void> {\n if (this.peerConnections.has(remoteId)) {\n console.warn(`[PeerManager ${this.localId}] Peer ${remoteId} already exists`);\n return;\n }\n\n const polite = this.localId > remoteId; // higher ID is polite, lower ID initiates\n const pc = this.createPeerConnection(remoteId, polite);\n this.peerConnections.set(remoteId, {\n connection: pc,\n remoteId,\n isInitiator,\n iceCandidateQueue: [],\n remoteDescriptionSet: false,\n makingOffer: false,\n polite\n });\n\n console.log(`[PeerManager ${this.localId}] Added peer: ${remoteId} (initiator: ${isInitiator})`);\n }\n\n /**\n * Remove a peer from the room\n */\n async removePeer(remoteId: string): Promise<void> {\n const peerInfo = this.peerConnections.get(remoteId);\n if (!peerInfo) {\n console.warn(`[PeerManager ${this.localId}] Peer ${remoteId} not found`);\n return;\n }\n\n console.log(`[PeerManager ${this.localId}] Removing peer: ${remoteId}`);\n peerInfo.connection.close();\n this.peerConnections.delete(remoteId);\n\n // Also clear legacy connection if it matches\n if (this.remoteId === remoteId) {\n this.peerConnection = null;\n this.remoteId = null;\n }\n }\n\n /**\n * Get all remote peer IDs\n */\n getRemotePeerIds(): string[] {\n return Array.from(this.peerConnections.keys());\n }\n\n /**\n * Check if a peer exists\n */\n hasPeer(remoteId: string): boolean {\n return this.peerConnections.has(remoteId);\n }\n\n /**\n * Get peer connection for a specific remote peer (multi-peer support)\n */\n getPeerConnectionFor(remoteId: string): RTCPeerConnection | null {\n return this.peerConnections.get(remoteId)?.connection ?? null;\n }\n\n /**\n * Create an offer for a specific peer (caller side)\n */\n async createOffer(remoteId: string): Promise<RTCSessionDescriptionInit> {\n const peerInfo = this.peerConnections.get(remoteId);\n if (!peerInfo) {\n throw new Error(`Peer ${remoteId} not found. Call addPeer() first.`);\n }\n\n const pc = peerInfo.connection;\n console.log(`[PeerManager ${this.localId}] Creating offer for ${remoteId}...`);\n \n peerInfo.makingOffer = true;\n try {\n const offer = await pc.createOffer();\n await pc.setLocalDescription(offer);\n peerInfo.isInitiator = true;\n console.log(`[PeerManager ${this.localId}] Offer created for ${remoteId}:`, offer);\n\n // Send offer via signaling\n if (this.signalingHandler) {\n await this.signalingHandler({\n type: 'offer',\n from: this.localId,\n to: remoteId,\n data: offer\n });\n }\n\n return offer;\n } finally {\n peerInfo.makingOffer = false;\n }\n }\n\n /**\n * Create offers for all peers (multi-peer support)\n */\n async createOffersForAll(): Promise<void> {\n const promises = Array.from(this.peerConnections.keys()).map(remoteId => \n this.createOffer(remoteId)\n );\n await Promise.all(promises);\n console.log(`[PeerManager ${this.localId}] Created offers for all ${this.peerConnections.size} peers`);\n }\n\n /**\n * Handle incoming offer from a peer (callee side)\n *\n * Collision strategy: the peer who already has a local offer ignores the\n * incoming offer (impolite); with deterministic initiator selection in the\n * caller, collisions should be rare. This keeps the signaling state valid.\n */\n async handleOffer(offer: RTCSessionDescriptionInit, remoteId: string): Promise<RTCSessionDescriptionInit | null> {\n let peerInfo = this.peerConnections.get(remoteId);\n \n // If peer doesn't exist, create it\n if (!peerInfo) {\n await this.addPeer(remoteId, false);\n peerInfo = this.peerConnections.get(remoteId)!;\n }\n\n const pc = peerInfo.connection;\n \n const collision = peerInfo.makingOffer || pc.signalingState === 'have-local-offer';\n const ignoreOffer = !peerInfo.polite && collision;\n if (ignoreOffer) {\n console.log(`[PeerManager ${this.localId}] Ignoring offer from ${remoteId} (collision, impolite)`);\n return null;\n }\n\n if (collision && peerInfo.polite) {\n console.log(`[PeerManager ${this.localId}] Offer collision with ${remoteId} (polite) - rolling back and accepting`);\n try {\n await pc.setLocalDescription({ type: 'rollback' } as RTCSessionDescriptionInit);\n } catch (err) {\n console.warn(`[PeerManager ${this.localId}] rollback failed:`, err);\n }\n }\n\n if (pc.signalingState !== 'stable' && pc.signalingState !== 'have-local-offer') {\n console.warn(`[PeerManager ${this.localId}] Cannot handle offer from ${remoteId}, signaling state is ${pc.signalingState}`);\n return null;\n }\n \n console.log(`[PeerManager ${this.localId}] Handling offer from ${remoteId}...`);\n \n try {\n const currentPc = peerInfo.connection;\n await currentPc.setRemoteDescription(new RTCSessionDescription(offer));\n peerInfo.remoteDescriptionSet = true;\n\n // Process queued ICE candidates\n await this.processIceCandidateQueue(remoteId);\n\n // Create and set answer\n const answer = await currentPc.createAnswer();\n await currentPc.setLocalDescription(answer);\n peerInfo.isInitiator = false;\n console.log(`[PeerManager ${this.localId}] Answer created for ${remoteId}:`, answer);\n\n // Send answer via signaling\n if (this.signalingHandler) {\n await this.signalingHandler({\n type: 'answer',\n from: this.localId,\n to: remoteId,\n data: answer\n });\n }\n\n return answer;\n } catch (error) {\n console.error(`[PeerManager ${this.localId}] Error handling offer from ${remoteId}:`, error);\n throw error;\n }\n }\n\n /**\n * Handle incoming answer from a peer (caller side)\n */\n async handleAnswer(answer: RTCSessionDescriptionInit, remoteId: string): Promise<void> {\n const peerInfo = this.peerConnections.get(remoteId);\n if (!peerInfo) {\n throw new Error(`Peer ${remoteId} not found. Call addPeer() first.`);\n }\n\n const pc = peerInfo.connection;\n \n // We should be in \"have-local-offer\" state (waiting for answer to our offer)\n if (pc.signalingState !== 'have-local-offer') {\n // If we're in stable, we likely already processed an answer\n if (pc.signalingState === 'stable') {\n console.log(`[PeerManager ${this.localId}] Ignoring answer from ${remoteId} - already in stable state`);\n return;\n }\n console.warn(`[PeerManager ${this.localId}] Cannot handle answer from ${remoteId}, signaling state is ${pc.signalingState}`);\n return;\n }\n \n console.log(`[PeerManager ${this.localId}] Handling answer from ${remoteId}...`);\n \n try {\n await pc.setRemoteDescription(new RTCSessionDescription(answer));\n peerInfo.remoteDescriptionSet = true;\n\n // Process queued ICE candidates\n await this.processIceCandidateQueue(remoteId);\n \n console.log(`[PeerManager ${this.localId}] Answer processed for ${remoteId}`);\n } catch (error) {\n console.error(`[PeerManager ${this.localId}] Error handling answer from ${remoteId}:`, error);\n throw error;\n }\n }\n\n /**\n * Process queued ICE candidates for a peer\n */\n private async processIceCandidateQueue(remoteId: string): Promise<void> {\n const peerInfo = this.peerConnections.get(remoteId);\n if (!peerInfo || peerInfo.iceCandidateQueue.length === 0) {\n return;\n }\n\n const pc = peerInfo.connection;\n console.log(`[PeerManager ${this.localId}] Processing ${peerInfo.iceCandidateQueue.length} queued ICE candidates for ${remoteId}`);\n\n const candidates = [...peerInfo.iceCandidateQueue];\n peerInfo.iceCandidateQueue = [];\n\n for (const candidate of candidates) {\n try {\n await pc.addIceCandidate(new RTCIceCandidate(candidate));\n } catch (error) {\n // Some candidates may fail if they're duplicates or invalid\n // This is normal and can be ignored\n console.warn(`[PeerManager ${this.localId}] Failed to add queued ICE candidate for ${remoteId}:`, error);\n }\n }\n }\n\n /**\n * Add ICE candidate for a specific peer\n * Queues candidates if remote description is not set yet\n */\n async addIceCandidate(candidate: RTCIceCandidateInit, remoteId: string): Promise<void> {\n const peerInfo = this.peerConnections.get(remoteId);\n if (!peerInfo) {\n console.warn(`[PeerManager ${this.localId}] Cannot add ICE candidate: Peer ${remoteId} not found`);\n return;\n }\n\n const pc = peerInfo.connection;\n\n // If remote description is not set, queue the candidate\n if (!peerInfo.remoteDescriptionSet) {\n console.log(`[PeerManager ${this.localId}] Queuing ICE candidate for ${remoteId} (remote description not set yet)`);\n peerInfo.iceCandidateQueue.push(candidate);\n return;\n }\n\n // Try to add the candidate\n try {\n console.log(`[PeerManager ${this.localId}] Adding ICE candidate for ${remoteId}`);\n await pc.addIceCandidate(new RTCIceCandidate(candidate));\n } catch (error) {\n // Handle common ICE candidate errors gracefully\n if (error instanceof Error) {\n // Check if it's a duplicate candidate error (common and harmless)\n if (error.message.includes('duplicate') || error.message.includes('already been added')) {\n console.log(`[PeerManager ${this.localId}] Duplicate ICE candidate for ${remoteId} (ignored)`);\n return;\n }\n \n // Check if connection is already closed\n if (pc.connectionState === 'closed' || pc.connectionState === 'failed') {\n console.warn(`[PeerManager ${this.localId}] Cannot add ICE candidate: connection to ${remoteId} is ${pc.connectionState}`);\n return;\n }\n }\n \n // For other errors, log but don't throw\n console.warn(`[PeerManager ${this.localId}] Failed to add ICE candidate for ${remoteId}:`, error);\n }\n }\n\n /**\n * Add a media track to a specific peer connection\n */\n addTrack(track: MediaStreamTrack, stream: MediaStream, remoteId?: string): void {\n if (remoteId) {\n // Add to specific peer\n const peerInfo = this.peerConnections.get(remoteId);\n if (!peerInfo) {\n throw new Error(`Peer ${remoteId} not found. Call addPeer() first.`);\n }\n console.log(`[PeerManager ${this.localId}] Adding track to ${remoteId}:`, track.kind);\n peerInfo.connection.addTrack(track, stream);\n } else {\n // Legacy 1:1 support\n if (!this.peerConnection) {\n throw new Error('Peer connection not initialized. Call initialize() first.');\n }\n console.log(`[PeerManager ${this.localId}] Adding track:`, track.kind);\n this.peerConnection.addTrack(track, stream);\n }\n }\n\n /**\n * Add media tracks to all peer connections (multi-peer support)\n */\n addTrackToAll(track: MediaStreamTrack, stream: MediaStream): void {\n this.peerConnections.forEach((peerInfo, remoteId) => {\n console.log(`[PeerManager ${this.localId}] Adding track to ${remoteId}:`, track.kind);\n peerInfo.connection.addTrack(track, stream);\n });\n }\n\n /**\n * Get the remote media stream for a specific peer\n */\n getRemoteStream(remoteId: string): MediaStream | null {\n const peerInfo = this.peerConnections.get(remoteId);\n if (!peerInfo) {\n return null;\n }\n\n const pc = peerInfo.connection;\n const tracks: MediaStreamTrack[] = [];\n pc.getReceivers().forEach(receiver => {\n if (receiver.track) {\n tracks.push(receiver.track);\n }\n });\n\n if (tracks.length === 0) {\n return null;\n }\n\n return new MediaStream(tracks);\n }\n\n /**\n * Set handler for when remote stream is available\n * Callback receives (stream, remoteId) for multi-peer support\n * Can be called before or after initialization\n */\n onRemoteStream(callback: (stream: MediaStream, remoteId: string) => void): void {\n this.remoteStreamCallback = callback;\n\n // If peer connections already exist, set up handlers immediately\n this.peerConnections.forEach((peerInfo, remoteId) => {\n peerInfo.connection.ontrack = (event) => {\n console.log(`[PeerManager ${this.localId}] Remote track received from ${remoteId}:`, event.track.kind);\n if (event.streams && event.streams[0]) {\n callback(event.streams[0], remoteId);\n } else {\n const stream = new MediaStream([event.track]);\n callback(stream, remoteId);\n }\n };\n });\n }\n\n /**\n * Set handler for connection state changes\n * Callback receives (state, remoteId) for multi-peer support\n */\n onConnectionStateChange(callback: (state: RTCPeerConnectionState, remoteId: string) => void): void {\n this.connectionStateCallback = callback;\n \n // Trigger with current states if connections exist\n this.peerConnections.forEach((peerInfo, remoteId) => {\n callback(peerInfo.connection.connectionState, remoteId);\n });\n }\n\n /**\n * Set handler for ICE connection state changes\n * Callback receives (state, remoteId) for multi-peer support\n */\n onIceConnectionStateChange(callback: (state: RTCIceConnectionState, remoteId: string) => void): void {\n this.iceConnectionStateCallback = callback;\n \n // Trigger with current states if connections exist\n this.peerConnections.forEach((peerInfo, remoteId) => {\n callback(peerInfo.connection.iceConnectionState, remoteId);\n });\n }\n\n /**\n * Get the peer connection instance (legacy 1:1 support)\n */\n getPeerConnection(): RTCPeerConnection | null {\n return this.peerConnection;\n }\n\n /**\n * Cleanup and close all peer connections\n */\n async cleanup(): Promise<void> {\n // Cleanup all multi-peer connections\n const cleanupPromises = Array.from(this.peerConnections.values()).map(peerInfo => {\n console.log(`[PeerManager ${this.localId}] Cleaning up connection to ${peerInfo.remoteId}...`);\n peerInfo.connection.close();\n });\n \n await Promise.all(cleanupPromises);\n this.peerConnections.clear();\n \n // Cleanup legacy connection\n if (this.peerConnection) {\n this.peerConnection.close();\n this.peerConnection = null;\n this.remoteId = null;\n }\n\n // Unregister from adapter if set\n if (this.signalingAdapter && this.registeredInAdapter) {\n this.signalingAdapter.unregisterPeer(this.localId);\n this.signalingAdapter = null;\n this.registeredInAdapter = false;\n }\n \n console.log(`[PeerManager ${this.localId}] All connections cleaned up`);\n }\n}\n","/**\r\n * MediaManager - Manages local media streams (getUserMedia)\r\n * \r\n * Handles:\r\n * - Requesting user media (audio/video)\r\n * - Managing local media streams\r\n * - Attaching streams to peer connections\r\n * \r\n * Currently supports single stream management.\r\n * TODO: Extend for multiple streams or screen sharing in the future.\r\n */\r\n\r\nexport interface MediaConstraints {\r\n audio?: boolean | MediaTrackConstraints;\r\n video?: boolean | MediaTrackConstraints;\r\n}\r\n\r\nexport class MediaManager {\r\n private localStream: MediaStream | null = null;\r\n\r\n /**\r\n * Request user media (audio and/or video)\r\n */\r\n async getUserMedia(constraints: MediaConstraints = { audio: true, video: true }): Promise<MediaStream> {\r\n console.log('[MediaManager] Requesting user media with constraints:', constraints);\r\n \r\n try {\r\n const stream = await navigator.mediaDevices.getUserMedia(constraints);\r\n this.localStream = stream;\r\n console.log('[MediaManager] User media obtained:', {\r\n audioTracks: stream.getAudioTracks().length,\r\n videoTracks: stream.getVideoTracks().length\r\n });\r\n return stream;\r\n } catch (error) {\r\n console.error('[MediaManager] Error getting user media:', error);\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Get the current local stream\r\n */\r\n getLocalStream(): MediaStream | null {\r\n return this.localStream;\r\n }\r\n\r\n /**\r\n * Stop all tracks in the local stream\r\n */\r\n stopLocalStream(): void {\r\n if (this.localStream) {\r\n console.log('[MediaManager] Stopping local stream...');\r\n this.localStream.getTracks().forEach(track => {\r\n track.stop();\r\n console.log(`[MediaManager] Stopped track: ${track.kind}`);\r\n });\r\n this.localStream = null;\r\n }\r\n }\r\n\r\n /**\r\n * Attach local stream to a peer connection (1:1 or specific peer)\r\n * This adds all tracks from the local stream to the peer connection\r\n */\r\n attachToPeer(peerManager: import('./PeerManager').PeerManager, remoteId?: string): void {\r\n if (!this.localStream) {\r\n throw new Error('No local stream available. Call getUserMedia() first.');\r\n }\r\n\r\n console.log(`[MediaManager] Attaching local stream to peer${remoteId ? ` ${remoteId}` : ''}...`);\r\n this.localStream.getTracks().forEach(track => {\r\n peerManager.addTrack(track, this.localStream!, remoteId);\r\n });\r\n console.log(`[MediaManager] Local stream attached${remoteId ? ` to ${remoteId}` : ''}`);\r\n }\r\n\r\n /**\r\n * Attach local stream to all peers in a room (multi-peer support)\r\n * This adds all tracks from the local stream to all peer connections\r\n */\r\n attachToAllPeers(peerManager: import('./PeerManager').PeerManager): void {\r\n if (!this.localStream) {\r\n throw new Error('No local stream available. Call getUserMedia() first.');\r\n }\r\n\r\n const peerIds = peerManager.getRemotePeerIds();\r\n console.log(`[MediaManager] Attaching local stream to ${peerIds.length} peers...`);\r\n \r\n this.localStream.getTracks().forEach(track => {\r\n peerManager.addTrackToAll(track, this.localStream!);\r\n });\r\n \r\n console.log(`[MediaManager] Local stream attached to all peers`);\r\n }\r\n\r\n /**\r\n * Check if camera (video) is off/muted\r\n * Returns true if camera is off, false if on\r\n */\r\n isCameraOff(): boolean {\r\n if (!this.localStream) {\r\n return true; // No stream means camera is off\r\n }\r\n\r\n const videoTracks = this.localStream.getVideoTracks();\r\n if (videoTracks.length === 0) {\r\n return true; // No video tracks\r\n }\r\n\r\n // Check if all video tracks are muted or disabled\r\n return videoTracks.every(track => track.muted || !track.enabled || track.readyState === 'ended');\r\n }\r\n\r\n /**\r\n * Check if microphone (audio) is off/muted\r\n * Returns true if microphone is off, false if on\r\n */\r\n isMicrophoneOff(): boolean {\r\n if (!this.localStream) {\r\n return true; // No stream means microphone is off\r\n }\r\n\r\n const audioTracks = this.localStream.getAudioTracks();\r\n if (audioTracks.length === 0) {\r\n return true; // No audio tracks\r\n }\r\n\r\n // Check if all audio tracks are muted or disabled\r\n return audioTracks.every(track => track.muted || !track.enabled || track.readyState === 'ended');\r\n }\r\n\r\n /**\r\n * Get camera track state\r\n * Returns the first video track or null\r\n */\r\n getCameraTrack(): MediaStreamTrack | null {\r\n if (!this.localStream) {\r\n return null;\r\n }\r\n const videoTracks = this.localStream.getVideoTracks();\r\n return videoTracks.length > 0 ? videoTracks[0] : null;\r\n }\r\n\r\n /**\r\n * Get microphone track state\r\n * Returns the first audio track or null\r\n */\r\n getMicrophoneTrack(): MediaStreamTrack | null {\r\n if (!this.localStream) {\r\n return null;\r\n }\r\n const audioTracks = this.localStream.getAudioTracks();\r\n return audioTracks.length > 0 ? audioTracks[0] : null;\r\n }\r\n\r\n /**\r\n * Get detailed media state information\r\n */\r\n getMediaState(): {\r\n hasStream: boolean;\r\n camera: {\r\n available: boolean;\r\n enabled: boolean;\r\n muted: boolean;\r\n readyState: MediaStreamTrackState | null;\r\n };\r\n microphone: {\r\n available: boolean;\r\n enabled: boolean;\r\n muted: boolean;\r\n readyState: MediaStreamTrackState | null;\r\n };\r\n } {\r\n const videoTrack = this.getCameraTrack();\r\n const audioTrack = this.getMicrophoneTrack();\r\n\r\n return {\r\n hasStream: this.localStream !== null,\r\n camera: {\r\n available: videoTrack !== null,\r\n enabled: videoTrack?.enabled ?? false,\r\n muted: videoTrack?.muted ?? true,\r\n readyState: videoTrack?.readyState ?? null,\r\n },\r\n microphone: {\r\n available: audioTrack !== null,\r\n enabled: audioTrack?.enabled ?? false,\r\n muted: audioTrack?.muted ?? true,\r\n readyState: audioTrack?.readyState ?? null,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Cleanup - stop all tracks\r\n */\r\n cleanup(): void {\r\n this.stopLocalStream();\r\n }\r\n}\r\n\r\n","/**\n * Media Utilities - Helper functions for checking media track states\n * \n * These utilities can work with any MediaStream, not just MediaManager instances.\n */\n\nexport interface MediaState {\n hasStream: boolean;\n camera: {\n available: boolean;\n enabled: boolean;\n muted: boolean;\n readyState: MediaStreamTrackState | null;\n };\n microphone: {\n available: boolean;\n enabled: boolean;\n muted: boolean;\n readyState: MediaStreamTrackState | null;\n };\n}\n\n/**\n * Check if camera (video) is off/muted in a given stream\n * Returns true if camera is off, false if on\n */\nexport function isCameraOff(stream: MediaStream | null): boolean {\n if (!stream) {\n return true;\n }\n\n const videoTracks = stream.getVideoTracks();\n if (videoTracks.length === 0) {\n return true;\n }\n\n return videoTracks.every(track => track.muted || !track.enabled || track.readyState === 'ended');\n}\n\n/**\n * Check if microphone (audio) is off/muted in a given stream\n * Returns true if microphone is off, false if on\n */\nexport function isMicrophoneOff(stream: MediaStream | null): boolean {\n if (!stream) {\n return true;\n }\n\n const audioTracks = stream.getAudioTracks();\n if (audioTracks.length === 0) {\n return true;\n }\n\n return audioTracks.every(track => track.muted || !track.enabled || track.readyState === 'ended');\n}\n\n/**\n * Get camera track from a stream\n */\nexport function getCameraTrack(stream: MediaStream | null): MediaStreamTrack | null {\n if (!stream) {\n return null;\n }\n const videoTracks = stream.getVideoTracks();\n return videoTracks.length > 0 ? videoTracks[0] : null;\n}\n\n/**\n * Get microphone track from a stream\n */\nexport function getMicrophoneTrack(stream: MediaStream | null): MediaStreamTrack | null {\n if (!stream) {\n return null;\n }\n const audioTracks = stream.getAudioTracks();\n return audioTracks.length > 0 ? audioTracks[0] : null;\n}\n\n/**\n * Get detailed media state from a stream\n */\nexport function getMediaState(stream: MediaStream | null): MediaState {\n const videoTrack = getCameraTrack(stream);\n const audioTrack = getMicrophoneTrack(stream);\n\n return {\n hasStream: stream !== null,\n camera: {\n available: videoTrack !== null,\n enabled: videoTrack?.enabled ?? false,\n muted: videoTrack?.muted ?? true,\n readyState: videoTrack?.readyState ?? null,\n },\n microphone: {\n available: audioTrack !== null,\n enabled: audioTrack?.enabled ?? false,\n muted: audioTrack?.muted ?? true,\n readyState: audioTrack?.readyState ?? null,\n },\n };\n}\n\n/**\n * Create a reactive media state observer\n * Returns a function that can be called to get current state and a cleanup function\n */\nexport function observeMediaState(\n stream: MediaStream | null,\n callback: (state: MediaState) => void\n): () => void {\n if (!stream) {\n callback(getMediaState(null));\n return () => {}; // No-op cleanup\n }\n\n // Initial state\n callback(getMediaState(stream));\n\n // Track state change handlers\n const trackHandlers: Map<MediaStreamTrack, () => void> = new Map();\n\n const updateState = () => {\n callback(getMediaState(stream));\n };\n\n // Listen to all tracks\n const allTracks = [...stream.getAudioTracks(), ...stream.getVideoTracks()];\n \n allTracks.forEach(track => {\n const handleMute = () => updateState();\n const handleUnmute = () => updateState();\n const handleEnded = () => updateState();\n\n track.addEventListener('mute', handleMute);\n track.addEventListener('unmute', handleUnmute);\n track.addEventListener('ended', handleEnded);\n\n trackHandlers.set(track, () => {\n track.removeEventListener('mute', handleMute);\n track.removeEventListener('unmute', handleUnmute);\n track.removeEventListener('ended', handleEnded);\n });\n });\n\n // Cleanup function\n return () => {\n trackHandlers.forEach(cleanup => cleanup());\n trackHandlers.clear();\n };\n}\n\n"]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media Utilities - Helper functions for checking media track states
|
|
3
|
+
*
|
|
4
|
+
* These utilities can work with any MediaStream, not just MediaManager instances.
|
|
5
|
+
*/
|
|
6
|
+
interface MediaState {
|
|
7
|
+
hasStream: boolean;
|
|
8
|
+
camera: {
|
|
9
|
+
available: boolean;
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
muted: boolean;
|
|
12
|
+
readyState: MediaStreamTrackState | null;
|
|
13
|
+
};
|
|
14
|
+
microphone: {
|
|
15
|
+
available: boolean;
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
muted: boolean;
|
|
18
|
+
readyState: MediaStreamTrackState | null;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Check if camera (video) is off/muted in a given stream
|
|
23
|
+
* Returns true if camera is off, false if on
|
|
24
|
+
*/
|
|
25
|
+
declare function isCameraOff(stream: MediaStream | null): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Check if microphone (audio) is off/muted in a given stream
|
|
28
|
+
* Returns true if microphone is off, false if on
|
|
29
|
+
*/
|
|
30
|
+
declare function isMicrophoneOff(stream: MediaStream | null): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Get camera track from a stream
|
|
33
|
+
*/
|
|
34
|
+
declare function getCameraTrack(stream: MediaStream | null): MediaStreamTrack | null;
|
|
35
|
+
/**
|
|
36
|
+
* Get microphone track from a stream
|
|
37
|
+
*/
|
|
38
|
+
declare function getMicrophoneTrack(stream: MediaStream | null): MediaStreamTrack | null;
|
|
39
|
+
/**
|
|
40
|
+
* Get detailed media state from a stream
|
|
41
|
+
*/
|
|
42
|
+
declare function getMediaState(stream: MediaStream | null): MediaState;
|
|
43
|
+
/**
|
|
44
|
+
* Create a reactive media state observer
|
|
45
|
+
* Returns a function that can be called to get current state and a cleanup function
|
|
46
|
+
*/
|
|
47
|
+
declare function observeMediaState(stream: MediaStream | null, callback: (state: MediaState) => void): () => void;
|
|
48
|
+
|
|
49
|
+
export { type MediaState as M, isMicrophoneOff as a, getMicrophoneTrack as b, getMediaState as c, getCameraTrack as g, isCameraOff as i, observeMediaState as o };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { M as MediaState } from '../mediaUtils-5gVEq26H.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* React Hooks for Callway
|
|
5
|
+
*
|
|
6
|
+
* Provides React hooks for checking camera and microphone state.
|
|
7
|
+
*
|
|
8
|
+
* Note: React is a peer dependency and must be installed separately.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Hook to check if camera is off/muted
|
|
13
|
+
*
|
|
14
|
+
* @param stream - The media stream to check (can be null)
|
|
15
|
+
* @returns true if camera is off, false if on
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* const isCamOff = useIsCameraOff(localStream);
|
|
20
|
+
*
|
|
21
|
+
* return (
|
|
22
|
+
* <div>
|
|
23
|
+
* {isCamOff ? 'Camera is off' : 'Camera is on'}
|
|
24
|
+
* </div>
|
|
25
|
+
* );
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
declare function useIsCameraOff(stream: MediaStream | null): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Hook to check if microphone is off/muted
|
|
31
|
+
*
|
|
32
|
+
* @param stream - The media stream to check (can be null)
|
|
33
|
+
* @returns true if microphone is off, false if on
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```tsx
|
|
37
|
+
* const isMicOff = useIsMicrophoneOff(localStream);
|
|
38
|
+
*
|
|
39
|
+
* return (
|
|
40
|
+
* <div>
|
|
41
|
+
* {isMicOff ? 'Microphone is off' : 'Microphone is on'}
|
|
42
|
+
* </div>
|
|
43
|
+
* );
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
declare function useIsMicrophoneOff(stream: MediaStream | null): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Hook to get detailed media state
|
|
49
|
+
*
|
|
50
|
+
* @param stream - The media stream to check (can be null)
|
|
51
|
+
* @returns Current media state object
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```tsx
|
|
55
|
+
* const mediaState = useMediaState(localStream);
|
|
56
|
+
*
|
|
57
|
+
* return (
|
|
58
|
+
* <div>
|
|
59
|
+
* <p>Camera: {mediaState.camera.enabled ? 'On' : 'Off'}</p>
|
|
60
|
+
* <p>Microphone: {mediaState.microphone.enabled ? 'On' : 'Off'}</p>
|
|
61
|
+
* </div>
|
|
62
|
+
* );
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
declare function useMediaState(stream: MediaStream | null): MediaState;
|
|
66
|
+
/**
|
|
67
|
+
* Hook to observe media state changes with a custom callback
|
|
68
|
+
*
|
|
69
|
+
* @param stream - The media stream to observe (can be null)
|
|
70
|
+
* @param callback - Function called whenever media state changes
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```tsx
|
|
74
|
+
* useMediaStateObserver(localStream, (state) => {
|
|
75
|
+
* console.log('Camera:', state.camera.enabled ? 'On' : 'Off');
|
|
76
|
+
* console.log('Mic:', state.microphone.enabled ? 'On' : 'Off');
|
|
77
|
+
* });
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
declare function useMediaStateObserver(stream: MediaStream | null, callback: (state: MediaState) => void): void;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Type definitions for React bindings
|
|
84
|
+
*/
|
|
85
|
+
type MediaStream$1 = globalThis.MediaStream;
|
|
86
|
+
|
|
87
|
+
export { MediaState, type MediaStream$1 as MediaStream, useIsCameraOff, useIsMicrophoneOff, useMediaState, useMediaStateObserver };
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { useState, useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
// src/react/hooks.ts
|
|
4
|
+
|
|
5
|
+
// src/core/mediaUtils.ts
|
|
6
|
+
function getCameraTrack(stream) {
|
|
7
|
+
if (!stream) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const videoTracks = stream.getVideoTracks();
|
|
11
|
+
return videoTracks.length > 0 ? videoTracks[0] : null;
|
|
12
|
+
}
|
|
13
|
+
function getMicrophoneTrack(stream) {
|
|
14
|
+
if (!stream) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const audioTracks = stream.getAudioTracks();
|
|
18
|
+
return audioTracks.length > 0 ? audioTracks[0] : null;
|
|
19
|
+
}
|
|
20
|
+
function getMediaState(stream) {
|
|
21
|
+
const videoTrack = getCameraTrack(stream);
|
|
22
|
+
const audioTrack = getMicrophoneTrack(stream);
|
|
23
|
+
return {
|
|
24
|
+
hasStream: stream !== null,
|
|
25
|
+
camera: {
|
|
26
|
+
available: videoTrack !== null,
|
|
27
|
+
enabled: videoTrack?.enabled ?? false,
|
|
28
|
+
muted: videoTrack?.muted ?? true,
|
|
29
|
+
readyState: videoTrack?.readyState ?? null
|
|
30
|
+
},
|
|
31
|
+
microphone: {
|
|
32
|
+
available: audioTrack !== null,
|
|
33
|
+
enabled: audioTrack?.enabled ?? false,
|
|
34
|
+
muted: audioTrack?.muted ?? true,
|
|
35
|
+
readyState: audioTrack?.readyState ?? null
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function observeMediaState(stream, callback) {
|
|
40
|
+
if (!stream) {
|
|
41
|
+
callback(getMediaState(null));
|
|
42
|
+
return () => {
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
callback(getMediaState(stream));
|
|
46
|
+
const trackHandlers = /* @__PURE__ */ new Map();
|
|
47
|
+
const updateState = () => {
|
|
48
|
+
callback(getMediaState(stream));
|
|
49
|
+
};
|
|
50
|
+
const allTracks = [...stream.getAudioTracks(), ...stream.getVideoTracks()];
|
|
51
|
+
allTracks.forEach((track) => {
|
|
52
|
+
const handleMute = () => updateState();
|
|
53
|
+
const handleUnmute = () => updateState();
|
|
54
|
+
const handleEnded = () => updateState();
|
|
55
|
+
track.addEventListener("mute", handleMute);
|
|
56
|
+
track.addEventListener("unmute", handleUnmute);
|
|
57
|
+
track.addEventListener("ended", handleEnded);
|
|
58
|
+
trackHandlers.set(track, () => {
|
|
59
|
+
track.removeEventListener("mute", handleMute);
|
|
60
|
+
track.removeEventListener("unmute", handleUnmute);
|
|
61
|
+
track.removeEventListener("ended", handleEnded);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
return () => {
|
|
65
|
+
trackHandlers.forEach((cleanup) => cleanup());
|
|
66
|
+
trackHandlers.clear();
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/react/hooks.ts
|
|
71
|
+
function useIsCameraOff(stream) {
|
|
72
|
+
const [isOff, setIsOff] = useState(true);
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (!stream) {
|
|
75
|
+
setIsOff(true);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const videoTracks = stream.getVideoTracks();
|
|
79
|
+
if (videoTracks.length === 0) {
|
|
80
|
+
setIsOff(true);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const updateState = () => {
|
|
84
|
+
const allOff = videoTracks.every(
|
|
85
|
+
(track) => track.muted || !track.enabled || track.readyState === "ended"
|
|
86
|
+
);
|
|
87
|
+
setIsOff(allOff);
|
|
88
|
+
};
|
|
89
|
+
updateState();
|
|
90
|
+
const cleanupFunctions = [];
|
|
91
|
+
videoTracks.forEach((track) => {
|
|
92
|
+
const handleMute = () => updateState();
|
|
93
|
+
const handleUnmute = () => updateState();
|
|
94
|
+
const handleEnded = () => updateState();
|
|
95
|
+
track.addEventListener("mute", handleMute);
|
|
96
|
+
track.addEventListener("unmute", handleUnmute);
|
|
97
|
+
track.addEventListener("ended", handleEnded);
|
|
98
|
+
const interval = setInterval(updateState, 100);
|
|
99
|
+
cleanupFunctions.push(() => {
|
|
100
|
+
track.removeEventListener("mute", handleMute);
|
|
101
|
+
track.removeEventListener("unmute", handleUnmute);
|
|
102
|
+
track.removeEventListener("ended", handleEnded);
|
|
103
|
+
clearInterval(interval);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
return () => {
|
|
107
|
+
cleanupFunctions.forEach((cleanup) => cleanup());
|
|
108
|
+
};
|
|
109
|
+
}, [stream]);
|
|
110
|
+
return isOff;
|
|
111
|
+
}
|
|
112
|
+
function useIsMicrophoneOff(stream) {
|
|
113
|
+
const [isOff, setIsOff] = useState(true);
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (!stream) {
|
|
116
|
+
setIsOff(true);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const audioTracks = stream.getAudioTracks();
|
|
120
|
+
if (audioTracks.length === 0) {
|
|
121
|
+
setIsOff(true);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const updateState = () => {
|
|
125
|
+
const allOff = audioTracks.every(
|
|
126
|
+
(track) => track.muted || !track.enabled || track.readyState === "ended"
|
|
127
|
+
);
|
|
128
|
+
setIsOff(allOff);
|
|
129
|
+
};
|
|
130
|
+
updateState();
|
|
131
|
+
const cleanupFunctions = [];
|
|
132
|
+
audioTracks.forEach((track) => {
|
|
133
|
+
const handleMute = () => updateState();
|
|
134
|
+
const handleUnmute = () => updateState();
|
|
135
|
+
const handleEnded = () => updateState();
|
|
136
|
+
track.addEventListener("mute", handleMute);
|
|
137
|
+
track.addEventListener("unmute", handleUnmute);
|
|
138
|
+
track.addEventListener("ended", handleEnded);
|
|
139
|
+
const interval = setInterval(updateState, 100);
|
|
140
|
+
cleanupFunctions.push(() => {
|
|
141
|
+
track.removeEventListener("mute", handleMute);
|
|
142
|
+
track.removeEventListener("unmute", handleUnmute);
|
|
143
|
+
track.removeEventListener("ended", handleEnded);
|
|
144
|
+
clearInterval(interval);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
return () => {
|
|
148
|
+
cleanupFunctions.forEach((cleanup) => cleanup());
|
|
149
|
+
};
|
|
150
|
+
}, [stream]);
|
|
151
|
+
return isOff;
|
|
152
|
+
}
|
|
153
|
+
function useMediaState(stream) {
|
|
154
|
+
const [state, setState] = useState(() => getMediaState(stream));
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
if (!stream) {
|
|
157
|
+
setState(getMediaState(null));
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const cleanup = observeMediaState(stream, (newState) => {
|
|
161
|
+
setState(newState);
|
|
162
|
+
});
|
|
163
|
+
return cleanup;
|
|
164
|
+
}, [stream]);
|
|
165
|
+
return state;
|
|
166
|
+
}
|
|
167
|
+
function useMediaStateObserver(stream, callback) {
|
|
168
|
+
const callbackRef = useRef(callback);
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
callbackRef.current = callback;
|
|
171
|
+
}, [callback]);
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
if (!stream) {
|
|
174
|
+
callbackRef.current(getMediaState(null));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const cleanup = observeMediaState(stream, (state) => {
|
|
178
|
+
callbackRef.current(state);
|
|
179
|
+
});
|
|
180
|
+
return cleanup;
|
|
181
|
+
}, [stream]);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export { useIsCameraOff, useIsMicrophoneOff, useMediaState, useMediaStateObserver };
|
|
185
|
+
//# sourceMappingURL=index.js.map
|
|
186
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/mediaUtils.ts","../../src/react/hooks.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Media Utilities - Helper functions for checking media track states\n * \n * These utilities can work with any MediaStream, not just MediaManager instances.\n */\n\nexport interface MediaState {\n hasStream: boolean;\n camera: {\n available: boolean;\n enabled: boolean;\n muted: boolean;\n readyState: MediaStreamTrackState | null;\n };\n microphone: {\n available: boolean;\n enabled: boolean;\n muted: boolean;\n readyState: MediaStreamTrackState | null;\n };\n}\n\n/**\n * Check if camera (video) is off/muted in a given stream\n * Returns true if camera is off, false if on\n */\nexport function isCameraOff(stream: MediaStream | null): boolean {\n if (!stream) {\n return true;\n }\n\n const videoTracks = stream.getVideoTracks();\n if (videoTracks.length === 0) {\n return true;\n }\n\n return videoTracks.every(track => track.muted || !track.enabled || track.readyState === 'ended');\n}\n\n/**\n * Check if microphone (audio) is off/muted in a given stream\n * Returns true if microphone is off, false if on\n */\nexport function isMicrophoneOff(stream: MediaStream | null): boolean {\n if (!stream) {\n return true;\n }\n\n const audioTracks = stream.getAudioTracks();\n if (audioTracks.length === 0) {\n return true;\n }\n\n return audioTracks.every(track => track.muted || !track.enabled || track.readyState === 'ended');\n}\n\n/**\n * Get camera track from a stream\n */\nexport function getCameraTrack(stream: MediaStream | null): MediaStreamTrack | null {\n if (!stream) {\n return null;\n }\n const videoTracks = stream.getVideoTracks();\n return videoTracks.length > 0 ? videoTracks[0] : null;\n}\n\n/**\n * Get microphone track from a stream\n */\nexport function getMicrophoneTrack(stream: MediaStream | null): MediaStreamTrack | null {\n if (!stream) {\n return null;\n }\n const audioTracks = stream.getAudioTracks();\n return audioTracks.length > 0 ? audioTracks[0] : null;\n}\n\n/**\n * Get detailed media state from a stream\n */\nexport function getMediaState(stream: MediaStream | null): MediaState {\n const videoTrack = getCameraTrack(stream);\n const audioTrack = getMicrophoneTrack(stream);\n\n return {\n hasStream: stream !== null,\n camera: {\n available: videoTrack !== null,\n enabled: videoTrack?.enabled ?? false,\n muted: videoTrack?.muted ?? true,\n readyState: videoTrack?.readyState ?? null,\n },\n microphone: {\n available: audioTrack !== null,\n enabled: audioTrack?.enabled ?? false,\n muted: audioTrack?.muted ?? true,\n readyState: audioTrack?.readyState ?? null,\n },\n };\n}\n\n/**\n * Create a reactive media state observer\n * Returns a function that can be called to get current state and a cleanup function\n */\nexport function observeMediaState(\n stream: MediaStream | null,\n callback: (state: MediaState) => void\n): () => void {\n if (!stream) {\n callback(getMediaState(null));\n return () => {}; // No-op cleanup\n }\n\n // Initial state\n callback(getMediaState(stream));\n\n // Track state change handlers\n const trackHandlers: Map<MediaStreamTrack, () => void> = new Map();\n\n const updateState = () => {\n callback(getMediaState(stream));\n };\n\n // Listen to all tracks\n const allTracks = [...stream.getAudioTracks(), ...stream.getVideoTracks()];\n \n allTracks.forEach(track => {\n const handleMute = () => updateState();\n const handleUnmute = () => updateState();\n const handleEnded = () => updateState();\n\n track.addEventListener('mute', handleMute);\n track.addEventListener('unmute', handleUnmute);\n track.addEventListener('ended', handleEnded);\n\n trackHandlers.set(track, () => {\n track.removeEventListener('mute', handleMute);\n track.removeEventListener('unmute', handleUnmute);\n track.removeEventListener('ended', handleEnded);\n });\n });\n\n // Cleanup function\n return () => {\n trackHandlers.forEach(cleanup => cleanup());\n trackHandlers.clear();\n };\n}\n\n","/**\n * React Hooks for Callway\n * \n * Provides React hooks for checking camera and microphone state.\n * \n * Note: React is a peer dependency and must be installed separately.\n */\n\nimport { useEffect, useState, useRef } from 'react';\nimport { getMediaState, observeMediaState, type MediaState } from '../core/mediaUtils';\n\n// Re-export types for convenience\nexport type { MediaState } from '../core/mediaUtils';\n\n/**\n * Hook to check if camera is off/muted\n * \n * @param stream - The media stream to check (can be null)\n * @returns true if camera is off, false if on\n * \n * @example\n * ```tsx\n * const isCamOff = useIsCameraOff(localStream);\n * \n * return (\n * <div>\n * {isCamOff ? 'Camera is off' : 'Camera is on'}\n * </div>\n * );\n * ```\n */\nexport function useIsCameraOff(stream: MediaStream | null): boolean {\n const [isOff, setIsOff] = useState(true);\n\n useEffect(() => {\n if (!stream) {\n setIsOff(true);\n return;\n }\n\n const videoTracks = stream.getVideoTracks();\n if (videoTracks.length === 0) {\n setIsOff(true);\n return;\n }\n\n const updateState = () => {\n const allOff = videoTracks.every(\n track => track.muted || !track.enabled || track.readyState === 'ended'\n );\n setIsOff(allOff);\n };\n\n // Initial check\n updateState();\n\n // Listen to track changes\n const cleanupFunctions: (() => void)[] = [];\n\n videoTracks.forEach(track => {\n const handleMute = () => updateState();\n const handleUnmute = () => updateState();\n const handleEnded = () => updateState();\n const handleEnabledChange = () => updateState();\n\n track.addEventListener('mute', handleMute);\n track.addEventListener('unmute', handleUnmute);\n track.addEventListener('ended', handleEnded);\n \n // Note: 'enabled' is a property, not an event, so we poll or use a different approach\n // For now, we'll check on mute/unmute events and use a small interval for enabled changes\n const interval = setInterval(updateState, 100);\n\n cleanupFunctions.push(() => {\n track.removeEventListener('mute', handleMute);\n track.removeEventListener('unmute', handleUnmute);\n track.removeEventListener('ended', handleEnded);\n clearInterval(interval);\n });\n });\n\n return () => {\n cleanupFunctions.forEach(cleanup => cleanup());\n };\n }, [stream]);\n\n return isOff;\n}\n\n/**\n * Hook to check if microphone is off/muted\n * \n * @param stream - The media stream to check (can be null)\n * @returns true if microphone is off, false if on\n * \n * @example\n * ```tsx\n * const isMicOff = useIsMicrophoneOff(localStream);\n * \n * return (\n * <div>\n * {isMicOff ? 'Microphone is off' : 'Microphone is on'}\n * </div>\n * );\n * ```\n */\nexport function useIsMicrophoneOff(stream: MediaStream | null): boolean {\n const [isOff, setIsOff] = useState(true);\n\n useEffect(() => {\n if (!stream) {\n setIsOff(true);\n return;\n }\n\n const audioTracks = stream.getAudioTracks();\n if (audioTracks.length === 0) {\n setIsOff(true);\n return;\n }\n\n const updateState = () => {\n const allOff = audioTracks.every(\n track => track.muted || !track.enabled || track.readyState === 'ended'\n );\n setIsOff(allOff);\n };\n\n // Initial check\n updateState();\n\n // Listen to track changes\n const cleanupFunctions: (() => void)[] = [];\n\n audioTracks.forEach(track => {\n const handleMute = () => updateState();\n const handleUnmute = () => updateState();\n const handleEnded = () => updateState();\n\n track.addEventListener('mute', handleMute);\n track.addEventListener('unmute', handleUnmute);\n track.addEventListener('ended', handleEnded);\n \n // Poll for enabled changes since it's not an event\n const interval = setInterval(updateState, 100);\n\n cleanupFunctions.push(() => {\n track.removeEventListener('mute', handleMute);\n track.removeEventListener('unmute', handleUnmute);\n track.removeEventListener('ended', handleEnded);\n clearInterval(interval);\n });\n });\n\n return () => {\n cleanupFunctions.forEach(cleanup => cleanup());\n };\n }, [stream]);\n\n return isOff;\n}\n\n/**\n * Hook to get detailed media state\n * \n * @param stream - The media stream to check (can be null)\n * @returns Current media state object\n * \n * @example\n * ```tsx\n * const mediaState = useMediaState(localStream);\n * \n * return (\n * <div>\n * <p>Camera: {mediaState.camera.enabled ? 'On' : 'Off'}</p>\n * <p>Microphone: {mediaState.microphone.enabled ? 'On' : 'Off'}</p>\n * </div>\n * );\n * ```\n */\nexport function useMediaState(stream: MediaStream | null): MediaState {\n const [state, setState] = useState<MediaState>(() => getMediaState(stream));\n\n useEffect(() => {\n if (!stream) {\n setState(getMediaState(null));\n return;\n }\n\n // Use the observeMediaState utility\n const cleanup = observeMediaState(stream, (newState) => {\n setState(newState);\n });\n\n return cleanup;\n }, [stream]);\n\n return state;\n}\n\n/**\n * Hook to observe media state changes with a custom callback\n * \n * @param stream - The media stream to observe (can be null)\n * @param callback - Function called whenever media state changes\n * \n * @example\n * ```tsx\n * useMediaStateObserver(localStream, (state) => {\n * console.log('Camera:', state.camera.enabled ? 'On' : 'Off');\n * console.log('Mic:', state.microphone.enabled ? 'On' : 'Off');\n * });\n * ```\n */\nexport function useMediaStateObserver(\n stream: MediaStream | null,\n callback: (state: MediaState) => void\n): void {\n const callbackRef = useRef(callback);\n \n // Keep callback ref up to date\n useEffect(() => {\n callbackRef.current = callback;\n }, [callback]);\n\n useEffect(() => {\n if (!stream) {\n callbackRef.current(getMediaState(null));\n return;\n }\n\n const cleanup = observeMediaState(stream, (state) => {\n callbackRef.current(state);\n });\n\n return cleanup;\n }, [stream]);\n}\n\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "callway",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A lightweight WebRTC call engine for building real-time audio and video calls with flexible, pluggable signaling.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./react": {
|
|
15
|
+
"import": "./dist/react/index.js",
|
|
16
|
+
"types": "./dist/react/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"dev": "tsup --watch",
|
|
25
|
+
"test": "vitest",
|
|
26
|
+
"test:run": "vitest run",
|
|
27
|
+
"typecheck": "tsc --noEmit"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"webrtc",
|
|
31
|
+
"call",
|
|
32
|
+
"video",
|
|
33
|
+
"audio",
|
|
34
|
+
"signaling",
|
|
35
|
+
"peer-connection"
|
|
36
|
+
],
|
|
37
|
+
"author": "Recovery Eyo",
|
|
38
|
+
"license": "ISC",
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^20.10.0",
|
|
41
|
+
"@types/react": "^19.2.7",
|
|
42
|
+
"react": "^19.2.3",
|
|
43
|
+
"tsup": "^8.0.0",
|
|
44
|
+
"typescript": "^5.3.0",
|
|
45
|
+
"vitest": "^1.0.0"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"react": {
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|