Instructions to use marcosremar2/MuseTalk1.5 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use marcosremar2/MuseTalk1.5 with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("marcosremar2/MuseTalk1.5", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
| """ | |
| WebRTC streaming for MuseTalk | |
| Uses aiortc to stream generated frames as H.264 video | |
| """ | |
| import asyncio | |
| import numpy as np | |
| import cv2 | |
| import time | |
| import fractions | |
| from av import VideoFrame | |
| from aiortc import RTCPeerConnection, RTCSessionDescription, VideoStreamTrack, RTCConfiguration, RTCIceServer | |
| from aiortc.contrib.media import MediaRelay | |
| # ICE Server configuration (STUN + TURN for NAT traversal) | |
| ICE_SERVERS = RTCConfiguration(iceServers=[ | |
| # STUN servers | |
| RTCIceServer(urls=["stun:stun.l.google.com:19302"]), | |
| RTCIceServer(urls=["stun:stun1.l.google.com:19302"]), | |
| # OpenRelay TURN servers (free) | |
| RTCIceServer( | |
| urls=["turn:openrelay.metered.ca:80"], | |
| username="openrelayproject", | |
| credential="openrelayproject" | |
| ), | |
| RTCIceServer( | |
| urls=["turn:openrelay.metered.ca:443?transport=tcp"], | |
| username="openrelayproject", | |
| credential="openrelayproject" | |
| ), | |
| RTCIceServer( | |
| urls=["turns:openrelay.metered.ca:443?transport=tcp"], | |
| username="openrelayproject", | |
| credential="openrelayproject" | |
| ), | |
| ]) | |
| # Video track that streams frames from a queue | |
| class MuseTalkVideoTrack(VideoStreamTrack): | |
| """ | |
| Video track that reads frames from an async queue. | |
| Frames are numpy arrays (BGR). | |
| """ | |
| def __init__(self, fps: int = 30): | |
| super().__init__() | |
| self.fps = fps | |
| self.frame_queue = asyncio.Queue(maxsize=60) # Buffer up to 60 frames | |
| self.frame_count = 0 | |
| self.start_time = None | |
| self._running = True | |
| self.last_frame = None # Keep last frame for idle | |
| async def recv(self): | |
| """Called by aiortc to get the next frame.""" | |
| if self.start_time is None: | |
| self.start_time = time.time() | |
| # Calculate expected timestamp | |
| pts = self.frame_count | |
| time_base = fractions.Fraction(1, self.fps) | |
| try: | |
| # Try to get frame from queue with timeout | |
| frame_bgr = await asyncio.wait_for( | |
| self.frame_queue.get(), | |
| timeout=1.0 / self.fps # Wait up to one frame period | |
| ) | |
| self.last_frame = frame_bgr | |
| except asyncio.TimeoutError: | |
| # No frame available, use last frame or black | |
| if self.last_frame is not None: | |
| frame_bgr = self.last_frame | |
| else: | |
| # Create black frame | |
| frame_bgr = np.zeros((480, 640, 3), dtype=np.uint8) | |
| # Convert BGR to RGB for av | |
| frame_rgb = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB) | |
| # Create VideoFrame | |
| video_frame = VideoFrame.from_ndarray(frame_rgb, format="rgb24") | |
| video_frame.pts = pts | |
| video_frame.time_base = time_base | |
| self.frame_count += 1 | |
| return video_frame | |
| async def put_frame(self, frame: np.ndarray): | |
| """Add a frame to the queue.""" | |
| if self._running: | |
| try: | |
| # Non-blocking put, drop frame if queue is full | |
| self.frame_queue.put_nowait(frame) | |
| if self.frame_count < 5: | |
| print(f"[WebRTC Track] Frame queued, queue size: {self.frame_queue.qsize()}") | |
| except asyncio.QueueFull: | |
| # Drop oldest frame and add new one | |
| try: | |
| self.frame_queue.get_nowait() | |
| self.frame_queue.put_nowait(frame) | |
| print(f"[WebRTC Track] Frame dropped and replaced") | |
| except: | |
| pass | |
| def stop(self): | |
| """Stop the track.""" | |
| self._running = False | |
| class WebRTCManager: | |
| """ | |
| Manages WebRTC connections for streaming video. | |
| """ | |
| def __init__(self): | |
| self.connections = {} # session_id -> RTCPeerConnection | |
| self.video_tracks = {} # session_id -> MuseTalkVideoTrack | |
| self.connection_ready = {} # session_id -> asyncio.Event | |
| self.relay = MediaRelay() | |
| async def create_connection(self, session_id: str, fps: int = 30) -> RTCPeerConnection: | |
| """Create a new WebRTC connection with TURN servers for NAT traversal.""" | |
| pc = RTCPeerConnection(configuration=ICE_SERVERS) | |
| # Create video track | |
| video_track = MuseTalkVideoTrack(fps=fps) | |
| pc.addTrack(video_track) | |
| # Store references | |
| self.connections[session_id] = pc | |
| self.video_tracks[session_id] = video_track | |
| self.connection_ready[session_id] = asyncio.Event() | |
| # Handle connection state changes | |
| async def on_connectionstatechange(): | |
| print(f"[WebRTC] Connection state: {pc.connectionState}") | |
| if pc.connectionState == "connected": | |
| # Signal that connection is ready for streaming | |
| if session_id in self.connection_ready: | |
| self.connection_ready[session_id].set() | |
| print(f"[WebRTC] Connection ready for streaming: {session_id}") | |
| elif pc.connectionState == "failed" or pc.connectionState == "closed": | |
| # Only close if session still exists | |
| if session_id in self.connections: | |
| await self.close_connection(session_id) | |
| async def on_iceconnectionstatechange(): | |
| print(f"[WebRTC] ICE connection state: {pc.iceConnectionState}") | |
| async def on_icegatheringstatechange(): | |
| print(f"[WebRTC] ICE gathering state: {pc.iceGatheringState}") | |
| async def on_signalingstatechange(): | |
| print(f"[WebRTC] Signaling state: {pc.signalingState}") | |
| return pc | |
| async def handle_offer(self, session_id: str, sdp: str, type: str, fps: int = 30) -> dict: | |
| """ | |
| Handle incoming SDP offer from client. | |
| Returns answer SDP. | |
| """ | |
| # Create connection if not exists | |
| if session_id not in self.connections: | |
| pc = await self.create_connection(session_id, fps) | |
| else: | |
| pc = self.connections[session_id] | |
| # Set remote description (offer from client) | |
| offer = RTCSessionDescription(sdp=sdp, type=type) | |
| await pc.setRemoteDescription(offer) | |
| # Create answer | |
| answer = await pc.createAnswer() | |
| await pc.setLocalDescription(answer) | |
| return { | |
| "sdp": pc.localDescription.sdp, | |
| "type": pc.localDescription.type | |
| } | |
| async def wait_for_connection(self, session_id: str, timeout: float = 10.0) -> bool: | |
| """Wait for WebRTC connection to be established.""" | |
| if session_id not in self.connection_ready: | |
| print(f"[WebRTC] No connection event for {session_id}") | |
| return False | |
| try: | |
| await asyncio.wait_for( | |
| self.connection_ready[session_id].wait(), | |
| timeout=timeout | |
| ) | |
| print(f"[WebRTC] Connection established for {session_id}") | |
| return True | |
| except asyncio.TimeoutError: | |
| print(f"[WebRTC] Connection timeout for {session_id}") | |
| return False | |
| def is_connected(self, session_id: str) -> bool: | |
| """Check if WebRTC connection is established.""" | |
| if session_id in self.connections: | |
| return self.connections[session_id].connectionState == "connected" | |
| return False | |
| async def send_frame(self, session_id: str, frame: np.ndarray): | |
| """Send a frame to the specified session.""" | |
| if session_id in self.video_tracks: | |
| await self.video_tracks[session_id].put_frame(frame) | |
| async def close_connection(self, session_id: str): | |
| """Close a WebRTC connection.""" | |
| try: | |
| if session_id in self.video_tracks: | |
| self.video_tracks[session_id].stop() | |
| del self.video_tracks[session_id] | |
| if session_id in self.connections: | |
| pc = self.connections.pop(session_id) | |
| await pc.close() | |
| if session_id in self.connection_ready: | |
| del self.connection_ready[session_id] | |
| print(f"[WebRTC] Closed connection: {session_id}") | |
| except Exception as e: | |
| print(f"[WebRTC] Error closing connection {session_id}: {e}") | |
| def get_video_track(self, session_id: str) -> MuseTalkVideoTrack: | |
| """Get the video track for a session.""" | |
| return self.video_tracks.get(session_id) | |
| # Global manager instance | |
| webrtc_manager = WebRTCManager() | |