This commit is contained in:
+11
-1
@@ -340,6 +340,7 @@ class RTPMedia:
|
||||
self.sequence = 0
|
||||
self.timestamp = 0
|
||||
self.ssrc = uuid.uuid4().int & 0xFFFFFFFF
|
||||
self._next_send_at: float | None = None
|
||||
self._send_lock = threading.Lock()
|
||||
|
||||
@property
|
||||
@@ -399,11 +400,18 @@ class RTPMedia:
|
||||
)
|
||||
payload = pcm16_to_alaw(raw, input_rate_hz, 8000)
|
||||
sent = 0
|
||||
packet_interval_s = 0.02
|
||||
with self._send_lock:
|
||||
now = time.monotonic()
|
||||
if self._next_send_at is None or self._next_send_at < now - packet_interval_s:
|
||||
self._next_send_at = now
|
||||
for offset in range(0, len(payload), 160):
|
||||
frame = payload[offset : offset + 160]
|
||||
if not frame:
|
||||
continue
|
||||
delay = self._next_send_at - time.monotonic()
|
||||
if delay > 0:
|
||||
time.sleep(delay)
|
||||
header = struct.pack(
|
||||
"!BBHII",
|
||||
0x80,
|
||||
@@ -421,7 +429,9 @@ class RTPMedia:
|
||||
self.sequence += 1
|
||||
self.timestamp += 160
|
||||
sent += len(frame)
|
||||
time.sleep(0.02)
|
||||
self._next_send_at = max(
|
||||
self._next_send_at + packet_interval_s, time.monotonic()
|
||||
)
|
||||
return sent
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -10,6 +10,7 @@ import wave
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from typing import Any, cast
|
||||
from unittest.mock import patch
|
||||
|
||||
from agent_call.real_cell import (
|
||||
CellCallConfig,
|
||||
@@ -154,6 +155,35 @@ class RealCellTests(unittest.TestCase):
|
||||
self.assertEqual(fake_ari.record_requests, 0)
|
||||
self.assertNotIn("opening", order)
|
||||
|
||||
def test_rtp_send_keeps_packet_clock_across_frames(self) -> None:
|
||||
media = RTPMedia("127.0.0.1", 0)
|
||||
receiver = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
receiver.bind(("127.0.0.1", 0))
|
||||
media.peer = ("127.0.0.1", receiver.getsockname()[1])
|
||||
clock = [0.0]
|
||||
sleeps: list[float] = []
|
||||
|
||||
def monotonic() -> float:
|
||||
return clock[0]
|
||||
|
||||
def sleep(seconds: float) -> None:
|
||||
sleeps.append(seconds)
|
||||
clock[0] += seconds
|
||||
|
||||
try:
|
||||
with (
|
||||
patch("agent_call.real_cell.time.monotonic", monotonic),
|
||||
patch("agent_call.real_cell.time.sleep", sleep),
|
||||
):
|
||||
media.send_pcm16(bytes([0, 0]) * 160, 8000)
|
||||
media.send_pcm16(bytes([0, 0]) * 160, 8000)
|
||||
self.assertEqual(sleeps, [0.02])
|
||||
self.assertEqual(len(receiver.recvfrom(512)[0]), 172)
|
||||
self.assertEqual(len(receiver.recvfrom(512)[0]), 172)
|
||||
finally:
|
||||
media.close()
|
||||
receiver.close()
|
||||
|
||||
def test_pcma_decode_and_rtp_payload(self) -> None:
|
||||
media = RTPMedia("127.0.0.1", 0)
|
||||
receiver = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
|
||||
Reference in New Issue
Block a user