Skip to content

Commit 8bf84ad

Browse files
Merge pull request #1715 from roboflow/turn-server-sdk-config
Turn server sdk config
2 parents 9b1f065 + 15b6289 commit 8bf84ad

File tree

3 files changed

+89
-31
lines changed

3 files changed

+89
-31
lines changed

inference_sdk/config.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,17 @@
1111
EXECUTION_ID_HEADER = os.getenv("EXECUTION_ID_HEADER", "execution_id")
1212
PROCESSING_TIME_HEADER = os.getenv("PROCESSING_TIME_HEADER", "X-Processing-Time")
1313

14+
15+
ALL_ROBOFLOW_API_URLS = {
16+
"https://detect.roboflow.com",
17+
"https://outline.roboflow.com",
18+
"https://classify.roboflow.com",
19+
"https://infer.roboflow.com",
20+
"https://serverless.roboflow.com",
21+
"https://serverless.roboflow.one",
22+
}
23+
24+
1425
# WebRTC configuration
1526
WEBRTC_INITIAL_FRAME_TIMEOUT = float(os.getenv("WEBRTC_INITIAL_FRAME_TIMEOUT", "90.0"))
1627
WEBRTC_VIDEO_QUEUE_MAX_SIZE = int(os.getenv("WEBRTC_VIDEO_QUEUE_MAX_SIZE", "8"))
@@ -26,6 +37,9 @@
2637
os.getenv("WEBRTC_VIDEO_UPLOAD_BUFFER_LIMIT", "262144")
2738
) # 256KB max buffered before backpressure
2839

40+
# Roboflow API base URL for TURN config and other services
41+
RF_API_BASE_URL = os.getenv("RF_API_BASE_URL", "https://api.roboflow.com")
42+
2943

3044
class InferenceSDKDeprecationWarning(Warning):
3145
"""Class used for warning of deprecated features in the Inference SDK"""

inference_sdk/http/entities.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
from dataclasses_json import DataClassJsonMixin
77
from PIL import Image
88

9-
from inference_sdk.config import WORKFLOW_RUN_RETRIES_ENABLED
9+
from inference_sdk.config import ( # noqa: F401
10+
ALL_ROBOFLOW_API_URLS,
11+
WORKFLOW_RUN_RETRIES_ENABLED,
12+
)
1013
from inference_sdk.http.errors import ModelTaskTypeNotSupportedError
1114
from inference_sdk.http.utils.iterables import remove_empty_values
1215

@@ -21,15 +24,6 @@
2124
KEYPOINTS_DETECTION_TASK = "keypoint-detection"
2225
DEFAULT_MAX_INPUT_SIZE = 1024
2326

24-
ALL_ROBOFLOW_API_URLS = {
25-
"https://detect.roboflow.com",
26-
"https://outline.roboflow.com",
27-
"https://classify.roboflow.com",
28-
"https://infer.roboflow.com",
29-
"https://serverless.roboflow.com",
30-
"https://serverless.roboflow.one",
31-
}
32-
3327

3428
@dataclass(frozen=True)
3529
class ServerInfo(DataClassJsonMixin):

inference_sdk/webrtc/session.py

Lines changed: 71 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
import cv2
1818
import numpy as np
1919
import requests
20+
from aiortc import RTCConfiguration, RTCIceServer
2021

2122
from inference_sdk.config import (
23+
ALL_ROBOFLOW_API_URLS,
24+
RF_API_BASE_URL,
2225
WEBRTC_EVENT_LOOP_SHUTDOWN_TIMEOUT,
2326
WEBRTC_INITIAL_FRAME_TIMEOUT,
2427
WEBRTC_VIDEO_QUEUE_MAX_SIZE,
@@ -557,25 +560,72 @@ def _invoke_data_handler(
557560
)
558561
raise
559562

560-
async def _get_turn_config(self) -> Optional[dict]:
561-
"""Get TURN configuration from user-provided config.
563+
@staticmethod
564+
def _to_list(value: Any) -> List[Any]:
565+
"""Convert value to list if it is not already a list."""
566+
if isinstance(value, list):
567+
return value
568+
return [value]
569+
570+
async def _get_turn_config(self) -> Optional[RTCConfiguration]:
571+
"""Get TURN configuration from user-provided config or Roboflow API.
562572
563573
Priority order:
564574
1. User-provided config via StreamConfig.turn_server (highest priority)
565-
2. Skip TURN for localhost connections
566-
3. Return None if not provided
575+
2. Auto-fetch from Roboflow API for serverless connections
576+
3. Return None for non-serverless connections
567577
568578
Returns:
569579
TURN configuration dict or None
570580
"""
581+
turn_config = None
571582
# 1. Use user-provided config if available
572583
if self._config.turn_server:
584+
turn_config = self._config.turn_server
573585
logger.debug("Using user-provided TURN configuration")
574-
return self._config.turn_server
575586

576-
# 3. No TURN config provided
577-
logger.debug("No TURN configuration provided, proceeding without TURN server")
578-
return None
587+
# 2. Auto-fetch from Roboflow API for Roboflow-hosted connections
588+
elif self._api_url in ALL_ROBOFLOW_API_URLS:
589+
try:
590+
logger.debug(
591+
"Fetching TURN config from Roboflow API for serverless connection"
592+
)
593+
response = requests.get(
594+
f"{RF_API_BASE_URL}/webrtc_turn_config",
595+
params={"api_key": self._api_key},
596+
timeout=5,
597+
)
598+
response.raise_for_status()
599+
turn_config = response.json()
600+
logger.debug("Successfully fetched TURN config from Roboflow API")
601+
except Exception as e:
602+
logger.warning(f"Failed to fetch TURN config from Roboflow API: {e}")
603+
return None
604+
# standardize the TURN config to the iceServers format
605+
if turn_config and "iceServers" in turn_config:
606+
turn_config = RTCConfiguration(
607+
iceServers=[
608+
RTCIceServer(
609+
urls=WebRTCSession._to_list(server.get("urls", [])),
610+
username=server.get("username"),
611+
credential=server.get("credential"),
612+
)
613+
for server in turn_config["iceServers"]
614+
]
615+
)
616+
logger.debug("Successfully converted TURN config to iceServers format")
617+
elif turn_config and "urls" in turn_config:
618+
turn_config = RTCConfiguration(
619+
iceServers=[
620+
RTCIceServer(
621+
urls=[turn_config["urls"]],
622+
username=turn_config["username"],
623+
credential=turn_config["credential"],
624+
)
625+
]
626+
)
627+
logger.debug("Successfully converted TURN config to iceServers format")
628+
return turn_config
579629

580630
def _handle_datachannel_video_frame(
581631
self, serialized_data: Any, metadata: Optional[VideoMetadata]
@@ -627,17 +677,7 @@ async def _init(self) -> None:
627677
# Fetch TURN configuration (auto-fetch or user-provided)
628678
turn_config = await self._get_turn_config()
629679

630-
# Create peer connection with TURN config if available
631-
configuration = None
632-
if turn_config:
633-
ice = RTCIceServer(
634-
urls=[turn_config.get("urls")],
635-
username=turn_config.get("username"),
636-
credential=turn_config.get("credential"),
637-
)
638-
configuration = RTCConfiguration(iceServers=[ice])
639-
640-
pc = RTCPeerConnection(configuration=configuration)
680+
pc = RTCPeerConnection(configuration=turn_config)
641681
relay = MediaRelay()
642682

643683
# Setup video receiver for frames from server
@@ -812,9 +852,19 @@ def _on_data_message(message: Any) -> None: # noqa: ANN401
812852
"data_output": self._config.data_output,
813853
}
814854

815-
# Add TURN config if available (auto-fetched or user-provided)
855+
# Add WebRTC config if available (auto-fetched or user-provided)
856+
# Server accepts webrtc_config with iceServers array format
816857
if turn_config:
817-
payload["webrtc_turn_config"] = turn_config
858+
payload["webrtc_config"] = {
859+
"iceServers": [
860+
{
861+
"urls": ice_server.urls,
862+
"username": ice_server.username,
863+
"credential": ice_server.credential,
864+
}
865+
for ice_server in turn_config.iceServers
866+
]
867+
}
818868

819869
# Add FPS if provided
820870
if self._config.declared_fps:

0 commit comments

Comments
 (0)