infinicode 2.8.91 → 2.8.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1257,17 +1257,14 @@ class LiveKitPublisher:
|
|
|
1257
1257
|
# — until it finished, so a short "Hello friend!" greeting could be
|
|
1258
1258
|
# over and gone before we ever got a chance to subscribe to it.
|
|
1259
1259
|
if audio_enabled:
|
|
1260
|
-
self._tasks.append(asyncio.create_task(self.
|
|
1260
|
+
self._tasks.append(asyncio.create_task(self._audio_retry_loop()))
|
|
1261
1261
|
try:
|
|
1262
1262
|
await asyncio.wait_for(self._mic_streaming.wait(), timeout=5.0)
|
|
1263
|
-
await self.agent._report_pipeline(
|
|
1264
|
-
"microphone_published", "ok", "Microphone track published with live PCM frames"
|
|
1265
|
-
)
|
|
1266
1263
|
except asyncio.TimeoutError:
|
|
1267
1264
|
await self.agent._report_pipeline(
|
|
1268
1265
|
"microphone_published", "blocked", "Microphone track published but PCM capture did not start"
|
|
1269
1266
|
)
|
|
1270
|
-
|
|
1267
|
+
logger.warning("microphone PCM is not ready; keeping the session alive while capture retries")
|
|
1271
1268
|
|
|
1272
1269
|
if self._capture and self.video_source:
|
|
1273
1270
|
self._tasks.append(asyncio.create_task(self._video_loop()))
|
|
@@ -1596,7 +1593,29 @@ class LiveKitPublisher:
|
|
|
1596
1593
|
except Exception as e:
|
|
1597
1594
|
logger.debug(f"preview motion sampling failed: {e}")
|
|
1598
1595
|
|
|
1599
|
-
async def
|
|
1596
|
+
async def _audio_retry_loop(self) -> None:
|
|
1597
|
+
"""Keep microphone capture alive across transient ALSA ownership errors."""
|
|
1598
|
+
while not self._stop_event.is_set():
|
|
1599
|
+
try:
|
|
1600
|
+
await self._audio_loop()
|
|
1601
|
+
except asyncio.CancelledError:
|
|
1602
|
+
raise
|
|
1603
|
+
except Exception as e:
|
|
1604
|
+
logger.warning(
|
|
1605
|
+
f"audio_loop: capture failed on {self.agent.audio_capture_device}: {e}; retrying"
|
|
1606
|
+
)
|
|
1607
|
+
finally:
|
|
1608
|
+
mic = self._mic_capture
|
|
1609
|
+
self._mic_capture = None
|
|
1610
|
+
if mic is not None:
|
|
1611
|
+
await asyncio.to_thread(mic.stop)
|
|
1612
|
+
if not self._stop_event.is_set():
|
|
1613
|
+
try:
|
|
1614
|
+
await asyncio.wait_for(self._stop_event.wait(), timeout=1.0)
|
|
1615
|
+
except asyncio.TimeoutError:
|
|
1616
|
+
pass
|
|
1617
|
+
|
|
1618
|
+
async def _audio_loop(self) -> None:
|
|
1600
1619
|
assert self.audio_source is not None
|
|
1601
1620
|
mic = create_audio_capture(self.agent.audio_capture_device)
|
|
1602
1621
|
if mic is None:
|
|
@@ -1642,7 +1661,12 @@ class LiveKitPublisher:
|
|
|
1642
1661
|
data=chunk, sample_rate=48000, num_channels=1, samples_per_channel=samples_per_frame,
|
|
1643
1662
|
)
|
|
1644
1663
|
await self.audio_source.capture_frame(frame)
|
|
1645
|
-
self._mic_streaming.
|
|
1664
|
+
if not self._mic_streaming.is_set():
|
|
1665
|
+
self._mic_streaming.set()
|
|
1666
|
+
await self.agent._report_pipeline(
|
|
1667
|
+
"microphone_published", "ok",
|
|
1668
|
+
"Microphone track published with live PCM frames",
|
|
1669
|
+
)
|
|
1646
1670
|
p = audioop.max(chunk, 2)
|
|
1647
1671
|
_diag_peak = max(_diag_peak, p)
|
|
1648
1672
|
_diag_count += 1
|
|
@@ -1852,7 +1876,7 @@ class AlsaAudioCapture(AudioCapture):
|
|
|
1852
1876
|
"-f", "S16_LE", "-r", "48000", "-c", "1",
|
|
1853
1877
|
],
|
|
1854
1878
|
stdout=subprocess.PIPE,
|
|
1855
|
-
stderr=subprocess.
|
|
1879
|
+
stderr=subprocess.PIPE,
|
|
1856
1880
|
)
|
|
1857
1881
|
if self.process.stdout is None:
|
|
1858
1882
|
raise RuntimeError("arecord did not provide a PCM stream")
|
|
@@ -1865,7 +1889,10 @@ class AlsaAudioCapture(AudioCapture):
|
|
|
1865
1889
|
while len(self.buffer) < bytes_needed:
|
|
1866
1890
|
chunk = self.process.stdout.read(bytes_needed - len(self.buffer))
|
|
1867
1891
|
if not chunk:
|
|
1868
|
-
|
|
1892
|
+
detail = ""
|
|
1893
|
+
if self.process.stderr is not None:
|
|
1894
|
+
detail = self.process.stderr.read().decode("utf-8", errors="replace").strip()
|
|
1895
|
+
raise OSError(f"ALSA capture stopped on {self.alsa_device}: {detail or 'no PCM data'}")
|
|
1869
1896
|
self.buffer.extend(chunk)
|
|
1870
1897
|
data = bytes(self.buffer[:bytes_needed])
|
|
1871
1898
|
del self.buffer[:bytes_needed]
|
|
@@ -1883,6 +1910,7 @@ class AlsaAudioCapture(AudioCapture):
|
|
|
1883
1910
|
self.process.wait(timeout=2)
|
|
1884
1911
|
except Exception:
|
|
1885
1912
|
self.process.kill()
|
|
1913
|
+
self.process.wait(timeout=1)
|
|
1886
1914
|
|
|
1887
1915
|
|
|
1888
1916
|
class PyAudioCapture(AudioCapture):
|