IQ.Pilot Release Commit @ 24db8ae

This commit is contained in:
IQ.Lvbs CI [bot]
2026-08-25 22:13:17 -05:00
parent 2f0ec679ec
commit 31a37f5a3c
67 changed files with 6134 additions and 137 deletions

View File

@@ -83,6 +83,7 @@ class WebRTCBaseStream(abc.ABC):
self._audio_track_state: List[Tuple[Track, Any, RtpPacketizationConfig]] = []
self._receiver_reports: Dict[str, RtcpReceiverReport] = {}
self._receiver_report_tracks: Dict[str, Tuple[Track, int]] = {}
self._negotiated_tracks: Dict[str, Track] = {}
self.incoming_media_ready_event = asyncio.Event()
self.messaging_channel_ready_event = asyncio.Event()
@@ -206,7 +207,11 @@ class WebRTCBaseStream(abc.ABC):
def _add_producer_tracks(self, remote_sdp: Optional[str] = None):
for track in self.outgoing_video_tracks:
media, ssrc, payload_type, cname = self._make_video_media(track, remote_sdp or "")
rtc_track = self.peer_connection.add_track(media)
rtc_track = self._negotiated_tracks.get(media.mid())
if rtc_track is not None:
rtc_track.set_description(media)
else:
rtc_track = self.peer_connection.add_track(media)
rtp_config = RtpPacketizationConfig(ssrc, cname, payload_type, H264RtpPacketizer.CLOCK_RATE)
rtp_config.start_timestamp = random.randint(0, 0xFFFFFFFF)
@@ -232,6 +237,8 @@ class WebRTCBaseStream(abc.ABC):
# same MID, which leaves libdatachannel stuck in signalling/ICE negotiation.
rtc_track = self.incoming_audio_tracks[0]
negotiated_media = rtc_track.description()
negotiated_media.clear_ssrcs()
negotiated_media.remove_attribute("msid")
negotiated_media.add_ssrc(ssrc, cname, "audio", "audio")
rtc_track.set_description(negotiated_media)
else:
@@ -276,6 +283,7 @@ class WebRTCBaseStream(abc.ABC):
def _on_incoming_track(self, track: Track):
self._log_debug("got track: %s", track.mid())
self._negotiated_tracks[track.mid()] = track
media_type = track.description().type()
if media_type == "audio":
if self.expected_incoming_audio:
@@ -478,6 +486,7 @@ class WebRTCBaseStream(abc.ABC):
self._audio_track_state.clear()
self._receiver_reports.clear()
self._receiver_report_tracks.clear()
self._negotiated_tracks.clear()
@abc.abstractmethod
async def start(self) -> RTCSessionDescription:

View File

@@ -19,8 +19,17 @@ async def test_native_video_audio_and_data_channel():
async def connect(offer):
nonlocal answer_session
answer_session = StreamSession(offer.sdp, offer.video, [], [], [], debug_mode=True)
browser_offer = offer.sdp.replace("profile-level-id=42e01f", "profile-level-id=640c1f", 1)
audio_offset = browser_offer.index("m=audio")
browser_offer = browser_offer[:audio_offset] + browser_offer[audio_offset:].replace(
"a=recvonly",
"a=sendrecv\r\na=msid:ios-microphone ios-audio-track\r\na=ssrc:123456 cname:ios-audio\r\na=ssrc:123456 msid:ios-microphone ios-audio-track",
1,
)
answer_session = StreamSession(browser_offer, offer.video, [], [], [], debug_mode=True)
answer = await answer_session.get_answer()
assert not any(line.startswith("m=video 0 ") for line in answer.sdp.splitlines())
assert answer_session.stream._track_state[0][0] is answer_session.stream._negotiated_tracks["road"]
answer_session.start()
return RTCSessionDescription(answer.sdp, answer.type)
@@ -56,9 +65,16 @@ async def test_native_duplex_audio_negotiation():
async def connect(offer):
nonlocal answer_session
sendrecv_offer = offer.sdp.replace("a=recvonly", "a=sendrecv", 1)
sendrecv_offer = offer.sdp.replace(
"a=recvonly",
"a=sendrecv\r\na=msid:ios-microphone ios-audio-track\r\na=ssrc:123456 cname:ios-audio\r\na=ssrc:123456 msid:ios-microphone ios-audio-track",
1,
)
answer_session = StreamSession(sendrecv_offer, offer.video, [], [], [], debug_mode=True)
answer = await answer_session.get_answer()
assert "ios-microphone" not in answer.sdp
assert "ios-audio-track" not in answer.sdp
assert answer.sdp.count("a=msid:audio audio") == 1
answer_session.start()
return RTCSessionDescription(answer.sdp, answer.type)