Files
facefusion/facefusion/rtc.py
T
2026-06-11 13:03:29 +02:00

264 lines
9.1 KiB
Python

import ctypes
from typing import List, Optional
from facefusion.libraries import datachannel as datachannel_module
from facefusion.types import AudioCodec, BitRate, Buffer, MediaDirection, PeerConnection, RtcAudioTrack, RtcPeer, RtcTrackInit, RtcVideoTrack, SdpAnswer, SdpOffer, VideoCodec
def create_peer_connection() -> PeerConnection:
datachannel_library = datachannel_module.create_static_library()
rtc_configuration = datachannel_module.define_rtc_configuration()
rtc_configuration.enableIceUdpMux = True
rtc_configuration.forceMediaTransport = True
rtc_configuration.disableAutoNegotiation = True
return datachannel_library.rtcCreatePeerConnection(ctypes.byref(rtc_configuration))
def create_sdp_offer(peer_connection : PeerConnection) -> Optional[SdpOffer]:
datachannel_library = datachannel_module.create_static_library()
datachannel_library.rtcSetLocalDescription(peer_connection, b'offer')
sdp_buffer = ctypes.create_string_buffer(8192)
if datachannel_library.rtcGetLocalDescription(peer_connection, sdp_buffer, 8192):
return sdp_buffer.value.decode()
return None
def create_sdp_answer(peer_connection : PeerConnection) -> Optional[SdpAnswer]:
datachannel_library = datachannel_module.create_static_library()
datachannel_library.rtcSetLocalDescription(peer_connection, b'answer')
sdp_buffer = ctypes.create_string_buffer(8192)
if datachannel_library.rtcGetLocalDescription(peer_connection, sdp_buffer, 8192):
return sdp_buffer.value.decode()
return None
def set_remote_description(peer_connection : PeerConnection, sdp_offer : SdpOffer) -> None:
datachannel_library = datachannel_module.create_static_library()
datachannel_library.rtcSetRemoteDescription(peer_connection, sdp_offer.encode(), b'offer')
return None
def send_video(rtc_peer : RtcPeer, video_buffer : Buffer, video_timestamp : int) -> None:
datachannel_library = datachannel_module.create_static_library()
if rtc_peer.get('video'):
video_track = rtc_peer.get('video').get('sender_track')
if datachannel_library.rtcIsOpen(video_track):
video_total = len(video_buffer)
datachannel_library.rtcSetTrackRtpTimestamp(video_track, video_timestamp)
datachannel_library.rtcSendMessage(video_track, video_buffer, video_total)
return None
def send_audio(rtc_peer : RtcPeer, audio_buffer : Buffer, audio_timestamp : int) -> None:
datachannel_library = datachannel_module.create_static_library()
if rtc_peer.get('audio'):
audio_track = rtc_peer.get('audio').get('sender_track')
if datachannel_library.rtcIsOpen(audio_track):
audio_total = len(audio_buffer)
datachannel_library.rtcSetTrackRtpTimestamp(audio_track, audio_timestamp)
datachannel_library.rtcSendMessage(audio_track, audio_buffer, audio_total)
return None
def delete_peers(rtc_peers : List[RtcPeer]) -> None:
datachannel_library = datachannel_module.create_static_library()
for rtc_peer in rtc_peers:
peer_connection = rtc_peer.get('peer_connection')
if peer_connection:
datachannel_library.rtcDeletePeerConnection(peer_connection)
return None
def add_audio_track(peer_connection : PeerConnection, media_direction : MediaDirection, audio_codec : AudioCodec, payload_type : int) -> RtcAudioTrack:
datachannel_library = datachannel_module.create_static_library()
audio_track_init = create_audio_track_init(media_direction, audio_codec, payload_type)
audio_track = datachannel_library.rtcAddTrackEx(peer_connection, audio_track_init)
if media_direction == 'sendonly':
audio_packetizer = datachannel_module.define_rtc_packetizer_init()
audio_packetizer.ssrc = 43
audio_packetizer.cname = b'audio'
audio_packetizer.payloadType = payload_type
audio_packetizer.clockRate = 48000
if audio_codec == 'opus':
datachannel_library.rtcSetOpusPacketizer(audio_track, ctypes.byref(audio_packetizer))
datachannel_library.rtcChainRtcpSrReporter(audio_track)
if media_direction == 'recvonly':
audio_depacketizer = datachannel_module.define_rtc_packetizer_init()
audio_depacketizer.ssrc = 0
audio_depacketizer.cname = b'audio'
audio_depacketizer.payloadType = payload_type
audio_depacketizer.clockRate = 48000
if audio_codec == 'opus':
datachannel_library.rtcSetOpusDepacketizer(audio_track, ctypes.byref(audio_depacketizer))
datachannel_library.rtcChainRtcpReceivingSession(audio_track)
return audio_track
def add_video_track(peer_connection : PeerConnection, media_direction : MediaDirection, video_codec : VideoCodec, payload_type : int) -> RtcVideoTrack:
datachannel_library = datachannel_module.create_static_library()
video_track_init = create_video_track_init(media_direction, video_codec, payload_type)
video_track = datachannel_library.rtcAddTrackEx(peer_connection, video_track_init)
if media_direction == 'sendonly':
video_packetizer = datachannel_module.define_rtc_packetizer_init()
video_packetizer.ssrc = 42
video_packetizer.cname = b'video'
video_packetizer.payloadType = payload_type
video_packetizer.clockRate = 90000
video_packetizer.maxFragmentSize = 1200
if video_codec == 'av1':
video_packetizer.obuPacketization = 1
datachannel_library.rtcSetAV1Packetizer(video_track, ctypes.byref(video_packetizer))
if video_codec == 'vp8':
datachannel_library.rtcSetVP8Packetizer(video_track, ctypes.byref(video_packetizer))
if video_codec == 'vp9':
datachannel_library.rtcSetVP9Packetizer(video_track, ctypes.byref(video_packetizer))
datachannel_library.rtcChainRtcpSrReporter(video_track)
datachannel_library.rtcChainRtcpNackResponder(video_track, 512)
if media_direction == 'recvonly':
if video_codec == 'av1':
datachannel_library.rtcSetAV1Depacketizer(video_track, 1)
if video_codec == 'vp8':
video_depacketizer = datachannel_module.define_rtc_packetizer_init()
video_depacketizer.ssrc = 0
video_depacketizer.cname = b'video'
video_depacketizer.payloadType = payload_type
video_depacketizer.clockRate = 90000
datachannel_library.rtcSetVP8Depacketizer(video_track, ctypes.byref(video_depacketizer))
if video_codec == 'vp9':
video_depacketizer = datachannel_module.define_rtc_packetizer_init()
video_depacketizer.ssrc = 0
video_depacketizer.cname = b'video'
video_depacketizer.payloadType = payload_type
video_depacketizer.clockRate = 90000
datachannel_library.rtcSetVP9Depacketizer(video_track, ctypes.byref(video_depacketizer))
datachannel_library.rtcChainRtcpReceivingSession(video_track)
return video_track
def create_audio_track_init(media_direction : MediaDirection, audio_codec : AudioCodec, payload_type : int) -> RtcTrackInit:
track_init = datachannel_module.define_rtc_track_init()
track_init.name = b'audio'
track_init.payloadType = payload_type
if media_direction == 'sendonly':
track_init.direction = 1
track_init.mid = b'3'
track_init.ssrc = 43
if media_direction == 'recvonly':
track_init.direction = 2
track_init.mid = b'2'
track_init.ssrc = 45
if media_direction == 'sendrecv':
track_init.direction = 3
track_init.mid = b'1'
track_init.ssrc = 43
if audio_codec == 'opus':
track_init.codec = 128
return ctypes.byref(track_init)
def create_video_track_init(media_direction : MediaDirection, video_codec : VideoCodec, payload_type : int) -> RtcTrackInit:
track_init = datachannel_module.define_rtc_track_init()
track_init.name = b'video'
track_init.payloadType = payload_type
if media_direction == 'sendonly':
track_init.direction = 1
track_init.mid = b'1'
track_init.ssrc = 42
if media_direction == 'recvonly':
track_init.direction = 2
track_init.mid = b'0'
track_init.ssrc = 44
if media_direction == 'sendrecv':
track_init.direction = 3
track_init.mid = b'0'
track_init.ssrc = 42
if video_codec == 'av1':
track_init.codec = 4
if video_codec == 'vp8':
track_init.codec = 1
if video_codec == 'vp9':
track_init.codec = 2
return ctypes.byref(track_init)
def get_payload_type(sdp_offer : SdpOffer, codec : AudioCodec | VideoCodec) -> int:
datachannel_library = datachannel_module.create_static_library()
payload_type_buffer = (ctypes.c_int * 16)()
payload_type_total = datachannel_library.rtcGetPayloadTypesForCodec(sdp_offer.encode(), codec.lower().encode(), payload_type_buffer, 16)
if payload_type_total:
return payload_type_buffer[0]
return 0
@ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_uint, ctypes.c_void_p)
def handle_sender_bitrate(_ : int, bitrate : BitRate, pointer : int) -> None:
ctypes.cast(pointer, ctypes.POINTER(ctypes.c_uint)).contents.value = bitrate // 1000
def wire_sender_bitrate(video_track : RtcVideoTrack, bitrate : ctypes.c_uint) -> None:
datachannel_library = datachannel_module.create_static_library()
datachannel_library.rtcSetUserPointer(video_track, ctypes.cast(ctypes.byref(bitrate), ctypes.c_void_p))
datachannel_library.rtcChainRembHandler(video_track, handle_sender_bitrate)
def adapt_receiver_bitrate(rtc_peer : RtcPeer, bitrate : BitRate) -> None:
datachannel_library = datachannel_module.create_static_library()
receiver_track = rtc_peer.get('video').get('receiver_track')
rtc_peer.get('receiver_bitrate').value = bitrate
datachannel_library.rtcRequestBitrate(receiver_track, bitrate * 1000)
def clear_bitrate(rtc_peer : RtcPeer) -> None:
rtc_peer.get('sender_bitrate').value = 0
rtc_peer.get('receiver_bitrate').value = 0