iOS SDK - Low Level APIs
The following low level APIs are provided in the iOS sdk:
- PCast - Use the Phenix platform to Publish and Subscribe to streams
- Room - Use the Phenix platform to broadcast changes between all of your users
- Chat - Use the Phenix platform to send and receive chat messages between users
PCast SDK
Initializing
Before using a PCast object, you need to initialize it.
@import PhenixSdk;
id<PhenixPCast> pcast = [PhenixPCastFactory createPCast];
// #1 Default initialization options
[pcast initialize];
// OR
// #2 Custom initialization options
PhenixPCastInitializeOptions* initOptions = [PhenixPCastInitializeOptions new];
initOptions.enableProcessTerminationSignalHandling = NO;
[pcast initialize:initOptions];
Initialize Options
Name |
Description |
enableProcessTerminationSignalHandling |
Controls if process termination signal handling is enabled or not |
configureLogging |
Control if phenix logging is enabled |
streamingSourceMapping |
Allows partial override of streaming source URIs |
Stream Source Mapping
Optional field that can be provided with PhenixPCastInitializeOptions
. It only has an effect on the subscriber side, and only for streams with the “streaming” capability (on both publisher and subscriber side). The property allows you to redirect requests from the SDKs player to your own CDN.
@import PhenixSdk;
id<PhenixPCastInitializeOptions> initOptions = [PhenixPCastInitializeOptions new];
initOptions.streamingSourceMapping = [PhenixStreamingSourceMapping new];
initOptions.streamingSourceMapping.patternToReplace = @"https:\\/\\/phenixrts\\.com\\/video";
initOptions.streamingSourceMapping.replacement = @"https://myown.cdn.com";
Name |
Description |
patternToReplace |
A regular expression indicating with part of the incoming streaming source URI to replace |
replacement |
The replacement string to insert into the streaming source URI |
Note: The regular expression has to be properly escaped to work correctly.
Connect And Authenticate
@import PhenixSdk;
// Obtained via Admin API
NSString* authenticationToken = ...;
// Previously initialized
id<PhenixPCast> pcast = ...;
[pcast start:
authenticationToken:
^(id<PhenixPCast> pcast, PhenixRequestStatus status, NSString* sessionId) {
if (status == PhenixRequestStatusOk) {
NSLog(@"PCast started...");
} else {
NSLog(@"Failed to start PCast...");
}
}:
^(id<PhenixPCast> pcast) {
NSLog(@"We are online...");
}:
^(id<PhenixPCast> pcast) {
NSLog(@"We are offline...");
}];
Parameters
Name |
Type |
Description |
authenticationToken (required) |
string |
The authentication token generated using the Admin API |
authenticationCallback (required) |
lambda (pcast, status, sessionId) |
Called upon successful authentication or when authentication failed or has to be redone. Upon successful authentication, the authenticationCallback will be called with status=PhenixRequestStatusOk. If at any time a new authenticationToken is required, then the authenticationCallback is called with status=PhenixRequestStatusUnauthorized to indicate that we are no longer authenticated. |
onlineCallback (required) |
lambda (pcast) |
Called when the client is connected to the streaming platform |
offlineCallback (required) |
lambda (pcast) |
Called when the client is disconnected from the streaming platform. Ongoing streams may continue while we are temporarily disconnected. However, no new streams can be started while being disconnected. The client automatically tries to reconnect and will call onlineCallback when it succeeds in doing so or eventually call authenticationCallback to indicate that re-authentication is required. |
Authentication Callback Status Codes
Status |
Valid Fields |
Description |
PhenixRequestStatusOk |
sessionId |
Authentication succeeded, the sessionId is populated |
PhenixRequestStatusUnauthorized |
<none> |
Authentication failed or re-authentication required |
<varies> |
<none> |
Authentication failed for other reasons |
Disconnecting
@import PhenixSdk;
// Previously initialized and started
id<PhenixPCast> pcast = ...;
[pcast stop];
// Once you are done using PCast (e.g. exiting the app)
[pcast shutdown];
@import PhenixSdk;
// Previously initialized and started
id<PhenixPCast> pcast = ...;
PhenixUserMediaOptions* gumOptions = [PhenixUserMediaOptions new]
// Customize options if desired
gumOptions.video.capabilityConstraints
[[NSNumber numberWithInteger:PhenixDeviceCapabilityFacingMode]] =
@[ [PhenixDeviceConstraint initWithFacingMode:PhenixFacingModeUser] ];
gumOptions.video.capabilityConstraints
[[NSNumber numberWithInteger: PhenixDeviceCapabilityFlashMode]] =
@[ [PhenixDeviceConstraint initWithFlashMode:PhenixFlashModeAlwaysOff] ];
gumOptions.video.capabilityConstraints
[[NSNumber numberWithInteger: PhenixDeviceCapabilityHeight]] =
@[ [PhenixDeviceConstraint initWithDouble:720:PhenixConstraintTypeExact] ];
gumOptions.video.capabilityConstraints
[[NSNumber numberWithInteger: PhenixDeviceCapabilityWidth]] =
@[ [PhenixDeviceConstraint initWithDouble:800:PhenixConstraintTypeMin],
[PhenixDeviceConstraint initWithDouble:1500:PhenixConstraintTypeMax] ];
gumOptions.audio.enabled = NO;
[pcast getUserMedia:
gumOptions:
^(id<PhenixPCast> pcast,
PhenixRequestStatus status,
id<PhenixUserMediaStream> userMediaStream) {
// Check status and store 'userMediaStream'
}];
Parameters
Name |
Type |
Description |
options (required) |
PhenixUserMediaOptions |
The options defining the requested user media stream |
userMediaCallback (required) |
lambda (pcast, status, userMediaStream) |
Upon acquiring of the user media stream, the userMediaCallback will be called with status=PhenixRequestStatusOk. If the user media is currently used by another application, then you may receive a code status=PhenixRequestStatusConflict . If the operation fails with status=PhenixRequestStatusFailed then please check the logs for more information |
Device Capability
Note: iPhone models earlier than the iPhone 6 will be limited to publishing at 720p due to hardware limitations.
Constraint Type
Name |
Description |
PhenixConstraintTypeMin |
Hard constraint: Capability must have at least the specified value |
PhenixConstraintTypeMax |
Hard constraint: Capability must have at most the specified value |
PhenixConstraintTypeExact |
Hard constraint: Capability must have exactly the specified value |
PhenixConstraintTypeIdeal |
Soft constraint: Capability should have specified value, but other values are acceptable (default) |
Facing Mode
Name |
Description |
PhenixFacingModeAutomatic |
Select a facing mode automatically (default) |
PhenixFacingModeEnvironment |
Facing the surrounding environment (e.g., back camera) |
PhenixFacingModeUser |
Facing the user (e.g., front camera) |
Flash Mode
Only applicable to video devices
Name |
Description |
PhenixFlashModeAutomatic |
Flash is turned on automatically when needed (default) |
PhenixFlashModeAlwaysOn |
Flash is on (if available) |
PhenixFlashModeAlwaysOff |
Flash is off |
Device Location
Name |
Description |
PhenixLocationAutomatic |
Select any device (default) |
PhenixLocationUpper |
Mounted on top of phone/tablet |
PhenixLocationLower |
Mounted at bottom of phone/tablet |
Polar Pattern
Only applicable to audio devices
Name |
Description |
PhenixPolarPatternAutomatic |
Automatically select pattern (default) |
PhenixPolarPatternOmnidirectional |
Equally sensitive to sound from any direction |
PhenixPolarPatternCardioid |
Most sensitive to sound from the direction in which the data source points and is (nearly) insensitive to sound from the opposite direction |
PhenixPolarPatternSubcardioid |
Most sensitive to sound from the direction in which the data source points and is less sensitive to sound from the opposite direction |
Audio Echo Cancelation Mode
Only applicable to audio devices
Name |
Description |
PhenixAudioEchoCancelationModeAutomatic |
Automatically select AEC (default) |
PhenixAudioEchoCancelationModeOn |
Enable AEC if available |
PhenixAudioEchoCancelationModeOff |
Disabled AEC |
Video source rotation mode
Only applicable to video devices. Determines how to orient captured video frames.
Follow device rotation will ensure that video frames match the orientation in which the user is holding the device, regardless of how the app UI may be oriented. This is generally the behavior expected by a user, i.e. if the user is holding the device sideways, then video should be in landscape mode.
Following UI rotation allows your app to keep the video orientation locked to the UI rotation, regardless of how the user is holding the device. This makes it possible for instance to lock your UI in portrait mode, and have portrait video output even if the user is holding the device sideways.
Name |
Description |
PhenixVideoSourceRotationModeAutomatic |
Automatically select rotation mode (default) |
PhenixVideoSourceRotationModeFollowDeviceRotation |
Video frames oriented according to how device is held |
PhenixVideoSourceRotationModeFollowUiRotation |
Video frames oriented according to UI orientation |
Updating Options
Sometimes you find it useful to change the camera while a stream is running or just would like to turn on the flash light temporarily.
@import PhenixSdk;
// Previously obtained via 'getUserMedia'
id<PhenixUserMediaStream> userMediaStream = ...;
// Previously initialized and used with 'getUserMedia'
PhenixUserMediaOptions* gumOptions;
[gumOptions.video.capabilityConstraints
[[NSNumber numberWithInteger:PhenixDeviceCapabilityFacingMode]][0]
updateFacingMode:PhenixFacingModeEnvironment];
gumOptions.video.capabilityConstraints
[[NSNumber numberWithInteger:PhenixDeviceCapabilityFlashMode]][0]
updateFlashMode: PhenixFlashModeAlwaysOn];
[userMediaStream applyOptions:gumOptions];
Enumerating Source Devices
You can get a list of available source devices.
@import PhenixSdk;
// Previously initialized and started
id<PhenixPCast> pcast = ...;
[pcast enumerateSourceDevices:^(id<PhenixPCast> pcast, NSArray<PhenixSourceDeviceInfo*>* devices) {
// Store devices as needed
}:PhenixMediaTypeVideo];
Parameters
Name |
Type |
Description |
mediaType (required) |
PhenixMediaType |
The media type for which to enumerate source devices |
PhenixSourceDeviceInfo fields
Name |
Type |
Description |
id |
NSString |
Source device ID |
name |
NSString |
Source device Name |
mediaType |
PhenixMediaType |
Source device media type |
deviceType |
PhenixSourceDeviceType |
Source device type |
facingMode |
PhenixFacingMode |
Source device facing mode |
Name |
Description |
PhenixMediaTypeVideo |
Video |
PhenixMediaTypeAudio |
Audio |
Source Device Type
Name |
Description |
PhenixSourceDeviceTypeNull |
Null device (e.g. blank screen or silence) |
PhenixSourceDeviceTypePhysical |
Physical device (e.g. camera or microphone) |
PhenixSourceDeviceTypeSystemOutput |
System output capture (screencast) |
PhenixSourceDeviceTypeSynthetic |
Synthetic source, used for testing |
PhenixSourceDeviceTypeUri |
Uri source, used to stream from uri |
Publish a Stream
@import PhenixSdk;
// Previously initialized and started
id<PhenixPCast> pcast = ...;
// Previously obtained via Admin API
NSString* streamToken = ...;
// Previously obtained via either PCast.subscribe or PhenixUserMediaStream.mediaStream
id<PhenixMediaStream> mediaStream = ...;
NSArray* tags = @[ @"my-tag" ];
[pcast publish:
streamToken:
mediaStream:
^(id<PhenixPCast> pcast, PhenixRequestStatus status, id<PhenixPublisher> publisher) {
// Check status and store 'publisher'
// The "streamId" of the publisher
NSString* streamId = publisher.streamId;
if (publisher.hasEnded == YES) {
// Checks if the publisher has ended
}
// Attach publisher ended callback
[publisher setPublisherEndedCallback:
^(id<PhenixPublisher> publisher,
PhenixStreamEndedReason reason,
NSString* reasonDescription) {
// Called when the stream has ended
NSLog(@"Publish stream ended with reason [%@]", reasonDescription);
}];
// To stop later
[publisher stop:@"I-am-done-publishing"];
}:
tags];
Parameters
Name |
Type |
Description |
streamToken (required) |
string |
The publish token is generated using the Admin API |
mediaStream (required) |
PhenixMediaStream |
The user media stream acquired through PCast.subscribe(…) or locally with PhenixUserMediaStream.mediaStream |
publishCallback (required) |
lambda |
Called upon completion of the operation |
tags (optional) |
array of strings |
Tags that will be provided with the stream notifications to your backend callback endpoint |
PhenixStreamEndedReason
Reason |
Description |
PhenixStreamEndedReasonEnded |
The stream ended normally |
PhenixStreamEndedReasonFailed |
The stream failed |
PhenixStreamEndedReasonCensored |
The stream was censored |
PhenixStreamEndedReasonMaintenance |
A maintenance event caused this stream to be terminated |
PhenixStreamEndedReasonCapacity |
The stream was terminated due to capacity limitations |
PhenixStreamEndedReasonAppBackground |
The stream was terminated due to the mobile app entering into the background |
PhenixStreamEndedReasonCustom |
A custom termination reason is provided in the “reasonDescription” field |
Subscribe to a Stream
@import PhenixSdk;
// Previously initialized and started
id<PhenixPCast> pcast = ...;
// Previously obtained via Admin API
NSString* streamToken = ...;
[pcast subscribe:
streamToken:
^(id<PhenixPCast> pcast,
PhenixRequestStatus status,
id<PhenixMediaStream> mediaStream) {
// Check status and store 'mediaStream'
// Attach stream ended callback
[mediaStream setStreamEndedCallback:
^(id<PhenixMediaStream> mediaStream,
PhenixStreamEndedReason reason,
NSString* reasonDescription) {
NSLog(@"Subscriber stream ended with reason [%@]", reasonDescription);
}];
// To stop later
[mediaStream stop];
}];
Parameters
Name |
Type |
Description |
streamToken (required) |
string |
The publish token is generated using the Admin API |
subscribeCallback (required) |
lambda |
Called upon completion of the operation |
View a Stream
In order to view a stream you have to attach it to a render surface.
@import PhenixSdk;
// Previously obtained via either PCast.subscribe or PhenixUserMediaStream.mediaStream
id<PhenixMediaStream> mediaStream = ...;
id<PhenixRenderer> renderer = [mediaStream createRenderer];
[renderer setRenderSurfaceReadyCallback:
^(id<PhenixRenderer> renderer, CALayer* renderSurface) {
// Attach layer to UI and set fill options and/or resize as needed
// NOTE: This may be called more than once (e.g. after interruptions)
}];
PhenixRendererStartStatus status = [renderer start];
// To stop later
[renderer stop];
Renderer options
It is possible to pass additional options when creating a renderer.
@import PhenixSdk;
// Previously obtained via either PCast.subscribe or PhenixUserMediaStream.mediaStream
id<PhenixMediaStream> mediaStream = ...;
PhenixRendererOptions* options = [PhenixRendererOptions new];
options.aspectRatioMode = PhenixAspectRatioModeFill;
options.useNullVideoDevice = NO;
options.useNullAudioDevice = YES;
id<PhenixRenderer> renderer = [mediaStream createRenderer:options];
Properties
Name |
Type |
Default |
Description |
aspectRatioMode (optional) |
PhenixAspectRatioMode |
PhenixAspectRatioModeFill |
How to fill available video render surface |
useNullAudioDevice (optional) |
BOOL |
false |
Audio will not be routed to a physical renderer device if true |
useNullVideoDevice (optional) |
BOOL |
false |
Video will not be routed to a physical renderer device if true. |
hardwareAcceleratedDecodingMode (optional) |
PhenixHardwareAcceleratedDecodingMode |
PhenixHardwareAcceleratedDecodingModeAutomatic |
Hardware accelerated decoding mode |
When using null devices you can still use the frame-ready API to receive the raw audio and/or video frames.
Aspect Ratio Mode
Name |
Description |
PhenixAspectRatioModeAutomatic |
Defaults to fill |
PhenixAspectRatioModeFill |
Fill entire render area. Video may be truncated |
PhenixAspectRatioModeLetterbox |
Black bars are added on sides or top/bottom of render area, video will not be truncated |
Hardware Accelerated Decoding Mode
Name |
Description |
PhenixHardwareAcceleratedDecodingModeAutomatic |
Use hardware decoding on certified devices |
PhenixHardwareAcceleratedDecodingModeOn |
Always use hardware decoding |
PhenixHardwareAcceleratedDecodingModeOff |
Always use software decoding |
@import PhenixSdk;
// Previously obtained via 'getUserMedia'
id<PhenixUserMediaStream> userMediaStream = ...;
id<PhenixRenderer> renderer = [userMediaStream.mediaStream createRenderer];
Muting and Unmuting of Audio
@import PhenixSdk;
// Previously obtained from media stream
id<PhenixRenderer> renderer = ...;
BOOL isMuted = renderer.audioMuted;
[renderer muteAudio];
[renderer unmuteAudio];
Taking a Screenshot
If you like to show a preview, you can take a still image from a renderer.
@import PhenixSdk;
// Previously obtained from media stream and started
id<PhenixRenderer> renderer = ...;
[renderer setLastVideoFrameRenderedReceivedCallback:
^(id<PhenixRenderer> pcast, CVPixelBufferRef nativeVideoFrame) {
// NOTE: If frame is dispatched to another thread, ensure its reference
// is kept alive by using CVPixelBufferRetain/CVPixelBufferRelease
}];
[renderer requestLastVideoFrameRendered];
Data Quality Feedback
If you like to show the user feedback about how their internet connectivity affects the stream quality, you can listen for data quality notifications.
@import PhenixSdk;
// Previously obtained from media stream and started
id<PhenixRenderer> renderer = ...;
[renderer setDataQualityChangedCallback:
^(id<PhenixRenderer> renderer,
PhenixDataQualityStatus status,
PhenixDataQualityReason reason) {
// Inform user, take action based on status and reason
}];
// Previously obtained via PCast.publish
id<PhenixPublisher> publisher = ...;
[publisher setDataQualityChangedCallback:
^(id<PhenixPublisher> publisher,
PhenixDataQualityStatus status,
PhenixDataQualityReason reason) {
// Inform user, take action based on status and reason
}];
Data Quality Status For Publishers
Status |
Reason |
Description |
PhenixDataQualityStatusNoData |
PhenixDataQualityReasonNone |
The publisher has a bad internet connection and no data is being streamed. |
PhenixDataQualityStatusNoData |
PhenixDataQualityReasonUploadLimited |
The publisher has a bad internet connection and no data is being streamed. |
PhenixDataQualityStatusAll |
PhenixDataQualityReasonNone |
Good internet connection and no quality reduction in effect. |
PhenixDataQualityStatusAll |
PhenixDataQualityReasonUploadLimited |
The publisher has a slow internet connection and the quality of the stream is reduced. |
PhenixDataQualityStatusAll |
PhenixDataQualityReasonNetworkLimited |
Subscribers have bad internet connections and the quality of the stream is reduced. |
PhenixDataQualityStatusAudioOnly |
PhenixDataQualityReasonUploadLimited |
The publisher has a bad internet connection and only audio is streamed. |
PhenixDataQualityStatusAudioOnly |
PhenixDataQualityReasonNetworkLimited |
Subscribers have bad internet connections and only audio is streamed. |
Data Quality Status For Viewers
Status |
Reason |
Description |
PhenixDataQualityStatusNoData |
PhenixDataQualityReasonNone |
The subscriber has a bad internet connection and no data is being received. |
PhenixDataQualityStatusNoData |
PhenixDataQualityReasonDownloadLimited |
The subscriber has a bad internet connection and no data is being received. |
PhenixDataQualityStatusNoData |
PhenixDataQualityReasonPublisherLimited |
The publisher has a bad internet connection and no data is being received. |
PhenixDataQualityStatusNoData |
PhenixDataQualityReasonNetworkLimited |
The network is limiting the quality of the stream and no data is being received. |
PhenixDataQualityStatusAll |
PhenixDataQualityReasonNone |
Good internet connection and no quality reduction in effect. |
PhenixDataQualityStatusAll |
PhenixDataQualityReasonDownloadLimited |
The subscriber has a bad internet connection and the quality of the stream is reduced. |
PhenixDataQualityStatusAll |
PhenixDataQualityReasonPublisherLimited |
The publisher has a bad internet connection and the quality of the stream is reduced. |
PhenixDataQualityStatusAll |
PhenixDataQualityReasonNetworkLimited |
Other subscribers have bad internet connections and the quality of the stream is reduced. |
PhenixDataQualityStatusAudioOnly |
PhenixDataQualityReasonNone |
Audio only stream, good internet connection and no quality reduction in effect. |
PhenixDataQualityStatusAudioOnly |
PhenixDataQualityReasonDownloadLimited |
The subscriber has a bad internet connection and is only receiving audio. |
PhenixDataQualityStatusAudioOnly |
PhenixDataQualityReasonPublisherLimited |
The publisher has a bad internet connection and the subscriber is only receiving audio. |
PhenixDataQualityStatusAudioOnly |
PhenixDataQualityReasonNetworkLimited |
The network is limiting the quality of the stream and the subscriber is only receiving audio. |
Handling Dimension Changes
Cameras may be switched at runtime and devices may be rotated. Register a handler to receive a notification whenever the video dimension changes.
@import PhenixSdk;
// Previously obtained from media stream
id<PhenixRenderer> renderer = ...;
[renderer setVideoDisplayDimensionsChangedCallback:
^(id<PhenixRenderer> renderer, const struct PhenixDimensions* displayDimensions) {
// displayDimensions->width, displayDimensions->height
// Can get called multiple times while rendering a stream
}];
Room Service
Set up a room and manage members, streams, and chat. Some typical uses for rooms include:
- Broadcast video chats between multiple people - few to many
- Large-scale video chat
- Conference calls with integrated video chat
- Real-time web channels for hosting media content. See View a Channel with the Express API
- Town hall meetings
To get started, create a room of the type associated with your need.
Initializing
First you will need to initialize a room service object. This object is used for creating, joining, and leaving a room. As well, use this object for managing the room you enter (active room), members, and the model of your Self.
Note: PhenixPCast must be initialized and started when invoking createRoomService
so that a valud session ID is present.
import PhenixSdk
let pcast: PhenixPCast = ... // previously obtained
let roomService = PhenixRoomServiceFactory.createRoomService(pcast)!
Parameters
Name |
Type |
Description |
pcast (required) |
PhenixPCast |
Instantiated PCast object. Must already be authenticated |
PhenixRoomService
Instantiate Self Member Model
Before entering a room the PhenixRoomService must have a valid model of the Self member. The Self member model may be updated at any time by changing observable values and calling commitChanges. See Update Self Model.
Update Self Model
Broadcast local changes of the Self model to all the members in the room. These changes are sent to our server which forwards the changes to all members in the room. This works whether or not you are currently in a room.
import PhenixSdk
let roomService: PhenixRoomService = ... // previously obtained
let selfMember = roomService.getSelf()
selfMember.getObservableScreenName().setValue("My New Screen Name")
selfMember.getObservableRole().setValue(NSNumber.init(value: PhenixMemberRole.audience.rawValue))
// Commit changes to self if already in a room
selfMember.commitChanges({ (status: PhenixRequestStatus, message: String?) in
if status == .ok {
// Successfully updated self
} else {
// Handle error
}
})
To undo uncommitted local changes, use reload:
import PhenixSdk
let roomService: PhenixRoomService = ... // previously obtained
let selfMember = roomService.getSelf()
selfMember.getObservableScreenName().setValue("My New Screen Name")
selfMember.getObservableRole().setValue(NSNumber.init(value: PhenixMemberRole.audience.rawValue))
// Decided to undo changes
selfMember.reload()
Update Self Callback Arguments
Name |
Type |
Description |
status |
PhenixRequestStatus |
The status of the operation |
message |
String |
Error message, if applicable |
Set Self Streams
The self model must have at least one stream when joining the room as any member role besides Audience.
import PhenixSdk
let roomService: PhenixRoomService = ... // previously obtained
let selfMember = roomService.getSelf()
let selfStream = roomService.createStream("http://myuri.stream", .user, .enabled, .enabled)
selfMember.getObservableStreams().setValue([selfStream!])
// Now commit changes
Parameters
Name |
Type |
Description |
uri (required) |
String |
Unique identifier for stream. |
type (required) |
PhenixStreamType |
Type of stream - see Stream Types |
audioState (required) |
PhenixTrackState |
State of the audio track - see Stream Track States |
videoState (required) |
PhenixTrackState |
State of the video track - see Stream Track States |
Get Room Info
Get room info with the provided room details without joining the room.
import PhenixSdk
let roomService: PhenixRoomService = ... // previously obtained
// Either provide alias or roomId, other can be nil or empty
let roomId = "us-central#demo#exampleRoomId"
let alias: String? = nil
roomService.getRoomInfo(
roomId,
alias,
{ (roomService: PhenixRoomService?, status: PhenixRequestStatus, room: PhenixRoom?) in
if status == .ok {
// Do something with room
} else {
// Handle error
}
})
Parameters
Name |
Type |
Description |
roomId (required) |
String |
ID of the room |
alias (optional) |
String |
Alternative to roomId - alias of the room |
getRoomInfoCallback (required) |
Function |
Callback with the status of the request and the room model |
Get Room Info Callback Arguments
Name |
Type |
Description |
roomService |
PhenixRoomService |
The room service making the callback |
status |
PhenixRequestStatus |
The status of the operation |
room |
PhenixRoom |
Immutable room object |
Get Room Info Callback Status Codes
Status |
Description |
ok |
Get room info succeeded |
not-found |
Get room info failed. Room does not exist - verify room data or create the room |
<varies> |
Get room info failed for other reasons |
Create a Room
Create a room with the provided details
import PhenixSdk
let roomService: PhenixRoomService = ... // previously obtained
let roomName = "My Room Name"
let description = "Deep thoughts only"
let roomOptions = PhenixRoomServiceFactory.createRoomOptionsBuilder()
.withName(roomName)
.withDescription(description)
.withType(.multiPartyChat)
.buildRoomOptions()
roomService.createRoom(
roomOptions,
{ (roomService: PhenixRoomService?, status: PhenixRequestStatus, room: PhenixRoom?) in
if status == .ok {
// Do something with room
} else {
// Handle error
}
})
Parameters
Name |
Type |
Description |
options (required) |
PhenixRoomOptions |
Room creation options |
callback (required) |
Function |
Callback with the status of the request and the room model |
Create Room Callback Arguments
Name |
Type |
Description |
roomService |
PhenixRoomService |
The room service making the callback |
status |
PhenixRequestStatus |
The status of the operation |
room |
PhenixRoom |
Immutable room object |
Create Room Callback Status Codes
Status |
Description |
PhenixRequestStatusOk |
Create room succeeded. This can also mean that the room already existed |
<varies> |
Create room failed for other reasons |
PhenixRoomOptionsBuilder
Name |
Type |
Default |
Description |
withName (required) |
String |
|
Name of room |
withType (required) |
PhenixRoomType |
|
Room type |
withAlias (optional) |
String |
<generated> |
Room Alias. If not passed in, it will be generated for you |
withDescription (optional) |
String |
<empty> |
Room description |
buildRoomOptions |
<none> |
|
Builds the PhenixRoomOptions |
Join a Room
Join a room with the Self model. After successfully joining a room that room becomes the active room and can be returned via the getObservableActiveRoom method.
import PhenixSdk
let roomService: PhenixRoomService = ... // previously obtained
// Need to provide either room ID or alias (other can be nil)
let roomId: String? = nil
let alias = "exampleRoom"
roomService.joinRoom(
roomId,
alias,
{ (roomService: PhenixRoomService?, status: PhenixRequestStatus, room: PhenixRoom?) in
if status == .ok {
// Do something with room
} else {
// Handle error
}
})
Parameters
Name |
Type |
Description |
roomId (required) |
String |
ID of the room |
alias (optional) |
String |
Alternative to roomId - alias of the room |
callback (required) |
Function |
Callback with the status of the request and the room model |
Join Room Callback Arguments
Name |
Type |
Description |
roomService |
PhenixRoomService |
The room service making the callback |
status |
PhenixRequestStatus |
The status of the operation |
room |
PhenixRoom |
Immutable room object |
Join Room Callback Status Codes
Status |
Description |
ok |
Join room succeeded |
not-found |
Join room failed. Room does not exist - create room first |
<varies> |
Join room failed for other reasons |
Subscribe to Room Member Streams
After entering a room you may use the active room to get the observable members and associated metadata. Here we provide an example for accessing each member’s streams whenever the list of members is updated. Remember: Since each observable behaves like a Behavior Rx Subject, your subscriber will always get called back immediately with the current value (in this case the list of members), see Observable.
import PhenixSdk
let roomService: PhenixRoomService = ... // previously obtained
let observableActiveRoom = roomService.getObservableActiveRoom()!
// For this simple example, we assume that there is an active room, hence we can force un-wrap.
// In a production environment, this can return nil if we have not currently joined a room. In that case
// you would want to listen as a subscriber on the 'observableActiveRoom' for updates
let activeRoom = observableActiveRoom.getValue()!
let observableMembers = activeRoom.getObservableMembers()!
let subscription = observableMembers.subscribe({ (change) in
let currentMembers = change?.value as! [PhenixMember]
for member in currentMembers {
let currentStreams = member.getObservableStreams().getValue() as! [PhenixStream]
// Now do something with this member's streams
}
})
// The subscription will remain active for as long as 'subscription' is kept alive
Update Room Model
Broadcast local changes of the Room model to all the members in the room. These changes are sent to our server which forwards the changes to all members in the room.
import PhenixSdk
let roomService: PhenixRoomService = ... // previously obtained
let room = roomService.getObservableActiveRoom().getValue()!
room.getObservableDescription().setValue("My New Room Description")
room.commitChanges({ (status: PhenixRequestStatus, message: String?) in
if status == .ok {
// Successfully updated room
} else {
// Handle error
}
})
Parameters
Name |
Type |
Description |
callback (required) |
Function |
Callback with the status of the request |
Update Room Callback Arguments
Name |
Type |
Description |
status |
PhenixRequestStatus |
The status of the operation |
message |
String |
Error message, if applicable |
Update Room Callback Status Codes
Status |
Description |
ok |
Update room succeeded |
<varies> |
Update room failed for other reasons |
Leave a Room
Leave the current active room. Self is not modified.
import PhenixSdk
let roomService: PhenixRoomService = ... // previously obtained
roomService.leaveRoom({ (roomService: PhenixRoomService?, status: PhenixRequestStatus) in
if status == .ok {
// Successfully left room, no action required
} else {
// Handle error
}
})
Parameters
Name |
Type |
Description |
callback (required) |
Function |
Callback with the status of the request |
Leave Room Callback Arguments
Name |
Type |
Description |
roomService |
PhenixRoomService |
The room service making the callback |
status |
PhenixRequestStatus |
The status of the operation |
Update Room Callback Status Codes
Status |
Description |
ok |
Leave room succeeded |
<varies> |
Leave room failed for other reasons |
Destroy a Room
Destroys the current active room assuming your self member has the permissions.
import PhenixSdk
let roomService: PhenixRoomService = ... // previously obtained
roomService.destroyRoom({ (roomService: PhenixRoomService?, status: PhenixRequestStatus) in
if status == .ok {
// Successfully destroyed room, no action required
} else {
// Handle error
}
})
Parameters
Name |
Type |
Description |
callback (required) |
Function |
Callback with the status of the request |
Leave Room Callback Arguments
Name |
Type |
Description |
roomService |
PhenixRoomService |
The room service making the callback |
status |
PhenixRequestStatus |
The status of the operation |
Update Room Callback Status Codes
Status |
Description |
ok |
Destroy room succeeded |
<varies> |
Destroy room failed for other reasons |
Observe Room size
Subscribe to observable to receive periodic room size updates. The room size corresponds to the total number of members currently in the room, which does include audience members.
This is a so called cold observable, which means that you may not immediately receive an update. It also means that both getValue
and setValue
methods will cause a failure.
Updates will be provided for as long as the disposable
reference is kept alive.
import PhenixSdk
let room: PhenixImmutableRoom = ... // previously obtained
let disposable = room.getObservableEstimatedSize().subscribe(
{ (change: PhenixObservableChange<NSNumber>?) in
let currentEstimatedRoomSize = change?.value.uintValue
// E.g. update UI
})
Room Model
The following details technical specifications for the Room, Members, and Streams.
PhenixImmutableRoom
Name |
Signature |
Returns |
Description |
getRoomId |
() |
NSString |
The room ID |
getObservableAlias |
() |
Observable(of NSString) |
Observable of room alias |
getObservableName |
() |
Observable(of NSString) |
Observable of room name |
getObservableDescription |
() |
Observable(of NSString) |
Observable of room description |
getObservableType |
() |
Observable(of PhenixRoomType via NSNumber) |
Observable of room type |
getObservableMembers |
() |
Observable(of NSArray of PhenixMember) |
Observable of array of visible members in the room |
getObservableBridgeId |
() |
Observable(of NSString) |
Observable of room bridgeId |
getObservablePin |
() |
Observable(of NSString) |
Observable of room pin |
getObservableEstimatedSize |
() |
Observable(of size_t) |
Observable of estimated room size. This is a cumulative count of all members in the room, including audience members |
PhenixRoom
PhenixRoom extends PhenixImmutableRoom
Name |
Signature |
Returns |
Description |
commitChanges |
(RoomCommitCallback) |
void |
Attempts to update server room model with any changes made locally |
reload |
() |
void |
Reverts all local changes to the server room model |
Room Types
Type |
Description |
PhenixRoomTypeDirectChat |
One to one chat |
PhenixRoomTypeMultiPartyChat |
Many to many or few to many chat |
PhenixRoomTypeModeratedChat |
Moderated chat with integrated SIP Bridge for conferencing abilities (join by phone). |
PhenixRoomTypeTownHall |
Chat with a few active participants or presenters and many audience or inactive participants |
PhenixRoomTypeChannel |
View only room with a single active member at a time. Broadcast content to many viewers. |
PhenixMember
Name |
Signature |
Returns |
Description |
getSessionId |
() |
NSString |
The session ID of the member in the room |
getObservableRole |
() |
Observable(of PhenixMemberRole via NSNumber) |
Observable of member role |
getObservableState |
() |
Observable(of PhenixMemberState via NSNumber) |
Observable of member state |
getObservableScreenName |
() |
Observable(of NSString) |
Observable of member screen name |
getObservableStreams |
() |
Observable(of NSArray of PhenixStream) |
Observable of array of streams belonging to the member |
getObservableLastUpdate |
() |
Observable(of NSDate) |
Observable of utc timestamp |
commitChanges |
(MemberCommitCallback) |
void |
Attempts to update server member model with any changes made locally |
reload |
() |
void |
Reverts all local changes to the server member model |
Member Roles
Role |
Description |
PhenixMemberRoleParticipant |
Member with streams and is visible to all members |
PhenixMemberRoleAudience |
Member with no streams and is not visible to other members |
PhenixMemberRolePresenter |
Presenter participant member |
PhenixMemberRoleModerator |
Privileged participant member |
Member States
Role |
Description |
PhenixMemberStateActive |
Member is active |
PhenixMemberStatePassive |
Member is not active |
PhenixMemberStateHandRaised |
Member is requesting to become active |
PhenixMemberStateInactive |
Member is not away |
PhenixMemberStateOffline |
Member has left the room |
PhenixStream
The stream object may be used to represent any value that will be shared between all members.
Name |
Signature |
Returns |
Description |
getUri |
() |
NSString |
The stream uri (may or may not be associated with phenix stream id) |
getType |
() |
PhenixStreamType |
The stream type |
getObservableAudioState |
() |
Observable(of PhenixTrackState via NSNumber) |
Observable of stream video state |
getObservableVideoState |
() |
Observable(of PhenixTrackState via NSNumber) |
Observable of stream audio state |
Stream Types
Type |
Description |
PhenixStreamTypeUser |
General purpose stream |
PhenixStreamTypePresentation |
Privileged stream |
PhenixStreamTypeAudio |
Audio-only stream |
Stream Track States
State |
Description |
PhenixTrackStateEnabled |
Track is enabled |
PhenixTrackStateDisabled |
Track is disabled |
PhenixTrackStateEnded |
Track has ended |
Chat Service
The Chat Service provides the ability to:
- Send messages to a room
- Receive messages from a room
- Get a room’s chat message history
All room members that are subscribed to the chat will receive the text messages. You must first enter the room before you may send and receive messages.
Initializing
First, instantiate the chat service. A Room Service is required to obtain the chat service.
import PhenixSdk
let roomService: PhenixRoomService = ... // previously obtained
let chatService = PhenixRoomChatServiceFactory.createRoomChatService(roomService)!
// Use a custom batch size, i.e. max number of message returned by 'getObservableChatMessages'
let batchSize: UInt = 10
let chatServiceWithBatchSize = PhenixRoomChatServiceFactory.createRoomChatService(roomService, batchSize)!
PhenixRoomChatService
Name |
Signature |
Returns |
Description |
getObservableChatMessages |
() |
Observable(of NSArray of PhenixChatMessage) |
Get the observable of the most recent chat messages. This is a buffer of the most recent N messages up to a max of 100. See Listen for Chat Messages |
getObservableChatEnabled |
() |
Observable(of bool via NSNumber) |
Get the observable of the enabled status of the chat service. Use this to disable or enable the chat. |
sendMessageToRoom |
(message, callback) |
void |
Send a message to room. |
getMessages |
(batchSize, afterMessageId, beforeMessageId, callback) |
void |
Get chat message history |
PhenixChatMessage
Name |
Type |
Description |
getMessageId |
NSString |
Unique ID of message |
getObservableTimeStamp |
Observable(of NSDate) |
Server UTC timestamp of message |
getObservableFrom |
Observable(of PhenixChatUser) |
Information on the member that sent the message |
getObservableMessage |
Observable(of NSString) |
Chat message |
PhenixChatUser
Name |
Type |
Description |
getSessionId |
NSString |
Member’s session ID |
getObservableScreenName |
Observable(of NSString) |
Screen name of the member |
getObservableMemberRole |
Observable(of PhenixMemberRole) |
Member’s role. See Member Roles |
getObservableLastUpdate |
Observable(of NSDate) |
Last time member was updated as UTC timestamp |
Listen for Chat Messages in the Active Room
The chat service exposes an observable of the most recent N (batchSize) messages in the active room up to a max of 100 messages. To view older messages you will need to use the getMessages method.
import PhenixSdk
let roomService: PhenixRoomService = ... // previously obtained
let batchSize: UInt = 10
let chatService = PhenixRoomChatServiceFactory.createRoomChatService(roomService, batchSize)!
let chatMessageObservable = chatService.getObservableChatMessages();
var subscription = chatMessageObservable?.subscribe({ change in
if let change = change {
let messages = change.value as! [PhenixChatMessage]
// Do something with chat messages
}
})
Send a Message to the Active Room
Send a single message to the active room. If the message is successfully sent the chatMessageObservable will trigger notifications on all subscribers with the new chat message.
import PhenixSdk
let chatService: PhenixRoomChatService = ... // previously obtained
// Without callback:
chatService.sendMessage(toRoom: "My First Message")
// With callback
chatService.sendMessage(toRoom: "My First Message") { (status: PhenixRequestStatus, message: String?) in
if status != .ok {
// handle error - send message again
}
}
Parameters
Name |
Type |
Description |
message (required) |
String |
Message to send to the room |
sendMessageCallback (optional) |
Function |
Callback with the status of the request |
Send Message Callback Arguments
Name |
Type |
Description |
status |
PhenixRequestStatus |
The status of the operation |
message |
NSString |
Optional additional error status in case of a failure |
Send Message Callback Status Codes
Status |
Description |
ok |
Send message succeeded |
<varies> |
Send message failed for other reasons |
Chat Message History
Get messages older than the most recent N buffered in the chatMessageObservable.
Get the 10 most recent messages after a message:
import PhenixSdk
let chatService: PhenixRoomChatService = ... // previously obtained
let batchSize: UInt32 = 10
let afterMessageId = "MessageId1"
let beforeMessageId: String? = nil
chatService.getMessages(batchSize, afterMessageId, beforeMessageId) {
(chatService: PhenixRoomChatService?, messages: [PhenixChatMessage]?, status: PhenixRequestStatus) in
if status == .ok, let messages = messages {
// Successfully got chat messages
} else {
// handle error - request messages again
}
}
Get all messages between two messages:
import PhenixSdk
let chatService: PhenixRoomChatService = ... // previously obtained
let batchSize: UInt32 = 100
let afterMessageId = "MessageId1"
let beforeMessageId = "MessageId2"
chatService.getMessages(batchSize, afterMessageId, beforeMessageId) {
(roomChatService: PhenixRoomChatService?, messages: [PhenixChatMessage]?, status: PhenixRequestStatus) in
if status == .ok, let messages = messages {
// Successfully got chat messages
} else {
// handle error - request messages again
}
}
Parameters
Name |
Type |
Description |
batchSize (required) |
UInt32 |
Limit of messages to receive. |
afterMessageId (optional) |
NSString |
The messageId to return after. The N messages after message with ID of afterMessageId but before beforeMessageId |
beforeMessageId (optional) |
NSString |
The messageId to return before. The N messages before message with ID of beforeMessageId |
callback (required) |
Function |
Callback with the status of the request |
Get Messages Callback Arguments
Name |
Type |
Description |
roomChatService |
PhenixRoomChatService |
The chat service making the callback |
messages |
NSArray of PhenixChatMessage |
Array of chat messages matching search criteria |
status |
PhenixRequestStatus |
The status of the operation |
Get Messages Callback Status Codes
Status |
Description |
ok |
Get messages succeeded |
<varies> |
Get messages failed for other reasons |
Observable
Observe value changes. Similar to the ReactiveX Behavior Subject.
Important differences for Phenix SDK observables:
- There is always an initial value (which can be
nil
for some objects)
- Only if observed value changes will subscribers get notified
Point 1 means that when subscribing, you will always get notified immediately with the current value. This is guaranteed atomic, meaning that you do not have to worry about missing any observable updates.
PhenixObservable
Subscribe to an Observable
Receive a notification every time the observable value changes.
@import PhenixSdk;
PhenixObservable<NSString*>* observable = ...; // previously obtained
id<PhenixDisposable> subscription =
[observable subscribe:^(PhenixObservableChange<NSString*>* change) {
NSString* newStringValue = change.value;
// Do something with newStringValue
}];
// Releasing subscription will cancel it:
subscription = nil;
import PhenixSdk
let observable: PhenixObservable<String> = ... // previously obtained
var subscription = observable.subscribe({ change in
if let change = change, let newStringValue = change.value {
// Do something with newStringValue
})
// Releasing subscription will cancel it:
subscription = nil
Parameters
Name |
Type |
Description |
onChanged (required) |
Function |
Function to call when value changes |
PhenixDisposable
Has no methods. Dropping the last reference will automatically cancel the subscription.
Get Observable Value
Synchronous method allowing retrieval of currently set value. Thread safe.
Note: Since enums are not full objects, they are proxied via NSNumber
.
@import PhenixSdk;
PhenixObservable<NSNumber*>* roleObservable = ...; // previously obtained
PhenixMemberRole role = (PhenixMemberRole)[roleObservable getValue].intValue;
PhenixObservable<NSString*>* stringObservable = ...; // previously obtained
NSString* stringValue = [stringObservable getValue];
import PhenixSdk
let roleObservable: PhenixObservable<NSNumber> = ... // previously obtained
let role = PhenixMemberRole(rawValue: roleObservable.getValue().intValue)
let stringObservable: PhenixObservable<NSString> = ... // previously obtained
let stringValue: String = stringObservable().getValue() as String
In swift, observables containing NSArray
need to be cast further:
import PhenixSdk
let member: PhenixMember = ... // previously obtained
let observable: PhenixObservable<NSArray> = (member.getObservableStreams())!
let streams: [PhenixStream] = observable.getValue() as! [PhenixStream]
Set Observable Value
Change the observable value. This will trigger notifications on all subscribers.
Enums require special attention when being set.
@import PhenixSdk;
PhenixObservable<NSNumber*>* roleObservable = ...; // previously obtained
[roleObservable setValue:[NSNumber numberWithInt:PhenixMemberRolePresenter]];
PhenixObservable<NSString*>* stringObservable = ...; // previously obtained
[stringObservable setValue:@"new value"];
import PhenixSdk
let roleObservable: PhenixObservable<NSNumber> = ... // previously obtained
roleObservable.setValue(NSNumber(value: PhenixMemberRole.presenter.rawValue))
let stringObservable: PhenixObservable<NSString> = ... // previously obtained
stringObservable.setValue("new value")