Skip to content

Commit 132c8d4

Browse files
authored
Merge pull request #226 from opentok/add-broadcast-and-dial-options
Add broadcast and dial options
2 parents e502713 + 1f23e2d commit 132c8d4

9 files changed

+151
-77
lines changed

.bumpversion.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 3.6.1
2+
current_version = 3.7.0
33
commit = True
44
tag = False
55

CHANGES.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# Release v3.7.0
2+
- Added the `maxBitrate` parameter to the `Client.start_broadcast` method
3+
- Added the `hlsStatus` parameter to the `Broadcast object`
4+
- Added the `streams` parameter so specific streams can be chosen to be included in a SIP call when using the `Client.dial` method
5+
16
# Release v3.6.1
27
- Fixed broken `opentok.Client.add_archive_stream`, `opentok.Client.remove_archive_stream`, `opentok.Client.add_broadcast_stream` and `opentok.Client.remove_broadcast_stream` methods and tests
38
- Fixed `opentok.Endpoints.get_archive_stream` and `opentok.Endpoints.get_broadcast_stream` methods

README.rst

+16-8
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,8 @@ Your application server can disconnect a client from an OpenTok session by calli
386386
Working with SIP Interconnect
387387
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
388388

389-
You can connect your SIP platform to an OpenTok session, the audio from your end of the SIP call is added to the OpenTok session as an audio-only stream. The OpenTok Media Router mixes audio from other streams in the session and sends the mixed audio to your SIP endpoint.
389+
You can connect your SIP platform to an OpenTok session, the audio from your end of the SIP call is added to the OpenTok session as an audio-only stream.
390+
The OpenTok Media Router mixes audio from other streams in the session and sends the mixed audio to your SIP endpoint.
390391

391392
.. code:: python
392393
@@ -397,8 +398,7 @@ You can connect your SIP platform to an OpenTok session, the audio from your end
397398
# call the method with the required parameters
398399
sip_call = opentok.dial(session_id, token, sip_uri)
399400
400-
# the method also support aditional options to establish the sip call
401-
401+
# the method also supports aditional options to establish the sip call
402402
options = {
403403
'from': '[email protected]',
404404
'headers': {
@@ -408,7 +408,10 @@ You can connect your SIP platform to an OpenTok session, the audio from your end
408408
'username': 'username',
409409
'password': 'password'
410410
},
411-
'secure': True
411+
'secure': True,
412+
'video': True,
413+
'observeForceMute': True,
414+
'streams': ['stream-id-1', 'stream-id-2']
412415
}
413416
414417
# call the method with aditional options
@@ -438,8 +441,9 @@ The live streaming broadcast can target one HLS endpoint and up to five RTMP ser
438441
'stylesheet': 'the layout stylesheet (only used with type == custom)'
439442
},
440443
'maxDuration': 5400,
441-
'hasAudio': True
442-
'hasVideo': True
444+
'hasAudio': True,
445+
'hasVideo': True,
446+
'maxBitrate': 2000000,
443447
'outputs': {
444448
'hls': {},
445449
'rtmp': [{
@@ -466,6 +470,8 @@ You can specify the following broadcast resolutions:
466470
* "1920x1080" (FHD landscape)
467471
* "1080x1920" (FHD portrait)
468472

473+
You can specify a maximum bitrate between 100000 and 6000000.
474+
469475
.. code:: python
470476
471477
session_id = 'SESSIONID'
@@ -476,6 +482,7 @@ You can specify the following broadcast resolutions:
476482
'stylesheet': 'the layout stylesheet (only used with type == custom)'
477483
},
478484
'maxDuration': 5400,
485+
'maxBitrate': 2000000,
479486
'outputs': {
480487
'hls': {},
481488
'rtmp': [{
@@ -508,8 +515,9 @@ to ``False`` as required. These fields are ``True`` by default.
508515
'stylesheet': 'the layout stylesheet (only used with type == custom)'
509516
},
510517
'maxDuration': 5400,
511-
'hasAudio': True
512-
'hasVideo': False
518+
'hasAudio': True,
519+
'hasVideo': False,
520+
'maxBitrate': 2000000,
513521
'outputs': {
514522
'hls': {},
515523
'rtmp': [{

opentok/broadcast.py

+25-15
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class Broadcast(object):
2424
2525
:ivar resolution:
2626
The resolution of the broadcast (either "640x480", "1280x720", "1920x1080", "480x640", "720x1280", or "1920x1080").
27-
27+
2828
:ivar status:
2929
The status of the broadcast.
3030
@@ -34,29 +34,36 @@ class Broadcast(object):
3434
:ivar hasVideo:
3535
Whether the broadcast has video.
3636
37+
:ivar 'maxBitrate' optional:
38+
The maximum bitrate (bits per second) used by the broadcast.
39+
3740
:ivar broadcastUrls:
3841
An object containing details about the HLS and RTMP broadcasts.
39-
40-
If you specified an HLS endpoint, the object includes an hls property, which is set to the URL for the HLS broadcast.
41-
Note this HLS broadcast URL points to an index file, an .M3U8-formatted playlist that contains a list of URLs
42+
43+
If you specified an HLS endpoint, the object includes an hls property, which is set to the URL for the HLS broadcast.
44+
Note this HLS broadcast URL points to an index file, an .M3U8-formatted playlist that contains a list of URLs
4245
to .ts media segment files (MPEG-2 transport stream files).
4346
While the URLs of both the playlist index file and media segment files are provided as soon as the HTTP response
44-
is returned, these URLs should not be accessed until 15 - 20 seconds later,
45-
after the initiation of the HLS broadcast, due to the delay between the HLS broadcast and the live streams
46-
in the OpenTok session.
47-
See https://developer.apple.com/library/ios/technotes/tn2288/_index.html for more information about the playlist index
47+
is returned, these URLs should not be accessed until 15 - 20 seconds later,
48+
after the initiation of the HLS broadcast, due to the delay between the HLS broadcast and the live streams
49+
in the OpenTok session.
50+
See https://developer.apple.com/library/ios/technotes/tn2288/_index.html for more information about the playlist index
4851
file and media segment files for HLS.
4952
50-
If you specified RTMP stream endpoints, the object includes an rtmp property.
51-
This is an array of objects that include information on each of the RTMP streams.
52-
Each of these objects has the following properties: id (the ID you assigned to the RTMP stream),
53-
serverUrl (the server URL), streamName (the stream name), and status property (which is set to "connecting").
54-
You can call the OpenTok REST method to check for status updates for the broadcast:
53+
If you specified an HLS endpoint, the object will also include an "hlsStatus" property with
54+
information about the HLS broadcast. This will have one of the following values:
55+
["connecting", "ready", "live", "ended", "error"].
56+
57+
If you specified RTMP stream endpoints, the object includes an rtmp property.
58+
This is an array of objects that include information on each of the RTMP streams.
59+
Each of these objects has the following properties: id (the ID you assigned to the RTMP stream),
60+
serverUrl (the server URL), streamName (the stream name), and status property (which is set to "connecting").
61+
You can call the OpenTok REST method to check for status updates for the broadcast:
5562
https://tokbox.com/developer/rest/#get_info_broadcast
5663
5764
:ivar streamMode:
5865
Whether streams included in the broadcast are selected automatically
59-
("auto", the default) or manually ("manual").
66+
("auto", the default) or manually ("manual").
6067
6168
:ivar streams:
6269
A list of streams currently being broadcasted. This is only set for a broadcast with
@@ -71,6 +78,8 @@ def __init__(self, kwargs):
7178
self.updatedAt = kwargs.get("updatedAt")
7279
self.hasAudio = kwargs.get("hasAudio")
7380
self.hasVideo = kwargs.get("hasVideo")
81+
self.maxBitrate = kwargs.get("maxBitrate")
82+
self.maxDuration = kwargs.get("maxDuration")
7483
self.resolution = kwargs.get("resolution")
7584
self.status = kwargs.get("status")
7685
self.broadcastUrls = kwargs.get("broadcastUrls")
@@ -83,8 +92,9 @@ def json(self):
8392
"""
8493
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4)
8594

95+
8696
class BroadcastStreamModes(Enum):
87-
""""List of valid settings for the stream_mode parameter of the OpenTok.start_broadcast()
97+
""" "List of valid settings for the stream_mode parameter of the OpenTok.start_broadcast()
8898
method."""
8999

90100
auto = u("auto")

opentok/exceptions.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,13 @@ class BroadcastError(OpenTokException):
8989

9090
pass
9191

92+
9293
class DTMFError(OpenTokException):
9394
"""
9495
Indicates that one of the properties digits, session_id or connection_id is invalid
9596
"""
9697

98+
9799
class ArchiveStreamModeError(OpenTokException):
98100
"""
99101
Indicates that the archive is configured with a streamMode that does not support stream manipulation.
@@ -110,12 +112,18 @@ class BroadcastStreamModeError(OpenTokException):
110112

111113
class BroadcastHLSOptionsError(OpenTokException):
112114
"""
113-
Indicates that HLS options have been set incorrectly.
114-
115+
Indicates that HLS options have been set incorrectly.
116+
115117
dvr and lowLatency modes cannot both be set to true in a broadcast.
116118
"""
117119

118120

121+
class BroadcastOptionsError(OpenTokException):
122+
"""
123+
Indicates that broadcast options have been set incorrectly.
124+
"""
125+
126+
119127
class InvalidWebSocketOptionsError(OpenTokException):
120128
"""
121129
Indicates that the WebSocket options selected are invalid.

opentok/opentok.py

+40-39
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from .websocket_audio_connection import WebSocketAudioConnection
3636
from .exceptions import (
3737
ArchiveStreamModeError,
38+
BroadcastOptionsError,
3839
BroadcastHLSOptionsError,
3940
BroadcastStreamModeError,
4041
OpenTokException,
@@ -1170,7 +1171,7 @@ def set_archive_layout(
11701171
else:
11711172
raise RequestError("OpenTok server error.", response.status_code)
11721173

1173-
def dial(self, session_id, token, sip_uri, options=[]):
1174+
def dial(self, session_id, token, sip_uri, options={}):
11741175
"""
11751176
Use this method to connect a SIP platform to an OpenTok session. The audio from the end
11761177
of the SIP call is added to the OpenTok session as an audio-only stream. The OpenTok Media
@@ -1213,27 +1214,30 @@ def dial(self, session_id, token, sip_uri, options=[]):
12131214
in the OpenTok stream that is sent to the OpenTok session. The SIP client will receive a single
12141215
composed video of the published streams in the OpenTok session.
12151216
1216-
This is an example of what the payload POST data body could look like:
1217+
List 'streams': An array of stream IDs for streams to include in the SIP call.
1218+
If you do not set this property, all streams in the session are included in the call.
1219+
1220+
This is an example of what the payload POST data dictionary could look like:
12171221
12181222
{
12191223
"sessionId": "Your OpenTok session ID",
12201224
"token": "Your valid OpenTok token",
12211225
"sip": {
1222-
"uri": "sip:[email protected];transport=tls",
1223-
"from": "[email protected]",
1224-
"headers": {
1225-
"headerKey": "headerValue"
1226-
},
1226+
"uri": "sip:[email protected];transport=tls",
1227+
"from": "[email protected]",
1228+
"headers": {
1229+
"headerKey": "headerValue"
1230+
},
12271231
"auth": {
12281232
"username": "username",
12291233
"password": "password"
12301234
},
1231-
"secure": true|false,
1232-
"observeForceMute": true|false,
1233-
"video": true|false
1234-
}
1235+
"secure": True,
1236+
"video": True,
1237+
"observeForceMute": True,
1238+
"streams": ["stream-id-1", "stream-id-2"]
12351239
}
1236-
1240+
}
12371241
12381242
:rtype: A SipCall object, which contains data of the SIP call: id, connectionId and streamId.
12391243
This is what the response body should look like after returning with a status code of 200:
@@ -1246,29 +1250,9 @@ def dial(self, session_id, token, sip_uri, options=[]):
12461250
12471251
Note: Your response will have a different: id, connectionId and streamId
12481252
"""
1249-
payload = {"sessionId": session_id, "token": token, "sip": {"uri": sip_uri}}
1250-
observeForceMute = False
1251-
video = False
1252-
1253-
if "from" in options:
1254-
payload["sip"]["from"] = options["from"]
1255-
1256-
if "headers" in options:
1257-
payload["sip"]["headers"] = options["headers"]
12581253

1259-
if "auth" in options:
1260-
payload["sip"]["auth"] = options["auth"]
1261-
1262-
if "secure" in options:
1263-
payload["sip"]["secure"] = options["secure"]
1264-
1265-
if "observeForceMute" in options:
1266-
observeForceMute = True
1267-
payload["sip"]["observeForceMute"] = options["observeForceMute"]
1268-
1269-
if "video" in options:
1270-
video = True
1271-
payload["sip"]["video"] = options["video"]
1254+
payload = {"sessionId": session_id, "token": token, "sip": {"uri": sip_uri}}
1255+
payload.update(options)
12721256

12731257
endpoint = self.endpoints.dial_url()
12741258

@@ -1367,11 +1351,11 @@ def start_broadcast(
13671351
13681352
:param String session_id: The session ID of the OpenTok session you want to broadcast
13691353
1370-
:param Boolean optional hasAudio: Whether the stream is broadcast with audio.
1354+
:param Dictionary options, with the following properties:
13711355
1372-
:param Boolean optional hasVideo: Whether the stream is broadcast with video.
1356+
:param Boolean optional hasAudio: Whether the stream is broadcast with audio.
13731357
1374-
:param Dictionary options, with the following properties:
1358+
:param Boolean optional hasVideo: Whether the stream is broadcast with video.
13751359
13761360
Dictionary 'layout' optional: Specify this to assign the initial layout type for the
13771361
broadcast.
@@ -1392,6 +1376,9 @@ def start_broadcast(
13921376
set the maximum duration to a value from 60 (60 seconds) to 36000 (10 hours). The
13931377
default maximum duration is 4 hours (14,400 seconds)
13941378
1379+
Integer 'maxBitrate' optional: The maximum bitrate (bits per second) used by the broadcast.
1380+
Value must be between 100_000 and 6_000_000.
1381+
13951382
Dictionary 'outputs': This object defines the types of broadcast streams you want to
13961383
start (both HLS and RTMP). You can include HLS, RTMP, or both as broadcast streams.
13971384
If you include RTMP streaming, you can specify up to five target RTMP streams. For
@@ -1448,6 +1435,16 @@ def start_broadcast(
14481435
'HLS options "lowLatency" and "dvr" cannot both be set to "True".'
14491436
)
14501437

1438+
if "maxBitrate" in options:
1439+
if (
1440+
type(options["maxBitrate"]) != int
1441+
or options["maxBitrate"] < 100000
1442+
or options["maxBitrate"] > 6000000
1443+
):
1444+
raise BroadcastOptionsError(
1445+
"maxBitrate must be an integer between 100000 and 6000000."
1446+
)
1447+
14511448
payload = {"sessionId": session_id, "streamMode": stream_mode.value}
14521449

14531450
payload.update(options)
@@ -1578,7 +1575,9 @@ def add_broadcast_stream(
15781575
"Your broadcast is configured with a streamMode that does not support stream manipulation."
15791576
)
15801577
elif response.status_code == 409:
1581-
raise BroadcastError("The broadcast has already started for the session.")
1578+
raise BroadcastError(
1579+
"The broadcast has already started for the session."
1580+
)
15821581
else:
15831582
raise RequestError("An unexpected error occurred.", response.status_code)
15841583

@@ -1621,7 +1620,9 @@ def remove_broadcast_stream(
16211620
"Your broadcast is configured with a streamMode that does not support stream manipulation."
16221621
)
16231622
elif response.status_code == 409:
1624-
raise BroadcastError("The broadcast has already started for the session.")
1623+
raise BroadcastError(
1624+
"The broadcast has already started for the session."
1625+
)
16251626
else:
16261627
raise RequestError("OpenTok server error.", response.status_code)
16271628

opentok/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# see: http://legacy.python.org/dev/peps/pep-0440/#public-version-identifiers
2-
__version__ = "3.6.1"
2+
__version__ = "3.7.0"
33

0 commit comments

Comments
 (0)