18 | | Created | 19 |Duration | 20 |Status | 21 ||
---|---|---|---|---|
28 | {% if item.status == "available" and item.url is defined and item.url|length > 0 %} 29 | 30 | {% endif %} 31 | {{ item.name|default("Untitled") }} 32 | {% if item.status == "available" and item.url is defined and item.url|length > 0 %} 33 | 34 | {% endif %} 35 | | 36 |{{ (item.createdAt//1000)|date }} | 37 |{{ item.duration }} seconds | 38 |{{ item.status }} | 39 |40 | {% if item.status == "available" %} 41 | Delete 42 | {% else %} 43 | 44 | {% endif %} 45 | | 46 |
53 | There are no archives currently. Try making one in the host view. 54 |
55 | {% endif %} 56 |53 | Click Start archiving to begin archiving this session. 54 | All publishers in the session will be included, and all publishers that 55 | join the session will be included as well. 56 |
57 |58 | Click Stop archiving to end archiving this session. 59 | You can then go to past archives to 60 | view your archive (once its status changes to available). 61 |
62 |When | 66 |You will see | 67 |
---|---|
Archiving is started | 72 |![]() |
73 |
Archiving remains on | 76 |![]() |
77 |
Archiving is stopped | 80 |![]() |
81 |
17 | Everyone who joins either the Host View or Participant View 18 | joins a single OpenTok session. Anyone with the Host View 19 | open can click Start Archive or Stop Archive to control 20 | recording of the entire session. 21 |
22 |36 | Click through to Past Archives to see examples of using the 37 | Archiving REST API to list archives showing status (started, 38 | stopped, available) and playback (for available archives). 39 |
40 |When | 29 |You will see | 30 |
---|---|
Archiving is started | 35 |![]() |
36 |
Archiving remains on | 39 |![]() |
40 |
Archiving is stopped | 43 |![]() |
44 |
false
).
21 | *
22 | * @property bool $hasAudio
23 | * Whether the archive has an audio track (true
) or not (false
).
24 | *
25 | * @property string $id
26 | * The archive ID.
27 | *
28 | * @property string $name
29 | * The name of the archive. If no name was provided when the archive was created, this is set
30 | * to null.
31 | *
32 | * @property string $outputMode
33 | * The name of the archive. If no name was provided when the archive was created, this is set
34 | * to null.
35 | *
36 | * @property string $partnerId
37 | * The API key associated with the archive.
38 | *
39 | * @property string $reason
40 | * For archives with the status "stopped" or "failed", this string describes the reason
41 | * the archive stopped (such as "maximum duration exceeded") or failed.
42 | *
43 | * @property string $resolution
44 | * The resolution of the archive, either "640x480" (SD landscape, the default), "1280x720" (HD landscape),
45 | * "1920x1080" (FHD landscape), "480x640" (SD portrait), "720x1280" (HD portrait), or "1080x1920" (FHD portrait).
46 | * You may want to use a portrait aspect ratio for archives that include video streams from mobile devices (which often use the portrait aspect ratio).
47 | *
48 | * @property string $sessionId
49 | * The session ID of the OpenTok session associated with this archive.
50 | *
51 | * @property string $multiArchiveTag
52 | * Whether Multiple Archive is switched on, which will be a unique string for each simultaneous archive of an ongoing session.
53 | * See https://tokbox.com/developer/guides/archiving/#simultaneous-archives for more information.
54 | *
55 | * @property string $size
56 | * The size of the MP4 file. For archives that have not been generated, this value is set to 0.
57 | *
58 | * @property string $streamMode
59 | * Whether streams included in the archive are selected automatically (StreamMode.AUTO
) or
60 | * manually (StreamMode.MANUAL
). When streams are selected automatically (StreamMode.AUTO
),
61 | * all streams in the session can be included in the archive. When streams are selected manually
62 | * (StreamMode.MANUAL
), you specify streams to be included based on calls to the
63 | * Archive.addStreamToArchive()
and Archive.removeStreamFromArchive()
methods.
64 | * With manual mode, you can specify whether a stream's audio, video, or both are included in the
65 | * archive. In both automatic and manual modes, the archive composer includes streams based on
66 | * stream
67 | * prioritization rules.
68 | *
69 | * @property string $status
70 | * The status of the archive, which can be one of the following:
71 | *
72 | * 198 | * Archives automatically stop recording after 120 minutes or when all clients have 199 | * disconnected from the session being archived. 200 | * 201 | * @throws Exception\ArchiveException The archive is not being recorded. 202 | */ 203 | public function stop() 204 | { 205 | if ($this->isDeleted) { 206 | // TODO: throw an logic error about not being able to stop an archive thats deleted 207 | } 208 | 209 | $archiveData = $this->client->stopArchive($this->data['id']); 210 | 211 | try { 212 | Validators::validateArchiveData($archiveData); 213 | } catch (InvalidArgumentException $e) { 214 | throw new ArchiveUnexpectedValueException('The archive JSON returned after stopping was not valid', null, $e); 215 | } 216 | 217 | $this->data = $archiveData; 218 | return $this; 219 | } 220 | 221 | /** 222 | * Deletes an OpenTok archive. 223 | *
224 | * You can only delete an archive which has a status of "available", "uploaded", or "deleted". 225 | * Deleting an archive removes its record from the list of archives. For an "available" archive, 226 | * it also removes the archive file, making it unavailable for download. For a "deleted" 227 | * archive, the archive remains deleted. 228 | * 229 | * @throws Exception\ArchiveException There archive status is not "available", "updated", 230 | * or "deleted". 231 | */ 232 | public function delete() 233 | { 234 | if ($this->isDeleted) { 235 | // TODO: throw an logic error about not being able to stop an archive thats deleted 236 | } 237 | 238 | if ($this->client->deleteArchive($this->data['id'])) { 239 | $this->data = array(); 240 | $this->isDeleted = true; 241 | return true; 242 | } 243 | return false; 244 | } 245 | 246 | /** 247 | * Returns a JSON representation of this Archive object. 248 | */ 249 | public function toJson() 250 | { 251 | return json_encode($this->jsonSerialize()); 252 | } 253 | 254 | /** 255 | * Adds a stream to a currently running archive that was started with the 256 | * the streamMode set to StreamMode.Manual. You can call the method 257 | * repeatedly with the same stream ID, to toggle the stream's audio or video in the archive. 258 | * 259 | * @param String $streamId The stream ID. 260 | * @param Boolean $hasAudio Whether the archive should include the stream's audio (true, the default) 261 | * or not (false). 262 | * @param Boolean $hasVideo Whether the archive should include the stream's video (true, the default) 263 | * or not (false). 264 | * 265 | * @return Boolean Returns true on success. 266 | */ 267 | public function addStreamToArchive(string $streamId, bool $hasAudio, bool $hasVideo): bool 268 | { 269 | if ($this->streamMode === StreamMode::AUTO) { 270 | throw new InvalidArgumentException('Cannot add stream to an Archive in auto stream mode'); 271 | } 272 | 273 | if ($hasAudio === false && $hasVideo === false) { 274 | throw new InvalidArgumentException('Both hasAudio and hasVideo cannot be false'); 275 | } 276 | 277 | if ($this->client->addStreamToArchive( 278 | $this->data['id'], 279 | $streamId, 280 | $hasVideo, 281 | $hasVideo 282 | )) { 283 | return true; 284 | } 285 | 286 | return false; 287 | } 288 | 289 | /** 290 | * Removes a stream from a currently running archive that was started with the 291 | * the streamMode set to StreamMode.Manual. 292 | * 293 | * @param String $streamId The stream ID. 294 | * 295 | * @return Boolean Returns true on success. 296 | */ 297 | public function removeStreamFromArchive(string $streamId): bool 298 | { 299 | if ($this->streamMode === StreamMode::AUTO) { 300 | throw new InvalidArgumentException('Cannot remove stream to an Archive in auto stream mode'); 301 | } 302 | 303 | if ($this->client->removeStreamFromArchive( 304 | $this->data['id'], 305 | $streamId 306 | )) { 307 | return true; 308 | } 309 | 310 | return false; 311 | } 312 | 313 | /** 314 | * Returns an associative array representation of this Archive object. 315 | * @deprecated 3.0.0 A more standard name for this method is supplied by JsonSerializable 316 | * @see Archive::jsonSerialize() for a method with the same behavior 317 | */ 318 | public function toArray() 319 | { 320 | return $this->data; 321 | } 322 | 323 | public function jsonSerialize() 324 | { 325 | return $this->data; 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /src/OpenTok/ArchiveList.php: -------------------------------------------------------------------------------- 1 | null, 37 | 'apiSecret' => null, 38 | 'apiUrl' => 'https://api.opentok.com', 39 | 'client' => null 40 | ); 41 | $options = array_merge($defaults, array_intersect_key($options, $defaults)); 42 | list($apiKey, $apiSecret, $apiUrl, $client) = array_values($options); 43 | 44 | // validate params 45 | Validators::validateArchiveListData($archiveListData); 46 | Validators::validateClient($client); 47 | 48 | $this->data = $archiveListData; 49 | 50 | $this->client = isset($client) ? $client : new Client(); 51 | if (!$this->client->isConfigured()) { 52 | Validators::validateApiUrl($apiUrl); 53 | 54 | $this->client->configure($apiKey, $apiSecret, $apiUrl); 55 | } 56 | } 57 | 58 | /** 59 | * Returns the number of total archives for the API key. 60 | */ 61 | public function totalCount() 62 | { 63 | return $this->data['count']; 64 | } 65 | 66 | /** 67 | * Returns an array of Archive objects. 68 | * 69 | * @return Archive[] 70 | */ 71 | public function getItems() 72 | { 73 | if (!$this->items) { 74 | $items = array(); 75 | foreach ($this->data['items'] as $archiveData) { 76 | $items[] = new Archive($archiveData, array( 'client' => $this->client )); 77 | } 78 | $this->items = $items; 79 | } 80 | return $this->items; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/OpenTok/ArchiveMode.php: -------------------------------------------------------------------------------- 1 | createSession method and 9 | * the return value for the Session->getArchiveMode() method. 10 | *
11 | * See OpenTok->createSession()
12 | * and Session->getArchiveMode().
13 | */
14 | abstract class ArchiveMode extends BasicEnum
15 | {
16 | /**
17 | * The session is not archived automatically. To archive the session, you can call the
18 | * \OpenTok\OpenTok->startArchive() method.
19 | */
20 | public const MANUAL = 'manual';
21 | /**
22 | * The session is archived automatically (as soon as there are clients connected
23 | * to the session).
24 | */
25 | public const ALWAYS = 'always';
26 | }
27 |
--------------------------------------------------------------------------------
/src/OpenTok/Broadcast.php:
--------------------------------------------------------------------------------
1 | OpenTok live streaming developer guide
32 | * for more information on how to use this URL. For each RTMP stream, the RTMP server URL and stream
33 | * name are provided, along with the RTMP stream's status.
34 | *
35 | * @property boolean $isStopped
36 | * Whether the broadcast is stopped (true) or in progress (false).
37 | *
38 | * @property string $multiBroadcastTag
39 | * Whether Multiple Broadcast is switched on, which will be a unique string for each simultaneous broadcast of an ongoing session.
40 | * See https://tokbox.com/developer/guides/archiving/#simultaneous-archives for more information.
41 | *
42 | * @property boolean $isHls
43 | * Whether the broadcast supports HLS.
44 | *
45 | * @property boolean $isDvr
46 | * Whether the broadcast supports DVR functionality for the HLS stream.
47 |
48 | * @property string $status
49 | * Broadcast state. Either `started` or `stopped`
50 | *
51 | * @property string $maxBitRate
52 | * Max Bitrate allowed for the broadcast composing. Must be between 400000 and 2000000
53 | *
54 | * @property boolean $isLowLatency
55 | * Whether the broadcast supports low-latency mode for the HLS stream.
56 | *
57 | * @property string $resolution
58 | * The resolution of the archive, either "640x480" (SD landscape, the default), "1280x720" (HD landscape),
59 | * "1920x1080" (FHD landscape), "480x640" (SD portrait), "720x1280" (HD portrait), or "1080x1920" (FHD portrait).
60 | * You may want to use a portrait aspect ratio for archives that include video streams from mobile devices (which often use the portrait aspect ratio).
61 | *
62 | * @property string $streamMode
63 | * Whether streams included in the broadcast are selected automatically (StreamMode.AUTO
)
64 | * or manually (StreamMode.MANUAL
). When streams are selected automatically (StreamMode.AUTO
),
65 | * all streams in the session can be included in the broadcast. When streams are selected manually
66 | * (StreamMode.MANUAL
), you specify streams to be included based on calls to the
67 | * Broadcast.addStreamToBroadcast()
and Broadcast.removeStreamFromBroadcast()
methods.
68 | * With manual mode, you can specify whether a stream's audio, video, or both are included in the
69 | * broadcast. In both automatic and manual modes, the broadcast composer includes streams based on
70 | * stream
71 | * prioritization rules.
72 | */
73 | class Broadcast
74 | {
75 | /** @ignore */
76 | private $data;
77 | /** @ignore */
78 | private $isStopped;
79 | /** @ignore */
80 | private $client;
81 | /** @ignore */
82 | private $isHls;
83 | /** @ignore */
84 | private $isLowLatency;
85 | /** @ignore */
86 | private $isDvr;
87 | /** @ignore */
88 | private $multiBroadcastTag;
89 | /** @ignore */
90 | private $resolution;
91 | /** @ignore */
92 | private $hasAudio;
93 | /** @ignore */
94 | private $hasVideo;
95 | /** @ignore */
96 | private $status;
97 | /** @ignore */
98 | private $maxBitRate;
99 |
100 | public function __construct($broadcastData, $options = array())
101 | {
102 | // unpack optional arguments (merging with default values) into named variables
103 | // when adding these properties like this, it's worth noting that the method that
104 | // starts a broadcast ALSO sets a load of defaults
105 | $defaults = array(
106 | 'apiKey' => null,
107 | 'apiSecret' => null,
108 | 'apiUrl' => 'https://api.opentok.com',
109 | 'client' => null,
110 | 'isStopped' => false,
111 | 'streamMode' => StreamMode::AUTO,
112 | 'isHls' => true,
113 | 'isLowLatency' => false,
114 | 'isDvr' => false,
115 | 'hasAudio' => true,
116 | 'hasVideo' => true
117 | );
118 |
119 | $options = array_merge($defaults, array_intersect_key($options, $defaults));
120 |
121 | Validators::validateBroadcastData($broadcastData);
122 | Validators::validateClient($options['client']);
123 | Validators::validateHasStreamMode($options['streamMode']);
124 |
125 | $this->data = $broadcastData;
126 |
127 | if (isset($this->data['multiBroadcastTag'])) {
128 | $this->multiBroadcastTag = $this->data['multiBroadcastTag'];
129 | }
130 |
131 | if (isset($this->data['maxBitRate'])) {
132 | $this->maxBitRate = $this->data['maxBitRate'];
133 | }
134 |
135 | if (isset($this->data['status'])) {
136 | $this->status = $this->data['status'];
137 | }
138 |
139 | $this->isStopped = $options['isStopped'];
140 | $this->resolution = $this->data['resolution'];
141 | $this->isHls = isset($this->data['settings']['hls']);
142 | $this->isLowLatency = $this->data['settings']['hls']['lowLatency'] ?? false;
143 | $this->isDvr = $this->data['settings']['hls']['dvr'] ?? false;
144 | $this->hasAudio = $options['hasAudio'];
145 | $this->hasVideo = $options['hasVideo'];
146 |
147 | $this->client = $options['client'] ?? new Client();
148 |
149 | if (!$this->client->isConfigured()) {
150 | Validators::validateApiUrl($options['apiUrl']);
151 |
152 | $this->client->configure($options['apiKey'], $options['apiSecret'], $options['apiUrl']);
153 | }
154 | }
155 |
156 | /** @ignore */
157 | public function __get($name)
158 | {
159 | switch ($name) {
160 | case 'createdAt':
161 | case 'updatedAt':
162 | case 'id':
163 | case 'partnerId':
164 | case 'sessionId':
165 | case 'broadcastUrls':
166 | case 'maxDuration':
167 | case 'streamMode':
168 | return $this->data[$name];
169 | case 'resolution':
170 | return $this->resolution;
171 | case 'hlsUrl':
172 | return $this->data['broadcastUrls']['hls'];
173 | case 'isStopped':
174 | return $this->isStopped;
175 | case 'isHls':
176 | return $this->isHls;
177 | case 'isLowLatency':
178 | return $this->isLowLatency;
179 | case 'isDvr':
180 | return $this->isDvr;
181 | case 'multiBroadcastTag':
182 | return $this->multiBroadcastTag;
183 | case 'hasAudio':
184 | return $this->hasAudio;
185 | case 'hasVideo':
186 | return $this->hasVideo;
187 | case 'status':
188 | return $this->status;
189 | case 'maxBitRate':
190 | return $this->maxBitRate;
191 | default:
192 | return null;
193 | }
194 | }
195 |
196 | /**
197 | * Stops the broadcast.
198 | */
199 | public function stop()
200 | {
201 | if ($this->isStopped) {
202 | throw new BroadcastDomainException(
203 | 'You cannot stop a broadcast which is already stopped.'
204 | );
205 | }
206 |
207 | $broadcastData = $this->client->stopBroadcast($this->data['id']);
208 |
209 | try {
210 | Validators::validateBroadcastData($broadcastData);
211 | } catch (InvalidArgumentException $e) {
212 | throw new BroadcastUnexpectedValueException('The broadcast JSON returned after stopping was not valid', null, $e);
213 | }
214 |
215 | $this->data = $broadcastData;
216 | return $this;
217 | }
218 |
219 | // TODO: not yet implemented by the platform
220 | // public function getLayout()
221 | // {
222 | // $layoutData = $this->client->getLayout($this->id, 'broadcast');
223 | // return Layout::fromData($layoutData);
224 | // }
225 |
226 | /**
227 | * Updates the layout of the broadcast.
228 | *
229 | * See Configuring 230 | * video layout for OpenTok live streaming broadcasts. 231 | * 232 | * @param Layout $layout An object defining the layout type for the broadcast. 233 | */ 234 | public function updateLayout($layout) 235 | { 236 | Validators::validateLayout($layout); 237 | 238 | // TODO: platform implementation did not meet API review spec 239 | // $layoutData = $this->client->updateLayout($this->id, $layout, 'broadcast'); 240 | // return Layout::fromData($layoutData); 241 | 242 | $this->client->updateLayout($this->id, $layout, 'broadcast'); 243 | } 244 | 245 | /** 246 | * Adds a stream to a currently running broadcast that was started with the 247 | * the streamMode set to StreamMode.Manual. You can call the method 248 | * repeatedly with the same stream ID, to toggle the stream's audio or video in the broadcast. 249 | * 250 | * @param String $streamId The stream ID. 251 | * @param Boolean $hasAudio Whether the broadcast should include the stream's audio (true, the default) 252 | * or not (false). 253 | * @param Boolean $hasVideo Whether the broadcast should include the stream's video (true, the default) 254 | * or not (false). 255 | * 256 | * @return Boolean Returns true on success. 257 | */ 258 | public function addStreamToBroadcast(string $streamId, bool $hasAudio, bool $hasVideo): bool 259 | { 260 | if ($this->streamMode === StreamMode::AUTO) { 261 | throw new InvalidArgumentException('Cannot add stream to a Broadcast in auto stream mode'); 262 | } 263 | 264 | if ($hasAudio === false && $hasVideo === false) { 265 | throw new InvalidArgumentException('Both hasAudio and hasVideo cannot be false'); 266 | } 267 | 268 | if ($this->client->addStreamToBroadcast( 269 | $this->data['id'], 270 | $streamId, 271 | $hasVideo, 272 | $hasVideo 273 | )) { 274 | return true; 275 | } 276 | 277 | return false; 278 | } 279 | 280 | /** 281 | * Removes a stream from a currently running broadcast that was started with the 282 | * the streamMode set to StreamMode.Manual. 283 | * 284 | * @param String $streamId The stream ID. 285 | * 286 | * @return Boolean Returns true on success. 287 | */ 288 | public function removeStreamFromBroadcast(string $streamId): bool 289 | { 290 | if ($this->streamMode === StreamMode::AUTO) { 291 | throw new InvalidArgumentException('Cannot remove stream from a Broadcast in auto stream mode'); 292 | } 293 | 294 | if ($this->client->removeStreamFromBroadcast( 295 | $this->data['id'], 296 | $streamId 297 | )) { 298 | return true; 299 | } 300 | 301 | return false; 302 | } 303 | 304 | public function jsonSerialize() 305 | { 306 | return $this->data; 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /src/OpenTok/Exception/ArchiveAuthenticationException.php: -------------------------------------------------------------------------------- 1 | setArchiveLayout() and OpenTok->updateBroadcastLayout() methods. 10 | *
11 | * To instantiate a Layout object, call one of the static methods of the Layout class:
12 | * getBestFit()
, getPIP()
, getVerticalPresentation()
,
13 | * getHorizontalPresentation()
, or createCustom()
.
14 | *
15 | * See OpenTok->setArchiveLayout(),
16 | * OpenTok->updateBroadcastLayout(),
17 | * Customizing
18 | * the video layout for composed archives, and
19 | * Configuring
20 | * video layout for OpenTok live streaming broadcasts.
21 | */
22 | class Layout implements \JsonSerializable
23 | {
24 | public const LAYOUT_BESTFIT = 'bestFit';
25 | public const LAYOUT_CUSTOM = 'custom';
26 | public const LAYOUT_HORIZONTAL = 'horizontalPresentation';
27 | public const LAYOUT_PIP = 'pip';
28 | public const LAYOUT_VERTICAL = 'verticalPresentation';
29 |
30 | /**
31 | * Type of layout that we are sending
32 | * @var string
33 | * @ignore
34 | * */
35 | private $type;
36 |
37 | /**
38 | * Type of layout to use for screen sharing
39 | * @var string
40 | * @ignore
41 | */
42 | private $screenshareType;
43 |
44 | /**
45 | * Custom stylesheet if our type is 'custom'
46 | * @var string
47 | * @ignore
48 | */
49 | private $stylesheet;
50 |
51 | /** @ignore */
52 | private function __construct(string $type, ?string $stylesheet = null)
53 | {
54 | $this->type = $type;
55 | $this->stylesheet = $stylesheet;
56 | }
57 |
58 | /**
59 | * Returns a Layout object defining a custom layout type.
60 | *
61 | * @param array $options An array containing one property:
132 | * For testing, you can also generate tokens or by logging in to your
133 | * OpenTok Video API account.
134 | *
135 | * @param array $options This array defines options for the token. This array include the
136 | * following keys, all of which are optional:
137 | *
138 | * $stylesheet
,
62 | * which is a string containing the stylesheet to be used for the layout.
63 | */
64 | public static function createCustom(array $options): Layout
65 | {
66 | // unpack optional arguments (merging with default values) into named variables
67 | // NOTE: the default value of stylesheet=null will not pass validation, this essentially
68 | // means that stylesheet is not optional. its still purposely left as part of the
69 | // $options argument so that it can become truly optional in the future.
70 | $defaults = ['stylesheet' => null];
71 | $options = array_merge($defaults, array_intersect_key($options, $defaults));
72 | list($stylesheet) = array_values($options);
73 |
74 | // validate arguments
75 | Validators::validateLayoutStylesheet($stylesheet);
76 |
77 | return new Layout(static::LAYOUT_CUSTOM, $stylesheet);
78 | }
79 |
80 | /** @ignore */
81 | public static function fromData(array $layoutData): Layout
82 | {
83 | if (array_key_exists('stylesheet', $layoutData)) {
84 | return new Layout($layoutData['type'], $layoutData['stylesheet']);
85 | }
86 |
87 | return new Layout($layoutData['type']);
88 | }
89 |
90 | /**
91 | * Returns a Layout object defining the "best fit" predefined layout type.
92 | */
93 | public static function getBestFit(): Layout
94 | {
95 | return new Layout(static::LAYOUT_BESTFIT);
96 | }
97 |
98 | /**
99 | * Returns a Layout object defining the "picture-in-picture" predefined layout type.
100 | */
101 | public static function getPIP(): Layout
102 | {
103 | return new Layout(static::LAYOUT_PIP);
104 | }
105 |
106 | /**
107 | * Returns a Layout object defining the "vertical presentation" predefined layout type.
108 | */
109 | public static function getVerticalPresentation(): Layout
110 | {
111 | return new Layout(static::LAYOUT_VERTICAL);
112 | }
113 |
114 | /**
115 | * Returns a Layout object defining the "horizontal presentation" predefined layout type.
116 | */
117 | public static function getHorizontalPresentation(): Layout
118 | {
119 | return new Layout(static::LAYOUT_HORIZONTAL);
120 | }
121 |
122 | public function setScreenshareType(string $screenshareType): Layout
123 | {
124 | if ($this->type === Layout::LAYOUT_BESTFIT) {
125 | $layouts = [
126 | Layout::LAYOUT_BESTFIT,
127 | Layout::LAYOUT_HORIZONTAL,
128 | Layout::LAYOUT_PIP,
129 | Layout::LAYOUT_VERTICAL
130 | ];
131 |
132 | if (!in_array($screenshareType, $layouts)) {
133 | throw new \RuntimeException('Screenshare type must be of a valid layout type');
134 | }
135 |
136 | $this->screenshareType = $screenshareType;
137 | return $this;
138 | }
139 |
140 | throw new \RuntimeException('Screenshare type cannot be set on a layout type other than bestFit');
141 | }
142 |
143 | #[\ReturnTypeWillChange]
144 | public function jsonSerialize()
145 | {
146 | return $this->toArray();
147 | }
148 |
149 | /**
150 | * Return a json-encoded string representation of the layout
151 | */
152 | public function toJson(): string
153 | {
154 | return json_encode($this->jsonSerialize());
155 | }
156 |
157 | public function toArray(): array
158 | {
159 | $data = array(
160 | 'type' => $this->type
161 | );
162 |
163 | // omit 'stylesheet' property unless it is explicitly defined
164 | if (isset($this->stylesheet)) {
165 | $data['stylesheet'] = $this->stylesheet;
166 | }
167 |
168 | // omit 'screenshareType' property unless it is explicitly defined
169 | if (isset($this->screenshareType)) {
170 | $data['screenshareType'] = $this->screenshareType;
171 | }
172 |
173 | return $data;
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/src/OpenTok/MediaMode.php:
--------------------------------------------------------------------------------
1 | createSession()
9 | * method.
10 | */
11 | abstract class MediaMode extends BasicEnum
12 | {
13 | /**
14 | * The session will send streams using the OpenTok Media Router.
15 | */
16 | const ROUTED = 'disabled';
17 | /**
18 | * The session will attempt send streams directly between clients. If clients cannot connect
19 | * due to firewall restrictions, the session uses the OpenTok TURN server to relay streams.
20 | */
21 | const RELAYED = 'enabled';
22 | }
23 |
--------------------------------------------------------------------------------
/src/OpenTok/OutputMode.php:
--------------------------------------------------------------------------------
1 | startArchive() method
9 | * and for the outputMode property of the Archive class.
10 | *
11 | * See OpenTok->startArchive()
12 | * and Archive.outputMode.
13 | */
14 | abstract class OutputMode extends BasicEnum
15 | {
16 | /**
17 | * All streams in the archive are recorded to a single (composed) file.
18 | */
19 | const COMPOSED = 'composed';
20 | /**
21 | * Each stream in the archive is recorded to its own individual file.
22 | */
23 | const INDIVIDUAL = 'individual';
24 | }
25 |
--------------------------------------------------------------------------------
/src/OpenTok/Render.php:
--------------------------------------------------------------------------------
1 | data = json_decode($data, true);
51 | }
52 |
53 | /** @internal */
54 | public function __get($name)
55 | {
56 | switch ($name) {
57 | case 'id':
58 | case 'sessionId':
59 | case 'projectId':
60 | case 'createdAt':
61 | case 'updatedAt':
62 | case 'url':
63 | case 'resolution':
64 | case 'status':
65 | case 'streamId':
66 | return $this->data[$name];
67 | default:
68 | return null;
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/OpenTok/Role.php:
--------------------------------------------------------------------------------
1 | generateToken()
9 | * method.
10 | */
11 | abstract class Role extends BasicEnum
12 | {
13 | /**
14 | * A subscriber can only subscribe to streams.
15 | */
16 | const SUBSCRIBER = 'subscriber';
17 | /**
18 | * A publisher can publish streams, subscribe to streams, and signal. (This is the default
19 | * value if you do not set a role.)
20 | */
21 | const PUBLISHER = 'publisher';
22 | /**
23 | * In addition to the privileges granted to a publisher, a moderator can perform
24 | * moderation functions, such as forcing clients to disconnect, to stop publishing streams,
25 | * or to mute audio in published streams.
26 | *
27 | * See the
28 | * Moderation developer guide.
29 | */
30 | const MODERATOR = 'moderator';
31 |
32 | /**
33 | * @var string
34 | */
35 | const PUBLISHER_ONLY = 'publisheronly';
36 | }
37 |
--------------------------------------------------------------------------------
/src/OpenTok/Session.php:
--------------------------------------------------------------------------------
1 |
10 | * Use the \OpenTok\OpenTok->createSession() method to create an OpenTok session. Use the
11 | * getSessionId() method of the Session object to get the session ID.
12 | */
13 | class Session
14 | {
15 | /**
16 | * @internal
17 | */
18 | protected $sessionId;
19 | /**
20 | * @internal
21 | */
22 | protected $location;
23 | /**
24 | * @internal
25 | */
26 | protected $mediaMode;
27 | /**
28 | * @internal
29 | */
30 | protected $archiveMode;
31 | /**
32 | * @internal
33 | */
34 | protected $opentok;
35 | /**
36 | * @internal
37 | */
38 | protected $e2ee;
39 |
40 | /**
41 | * @internal
42 | */
43 | public function __construct($opentok, $sessionId, $properties = array())
44 | {
45 | $defaults = [
46 | 'mediaMode' => MediaMode::ROUTED,
47 | 'archiveMode' => ArchiveMode::MANUAL,
48 | 'location' => null,
49 | 'e2ee' => false
50 | ];
51 |
52 | $properties = array_merge($defaults, array_intersect_key($properties, $defaults));
53 | list($mediaMode, $archiveMode, $location, $e2ee) = array_values($properties);
54 |
55 | Validators::validateOpenTok($opentok);
56 | Validators::validateSessionId($sessionId);
57 | Validators::validateLocation($location);
58 | Validators::validateMediaMode($mediaMode);
59 | Validators::validateArchiveMode($archiveMode);
60 |
61 | $this->opentok = $opentok;
62 | $this->sessionId = $sessionId;
63 | $this->location = $location;
64 | $this->mediaMode = $mediaMode;
65 | $this->archiveMode = $archiveMode;
66 | $this->e2ee = $e2ee;
67 | }
68 |
69 | /**
70 | * Returns the session ID, which uniquely identifies the session.
71 | *
72 | * @return string
73 | */
74 | public function getSessionId()
75 | {
76 | return $this->sessionId;
77 | }
78 |
79 | /**
80 | * Returns the location hint IP address.
81 | *
82 | * See OpenTok->createSession().
83 | *
84 | * @return string
85 | */
86 | public function getLocation()
87 | {
88 | return $this->location;
89 | }
90 |
91 | /**
92 | * Returns MediaMode::RELAYED if the session's streams will be transmitted directly between
93 | * peers; returns MediaMode::ROUTED if the session's streams will be transmitted using the
94 | * OpenTok Media Router.
95 | *
96 | * See OpenTok->createSession()
97 | * and MediaMode.
98 | *
99 | * @return MediaMode
100 | */
101 | public function getMediaMode()
102 | {
103 | return $this->mediaMode;
104 | }
105 |
106 | /**
107 | * Defines whether the session is automatically archived (ArchiveMode::ALWAYS)
108 | * or not (ArchiveMode::MANUAL).
109 | *
110 | * See OpenTok->createSession()
111 | * and ArchiveMode.
112 | *
113 | * @return ArchiveMode
114 | */
115 | public function getArchiveMode()
116 | {
117 | return $this->archiveMode;
118 | }
119 |
120 | /**
121 | * @internal
122 | */
123 | public function __toString()
124 | {
125 | return $this->sessionId;
126 | }
127 |
128 | /**
129 | * Creates a token for connecting to the session. In order to authenticate a user,
130 | * the client passes a token when connecting to the session.
131 | *
139 | *
140 | *
154 | *
155 | * @param bool $legacy Connection tokens are now SHA-256 signed JWTs.
156 | * Set this to 'role'
(string) — One of the constants defined in the RoleConstants
141 | * class. The default role is publisher'expireTime'
(int) — The timestamp for when the token expires,
144 | * in milliseconds since the Unix epoch. The default expiration time is 24 hours
145 | * after the token creation time. The maximum expiration time is 30 days after the
146 | * token creation time.'data'
(string) — A string containing connection metadata
149 | * describing the end-user. For example, you can pass the user ID, name, or other data
150 | * describing the end-user. The length of the string is limited to 1000 characters.
151 | * This data cannot be updated once it is set.true
to create a token using the legacy T1 format.
157 | *
158 | * @return string The token string.
159 | */
160 | public function generateToken($options = array(), bool $legacy = false)
161 | {
162 | return $this->opentok->generateToken($this->sessionId, $options, $legacy);
163 | }
164 |
165 | /**
166 | * Whether end-to-end encryption
167 | * is set for the session.
168 | *
169 | * @return bool
170 | */
171 | public function getE2EE(): bool
172 | {
173 | return (bool)$this->e2ee;
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/src/OpenTok/SipCall.php:
--------------------------------------------------------------------------------
1 | data['id'] = $sipCallData['id'];
30 | $this->data['connectionId'] = $sipCallData['connectionId'];
31 | $this->data['streamId'] = $sipCallData['streamId'];
32 | }
33 |
34 | /**
35 | * Returns the conference ID.
36 | */
37 | /** @internal */
38 | public function __get($name)
39 | {
40 | switch ($name) {
41 | case 'id':
42 | case 'connectionId':
43 | case 'streamId':
44 | return $this->data[$name];
45 | default:
46 | return null;
47 | }
48 | }
49 |
50 |
51 | /**
52 | * Returns a JSON representation of this SipCall object.
53 | */
54 | public function toJson()
55 | {
56 | return json_encode($this->data);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/OpenTok/Stream.php:
--------------------------------------------------------------------------------
1 |
8 | * See OpenTok.getStream() and
9 | * OpenTok.listStreams().
10 | *
11 | * @property String $id
12 | * The stream ID.
13 | *
14 | * @property Array $layoutClassList
15 | * An array of the layout classes for the stream.
16 | *
17 | * @property String $name
18 | * The stream name (if one was set when the client published the stream).
19 | *
20 | * @property String $videoType
21 | * The type of video in the stream, which is set to either "camera" or "screen".
22 | */
23 |
24 | class Stream
25 | {
26 |
27 | private $data;
28 |
29 | public function __construct($streamData)
30 | {
31 |
32 | $this->data = $streamData;
33 | }
34 |
35 | /** @ignore */
36 | public function __get($name)
37 | {
38 | switch ($name) {
39 | case 'id':
40 | case 'videoType':
41 | case 'name':
42 | case 'layoutClassList':
43 | return $this->data[$name];
44 | default:
45 | return null;
46 | }
47 | }
48 |
49 | public function jsonSerialize()
50 | {
51 | return $this->data;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/OpenTok/StreamList.php:
--------------------------------------------------------------------------------
1 | OpenTok.listStreams()
7 | * method, representing a list of streams in an OpenTok session.
8 | */
9 | class StreamList
10 | {
11 | /** @ignore */
12 | private $data;
13 |
14 | /** @ignore */
15 | private $items;
16 |
17 | /** @ignore */
18 | public function __construct($streamListData)
19 | {
20 | $this->data = $streamListData;
21 | }
22 |
23 | /**
24 | * Returns the number of total streams for the session ID.
25 | *
26 | * @return int
27 | */
28 | public function totalCount()
29 | {
30 | return $this->data['count'];
31 | }
32 |
33 | /**
34 | * Returns an array of Stream objects.
35 | *
36 | * @return Stream[]
37 | */
38 | public function getItems()
39 | {
40 | if (!is_array($this->items)) {
41 | $items = array();
42 | foreach ($this->data['items'] as $streamData) {
43 | $items[] = new Stream($streamData);
44 | }
45 | $this->items = $items;
46 | }
47 | return $this->items;
48 | }
49 |
50 | /**
51 | * @return array
52 | */
53 | public function jsonSerialize()
54 | {
55 | return $this->data;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/OpenTok/StreamMode.php:
--------------------------------------------------------------------------------
1 | getConstants();
22 | }
23 |
24 | return self::$constCacheArray[$calledClass];
25 | }
26 |
27 | public static function isValidName($name, $strict = false)
28 | {
29 | $constants = self::getConstants();
30 |
31 | if ($strict) {
32 | return array_key_exists($name, $constants);
33 | }
34 |
35 | $keys = array_map('strtolower', array_keys($constants));
36 | return in_array(strtolower($name), $keys);
37 | }
38 |
39 | public static function isValidValue($value)
40 | {
41 | $values = array_values(self::getConstants());
42 | return in_array($value, $values, $strict = true);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/OpenTok/Util/archive-schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-04/schema#",
3 | "title": "Archive List",
4 | "description": "A list of OpenTok Archives",
5 | "type": "object",
6 | "properties": {
7 | "count": {
8 | "type": "integer"
9 | },
10 | "items": {
11 | "type": "array",
12 | "items": {
13 | "$ref": "#/definitions/archive"
14 | }
15 | }
16 | },
17 | "required": ["count", "items"],
18 | "definitions": {
19 | "archive": {
20 | "title": "Archive",
21 | "description": "An OpenTok Archive",
22 | "type": "object",
23 | "properties": {
24 | "createdAt": {
25 | "type" : "integer"
26 | },
27 | "duration": {
28 | "type" : "integer",
29 | "minimum": 0
30 | },
31 | "id": {
32 | "type": "string",
33 | "pattern": "^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$"
34 | },
35 | "name": {
36 | "type": ["string", "null"]
37 | },
38 | "partnerId": {
39 | "type": "integer"
40 | },
41 | "reason": {
42 | "type": "string"
43 | },
44 | "sessionId": {
45 | "type": "string"
46 | },
47 | "size": {
48 | "type": "integer",
49 | "minimum" : 0
50 | },
51 | "status": {
52 | "type": "string",
53 | "enum": ["available", "started", "stopped", "failed", "deleted", "uploaded", "expired", "paused"]
54 | },
55 | "url": {
56 | "type": ["string", "null"]
57 | },
58 | "hasVideo": {
59 | "type": "boolean"
60 | },
61 | "hasAudio": {
62 | "type": "boolean"
63 | }
64 | },
65 | "required": [ "id", "partnerId", "sessionId", "status" ]
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/OpenTok/Util/broadcast-schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-04/schema#",
3 | "title": "Broadcast",
4 | "description": "An OpenTok Broadcast",
5 | "type": "object",
6 | "properties": {
7 | "id": {
8 | "type": "string",
9 | "pattern": "^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$"
10 | },
11 | "sessionId": {
12 | "type": "string"
13 | },
14 | "partnerId": {
15 | "type": "integer"
16 | },
17 | "createdAt": {
18 | "type" : "integer"
19 | },
20 | "updatedAt": {
21 | "type" : "integer"
22 | },
23 | "broadcastUrls": {
24 | "oneOf": [
25 | {
26 | "type": "null"
27 | },
28 | {
29 | "type": "object",
30 | "properties": {
31 | "hls": {
32 | "type": "string"
33 | }
34 | }
35 | }
36 | ]
37 | }
38 | },
39 | "required": ["id", "sessionId", "partnerId", "createdAt", "updatedAt"]
40 | }
41 |
--------------------------------------------------------------------------------
/tests/OpenTokTest/BroadcastTest.php:
--------------------------------------------------------------------------------
1 | broadcastData = [
32 | 'id' => '063e72a4-64b4-43c8-9da5-eca071daab89',
33 | 'createdAt' => 1394394801000,
34 | 'updatedAt' => 1394394801000,
35 | 'partnerId' => 685,
36 | 'sessionId' => '2_MX44NTQ1MTF-flR1ZSBOb3YgMTIgMDk6NDA6NTkgUFNUIDIwMTN-MC43NjU0Nzh-',
37 | 'multiBroadcastTag' => 'broadcast-1234b',
38 | 'layout' => [
39 | 'type' => 'custom',
40 | 'stylesheet' => 'a layout stylesheet',
41 | 'screenshareType' => 'some options'
42 | ],
43 | 'maxDuration' => 5400,
44 | 'resolution' => '640x480',
45 | 'streamMode' => StreamMode::AUTO,
46 | 'status' => 'started',
47 | 'hasAudio' => true,
48 | 'hasVideo' => true
49 | ];
50 | }
51 |
52 | public static function setUpBeforeClass(): void
53 | {
54 | self::$mockBasePath = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'mock' . DIRECTORY_SEPARATOR;
55 | }
56 |
57 | public function setupBroadcasts($streamMode)
58 | {
59 | $data = $this->broadcastData;
60 | $data['streamMode'] = $streamMode;
61 |
62 | $this->broadcast = new Broadcast($data, array(
63 | 'apiKey' => $this->API_KEY,
64 | 'apiSecret' => $this->API_SECRET,
65 | 'client' => $this->client
66 | ));
67 | }
68 |
69 | private function setupOTWithMocks($mocks)
70 | {
71 | $this->API_KEY = defined('API_KEY') ? API_KEY : '12345678';
72 | $this->API_SECRET = defined('API_SECRET') ? API_SECRET : 'b60d0b2568f3ea9731bd9d3f71be263ce19f802f';
73 |
74 | if (is_array($mocks)) {
75 | $responses = TestHelpers::mocksToResponses($mocks, self::$mockBasePath);
76 | } else {
77 | $responses = [];
78 | }
79 |
80 | $mock = new MockHandler($responses);
81 | $handlerStack = HandlerStack::create($mock);
82 | $clientOptions = [
83 | 'handler' => $handlerStack
84 | ];
85 |
86 | $this->client = new Client();
87 | $this->client->configure(
88 | $this->API_KEY,
89 | $this->API_SECRET,
90 | 'https://api.opentok.com',
91 | $clientOptions
92 | );
93 |
94 | // Push history onto handler stack *after* configuring client to
95 | // ensure auth header is added before history handler is invoked
96 | $this->historyContainer = [];
97 | $history = Middleware::history($this->historyContainer);
98 | $handlerStack->push($history);
99 | }
100 |
101 | private function setupOT()
102 | {
103 | return $this->setupOTWithMocks([]);
104 | }
105 |
106 | public function testInitializes(): void
107 | {
108 | $this->setupOT();
109 | $this->setupBroadcasts(StreamMode::AUTO);
110 | $this->assertInstanceOf(Broadcast::class, $this->broadcast);
111 |
112 | }
113 |
114 | public function testCannotAddStreamToBroadcastInAutoMode(): void
115 | {
116 | $this->expectException(InvalidArgumentException::class);
117 | $this->setupOTWithMocks([[
118 | 'code' => 200,
119 | 'headers' => [
120 | 'Content-Type' => 'application/json'
121 | ],
122 | 'path' => 'v2/project/APIKEY/broadcast/BROADCASTID/get'
123 | ]]);
124 |
125 | $this->setupBroadcasts(StreamMode::AUTO);
126 |
127 | $this->broadcast->addStreamToBroadcast(
128 | '5dfds4-asdda4asf4',
129 | true,
130 | true
131 | );
132 | }
133 |
134 | public function testCannotAddStreamToBroadcastWithNoAudioAndVideo(): void
135 | {
136 | $this->expectException(InvalidArgumentException::class);
137 | $this->setupOTWithMocks([[
138 | 'code' => 200,
139 | 'headers' => [
140 | 'Content-Type' => 'application/json'
141 | ],
142 | 'path' => 'v2/project/APIKEY/broadcast/BROADCASTID/get'
143 | ]]);
144 |
145 | $this->setupBroadcasts(StreamMode::MANUAL);
146 |
147 | $this->broadcast->addStreamToBroadcast(
148 | '5dfds4-asdda4asf4',
149 | false,
150 | false
151 | );
152 | }
153 |
154 | public function testCanAddStreamToBroadcast(): void
155 | {
156 | $this->setupOTWithMocks([[
157 | 'code' => 200,
158 | 'headers' => [
159 | 'Content-Type' => 'application/json'
160 | ],
161 | 'path' => 'v2/project/APIKEY/broadcast/BROADCASTID/get'
162 | ]]);
163 |
164 | $this->setupBroadcasts(StreamMode::MANUAL);
165 |
166 | $return = $this->broadcast->addStreamToBroadcast(
167 | '5dfds4-asdda4asf4',
168 | true,
169 | true
170 | );
171 | $this->assertTrue($return);
172 | }
173 |
174 | public function testCanRemoveStreamFromBroadcast(): void
175 | {
176 | $this->setupOTWithMocks([[
177 | 'code' => 200,
178 | 'headers' => [
179 | 'Content-Type' => 'application/json'
180 | ],
181 | 'path' => 'v2/project/APIKEY/broadcast/BROADCASTID/get'
182 | ]]);
183 |
184 | $this->setupBroadcasts(StreamMode::MANUAL);
185 |
186 | $return = $this->broadcast->removeStreamFromBroadcast(
187 | '5dfds4-asdda4asf4'
188 | );
189 | $this->assertTrue($return);
190 | }
191 |
192 | public function testCannotRemoveStreamFromBroadcastOnAuto(): void
193 | {
194 | $this->expectException(InvalidArgumentException::class);
195 |
196 | $this->setupOTWithMocks([[
197 | 'code' => 200,
198 | 'headers' => [
199 | 'Content-Type' => 'application/json'
200 | ],
201 | 'path' => 'v2/project/APIKEY/broadcast/BROADCASTID/get'
202 | ]]);
203 |
204 | $this->setupBroadcasts(StreamMode::AUTO);
205 |
206 | $return = $this->broadcast->removeStreamFromBroadcast(
207 | '5dfds4-asdda4asf4'
208 | );
209 | }
210 |
211 | public function testGetters(): void
212 | {
213 | $broadcastObject = new Broadcast($this->broadcastData, [
214 | 'apiKey' => 'abc',
215 | 'apiSecret' => 'efg',
216 | 'client' => $this->client
217 | ]);
218 |
219 | $this->assertTrue($broadcastObject->hasAudio);
220 | $this->assertTrue($broadcastObject->hasVideo);
221 | $this->assertEquals('broadcast-1234b', $broadcastObject->multiBroadcastTag);
222 | $this->assertEquals('started', $broadcastObject->status);
223 | $this->assertNull($broadcastObject->wrongKey);
224 | }
225 | }
226 |
227 |
--------------------------------------------------------------------------------
/tests/OpenTokTest/LayoutTest.php:
--------------------------------------------------------------------------------
1 | Layout::getBestFit(),
15 | Layout::LAYOUT_HORIZONTAL => Layout::getHorizontalPresentation(),
16 | Layout::LAYOUT_PIP => Layout::getPIP(),
17 | Layout::LAYOUT_VERTICAL => Layout::getVerticalPresentation(),
18 | ];
19 |
20 | foreach ($layouts as $type => $object) {
21 | $this->assertSame(['type' => $type], $object->toArray());
22 | }
23 | }
24 |
25 | public function testWillValidateLayout(): void
26 | {
27 | $this->expectException(\InvalidArgumentException::class);
28 | $object = ['bestFit' => true];
29 |
30 | Validators::validateLayout($object);
31 | }
32 |
33 | public function testStylesheetIsInSerializedArrayIfCustom()
34 | {
35 | $layout = Layout::createCustom(['stylesheet' => 'foo']);
36 |
37 | $this->assertSame(['type' => LAYOUT::LAYOUT_CUSTOM, 'stylesheet' => 'foo'], $layout->toArray());
38 | }
39 |
40 | public function testScreenshareTypeSerializesProperly()
41 | {
42 | $layout = Layout::getBestFit();
43 | $layout->setScreenshareType(Layout::LAYOUT_PIP);
44 |
45 | $expected = [
46 | 'type' => 'bestFit',
47 | 'screenshareType' => 'pip'
48 | ];
49 |
50 | $this->assertSame($expected, $layout->toArray());
51 | }
52 |
53 | public function testScreenshareTypeCannotBeSetToInvalidValue()
54 | {
55 | $this->expectException(\RuntimeException::class);
56 | $this->expectExceptionMessage('Screenshare type must be of a valid layout type');
57 |
58 | $layout = Layout::getBestFit();
59 | $layout->setScreenshareType('bar');
60 | }
61 |
62 | public function testScreenshareTypeCannotBeSetOnInvalidLayoutType()
63 | {
64 | $this->expectException(\RuntimeException::class);
65 | $this->expectExceptionMessage('Screenshare type cannot be set on a layout type other than bestFit');
66 |
67 | $layout = Layout::createCustom(['stylesheet' => 'foo']);
68 | $layout->setScreenshareType(Layout::LAYOUT_PIP);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/tests/OpenTokTest/RenderTest.php:
--------------------------------------------------------------------------------
1 | expectError(\TypeError::class);
13 | $payload = [
14 | 'id' => '1248e7070b81464c9789f46ad10e7764',
15 | 'sessionId' => '2_MX4xMDBfjE0Mzc2NzY1NDgwMTJ-TjMzfn4',
16 | 'projectId' => 'e2343f23456g34709d2443a234',
17 | 'createdAt' => 1437676551000,
18 | 'updatedAt' => 1437676551000,
19 | 'url' => 'https://webapp.customer.com',
20 | 'resolution' => '1280x720',
21 | 'status«' => 'started',
22 | 'streamId' => 'e32445b743678c98230f238'
23 | ];
24 |
25 | $render = new Render($payload);
26 | }
27 |
28 | public function testCanHydrateFromPayload(): void
29 | {
30 | $payload = [
31 | 'id' => '1248e7070b81464c9789f46ad10e7764',
32 | 'sessionId' => '2_MX4xMDBfjE0Mzc2NzY1NDgwMTJ-TjMzfn4',
33 | 'projectId' => 'e2343f23456g34709d2443a234',
34 | 'createdAt' => 1437676551000,
35 | 'updatedAt' => 1437676551000,
36 | 'url' => 'https://webapp.customer.com',
37 | 'resolution' => '1280x720',
38 | 'status' => 'started',
39 | 'streamId' => 'e32445b743678c98230f238'
40 | ];
41 |
42 | $jsonPayload = json_encode($payload);
43 |
44 | $render = new Render($jsonPayload);
45 |
46 | $this->assertEquals('1248e7070b81464c9789f46ad10e7764', $render->id);
47 | $this->assertEquals('2_MX4xMDBfjE0Mzc2NzY1NDgwMTJ-TjMzfn4', $render->sessionId);
48 | $this->assertEquals('e2343f23456g34709d2443a234', $render->projectId);
49 | $this->assertEquals(1437676551000, $render->createdAt);
50 | $this->assertEquals(1437676551000, $render->updatedAt);
51 | $this->assertEquals('https://webapp.customer.com', $render->url);
52 | $this->assertEquals('1280x720', $render->resolution);
53 | $this->assertEquals('started', $render->status);
54 | $this->assertEquals('e32445b743678c98230f238', $render->streamId);
55 | }
56 | }
57 |
58 |
--------------------------------------------------------------------------------
/tests/OpenTokTest/SessionTest.php:
--------------------------------------------------------------------------------
1 | API_KEY = defined('API_KEY') ? API_KEY : '12345678';
27 | $this->API_SECRET = defined('API_SECRET') ? API_SECRET : 'b60d0b2568f3ea9731bd9d3f71be263ce19f802f';
28 | $this->opentok = new OpenTok($this->API_KEY, $this->API_SECRET);
29 | }
30 |
31 | public function testSessionWithId()
32 | {
33 | $sessionId = 'SESSIONID';
34 | $session = new Session($this->opentok, $sessionId);
35 | $this->assertEquals($sessionId, $session->getSessionId());
36 | $this->assertEquals(MediaMode::ROUTED, $session->getMediaMode());
37 | $this->assertEmpty($session->getLocation());
38 | }
39 |
40 | public function testSessionWithIdAndLocation()
41 | {
42 | $sessionId = 'SESSIONID';
43 | $location = '12.34.56.78';
44 | $session = new Session($this->opentok, $sessionId, array( 'location' => $location ));
45 | $this->assertEquals($sessionId, $session->getSessionId());
46 | $this->assertEquals(MediaMode::ROUTED, $session->getMediaMode());
47 | $this->assertEquals($location, $session->getLocation());
48 | }
49 |
50 | public function testSessionWithIdAndMediaMode()
51 | {
52 | $sessionId = 'SESSIONID';
53 | $mediaMode = MediaMode::RELAYED;
54 | $session = new Session($this->opentok, $sessionId, array( 'mediaMode' => $mediaMode ));
55 | $this->assertEquals($sessionId, $session->getSessionId());
56 | $this->assertEquals($mediaMode, $session->getMediaMode());
57 | $this->assertEmpty($session->getLocation());
58 |
59 | $mediaMode = MediaMode::ROUTED;
60 | $session = new Session($this->opentok, $sessionId, array( 'mediaMode' => $mediaMode ));
61 | $this->assertEquals($sessionId, $session->getSessionId());
62 | $this->assertEquals($mediaMode, $session->getMediaMode());
63 | $this->assertEmpty($session->getLocation());
64 | }
65 |
66 | public function testSessionWithIdAndLocationAndMediaMode()
67 | {
68 | $sessionId = 'SESSIONID';
69 | $location = '12.34.56.78';
70 | $mediaMode = MediaMode::RELAYED;
71 | $session = new Session($this->opentok, $sessionId, array( 'location' => $location, 'mediaMode' => $mediaMode ));
72 | $this->assertEquals($sessionId, $session->getSessionId());
73 | $this->assertEquals($mediaMode, $session->getMediaMode());
74 | $this->assertEquals($location, $session->getLocation());
75 |
76 | $mediaMode = MediaMode::ROUTED;
77 | $session = new Session($this->opentok, $sessionId, array( 'location' => $location, 'mediaMode' => $mediaMode ));
78 | $this->assertEquals($sessionId, $session->getSessionId());
79 | $this->assertEquals($mediaMode, $session->getMediaMode());
80 | $this->assertEquals($location, $session->getLocation());
81 | }
82 |
83 | public function testSessionWithArchiveMode()
84 | {
85 | $sessionId = 'SESSIONID';
86 | $archiveMode = ArchiveMode::ALWAYS;
87 | $session = new Session($this->opentok, $sessionId, array( 'archiveMode' => $archiveMode ));
88 | $this->assertEquals($sessionId, $session->getSessionId());
89 | $this->assertEquals($archiveMode, $session->getArchiveMode());
90 |
91 | $archiveMode = ArchiveMode::MANUAL;
92 | $session = new Session($this->opentok, $sessionId, array( 'archiveMode' => $archiveMode ));
93 | $this->assertEquals($sessionId, $session->getSessionId());
94 | $this->assertEquals($archiveMode, $session->getArchiveMode());
95 | }
96 |
97 | /**
98 | * @dataProvider badParameterProvider
99 | */
100 | public function testInitializationWithBadParams($sessionId, $props)
101 | {
102 | $this->expectException('InvalidArgumentException');
103 | if (!$props || empty($props)) {
104 | $session = new Session($this->opentok, $sessionId);
105 | } else {
106 | $session = new Session($this->opentok, $sessionId, $props);
107 | }
108 | }
109 |
110 | public function badParameterProvider()
111 | {
112 | return array(
113 | array(array(), array()),
114 | array('SESSIONID', array( 'location' => 'NOTALOCATION') ),
115 | array('SESSIONID', array( 'mediaMode' => 'NOTAMODE' ) ),
116 | array('SESSIONID', array( 'location' => '127.0.0.1', 'mediaMode' => 'NOTAMODE' ) ),
117 | array('SESSIONID', array( 'location' => 'NOTALOCATION', 'mediaMode' => MediaMode::RELAYED ) )
118 | );
119 | }
120 |
121 | public function testInitialzationWithoutE2ee()
122 | {
123 | $sessionId = 'SESSIONID';
124 | $session = new Session($this->opentok, $sessionId);
125 | $this->assertEquals(false, $session->getE2EE());
126 | }
127 |
128 | public function testInitialzationWithE2ee()
129 | {
130 | $sessionId = 'SESSIONID';
131 | $session = new Session($this->opentok, $sessionId, ['e2ee' => true]);
132 | $this->assertEquals(true, $session->getE2EE());
133 | }
134 |
135 | public function testInitializationWithExtraneousParams()
136 | {
137 | $sessionId = 'SESSIONID';
138 | $session = new Session($this->opentok, $sessionId, array( 'notrealproperty' => 'notrealvalue' ));
139 | $this->assertEquals($sessionId, $session->getSessionId());
140 | $this->assertEmpty($session->getLocation());
141 | $this->assertEquals(MediaMode::ROUTED, $session->getMediaMode());
142 | }
143 |
144 | public function testCastingToString()
145 | {
146 | $sessionId = 'SESSIONID';
147 | $session = new Session($this->opentok, $sessionId);
148 | $this->assertEquals($sessionId, (string)$session);
149 | }
150 |
151 | public function testGeneratesToken()
152 | {
153 | $sessionId = '1_MX4xMjM0NTY3OH4-VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI';
154 | $bogusApiKey = '12345678';
155 | $bogusApiSecret = 'b60d0b2568f3ea9731bd9d3f71be263ce19f802f';
156 | $opentok = new OpenTok($bogusApiKey, $bogusApiSecret);
157 | $session = new Session($opentok, $sessionId);
158 |
159 | $token = $session->generateToken([], true);
160 |
161 | $this->assertIsString($token);
162 | $decodedToken = TestHelpers::decodeToken($token);
163 | $this->assertEquals($sessionId, $decodedToken['session_id']);
164 | $this->assertEquals($bogusApiKey, $decodedToken['partner_id']);
165 | $this->assertNotEmpty($decodedToken['nonce']);
166 | $this->assertNotEmpty($decodedToken['create_time']);
167 | $this->assertArrayNotHasKey('connection_data', $decodedToken);
168 | // TODO: should all tokens have a role of publisher even if this wasn't specified?
169 | //$this->assertNotEmpty($decodedToken['role']);
170 | // TODO: should all tokens have a default expire time even if it wasn't specified?
171 | //$this->assertNotEmpty($decodedToken['expire_time']);
172 |
173 | $this->assertNotEmpty($decodedToken['sig']);
174 | $this->assertEquals(hash_hmac('sha1', $decodedToken['dataString'], $bogusApiSecret), $decodedToken['sig']);
175 | }
176 | }
--------------------------------------------------------------------------------
/tests/OpenTokTest/SipCallTest.php:
--------------------------------------------------------------------------------
1 | '1_MX4xMjM0NTY3OH4',
13 | 'connectionId' => 'VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI',
14 | 'streamId' => 'b60d0b2568f3ea9731bd9d3f71be263ce19f802f'
15 | ];
16 |
17 | $sipCall = new SipCall($sipCallData);
18 |
19 | $this->assertEquals('1_MX4xMjM0NTY3OH4', $sipCall->id);
20 | $this->assertEquals('VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI', $sipCall->connectionId);
21 | $this->assertEquals('b60d0b2568f3ea9731bd9d3f71be263ce19f802f', $sipCall->streamId);
22 | $this->assertNull($sipCall->observeForceMute);
23 | }
24 | }
--------------------------------------------------------------------------------
/tests/OpenTokTest/TestHelpers.php:
--------------------------------------------------------------------------------
1 | $parts[1]
34 | ));
35 | }
36 |
37 | public static function validateOpenTokAuthHeader($apiKey, $apiSecret, $token)
38 | {
39 | if (!isset($token)) {
40 | return false;
41 | }
42 |
43 | try {
44 | $decodedToken = JWT::decode($token, new Key($apiSecret, 'HS256'));
45 | } catch (\Exception $e) {
46 | return false;
47 | }
48 |
49 | if (!property_exists($decodedToken, 'iss') || $decodedToken->iss !== $apiKey) {
50 | return false;
51 | }
52 |
53 | if (!property_exists($decodedToken, 'ist') || 'project' !== $decodedToken->ist) {
54 | return false;
55 | }
56 |
57 | if (!property_exists($decodedToken, 'exp') || time() >= $decodedToken->exp) {
58 | return false;
59 | }
60 |
61 | if (!property_exists($decodedToken, 'jti')) {
62 | return false;
63 | }
64 |
65 | return true;
66 | }
67 |
68 | public static function mocksToResponses($mocks, $basePath)
69 | {
70 | return array_map(function ($mock) use ($basePath) {
71 | $code = !empty($mock['code']) ? $mock['code'] : 200;
72 | $headers = !empty($mock['headers']) ? $mock['headers'] : [];
73 | $body = null;
74 | if (!empty($mock['body'])) {
75 | $body = $mock['body'];
76 | } elseif (!empty($mock['path'])) {
77 | $body = file_get_contents($basePath . $mock['path']);
78 | }
79 | return new Response($code, $headers, $body);
80 | }, $mocks);
81 | }
82 | }
83 |
84 |
--------------------------------------------------------------------------------
/tests/OpenTokTest/Util/ClientTest.php:
--------------------------------------------------------------------------------
1 | getResponse('connect')
21 | ]);
22 | $handlerStack = HandlerStack::create($mock);
23 | $guzzle = new GuzzleHttpClient(['handler' => $handlerStack]);
24 |
25 | $client = new Client();
26 | $client->configure('asdf', 'asdf', 'http://localhost/', ['client' => $guzzle]);
27 |
28 | $websocketDummy = [
29 | 'uri' => 'ws://test'
30 | ];
31 |
32 | $response = $client->connectAudio('ddd', 'sarar55r', $websocketDummy);
33 | $this->assertEquals('063e72a4-64b4-43c8-9da5-eca071daab89', $response['id']);
34 | $this->assertEquals('7aebb3a4-3d86-4962-b317-afb73e05439d', $response['connectionId']);
35 | }
36 |
37 | public function testHandlesSignalErrorHandles400Response()
38 | {
39 | $this->expectException(SignalUnexpectedValueException::class);
40 |
41 | $mock = new MockHandler([
42 | $this->getResponse('signal-failure-payload', 400)
43 | ]);
44 | $handlerStack = HandlerStack::create($mock);
45 | $guzzle = new GuzzleHttpClient(['handler' => $handlerStack]);
46 |
47 | $client = new Client();
48 | $client->configure('asdf', 'asdf', 'http://localhost/', ['client' => $guzzle]);
49 | $client->signal('sessionabcd', ['type' => 'foo', 'data' => 'bar'], 'connection1234');
50 | }
51 |
52 | public function testHandlesSignalErrorHandles403Response()
53 | {
54 | $this->expectException(SignalAuthenticationException::class);
55 |
56 | $mock = new MockHandler([
57 | $this->getResponse('signal-failure-invalid-token', 403)
58 | ]);
59 | $handlerStack = HandlerStack::create($mock);
60 | $guzzle = new GuzzleHttpClient(['handler' => $handlerStack]);
61 |
62 | $client = new Client();
63 | $client->configure('asdf', 'asdf', 'http://localhost/', ['client' => $guzzle]);
64 | $client->signal('sessionabcd', ['type' => 'foo', 'data' => 'bar'], 'connection1234');
65 | }
66 |
67 | public function testHandlesSignalErrorHandles404Response()
68 | {
69 | $this->expectException(SignalConnectionException::class);
70 | $this->expectExceptionMessage('The client specified by the connectionId property is not connected to the session.');
71 |
72 | $mock = new MockHandler([
73 | $this->getResponse('signal-failure-no-clients', 404)
74 | ]);
75 | $handlerStack = HandlerStack::create($mock);
76 | $guzzle = new GuzzleHttpClient(['handler' => $handlerStack]);
77 |
78 | $client = new Client();
79 | $client->configure('asdf', 'asdf', 'http://localhost/', ['client' => $guzzle]);
80 | $client->signal('sessionabcd', ['type' => 'foo', 'data' => 'bar'], 'connection1234');
81 | }
82 |
83 | public function testHandlesSignalErrorHandles413Response()
84 | {
85 | $this->expectException(SignalUnexpectedValueException::class);
86 | $this->expectExceptionMessage('The type string exceeds the maximum length (128 bytes), or the data string exceeds the maximum size (8 kB).');
87 |
88 | $mock = new MockHandler([
89 | $this->getResponse('signal-failure', 413)
90 | ]);
91 | $handlerStack = HandlerStack::create($mock);
92 | $guzzle = new GuzzleHttpClient(['handler' => $handlerStack]);
93 |
94 | $client = new Client();
95 | $client->configure('asdf', 'asdf', 'http://localhost/', ['client' => $guzzle]);
96 | $client->signal('sessionabcd', ['type' => 'foo', 'data' => 'bar'], 'connection1234');
97 | }
98 |
99 | /**
100 | * Get the API response we'd expect for a call to the API.
101 | */
102 | protected function getResponse(string $type = 'success', int $status = 200): Response
103 | {
104 | return new Response(
105 | $status,
106 | ['Content-Type' => 'application/json'],
107 | fopen(__DIR__ . '/responses/' . $type . '.json', 'rb')
108 | );
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/tests/OpenTokTest/Util/responses/connect.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "063e72a4-64b4-43c8-9da5-eca071daab89",
3 | "connectionId": "7aebb3a4-3d86-4962-b317-afb73e05439d"
4 | }
--------------------------------------------------------------------------------
/tests/OpenTokTest/Util/responses/signal-failure-invalid-token.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": -1,
3 | "message": "Id in the token does not match the one in the url",
4 | "description": "Id in the token does not match the one in the url"
5 | }
--------------------------------------------------------------------------------
/tests/OpenTokTest/Util/responses/signal-failure-no-clients.json:
--------------------------------------------------------------------------------
1 | {
2 | "message": "Not found. No clients are actively connected to the OpenTok session."
3 | }
--------------------------------------------------------------------------------
/tests/OpenTokTest/Util/responses/signal-failure-payload.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": 15202,
3 | "message": "Signal payload 'data' must be set",
4 | "description": "Signal payload 'data' must be set"
5 | }
--------------------------------------------------------------------------------
/tests/OpenTokTest/Util/responses/signal-failure.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": -99,
3 | "message": "Unknown error",
4 | "description": "An unknown error occurred"
5 | }
--------------------------------------------------------------------------------
/tests/OpenTokTest/Validators/ValidatorsTest.php:
--------------------------------------------------------------------------------
1 | assertTrue(Validators::isVonageKeypair($apiKey, $apiSecret));
17 | }
18 |
19 | public function testIsNotVonageKeypair(): void
20 | {
21 | $apiKey = '4349501';
22 | $apiSecret = 'b60d0b2568f3ea9731bd9d3f71be263ce19f802f';
23 | $this->assertFalse(Validators::isVonageKeypair($apiKey, $apiSecret));
24 | }
25 |
26 | public function testVonageKeypairFailsWithOnlyAppId(): void
27 | {
28 | $this->expectException(InvalidArgumentException::class);
29 | $apiKey = '1ab38a10-ed9d-4e2b-8b14-95e52d76a13c';
30 | $apiSecret = 'b60d0b2568f3ea9731bd9d3f71be263ce19f802f';
31 | Validators::isVonageKeypair($apiKey, $apiSecret);
32 | }
33 |
34 | public function testVonageKeypairFailsWithOnlyPrivateKey(): void
35 | {
36 | $this->expectException(InvalidArgumentException::class);
37 | $apiKey = '1ab38a10-ed9d-4e2b-8b14-95e52d76a13c';
38 | $apiSecret = 'b60d0b2568f3ea9731bd9d3f71be263ce19f802f';
39 | Validators::isVonageKeypair($apiKey, $apiSecret);
40 | }
41 |
42 | public function testWillValidateApiUrl(): void
43 | {
44 | $this->expectNotToPerformAssertions();
45 | $apiUrl = 'https://api.opentok.com';
46 | Validators::validateApiUrl($apiUrl);
47 | }
48 |
49 | public function testWillInvalidateApiUrl(): void
50 | {
51 | $this->expectException(InvalidArgumentException::class);
52 | $apiUrl = 'dave@opentok.com';
53 | Validators::validateApiUrl($apiUrl);
54 | }
55 |
56 | public function testWillPassCorrectForceMutePayload(): void
57 | {
58 | $this->expectNotToPerformAssertions();
59 |
60 | $options = [
61 | 'excludedStreams' => [
62 | 'streamId1',
63 | 'streamId2'
64 | ],
65 | 'active' => true
66 | ];
67 |
68 | Validators::validateForceMuteAllOptions($options);
69 | }
70 |
71 | public function testIsAssocWithIndexedArray(): void
72 | {
73 | $array = [1, 2, 3, 4];
74 | $this->assertFalse(Validators::isAssoc($array));
75 | }
76 |
77 | public function testIsAssocWithAssociativeArray(): void
78 | {
79 | $array = ['a' => 1, 'b' => 2, 'c' => 3];
80 | $this->assertTrue(Validators::isAssoc($array));
81 | }
82 |
83 | public function testIsAssocWithMixedKeysArray(): void
84 | {
85 | $array = [1, 'a' => 2, 3];
86 | $this->assertTrue(Validators::isAssoc($array));
87 | }
88 |
89 | public function testIsAssocWithEmptyArray(): void
90 | {
91 | $array = [];
92 | $this->assertFalse(Validators::isAssoc($array));
93 | }
94 |
95 | public function testWillFailWhenStreamIdsAreNotCorrect(): void
96 | {
97 | $this->expectException(InvalidArgumentException::class);
98 |
99 | $options = [
100 | 'excludedStreams' => [
101 | 3536,
102 | 'streamId2'
103 | ],
104 | 'active' => true
105 | ];
106 |
107 | Validators::validateForceMuteAllOptions($options);
108 | }
109 |
110 | public function testWillFailWhenActiveIsNotBool(): void
111 | {
112 | $this->expectException(InvalidArgumentException::class);
113 |
114 | $options = [
115 | 'excludedStreams' => [
116 | 'streamId1',
117 | 'streamId2'
118 | ],
119 | 'active' => 'true'
120 | ];
121 |
122 | Validators::validateForceMuteAllOptions($options);
123 | }
124 |
125 | public function testWillFailWhenStreamIdsIsNotArray(): void
126 | {
127 | $this->expectException(InvalidArgumentException::class);
128 |
129 | $options = [
130 | 'excludedStreams' => 'streamIdOne',
131 | 'active' => false
132 | ];
133 |
134 | Validators::validateForceMuteAllOptions($options);
135 | }
136 |
137 | public function testWillValidateWebsocketConfiguration(): void
138 | {
139 | $this->expectNotToPerformAssertions();
140 | $websocketConfig = [
141 | 'uri' => 'ws://valid-websocket',
142 | 'streams' => [
143 | '525503c7-913e-43a1-84b4-31b2e9fe668b',
144 | '14026813-4f50-4a5a-9b72-fea25430916d'
145 | ]
146 | ];
147 | Validators::validateWebsocketOptions($websocketConfig);
148 | }
149 |
150 | public function testWillThrowExceptionOnInvalidWebsocketConfiguration(): void
151 | {
152 | $this->expectException(InvalidArgumentException::class);
153 |
154 | $websocketConfig = [
155 | 'streams' => [
156 | '525503c7-913e-43a1-84b4-31b2e9fe668b',
157 | '14026813-4f50-4a5a-9b72-fea25430916d'
158 | ]
159 | ];
160 | Validators::validateWebsocketOptions($websocketConfig);
161 | }
162 |
163 | /**
164 | * @dataProvider resolutionProvider
165 | */
166 | public function testValidResolutions($resolution, $isValid): void
167 | {
168 | if ( ! $isValid) {
169 | $this->expectException(InvalidArgumentException::class);
170 | } else {
171 | $this->expectNotToPerformAssertions();
172 | }
173 |
174 | Validators::validateResolution($resolution);
175 | }
176 |
177 | public function testValidLayoutClassListItemErrorOnString(): void
178 | {
179 | $input = 'example_id';
180 | $this->expectException(\InvalidArgumentException::class);
181 | Validators::validateLayoutClassListItem($input);
182 | }
183 |
184 | public function testValidLayoutClassListItem(): void
185 | {
186 | $layoutClassList = [
187 | 'id' => 'example_id',
188 | 'layoutClassList' => ['class1', 'class2']
189 | ];
190 |
191 | $this->assertNull(Validators::validateLayoutClassListItem($layoutClassList));
192 | }
193 |
194 | public function testInvalidIdType(): void
195 | {
196 | $this->expectException(InvalidArgumentException::class);
197 | $layoutClassList = [
198 | 'id' => 123,
199 | 'layoutClassList' => ['class1', 'class2']
200 | ];
201 |
202 | Validators::validateLayoutClassListItem($layoutClassList);
203 | }
204 |
205 | public function testMissingLayoutClassList(): void
206 | {
207 | $this->expectException(InvalidArgumentException::class);
208 | $layoutClassList = [
209 | 'id' => 'example_id'
210 | ];
211 |
212 | Validators::validateLayoutClassListItem($layoutClassList);
213 | }
214 |
215 | public function testInvalidLayoutClassListType(): void
216 | {
217 | $this->expectException(InvalidArgumentException::class);
218 | $layoutClassList = [
219 | 'id' => 'example_id',
220 | 'layoutClassList' => 'invalid_class'
221 | ];
222 |
223 | Validators::validateLayoutClassListItem($layoutClassList);
224 | }
225 | public function testValidateClient(): void
226 | {
227 | $client = new Client();
228 | Validators::validateClient($client);
229 |
230 | // No exception, which was the test so fake a pass
231 | $this->assertTrue(true);
232 | }
233 |
234 | public function testExceptionOnInvalidClient(): void
235 | {
236 | $this->expectException(\InvalidArgumentException::class);
237 | $client = new \stdClass();
238 | Validators::validateClient($client);
239 | }
240 |
241 | public function testThrowsErrorOnInvalidStreamMode(): void
242 | {
243 | $this->expectException(\InvalidArgumentException::class);
244 | $streamMode = ['auto'];
245 | Validators::validateHasStreamMode($streamMode);
246 | }
247 |
248 | public function testValidateSignalPayload(): void
249 | {
250 | $validPayload = ['type' => 'signal_type', 'data' => 'signal_data'];
251 | $this->assertNull(Validators::validateSignalPayload($validPayload));
252 |
253 | $invalidDataPayload = ['type' => 'signal_type', 'data' => 123];
254 | $this->expectException(InvalidArgumentException::class);
255 | $this->expectExceptionMessage("Signal Payload cannot be null:");
256 | Validators::validateSignalPayload($invalidDataPayload);
257 |
258 | $invalidTypePayload = ['type' => null, 'data' => 'signal_data'];
259 | $this->expectException(InvalidArgumentException::class);
260 | $this->expectExceptionMessage("Signal Payload cannot be null:");
261 | Validators::validateSignalPayload($invalidTypePayload);
262 |
263 | // Invalid payload: both type and data are null
264 | $invalidBothPayload = ['type' => null, 'data' => null];
265 | $this->expectException(InvalidArgumentException::class);
266 | $this->expectExceptionMessage("Signal Payload cannot be null:");
267 | Validators::validateSignalPayload($invalidBothPayload);
268 | }
269 |
270 | /**
271 | * @dataProvider connectionIdProvider
272 | */
273 | public function testConnectionId($input, $expectException): void
274 | {
275 | if ($expectException) {
276 | $this->expectException(\InvalidArgumentException::class);
277 | }
278 |
279 | Validators::validateConnectionId($input);
280 | $this->assertTrue(true);
281 | }
282 |
283 | public function connectionIdProvider(): array
284 | {
285 | return [
286 | [['this' => 'is not a string'], true],
287 | ['', true],
288 | ['valid_connection_string', false]
289 | ];
290 | }
291 |
292 | public function resolutionProvider(): array
293 | {
294 | return [
295 | ['640x480', true],
296 | ['1280x720', true],
297 | ['1920x1080', true],
298 | ['480x640', true],
299 | ['720x1280', true],
300 | ['1080x1920', true],
301 | ['1080X1920', true],
302 | ['923x245', false]
303 | ];
304 | }
305 | }
306 |
--------------------------------------------------------------------------------
/tests/OpenTokTest/test.key:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCy3YiiZ206+/7j
3 | oDzF9qGHhFuxEGuGL1ufGm0LvCiOgNJpV4KGatjdminomS0PwI6v9Gz3r4mYBxeR
4 | 3xXV3xPpr3yEDu+ivQN8oMei4ttg++nuyk26gjAdEvz5uSQBV5lWvgjR6tlSPb/j
5 | ca4d5AymuWmT110qcQ+ui7eoOWAfQIWj5Tlqk6YyXUnoMlD4c2c9/hOJZR51VF1n
6 | diDONkHplqjzGfdHxDxCBXsIW1wi8h6PVHH54rDmYay1ojRVPJ7b2RBu8vgvWYoR
7 | du6cY8Bp/1skQUEvtOBhFN62vAZ12s91jFO+plzyA9oDfAXVguW0yRWj5GNJmtPZ
8 | YIcynpjvAgMBAAECggEAM1tzq3n5/Zk0jyRHvum5aKFi+HzH+t/nNVBPpjJxDLXF
9 | dLTJSBIu0bY9uUkeDKtT7QbIMPgokEvdAyfka6PhYlRecsadHQObmDHMEKOFrRu4
10 | CDXzSo2uBfMZSxTTV0VRRHxNKQT/QGN1kPdnsLJ1xXtwaqBIYnLTN2FrqvRKer4+
11 | molQK8v838q4tOVT0ZKjIFHX+zyebKqJDOsCO+jDnrKIw3VTID6iZ0DNwGp5xNJT
12 | HOLiT7CQM7Lg7fMdQp9xQxrGWJFw26cuvewmBuPdZRdqc8indgT5pq+VIZkLeTAB
13 | XMnf0W35abwIUlfMIvhVZOiaeZzMwzNuV/Qp9GPFAQKBgQDzVpWxefxmTyHCWXz4
14 | 2m+wojoxEBgn6GRdvUbji7vyRRlmGZRNVybwqqxXyCq3RYdIwV55Ihk7O0mmO4g+
15 | ESuDchZaMxouzXzDKz8jyoG6Gxymd8QJetnA8ctFwUGnTrK3sCxnTAjphGSf1PlT
16 | vdev+f6T/QT0MT7qxUloDC9hAQKBgQC8LCF6OaFlZdi/kKPFcXbYIxFWRCdiun/A
17 | xAL3+UsMllF53aZcWnIsm9rv0sKhPfrTGCp0eLUyinZzjxzdGDeb5hqafWVArXfT
18 | xj4NC5uQVO2w0HGqB0BpZkBCtiyu25Pw0xPvXlcvwCBYcIDvJuf0hwiGBBKPk1b8
19 | LjlkbfoJ7wKBgQDZsU499gmtZYGoIvLAlnpxJNC2b9WMbkTL77bpfmrntJWiV6Pr
20 | BNrbV3TTG0nLp7H9jrB74dt8t++NfZjHHgk1kO0aSLlVwZOp7piP5mzkF7kr291P
21 | Nc505FubzeZ0TN1po3w19TnL3xs+OgPLvPymfBoaPrMd2qiU02Z2ZOBGAQKBgA9V
22 | GTU4VOpKLisNwgpogGKEGPmKfBsTTy2JyyQhb/gKl4Dyioej5wGzgVdhOPKidjmV
23 | EoCDBWCk35ny40swmfdd/HTyGrn2aHkdAhlWBMrx4Jwzn89W3+y2pC3LYkCtK5TH
24 | 3iv25+vAH+KU6CyUYvoNtqgU1N5WBxRtP8frHiCJAoGAOtq0v8K2KKrEI6L/jHhJ
25 | eNZVUWdGQTZx33vvt5lIboxrNyajaBo25LpmmEAKOhgD61ivHJNrxVDkOMKNx2xt
26 | WnLZvQr7M62SdjWFmcFuY/xRbaX6IOW6C9qps3MdtJeFVw9P6z7udXfXHsCEbQvd
27 | YyR0bVDYLxbCAdYjs7PyCA8=
28 | -----END PRIVATE KEY-----
29 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | addPsr4('OpenTok\\', __DIR__.'/OpenTok');
5 |
6 |
--------------------------------------------------------------------------------
/tests/mock/session/create/alwaysarchived:
--------------------------------------------------------------------------------
1 |