From 928e5010e5ef805d302010eb4efe0bf01b7bb506 Mon Sep 17 00:00:00 2001 From: harisreedhar Date: Tue, 21 Apr 2026 23:10:19 +0530 Subject: [PATCH] Replace the mutable RTC_LIBRARY global with @lru_cache on create_static_rtc_library. Expose all RTC_CONFIGURATION fields as parameters with defaults on create_peer_connection. Split add_media_tracks into add_video_track and add_audio_track, each with configurable defaults and descriptive parameter names (sync_source_id, canonical_name). Use os.linesep for SDP media descriptions. Simplify init_ctypes to return None and remove the redundant null guard. --- facefusion/rtc.py | 132 +++++++++++++++++++++---------------- facefusion/rtc_bindings.py | 61 ++++++++--------- 2 files changed, 104 insertions(+), 89 deletions(-) diff --git a/facefusion/rtc.py b/facefusion/rtc.py index 470b7400..ded4bcba 100644 --- a/facefusion/rtc.py +++ b/facefusion/rtc.py @@ -1,7 +1,8 @@ import ctypes +import os import time from functools import lru_cache -from typing import Dict, List, Optional, Tuple +from typing import Dict, List, Optional from facefusion.common_helper import is_linux, is_macos, is_windows from facefusion.download import conditional_download_hashes, conditional_download_sources @@ -9,8 +10,6 @@ from facefusion.filesystem import resolve_relative_path from facefusion.rtc_bindings import RTC_CONFIGURATION, RTC_PACKETIZER_INIT, init_ctypes from facefusion.types import DownloadSet, RtcAudioTrack, RtcPeer, RtcVideoTrack -RTC_LIBRARY: Optional[ctypes.CDLL] = None - def resolve_binary_file() -> Optional[str]: if is_linux(): @@ -55,74 +54,92 @@ def pre_check() -> bool: return conditional_download_sources(download_set.get('sources')) -def get_rtc_library() -> Optional[ctypes.CDLL]: - global RTC_LIBRARY - - if RTC_LIBRARY: - return RTC_LIBRARY +@lru_cache +def create_static_rtc_library() -> Optional[ctypes.CDLL]: binary_path = create_static_download_set().get('sources').get('datachannel').get('path') if binary_path: rtc_library = ctypes.CDLL(binary_path) - if init_ctypes(rtc_library): - RTC_LIBRARY = rtc_library - - return RTC_LIBRARY + init_ctypes(rtc_library) + return rtc_library + return None -def create_peer_connection() -> int: # TODO: change method to have arguments with default values - rtc_library = get_rtc_library() - config = RTC_CONFIGURATION() - config.iceServers = None - config.iceServersCount = 0 - config.proxyServer = None - config.bindAddress = None - config.certificateType = 0 - config.iceTransportPolicy = 0 - config.enableIceTcp = False - config.enableIceUdpMux = True - config.disableAutoNegotiation = False - config.forceMediaTransport = True - config.portRangeBegin = 0 - config.portRangeEnd = 0 - config.mtu = 0 - config.maxMessageSize = 0 - return rtc_library.rtcCreatePeerConnection(ctypes.byref(config)) +def create_peer_connection(ice_servers : Optional[ctypes.Array[ctypes.c_char_p]] = None, ice_servers_count : int = 0, proxy_server : Optional[bytes] = None, bind_address : Optional[bytes] = None, certificate_type : int = 0, ice_transport_policy : int = 0, enable_ice_tcp : bool = False, enable_ice_udp_mux : bool = True, disable_auto_negotiation : bool = False, force_media_transport : bool = True, port_range_begin : int = 0, port_range_end : int = 0, max_packet_size : int = 0, max_message_size : int = 0) -> int: + rtc_library = create_static_rtc_library() + rtc_configuration = RTC_CONFIGURATION() + + rtc_configuration.iceServers = ice_servers + rtc_configuration.iceServersCount = ice_servers_count + rtc_configuration.proxyServer = proxy_server + rtc_configuration.bindAddress = bind_address + rtc_configuration.certificateType = certificate_type + rtc_configuration.iceTransportPolicy = ice_transport_policy + rtc_configuration.enableIceTcp = enable_ice_tcp + rtc_configuration.enableIceUdpMux = enable_ice_udp_mux + rtc_configuration.disableAutoNegotiation = disable_auto_negotiation + rtc_configuration.forceMediaTransport = force_media_transport + rtc_configuration.portRangeBegin = port_range_begin + rtc_configuration.portRangeEnd = port_range_end + rtc_configuration.mtu = max_packet_size + rtc_configuration.maxMessageSize = max_message_size + return rtc_library.rtcCreatePeerConnection(ctypes.byref(rtc_configuration)) -def add_media_tracks(peer_connection : int) -> Tuple[RtcVideoTrack, RtcAudioTrack]: # TODO: split into add_audio_track and add_video_track. add arguments with default values - rtc_library = get_rtc_library() - video_media_description = b'm=video 9 UDP/TLS/RTP/SAVPF 96\r\na=rtpmap:96 VP8/90000\r\na=sendonly\r\na=mid:0\r\na=rtcp-mux\r\n' - audio_media_description = b'm=audio 9 UDP/TLS/RTP/SAVPF 111\r\na=rtpmap:111 opus/48000/2\r\na=sendonly\r\na=mid:1\r\na=rtcp-mux\r\n' +def add_audio_track(peer_connection : int, sync_source_id : int = 43, canonical_name : bytes = b'audio', payload_type : int = 111, clock_rate : int = 48000) -> RtcAudioTrack: + rtc_library = create_static_rtc_library() + media_description = os.linesep.join( + [ + 'm=audio 9 UDP/TLS/RTP/SAVPF 111', + 'a=rtpmap:111 opus/48000/2', + 'a=sendonly', + 'a=mid:1', + 'a=rtcp-mux', + '' + ]).encode() - video_track = rtc_library.rtcAddTrack(peer_connection, video_media_description) - audio_track = rtc_library.rtcAddTrack(peer_connection, audio_media_description) - - video_packetizer = RTC_PACKETIZER_INIT() - video_packetizer.ssrc = 42 - video_packetizer.cname = b'video' - video_packetizer.payloadType = 96 - video_packetizer.clockRate = 90000 - video_packetizer.maxFragmentSize = 1200 - - rtc_library.rtcSetVP8Packetizer(video_track, ctypes.byref(video_packetizer)) - rtc_library.rtcChainRtcpSrReporter(video_track) - rtc_library.rtcChainRtcpNackResponder(video_track, 512) + audio_track = rtc_library.rtcAddTrack(peer_connection, media_description) audio_packetizer = RTC_PACKETIZER_INIT() - audio_packetizer.ssrc = 43 - audio_packetizer.cname = b'audio' - audio_packetizer.payloadType = 111 - audio_packetizer.clockRate = 48000 + audio_packetizer.ssrc = sync_source_id + audio_packetizer.cname = canonical_name + audio_packetizer.payloadType = payload_type + audio_packetizer.clockRate = clock_rate rtc_library.rtcSetOpusPacketizer(audio_track, ctypes.byref(audio_packetizer)) rtc_library.rtcChainRtcpSrReporter(audio_track) + return audio_track - return video_track, audio_track + +def add_video_track(peer_connection : int, sync_source_id : int = 42, canonical_name : bytes = b'video', payload_type : int = 96, clock_rate : int = 90000, max_fragment_size : int = 1200, nack_buffer_size : int = 512) -> RtcVideoTrack: + rtc_library = create_static_rtc_library() + media_description = os.linesep.join( + [ + 'm=video 9 UDP/TLS/RTP/SAVPF 96', + 'a=rtpmap:96 VP8/90000', + 'a=sendonly', + 'a=mid:0', + 'a=rtcp-mux', + '' + ]).encode() + + video_track = rtc_library.rtcAddTrack(peer_connection, media_description) + + video_packetizer = RTC_PACKETIZER_INIT() + video_packetizer.ssrc = sync_source_id + video_packetizer.cname = canonical_name + video_packetizer.payloadType = payload_type + video_packetizer.clockRate = clock_rate + video_packetizer.maxFragmentSize = max_fragment_size + + rtc_library.rtcSetVP8Packetizer(video_track, ctypes.byref(video_packetizer)) + rtc_library.rtcChainRtcpSrReporter(video_track) + rtc_library.rtcChainRtcpNackResponder(video_track, nack_buffer_size) + return video_track def negotiate_sdp(peer_connection : int, sdp_offer : str) -> Optional[str]: - rtc_library = get_rtc_library() + rtc_library = create_static_rtc_library() rtc_library.rtcSetRemoteDescription(peer_connection, sdp_offer.encode('utf-8'), b'offer') buffer_size = 16384 buffer_string = ctypes.create_string_buffer(buffer_size) @@ -138,7 +155,8 @@ def negotiate_sdp(peer_connection : int, sdp_offer : str) -> Optional[str]: def handle_whep_offer(peers : List[RtcPeer], sdp_offer : str) -> Optional[str]: peer_connection = create_peer_connection() - video_track, audio_track = add_media_tracks(peer_connection) + audio_track = add_audio_track(peer_connection) + video_track = add_video_track(peer_connection) local_sdp = negotiate_sdp(peer_connection, sdp_offer) if local_sdp: @@ -154,7 +172,7 @@ def handle_whep_offer(peers : List[RtcPeer], sdp_offer : str) -> Optional[str]: def send_to_peers(peers : List[RtcPeer], data : bytes) -> None: - rtc_library = get_rtc_library() + rtc_library = create_static_rtc_library() if peers: timestamp = int(time.monotonic() * 90000) & 0xFFFFFFFF @@ -172,7 +190,7 @@ def send_to_peers(peers : List[RtcPeer], data : bytes) -> None: def delete_peers(peers : List[RtcPeer]) -> None: - rtc_library = get_rtc_library() + rtc_library = create_static_rtc_library() for rtc_peer in peers: peer_connection_id = rtc_peer.get('peer_connection') @@ -184,7 +202,7 @@ def delete_peers(peers : List[RtcPeer]) -> None: def is_peer_connected(peers : List[RtcPeer]) -> bool: - rtc_library = get_rtc_library() + rtc_library = create_static_rtc_library() for rtc_peer in peers: video_track_id = rtc_peer.get('video_track') diff --git a/facefusion/rtc_bindings.py b/facefusion/rtc_bindings.py index eeea200a..e5b1a1fe 100644 --- a/facefusion/rtc_bindings.py +++ b/facefusion/rtc_bindings.py @@ -41,48 +41,45 @@ RTC_PACKETIZER_INIT = type('RtcPacketizerInit', (ctypes.Structure,), LOG_CB_TYPE = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p) -def init_ctypes(rtc_library : ctypes.CDLL) -> bool: - if rtc_library: - rtc_library.rtcInitLogger.argtypes = [ ctypes.c_int, LOG_CB_TYPE ] - rtc_library.rtcInitLogger.restype = None - rtc_library.rtcInitLogger(4, LOG_CB_TYPE(0)) +def init_ctypes(rtc_library : ctypes.CDLL) -> None: + rtc_library.rtcInitLogger.argtypes = [ ctypes.c_int, LOG_CB_TYPE ] + rtc_library.rtcInitLogger.restype = None + rtc_library.rtcInitLogger(4, LOG_CB_TYPE(0)) - rtc_library.rtcCreatePeerConnection.argtypes = [ ctypes.POINTER(RTC_CONFIGURATION) ] - rtc_library.rtcCreatePeerConnection.restype = ctypes.c_int + rtc_library.rtcCreatePeerConnection.argtypes = [ ctypes.POINTER(RTC_CONFIGURATION) ] + rtc_library.rtcCreatePeerConnection.restype = ctypes.c_int - rtc_library.rtcDeletePeerConnection.argtypes = [ ctypes.c_int ] - rtc_library.rtcDeletePeerConnection.restype = ctypes.c_int + rtc_library.rtcDeletePeerConnection.argtypes = [ ctypes.c_int ] + rtc_library.rtcDeletePeerConnection.restype = ctypes.c_int - rtc_library.rtcSetRemoteDescription.argtypes = [ ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p ] - rtc_library.rtcSetRemoteDescription.restype = ctypes.c_int + rtc_library.rtcSetRemoteDescription.argtypes = [ ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p ] + rtc_library.rtcSetRemoteDescription.restype = ctypes.c_int - rtc_library.rtcAddTrack.argtypes = [ ctypes.c_int, ctypes.c_char_p ] - rtc_library.rtcAddTrack.restype = ctypes.c_int + rtc_library.rtcAddTrack.argtypes = [ ctypes.c_int, ctypes.c_char_p ] + rtc_library.rtcAddTrack.restype = ctypes.c_int - rtc_library.rtcSendMessage.argtypes = [ ctypes.c_int, ctypes.c_void_p, ctypes.c_int ] - rtc_library.rtcSendMessage.restype = ctypes.c_int + rtc_library.rtcSendMessage.argtypes = [ ctypes.c_int, ctypes.c_void_p, ctypes.c_int ] + rtc_library.rtcSendMessage.restype = ctypes.c_int - rtc_library.rtcSetVP8Packetizer.argtypes = [ ctypes.c_int, ctypes.POINTER(RTC_PACKETIZER_INIT) ] - rtc_library.rtcSetVP8Packetizer.restype = ctypes.c_int + rtc_library.rtcSetVP8Packetizer.argtypes = [ ctypes.c_int, ctypes.POINTER(RTC_PACKETIZER_INIT) ] + rtc_library.rtcSetVP8Packetizer.restype = ctypes.c_int - rtc_library.rtcChainRtcpSrReporter.argtypes = [ ctypes.c_int ] - rtc_library.rtcChainRtcpSrReporter.restype = ctypes.c_int + rtc_library.rtcChainRtcpSrReporter.argtypes = [ ctypes.c_int ] + rtc_library.rtcChainRtcpSrReporter.restype = ctypes.c_int - rtc_library.rtcSetTrackRtpTimestamp.argtypes = [ ctypes.c_int, ctypes.c_uint32 ] - rtc_library.rtcSetTrackRtpTimestamp.restype = ctypes.c_int + rtc_library.rtcSetTrackRtpTimestamp.argtypes = [ ctypes.c_int, ctypes.c_uint32 ] + rtc_library.rtcSetTrackRtpTimestamp.restype = ctypes.c_int - rtc_library.rtcIsOpen.argtypes = [ ctypes.c_int ] - rtc_library.rtcIsOpen.restype = ctypes.c_bool + rtc_library.rtcIsOpen.argtypes = [ ctypes.c_int ] + rtc_library.rtcIsOpen.restype = ctypes.c_bool - rtc_library.rtcChainRtcpNackResponder.argtypes = [ ctypes.c_int, ctypes.c_uint ] - rtc_library.rtcChainRtcpNackResponder.restype = ctypes.c_int + rtc_library.rtcChainRtcpNackResponder.argtypes = [ ctypes.c_int, ctypes.c_uint ] + rtc_library.rtcChainRtcpNackResponder.restype = ctypes.c_int - rtc_library.rtcGetLocalDescription.argtypes = [ ctypes.c_int, ctypes.c_char_p, ctypes.c_int ] - rtc_library.rtcGetLocalDescription.restype = ctypes.c_int + rtc_library.rtcGetLocalDescription.argtypes = [ ctypes.c_int, ctypes.c_char_p, ctypes.c_int ] + rtc_library.rtcGetLocalDescription.restype = ctypes.c_int - rtc_library.rtcSetOpusPacketizer.argtypes = [ ctypes.c_int, ctypes.POINTER(RTC_PACKETIZER_INIT) ] - rtc_library.rtcSetOpusPacketizer.restype = ctypes.c_int + rtc_library.rtcSetOpusPacketizer.argtypes = [ ctypes.c_int, ctypes.POINTER(RTC_PACKETIZER_INIT) ] + rtc_library.rtcSetOpusPacketizer.restype = ctypes.c_int - return True - - return False + return None