Skip to content

Commit 0837df6

Browse files
authored
Merge pull request #267 from opentok/feature/support-trasnport-header-audio-connector/VIDMR-1483
[VIDMR-1483] Support for AudioTransport
2 parents 2cf018f + 0e955c3 commit 0837df6

2 files changed

Lines changed: 167 additions & 1 deletion

File tree

opentok/opentok.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2010,13 +2010,25 @@ def connect_audio_to_websocket(
20102010
List 'streams' Optional: A list of stream IDs for the OpenTok streams you want to include in the WebSocket audio. If you omit this property, all streams in the session will be included.
20112011
Dictionary 'headers' Optional: An object of key-value pairs of headers to be sent to your WebSocket server with each message, with a maximum length of 512 bytes.
20122012
Boolean 'bidirectional' Optional: If true, enables bidirectional audio streaming over the WebSocket connection.
2013+
Dictionary 'audio_transport' Optional: Configuration for audio transport format.
2014+
String 'transport': The transport type ('binary' or 'json').
2015+
String 'encoding' Optional: The encoding type (required for 'json' transport, e.g. 'base64').
2016+
String 'audio_field' Optional: The JSON field name for outbound audio data.
2017+
String 'receive_audio_field' Optional: The JSON field name for inbound audio data.
2018+
Dictionary 'static_fields' Optional: Static fields included in every outbound JSON message.
20132019
"""
20142020
self.validate_websocket_options(websocket_options)
20152021

2022+
ws_opts = dict(websocket_options)
2023+
if "audio_transport" in ws_opts:
2024+
audio_transport = ws_opts.pop("audio_transport")
2025+
filtered = {k: v for k, v in audio_transport.items() if v is not None}
2026+
ws_opts["audioTransport"] = json.dumps(filtered, separators=(',', ':'))
2027+
20162028
payload = {
20172029
"sessionId": session_id,
20182030
"token": opentok_token,
2019-
"websocket": websocket_options,
2031+
"websocket": ws_opts,
20202032
}
20212033

20222034
logger.debug(
@@ -2065,6 +2077,18 @@ def validate_websocket_options(self, options):
20652077
if not isinstance(options["bidirectional"], bool):
20662078
raise InvalidWebSocketOptionsError("'bidirectional' must be a boolean if provided.")
20672079

2080+
if "audio_transport" in options:
2081+
audio_transport = options["audio_transport"]
2082+
if not isinstance(audio_transport, dict):
2083+
raise InvalidWebSocketOptionsError("'audio_transport' must be a dictionary if provided.")
2084+
if "transport" not in audio_transport:
2085+
raise InvalidWebSocketOptionsError("'audio_transport' must include a 'transport' field.")
2086+
valid_transports = ("binary", "json")
2087+
if audio_transport["transport"] not in valid_transports:
2088+
raise InvalidWebSocketOptionsError(
2089+
f"'transport' must be one of {valid_transports}."
2090+
)
2091+
20682092
def start_captions(
20692093
self,
20702094
session_id: str,

tests/test_audio_connector.py

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,145 @@ def test_connect_audio_to_websocket_missing_uri_error(self):
165165
with self.assertRaises(InvalidWebSocketOptionsError) as context:
166166
self.opentok.connect_audio_to_websocket(self.session_id, self.token, websocket_options)
167167
self.assertTrue("Provide a WebSocket URI." in str(context.exception))
168+
169+
@httpretty.activate
170+
def test_connect_audio_to_websocket_with_audio_transport_json(self):
171+
httpretty.register_uri(
172+
httpretty.POST,
173+
u(f"https://api.opentok.com/v2/project/{self.api_key}/connect"),
174+
body=self.response_body,
175+
status=200,
176+
content_type=u("application/json"),
177+
)
178+
179+
websocket_options = {
180+
"uri": "wss://service.com/ws-endpoint",
181+
"audio_transport": {
182+
"transport": "json",
183+
"encoding": "base64",
184+
},
185+
}
186+
187+
websocket_audio_connection = self.opentok.connect_audio_to_websocket(
188+
self.session_id, self.token, websocket_options
189+
)
190+
191+
if PY3:
192+
body = json.loads(httpretty.last_request().body.decode("utf-8"))
193+
194+
expect(body["websocket"]).to(have_key("audioTransport"))
195+
expect(body["websocket"]).not_to(have_key("audio_transport"))
196+
parsed = json.loads(body["websocket"]["audioTransport"])
197+
expect(parsed["transport"]).to(equal("json"))
198+
expect(parsed["encoding"]).to(equal("base64"))
199+
expect(websocket_audio_connection).to(be_a(WebSocketAudioConnection))
200+
201+
@httpretty.activate
202+
def test_connect_audio_to_websocket_with_audio_transport_full(self):
203+
httpretty.register_uri(
204+
httpretty.POST,
205+
u(f"https://api.opentok.com/v2/project/{self.api_key}/connect"),
206+
body=self.response_body,
207+
status=200,
208+
content_type=u("application/json"),
209+
)
210+
211+
websocket_options = {
212+
"uri": "wss://service.com/ws-endpoint",
213+
"audio_transport": {
214+
"transport": "json",
215+
"encoding": "base64",
216+
"audio_field": "data",
217+
"static_fields": {"event": "media"},
218+
},
219+
}
220+
221+
websocket_audio_connection = self.opentok.connect_audio_to_websocket(
222+
self.session_id, self.token, websocket_options
223+
)
224+
225+
if PY3:
226+
body = json.loads(httpretty.last_request().body.decode("utf-8"))
227+
228+
parsed = json.loads(body["websocket"]["audioTransport"])
229+
expect(parsed["transport"]).to(equal("json"))
230+
expect(parsed["encoding"]).to(equal("base64"))
231+
expect(parsed["audio_field"]).to(equal("data"))
232+
expect(parsed["static_fields"]).to(equal({"event": "media"}))
233+
expect(websocket_audio_connection).to(be_a(WebSocketAudioConnection))
234+
235+
@httpretty.activate
236+
def test_connect_audio_to_websocket_with_binary_transport(self):
237+
httpretty.register_uri(
238+
httpretty.POST,
239+
u(f"https://api.opentok.com/v2/project/{self.api_key}/connect"),
240+
body=self.response_body,
241+
status=200,
242+
content_type=u("application/json"),
243+
)
244+
245+
websocket_options = {
246+
"uri": "wss://service.com/ws-endpoint",
247+
"audio_transport": {
248+
"transport": "binary",
249+
},
250+
}
251+
252+
websocket_audio_connection = self.opentok.connect_audio_to_websocket(
253+
self.session_id, self.token, websocket_options
254+
)
255+
256+
if PY3:
257+
body = json.loads(httpretty.last_request().body.decode("utf-8"))
258+
259+
expect(body["websocket"]["audioTransport"]).to(equal('{"transport":"binary"}'))
260+
expect(websocket_audio_connection).to(be_a(WebSocketAudioConnection))
261+
262+
@httpretty.activate
263+
def test_connect_audio_to_websocket_without_audio_transport(self):
264+
httpretty.register_uri(
265+
httpretty.POST,
266+
u(f"https://api.opentok.com/v2/project/{self.api_key}/connect"),
267+
body=self.response_body,
268+
status=200,
269+
content_type=u("application/json"),
270+
)
271+
272+
websocket_options = {"uri": "wss://service.com/ws-endpoint"}
273+
274+
self.opentok.connect_audio_to_websocket(
275+
self.session_id, self.token, websocket_options
276+
)
277+
278+
if PY3:
279+
body = json.loads(httpretty.last_request().body.decode("utf-8"))
280+
281+
expect(body["websocket"]).not_to(have_key("audioTransport"))
282+
expect(body["websocket"]).not_to(have_key("audio_transport"))
283+
284+
def test_connect_audio_to_websocket_invalid_audio_transport_type(self):
285+
websocket_options = {
286+
"uri": "wss://service.com/ws-endpoint",
287+
"audio_transport": "invalid",
288+
}
289+
with self.assertRaises(InvalidWebSocketOptionsError) as context:
290+
self.opentok.connect_audio_to_websocket(self.session_id, self.token, websocket_options)
291+
self.assertTrue("'audio_transport' must be a dictionary if provided." in str(context.exception))
292+
293+
def test_connect_audio_to_websocket_audio_transport_missing_transport(self):
294+
websocket_options = {
295+
"uri": "wss://service.com/ws-endpoint",
296+
"audio_transport": {"encoding": "base64"},
297+
}
298+
with self.assertRaises(InvalidWebSocketOptionsError) as context:
299+
self.opentok.connect_audio_to_websocket(self.session_id, self.token, websocket_options)
300+
self.assertTrue("'audio_transport' must include a 'transport' field." in str(context.exception))
301+
302+
def test_connect_audio_to_websocket_audio_transport_invalid_transport(self):
303+
websocket_options = {
304+
"uri": "wss://service.com/ws-endpoint",
305+
"audio_transport": {"transport": "invalid"},
306+
}
307+
with self.assertRaises(InvalidWebSocketOptionsError) as context:
308+
self.opentok.connect_audio_to_websocket(self.session_id, self.token, websocket_options)
309+
self.assertTrue("'transport' must be one of" in str(context.exception))

0 commit comments

Comments
 (0)