NAV Navbar
Phenix logo
Version 2022.0.9
ObjectiveC Swift

iOS SDK

The iOS SDK APIs are split into two main categories: Express and Low-Level. We recommend that you start with the Express API as this:

  1. Simplifies integration
  2. Automatically recovers from stream failures
  3. Handles edge cases such as network reconnects

For more information see the Express API

If you find that the Express API doesn’t work for you, take a look at our Lower Level API;

Setup

The Phenix SDK is provided as private Cocoapod. Please check our private Github repository for details.

Debugging

In order to identify issues it can be helpful to see what is happening at the Phenix SDK level. Phenix logs are disabled by default in the Xcode console. To enable them, set the following environment variable (via Xcode menu: Product -> Scheme -> Edit Scheme… -> Run -> Arguments -> Environment Variables):

Name: PHENIX_LOGGING_CONSOLE_LOG_LEVEL Value: Debug

To reduce output, you can change the value to Info or Warn for instance. You will only be able to view these logs when either running in the simulator or with a device attached to Xcode.

You can also retrieve the Phenix SDK logs programmatically using the collectLogMessages PCast API.

@import PhenixSdk;

id<PhenixPCast> pcast = ...;  // previously obtained

[pcast collectLogMessages:^(id<PhenixPCast> pcast, PhenixRequestStatus status, NSString* messages) {
    if (status != PhenixRequestStatusOk) {
      // Handle error
      return;
    }

    NSArray* messagesArray = [messages componentsSeparatedByString:@"\n"];
    for (NSString* message in messagesArray) {
      // Do something with 'message'
    }
  }];
import PhenixSdk

let pcast: PhenixPCast = ...  // previously obtained

pcast.collectLogMessages({ (pcast: PhenixPCast?, status: PhenixRequestStatus, messages: String?) in
    guard let messages = messages, status == .ok else {
      // Handle error
      return
    }

    let messagesArray = messages.components(separatedBy: "\n")
    for message in messagesArray {
      // Do something with 'message'
    }
})

Notes:

Express iOS SDK

Our Express APIs provide a simple, single-step API for the easiest integration of streaming into your application

Common Use Cases

Channel Express

Single-step configuration based API for setting up a channel - a one-size-fits-all solution for all your one-to-many streaming applications. The Channel Express extends the lower-level Room Service API to provide easy solutions to:

Initializing

import PhenixSdk

let backendEndpointUri = "https://example.yourdomain.com/phenix/"

let pcastExpressOptions = PhenixPCastExpressFactory.createPCastExpressOptionsBuilder()
    .withBackendUri(backendEndpointUri)
    .buildPCastExpressOptions()

let roomExpressOptions = PhenixRoomExpressFactory.createRoomExpressOptionsBuilder()
    .withPCastExpressOptions(pcastExpressOptions)
    .buildRoomExpressOptions()

let channelExpressOptions = PhenixChannelExpressFactory.createChannelExpressOptionsBuilder()
    .withRoomExpressOptions(roomExpressOptions)
    .buildChannelExpressOptions()

let channelExpress = PhenixChannelExpressFactory.createChannelExpress(channelExpressOptions)

ChannelExpressOptionsBuilder

Name Type Default Description
withRoomExpressOptions (required) PhenixRoomExpressOptions See PhenixRoomExpressOptionsBuilder
buildChannelExpressOptions <none> Builds the PhenixChannelExpressOptions

View a Channel

Join a channel and automatically view the most recent content published to that channel.

import PhenixSdk

let channelExpress: PhenixChannelExpress = ...  // previously obtained
let renderLayer: CALayer = ...  // previously obtained

// Just an example (you can omit renderer options if defaults are ok)
let rendererOptions = PhenixRendererOptions()
options.aspectRatioMode = .letterbox

let joinRoomOptions = PhenixRoomExpressFactory.createJoinRoomOptionsBuilder()
    .withRoomAlias("MyAwesomeChannel")
    .buildJoinRoomOptions()

let joinChannelOptions = PhenixChannelExpressFactory.createJoinChannelOptionsBuilder()
    .withJoinRoomOptions(joinRoomOptions)
    .withStreamSelectionStrategy(.mostRecent)
    .withRenderer(renderLayer)
    .withRendererOptions(rendererOptions)
    .buildJoinChannelOptions()

channelExpress.joinChannel(
    joinChannelOptions,
    { [weak self] (requestStatus: PhenixRequestStatus, roomService: PhenixRoomService?) in
      guard requestStatus == .ok, let strongSelf = self else {
        // Handle channel join error
        return
      }

      // Important: Store room service reference, otherwise we will leave channel again immediately:
      strongSelf.currentRoomService = roomService
    },
    { [weak self]
        (requestStatus: PhenixRequestStatus,
         expressSubscriber: PhenixExpressSubscriber?,
         renderer: PhenixRenderer?) in
      guard let strongSelf = self else {
        return
      }
      if (requestStatus == .ok) {
        // Successfully subscribed to a stream. No need to hold on to any references
      } else if (requestStatus == .noStreamPlaying) {
        // No stream playing in channel, update UI accordingy
      } else {
        // We failed to subscribe and retry attempts must have failed
      }
    });

Parameters

Name Type Description
options (required) Options Options to join channel with
joinChannelCallback (required) Function Function to call on success/failure of joining the channel.
subscriberCallback (required) Function Function to call on when the most recent presenter changes.

PhenixJoinChannelOptionsBuilder

Name Type Default Description
withJoinRoomOptions (required) PhenixJoinRoomOptions See PhenixJoinRoomOptionsBuilder.
withRenderer (optional) CALayer Render layer on which to display stream. If none of the withRenderer... methods are called, no renderer will be instantiated.
withRenderer (optional) <none> Will trigger instantiation of renderer. Useful for audio only type streams that do not require a render surface.
withRendererOptions (optional) PhenixRendererOptions Options passed to renderer. Will trigger instantiation of renderer.
withStreamSelectionStrategy (optional) PhenixStreamSelectionStrategy PhenixStreamSelectionStrategyMostRecent Determines how member streams are selected for subscriptions.
buildJoinChannelOptions <none> Builds the PhenixJoinChannelOptions

Stream Selection Strategy

Strategy Description
PhenixStreamSelectionStrategyMostRecent Select the most recent stream. Viewing stream changes any time a stream starts or is updated in the room.
PhenixStreamSelectionStrategyHighAvailability Select streams for increased reliability and redundancy. Viewing stream will only change in the event of a failure of the prior selected stream.

Express Join Channel Callback Arguments

Name Type Description
status PhenixRequestStatus The status of the operation
roomService PhenixRoomService Phenix room service object

View Channel Subscriber Callback Status Codes

Status Description
PhenixRequestStatusOk Successfully subscribed to presenter
PhenixRequestStatusNoStreamPlaying No presenter in channel to subscribe to. Wait for presenter to join.
<varies>

Publish to a Channel

Publish a local or remote media to a channel. Users that are viewing the channel will see your media.

import PhenixSdk

let channelExpress: PhenixChannelExpress = ...  // previously obtained
let renderLayer: CALayer = ...  // previously obtained

// Using ChannelOptions means that the channel may or may not already exist.
// If the channel ID is known in advance, it is recommended to use `withChannelId` instead
// of `withChannelOptions` when assembling the `PhenixPublishToChannelOptions` below
let channelOptions = PhenixRoomServiceFactory.createChannelOptionsBuilder()
    .withName("MyAwesomeChannel")
    // Not required but if it is provided we will use this as the alias instead
    // of pre-generating one for you:
    .withAlias("MyAwesomeChannelAlias")
    .buildChannelOptions()

// Example constraints. Audio and video are enabled by default
let mediaConstraints = PhenixUserMediaOptions()
mediaConstraints.video.capabilityConstraints[PhenixDeviceCapability.facingMode.rawValue] =
    [PhenixDeviceConstraint.initWith(.user)]
mediaConstraints.video.capabilityConstraints[PhenixDeviceCapability.frameRate.rawValue] =
    [PhenixDeviceConstraint.initWith(15)]
mediaConstraints.video.capabilityConstraints[PhenixDeviceCapability.height.rawValue] =
    [PhenixDeviceConstraint.initWith(720)]
mediaConstraints.video.capabilityConstraints[PhenixDeviceCapability.width.rawValue] =
    [PhenixDeviceConstraint.initWith(1280)]

mediaConstraints.audio.capabilityConstraints[PhenixDeviceCapability.audioEchoCancelationMode.rawValue] =
    [PhenixDeviceConstraint.initWith(PhenixAudioEchoCancelationMode.on)]

let publishOptions = PhenixPCastExpressFactory.createPublishOptionsBuilder()
    .withCapabilities(["hd", "streaming"])  // Example capabilities
    .withMediaConstraints(mediaConstraints)
    .withPreviewRenderer(renderLayer)
    .buildPublishOptions()

let publishToChannelOptions = PhenixChannelExpressFactory.createPublishToChannelOptionsBuilder()
    .withChannelOptions(channelOptions)
    .withPublishOptions(publishOptions)
    .buildPublishToChannelOptions()

channelExpress.publish(
    toChannel: publishToChannelOptions,
    withPreviewCallback: { [weak self] (
        status: PhenixRequestStatus,
        roomService: PhenixRoomService?,
        publisher: PhenixExpressPublisher?,
        previewRenderer: PhenixRenderer?) in
      guard status == .ok, let strongSelf = self else {
        // Handle channel publish error
        return
      }

      // Important: Store publisher reference, otherwise we will stop publishing again immediately:
      strongSelf.currentPublisher = publisher
    })

// OR (without a preview):

channelExpress.publish(
    toChannel: publishToChannelOptions,
    withCallback: { [weak self] (
        status: PhenixRequestStatus,
        roomService: PhenixRoomService?,
        publisher: PhenixExpressPublisher?) in
      guard status == .ok, let strongSelf = self else {
        // Handle channel publish error
        return
      }

      // Important: Store publisher reference, otherwise we will stop publishing again immediately:
      strongSelf.currentPublisher = publisher
    })

Parameters

Name Type Description
options (required) Options Options to publish to channel with
publisherCallback (required) Function Function to call on success/failure of publishing to the channel

PhenixPublishToChannelOptionsBuilder

Name Type Default Description
withChannelOptions (required) PhenixChannelOptions See PhenixChannelOptionsBuilder. If omitted, then withChannelId needs to be provided.
withChannelId (required) String ID of channel to publish to. If omitted, then withChannelOptions needs to be provided.
withPublishOptions (required) PhenixPublishOptions Either provide this or remote publish options.
withPublishRemoteOptions (required) PhenixRemotePublishOptions Either provide this or publish options.
withMemberRole (optional) PhenixMemberRole PhenixMemberRolePresenter Role of member to join channel as (used if not already in channel).
withStreamType (optional) PhenixStreamType PhenixStreamTypePresentation Type of stream to publish
withScreenName (optional) String <random unique string> Your screen name
withViewerStreamSelectionStrategy (optional) PhenixStreamSelectionStrategy PhenixStreamSelectionStrategyMostRecent This has to match the strategy channel viewers are using. When PhenixStreamSelectionStrategyHighAvailability is selected, then wildcard tokens will be generated for this as well as other streams published in this channel to ensure viewers can subscribe to any of them without having to first obtain a stream token.
buildPublishToChannelOptions <none> Builds the PhenixPublishToChannelOptions

Notes: * Wildcard token generation is always enabled when publishing to a channel

PhenixChannelOptionsBuilder

Name Type Default Description
withName (required) String Name of channel
withAlias (optional) String <generated> Channel Alias. If not passed in, it will be generated for you.
withDescription (optional) String <empty> Channel description
buildChannelOptions <none> Builds the PhenixChannelOptions

Publish To Channel Callback Arguments

Name Type Description
status PhenixRequestStatus The status of the operation
roomService RoomService Phenix room service
publisher PhenixExpressPublisher Phenix publisher object

Create a Channel

import PhenixSdk

let channelExpress: PhenixChannelExpress = ...  // previously obtained

let channelOptions = PhenixRoomServiceFactory.createChannelOptionsBuilder()
    .withName("MyAwesomeChannel")
    .withAlias("MyAwesomeChannelAlias")  // Not required but if it is provided we will use this as the alias instead
                                         // of pre-generating one for you.
    .buildChannelOptions()

channelExpress.createChannel(
    channelOptions,
    { (status: PhenixRequestStatus, channel: PhenixImmutableRoom?) in
      guard status == .ok else {
        // Handle room create error
        return
      }

      // use `channel` to e.g. join
    })

Parameters

Name Type Description
options (required) PhenixChannelOptions Options to create channel with
callback (required) Function Function to call on success/failure of creating to the channel. See Create Channel Callback Arguments

Express Create Channel Callback Arguments

Name Type Description
status PhenixRequestStatus The status of the operation.
channel PhenixImmutableRoom Immutable Phenix room object

Room Express

Single-step configuration based API for setting up a room - a solution for all your other streaming applications including Group Broadcast, Group Chat, and One to One Chat. The Room Express extends the lower-level Room Service API to provide easy solutions to:

Initializing

import PhenixSdk

let backendEndpointUri = "https://example.yourdomain.com/phenix/"

let pcastExpressOptions = PhenixPCastExpressFactory.createPCastExpressOptionsBuilder()
    .withBackendUri(backendEndpointUri)
    .buildPCastExpressOptions()

let roomExpressOptions = PhenixRoomExpressFactory.createRoomExpressOptionsBuilder()
    .withPCastExpressOptions(pcastExpressOptions)
    .buildRoomExpressOptions()

let roomExpress = PhenixRoomExpressFactory.createRoomExpress(roomExpressOptions)

PhenixRoomExpressOptionsBuilder

Name Type Default Description
withPCastExpressOptions (required) PhenixPCastExpressOptions See PhenixPCastExpressOptionsBuilder
buildRoomExpressOptions <none> Builds the PhenixRoomExpressOptions

Join a Room

Join a room and optionally, automatically subscribe to member changes.

import PhenixSdk

let roomExpress: PhenixRoomExpress = ...  // previously obtained

let joinRoomOptions = PhenixRoomExpressFactory.createJoinRoomOptionsBuilder()
    .withRoomAlias("myRoom42")
    .withCapabilities(["streaming"])
    .buildJoinRoomOptions()

roomExpress.joinRoom(joinRoomOptions, { (status: PhenixRequestStatus, roomService: PhenixRoomService?) in
    if status == .ok {
      // Hold on to roomService reference for as long as you wish to stay in the room
    } else {
      // Handle error
    }
  })

// With optional member update notification:
roomExpress.joinRoom(
    joinRoomOptions,
    { (status: PhenixRequestStatus, roomService: PhenixRoomService?) in
      if status == .ok {
        // Hold on to roomService reference for as long as you wish to stay in the room
      } else {
        // Handle error
      }
    },
    { (members: [PhenixMember]?) in
      // Do something with room members
    })

Parameters

Name Type Description
options (required) PhenixJoinRoomOptions Options to join room with
joinRoomCallback (required) Function Function to call on success/failure of joining the room
membersChangedCallback (optional) Function Function to call on when the participant members in the room changes. Returns array of Members. Callback is guaranteed to be called at least once when room is joined.

PhenixJoinRoomOptionsBuilder

Name Type Default Description
withRoomId (required) NSString Id of channel to view
withRoomAlias (optional) NSString Alias, alternative to ID
withCapabilities (required) NSArray of NSString The list of all capabilities to subscribe with.
withRole (optional) PhenixMemberRole PhenixMemberRoleAudience The Member Role to join with
withScreenName (optional) NSString The member screen name to join with. A random string will be generated if not provided.
withStreams (optional) NSArray of PhenixStream [] The member streams to join with. Empty if not provided
buildJoinRoomOptions <none> Builds the PhenixJoinRoomOptions

Express Join Room Callback Arguments

Name Type Description
status PhenixRequestStatus The status of the operation.
roomService PhenixRoomService Phenix room service object

Subscribe to a Member’s Stream

Subscribe to a room member’s stream and automatically handle audio and video state changes.

import PhenixSdk

let roomExpress: PhenixRoomExpress = ... // previously obtained
let room: PhenixImmutableRoom = ...  // previously obtained
let renderLayer: CALayer = ...  // previously obtained

// Just an example showing how to get a stream from a member.
// In a real-world app you would want to subscribe to the room-members-observable on the room
// to receive updates when the list of members changes, and then subscribe to the streams-observable
// on each member to access their streams.
let member = room.getObservableMembers().getValue()[0] as! PhenixMember
let memberStream = member.getObservableStreams().getValue[0] as! PhenixStream

let options = PhenixRoomExpressFactory.createSubscribeToMemberStreamOptionsBuilder()
    .withRenderer(renderLayer)
    .buildSubscribeToMemberStreamOptions()

roomExpress.subscribe(
    toMemberStream: memberStream,
    options,
    { [weak self] (
        status: PhenixRequestStatus,
        subscriber: PhenixExpressSubscriber?,
        renderer: PhenixRenderer?) in
      guard status == .ok, let strongSelf = self else {
        // Handle subscribe error
        return
      }

      // Important: Store subscriber reference, otherwise we will stop subscription immediately:
      strongSelf.currentSubscriber = subscriber
    })

Parameters

Name Type Description
memberStream (required) PhenixStream The room member’s stream to subscribe to
options (required) Subscribe to Member’s Stream Options PCast Express Subscribe Options to subscribe to member stream with
callback (required) Function Function to call on success/failure of subscribing to the member stream

PhenixSubscribeToMemberStreamOptionsBuilder

Name Type Default Description
withCapabilities (optional) NSArray of NSString [] The list of all capabilities to subscribe with
withRenderer (optional) CALayer <none> Render layer on which to display stream. If none of the withRenderer... methods are called, no renderer will be instantiated.
withRenderer (optional) <none> false Will trigger instantiation of renderer. Useful for audio only type streams that do not require a render surface
withRendererOptions (optional) PhenixRendererOptions <none> Options passed to renderer. Will trigger instantiation of renderer
withMonitor (optional) PhenixMonitorSetupFailedCallback, PhenixMonitorStreamEndedCallback, PhenixMonitorOptions <none> Options for monitoring a subscriber for failure
withConnectOptions (optional) NSArray of NSStrings [] List of options for subscribing
withTags (optional) NSArray of NSStrings [] Tags for the stream
buildSubscribeToMemberStreamOptions <none> Builds the PhenixSubscribeToMemberStreamOptions

Subscribe To Member Stream Callback Arguments

Name Type Description
status PhenixRequestStatus The status of the operation.
publisher Subscriber Phenix subscriber object
renderer Renderer Optional renderer if renderer was enabled, else nil

Publish to a Room

Publish local or remote media to a room. The room will be created if a room corresponding to the Room Options passed does not exist. If you have not entered the room via joinRoom or publishToRoom methods then a model for Self will be created.

import PhenixSdk

let roomExpress: PhenixRoomExpress = ... // previously obtained
let renderLayer: CALayer = ...  // previously obtained

let mediaConstraints = PhenixUserMediaOptions()
// Customize constraints if needed

let localPublishOptions = PhenixPCastExpressFactory.createPublishOptionsBuilder()
    .withCapabilities(["hd", "streaming"])
    .withMediaConstraints(mediaConstraints)
    .withPreviewRenderer(renderLayer)
    .buildPublishOptions()

let roomOptions = PhenixRoomServiceFactory.createRoomOptionsBuilder()
    .withName("MyAwesomeRoom")
    .buildRoomOptions()

let localPublishToRoomOptions = PhenixRoomExpressFactory.createPublishToRoomOptionsBuilder()
    .withStreamType(.user)
    .withMemberRole(.participant)
    .withRoomOptions(roomOptions)
    .withPublishOptions(localPublishOptions)
    .buildPublishToRoomOptions()

roomExpress.publish(
    toRoom: localPublishToRoomOptions,
    withCallback: { [weak self] (
        status: PhenixRequestStatus,
        roomService: PhenixRoomService?,
        publisher: PhenixExpressPublisher?) in
      guard status == .ok, let strongSelf = self else {
        // Handle publish error
        return
      }

      // Important: Store publisher reference, otherwise we will stop publisher immediately:
      strongSelf.currentPublisher = publisher
    })


// OR for a remote stream:
let remotePublishOptions = PhenixPCastExpressFactory.createPublishRemoteOptionsBuilder()
    .withStreamUri("http://example.com/mystream.mp4")
    .buildPublishRemoteOptions()

let remotePublishToRoomOptions = PhenixRoomExpressFactory.createPublishToRoomOptionsBuilder()
    .withStreamType(.user)
    .withMemberRole(.participant)
    .withRoomOptions(roomOptions)
    .withPublishRemoteOptions(remotePublishOptions)
    .buildPublishToRoomOptions()

// Remaining code is the same as for local stream

Parameters

Name Type Description
options (required) Options Options to publish to room with
callback (required) Function Function to call on success/failure of publishing to the room

PhenixPublishToRoomOptionsBuilder

Name Type Default Description
withRoomOptions (required) PhenixRoomOptions If omitted, then withRoomId needs to be provided.
withRoomId (required) NSString ID of room to publish to. If omitted, then withRoomOptions needs to be provided.
withPublishOptions *(required) * PhenixPublishOptions Either local or remote publish options are required
withPublishRemoteOptions (required) PhenixPublishRemoteOptions Either local or remote publish options are required
withMemberRole (required) PhenixMemberRole Role of member to join room as (used if not already in room). See Member Roles
withStreamType (required) PhenixStreamType Type of stream to publish. See Stream Types
withScreenName (optional) NSString <automatically generated> Screen name of self member
withViewerStreamSelectionStrategy (optional) PhenixStreamSelectionStrategy PhenixStreamSelectionStrategyMostRecent Stream Selection Strategy
withWildcardTokens (optional) true Generate wildcard stream tokens to be appended to member’s stream uri. Reduces time for subscribing to published stream.
buildPublishToRoomOptions <none> Builds the PhenixPublishToRoomOptions

Publish To Room Callback Arguments

Name Type Description
status PhenixRequestStatus The status of the operation.
roomService RoomService Phenix room service
publisher Publisher Phenix publisher object
previewRenderer Renderer Optional renderer if preview renderer was enabled

Get PCast Express

Get the underlying instance of the PCast Express. This is preferred to creating another instance as this will introduce more overhead.

import PhenixSdk

let roomExpress: PhenixRoomExpress = ...  // previously obtained

let pcastExpress = roomExpress.pcastExpress

Clean Up

Underlying resources are kept alive for as long as you hold any references to any of the returned objects (room service, subscriber, renderer). Once those references as well as any reference to the room express instance itself have been released, all underlying resources will be automatically cleaned up and released.

PCast Express

The PCast Express extends the PCast api to provide a single-step configuration based API for:

This API is intended to be used as a supplement to the Room Express although it can be used to stream all on its own.

Initializing

import PhenixSdk

let pcastExpressOptions = PhenixPCastExpressFactory.createPCastExpressOptionsBuilder()
    .withBackendUri("https://example.yourdomain.com/phenix/")
    .buildPCastExpressOptions()

let pcastExpress = PhenixPCastExpressFactory.createPCastExpress(pcastExpressOptions)

PhenixPCastExpressOptionsBuilder

Name Type Default Description
withBackendUri (required) NSString Url to your backend. We send requests here to authenticate and get streaming tokens.
withAuthenticationData (optional) NSString Your authentication data for the user. On every request, this will be sent to your backend through a HTTP POST request and all its attributes would be accessible on the request body. Needs to be valid JSON.
withAuthenticationToken (optional) NSString The authentication token generated using the Admin API. If not passed one will be generated automatically by querying the provided backend uri (may happen multiple times depending on the lifetime of the session).
withUnrecoverableErrorCallback (optional) PhenixPCastExpressUnrecoverableErrorCallback Function to be called when authentication fails or a failure occurs that is unrecoverable.
withPCastUri (optional) NSString Allows overriding default PCast URI.
withPCastInitializationOptions (optional) PhenixPCastInitializeOptions Use custom options when initializing PCast.
withAuthenticationRouteOverride (optional) NSString auth Allows override of default route for authentication tokens
withStreamRouteOverride (optional) NSString stream Allows override of default route for stream tokens
buildPCastExpressOptions <none> Builds the PhenixPCastExpressOptions

Publishing Local Media

Publish local user media:

import PhenixSdk

let pcastExpress: PhenixPCastExpress = ...  // previously obtained

let userMediaConstraints = PhenixUserMediaOptions()
userMediaConstraints.video.enabled = true
userMediaConstraints.video.capabilityConstraints[PhenixDeviceCapability.facingMode.rawValue] =
    [PhenixDeviceConstraint.initWith(PhenixFacingMode.user)]
userMediaConstraints.audio.enabled = true
userMediaConstraints.audio.capabilityConstraints[PhenixDeviceCapability.audioEchoCancelationMode.rawValue] =
    [PhenixDeviceConstraint.initWith(PhenixAudioEchoCancelationMode.on)]

let publishOptions = PhenixPCastExpressFactory.createPublishOptionsBuilder()
    .withCapabilities(["real-time"])
    .withMediaConstraints(userMediaConstraints)
    .buildPublishOptions()

pcastExpress.publish(publishOptions) { (status: PhenixRequestStatus, publisher: PhenixExpressPublisher?) in
  if status == .ok {
    // Do something with publisher
  } else {
    // Handle error
  }
}


// Create a publisher with an automatically started preview renderer
let renderLayer: CALayer = ...  // previously obtained
let publishOptionsWithPreview = PhenixPCastExpressFactory.createPublishOptionsBuilder()
    .withCapabilities(["real-time"])
    .withMediaConstraints(userMediaConstraints)
    .withPreviewRenderer(renderLayer)
    .buildPublishOptions()

pcastExpress.publish(withPreview: publishOptionsWithPreview) {
    (status: PhenixRequestStatus, publisher: PhenixExpressPublisher?, preview: PhenixRenderer?) in
  if status == .ok {
    // Do something with publisher and preview renderer
  } else {
    // Handle error
  }
}

Notes:

Parameters

Name Type Description
options (required) PhenixPublishOptions Publish options
callback (required) function Callback for error/success handling

PhenixPublishOptionsBuilder

Name Type Default Description
withMediaConstraints (required) PhenixUserMediaOptions getUserMedia options Constraints to get the user media.
withUserMedia (optional) PhenixUserMediaStream alternative to withMediaConstraints - you can pass user media stream returned from getUserMedia.
withCapabilities (optional) NSArray of NSString The list of all capabilities to publish with. Default is empty array.
withPreviewRenderer (optional) CALayer Render layer on which to display local preview. If none of the withPreview... methods are called, no preview renderer will be instantiated.
withPreviewRenderer (optional) <none> Will trigger instantiation of preview renderer. Useful for audio only type streams that do not require a render surface.
withPreviewRendererOptions (optional) PhenixRendererOptions Options passed to preview renderer. Will trigger instantiation of preview renderer.
withMonitor (optional) PhenixMonitorSetupFailedCallback, PhenixMonitorStreamEndedCallback, PhenixMonitorOptions Options for monitoring a publisher for failure.
withConnectOptions (optional) NSArray of NSStrings List of options for publishing.
withTags (optional) NSArray of NSStrings Tags for the stream.
withStreamToken (optional) NSString The publish token generated using the Admin API. If not passed one will be generated automatically by querying the backend uri that was passed when instantiating the Express API.
buildPublishOptions <none> Builds the PhenixPublishOptions

Callback Arguments

Name Type Description
status PhenixRequestStatus The status of the operation
publisher PhenixExpressPublisher Phenix publisher object
previewRenderer PhenixRenderer Optionally provided if any of the withPreview... methods were called on the options builder and publish is invoked with withPreview.

PhenixExpressPublisher

Shares most methods with regular PhenixPublisher returned by PhenixPCast, see Publish a Stream.

Name Signature Returns Description
stop () void Stops publisher. Subscribers will receive stream ended.
stop (reason) void Stops publisher with a custom reason. Subscribers will receive PhenixStreamEndedReasonCustom reason.
enableAudio () void Unmutes audio.
disableAudio () void Mutes audio.
enableVideo () void Unmutes video.
disableVideo () void Mutes video (black frames).
setDataQualityChangedCallback (callback) void Listen for Data Quality Feedback
limitBandwidth (bandwidthLimitInBps) PhenixDisposable Temporarily limit published video bitrate, see Limit Bitrate
getStreamId () NSString Returns stream ID of publisher
hasEnded () bool Indicates whether publisher has ended, e.g. by stop having been invoked

Publishing Remote Media (Ingest)

Publish from remote sources into the Phenix platform. This enables us to distribute your source media using the Phenix platform. After publishing users may subscribe to the stream using either pcast subscribe or express subscribe.

import PhenixSdk

let pcastExpress: PhenixPCastExpress = ...  // previously obtained

let publishRemoteOptions = PhenixPCastExpressFactory.createPublishRemoteOptionsBuilder()
    .withStreamUri("http://mycdn.example.com/mystream.mp4")
    .withCapabilities([""])
    .buildPublishRemoteOptions()

pcastExpress.publishRemote(publishRemoteOptions) { (status: PhenixRequestStatus, publisher: PhenixExpressPublisher?) in
  if status == .ok {
    // Do something with publisher
  } else {
    // Handle error
  }
}

Parameters

Name Type Description
options (required) PhenixPublishRemoteOptions Publish Remote options
callback (required) function Callback for error/success handling

PhenixPublishRemoteOptionsBuilder

Name Type Default Description
withCapabilities (required) NSArray of NSString The list of all capabilities to publish with.
withStreamUri (required) NSString Link to remote media (mp4, rtmp, etc.)
withStreamToken (optional) NSString The publish token generated using the Admin API. If not passed one will be generated automatically by querying the backend uri that was passed when instantiating the Express API.
withConnectOptions (optional) NSArray of NSStrings List of options for publishing from a remote source.
withTags (optional) NSArray of NSStrings Tags for the stream
withMaximumFrameRateConstraint (optional) double Maximum frame rate constraint.
withExactFrameRateConstraint (optional) double Exact frame rate constraint.
withPrerollSkipDuration (optional) NSTimeInterval 500 The amount of time to skip at the beginning of the media.
buildPublishRemoteOptions <none> Builds the PhenixPublishRemoteOptions

Callback Arguments

Name Type Description
status PhenixRequestStatus The status of the operation.
publisher PhenixExpressPublisher Phenix publisher object

Subscribing to Published Media

Subscribe to streams published with the Phenix platform

import PhenixSdk

let pcastExpress: PhenixPCastExpress = ...  // previously obtained
let renderLayer: CALayer = ...  // previously obtained

let subscribeOptions = PhenixPCastExpressFactory.createSubscribeOptionsBuilder()
    .withStreamId("us-west#us-west1-b.zzzzzzzz.20000000.xxxxxxxx")
    .withCapabilities(["real-time"])
    .withRenderer(renderLayer)
    .buildSubscribeOptions()

pcastExpress.subscribe(subscribeOptions) { (status: PhenixRequestStatus, subscriber: PhenixExpressSubscriber?, renderer: PhenixRenderer?) in
  if status == .ok {
    // Do something with subscriber

    if let renderer = renderer {
      // Returned if `withRenderer...` option was enabled - Do something with renderer
    }
  } else {
    // Handle error
  }
}

Notes:

Parameters

Name Type Description
options (required) PhenixSubscribeOptions Subscribe options
callback (required) function Callback for error/success handling

Subscribe Options

Name Type Default Description
withStreamId (required) NSString The stream ID of the published stream
withCapabilities (required) NSArray of NSString The list of all capabilities to subscribe with.
withStreamToken (optional) NSString The subscribe token generated using the Admin API. If not passed one will be generated automatically by querying the backend uri that was passed when instantiating the Express API.
withRenderer (optional) CALayer Render layer on which to display stream. If none of the withRenderer... methods are called, no renderer will be instantiated.
withRenderer (optional) <none> Will trigger instantiation of renderer. Useful for audio only type streams that do not require a render surface.
withRendererOptions (optional) PhenixRendererOptions Options passed to renderer. Will trigger instantiation of renderer.
withMonitor (optional) PhenixMonitorSetupFailedCallback, PhenixMonitorStreamEndedCallback, PhenixMonitorOptions Options for monitoring a subscriber for failure.
withConnectOptions (optional) NSArray of NSStrings List of options for subscribing.
withTags (optional) NSArray of NSStrings Tags for the stream
buildSubscribeOptions <none> Builds the PhenixSubscribeOptions

PhenixExpressSubscriber

Shares most methods with regular PhenixMediaStream returned by PhenixPCast, see Subscribe to a Stream.

Name Signature Returns Description
createRenderer () PhenixRenderer Creates a new renderer. This should only be called if none of the withRenderer... builder methods were invoked.
createRenderer (PhenixRendererOptions) PhenixRenderer Creates a new renderer with PhenixRendererOptions. This should only be called if none of the withRenderer... builder methods were invoked.
getAudioTracks () NSArray of PhenixMediaStreamTrack Returns all associated audio tracks of this stream
getVideoTracks () NSArray of PhenixMediaStreamTrack Returns all associated video tracks of this stream
getTracks () NSArray of PhenixMediaStreamTrack Returns all associated tracks of this stream
stop () void Stops subscription. This will trigger the stream ended event.
disableAudio () void Mutes audio.
enableVideo () void Unmutes video.
disableVideo () void Mutes video (black frames).

Monitor

Note: On iOS, the monitor options are currently ignored, but the callbacks for stream setup and stream ended will be triggered.

Monitor callbacks and options can be passed to subscribe and publish options builders. The first callback gets invoked only when we internally fail to setup a stream. The second callback gets invoked whenever a stream ends, whether it is due to failure or not. The retry PhenixOptionalAction allows you to retry publishing or subscribing the failed stream. You must test first whether there is a retry action present by calling isPresent as it may not be possible to retry the stream (example: a stream that ended normally cannot be retried). You may call dismiss on the retry action, but you do not have to (the action is automatically dismissed when no longer referenced). You also may defer invoking the retry action.

Example Monitor for subscribing

import PhenixSdk

let monitorOptions = PhenixPCastExpressFactory.createMonitorOptionsBuilder().buildMonitorOptions();

let subscribeOptions = PhenixPCastExpressFactory.createSubscribeOptionsBuilder()
    .withStreamId("us-west#us-west1-b.zzzzzzzz.20000000.xxxxxxxx")
    .withCapabilities(["real-time"])
    .withMonitor({ (status: PhenixRequestStatus, retry: PhenixOptionalAction?) in
          // Stream failed to setup, check if retry is a possibility:
          if let retry = retry, retry.isPresent() {
            if determineWhetherToRetry() {
              retry.perform()
            } else {
              // Not technically necessary, but here for clarity
              retry.dismiss()
            }
          }
        },
        { (reason: PhenixStreamEndedReason, description: String?, retry: PhenixOptionalAction?) in
          // Stream has ended, check if due to failure
          if let retry = retry, retry.isPresent() {
            if reason == .failed {
              retry.perform()
            } else {
              // Not technically necessary, but here for clarity
              retry.dismiss()
            }
          }
        },
        monitorOptions)
    .buildSubscribeOptions()

PhenixOptionalAction

Name Signature Returns Description
perform () void Performes the action. This will cause a failure if isPresent is false.
dismiss () void Dismisses the action (if any). Can be called multiple times, will result in isPresent to return false. Dropping reference to PhenixOptionsAction has same effect.
isPresent () bool Indicates whether an action can be performed.

PhenixMonitorSetupFailedCallback Callback Arguments

Name Type Description
status PhenixRequestStatus The status of the operation.
retry PhenixOptionalAction Optionally allow retrying the failed stream.

PhenixMonitorStreamEndedCallback Callback Arguments

Name Type Description
reason PhenixStreamEndedReason Reason for stream ended.
description NSString Optional additional ended reason description. Carries custom message.
retry PhenixOptionalAction Optionally allow retrying the failed stream. For normally ended streams isPresent will always return false.

Express Get User Media

Get local user media. For now this is merely a wrapper around Get Local User Media.

import PhenixSdk

let pcastExpress: PhenixPCastExpress = ...  // previously obtained

let userMediaOptions = PhenixUserMediaOptions()

pcastExpress.getUserMedia(userMediaOptions) { (status: PhenixRequestStatus, userMedia: PhenixUserMediaStream?) in
  if status == .ok {
    // Do something with user media stream
  } else {
    // Handle error
  }
}

Parameters

Name Type Description
options (required) PhenixUserMediaOptions User media options
callback (required) function Callback for error/success handling

Callback Arguments

Name Type Description
status PhenixRequestStatus The status of the operation.
userMedia PhenixUserMediaStream User media stream

Get PCast

Get the underlying instance of the PCast. This is preferred to creating another instance as this will introduce more overhead.

import PhenixSdk

let pcastExpress: PhenixPCastExpress = ...  // previously obtained

let pcast = pcastExpress.pcast

Clean up

Subscribers and publishers are kept alive for as long as they are being referenced in your app. PhenixPCastExpress will only shutdown once it is no longer being referenced.

Process Raw Frames

The Frame Ready API allows access to raw unencoded audio and video frames on the publisher as well as subscriber side. This enables use cases such as the following:

Please note: Any processing needs to keep up with the incoming frame rate; otherwise some of the incoming frames will be dropped to compensate.

Publisher side

The API is attached to PhenixUserMediaStream and called setFrameReadyCallback. A track needs to be passed in to indicate for which frames to receive notifications.

PhenixUserMediaStream (via its contained PhenixMediaStream) provides access to the currently available tracks via getVideoTracks and getAudioTracks.

Example Code for processing video raw frames

import PhenixSdk

let userMediaStream: PhenixUserMediaStream = ... // previously obtained

// Assume there is at least one video track:
let videoTrack = userMediaStream.mediaStream.getVideoTracks()[0]

// 1) Example showing how read incoming video frame and produce and outgoing one:
userMediaStream.setFrameReadyCallback(videoTrack) { (frameNotification: PhenixFrameNotification?) in
  frameNotification?.read { (inputFrame: CMSampleBuffer?) in
    let pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(inputFrame!)!

    // Process, manipulate pixel buffer
    // ...

    // Assume we generate an output CVPixelBuffer, which may or may not be based
    // on the incoming frame:
    let outputPixelBuffer: CVPixelBuffer = ...

    // Assemble an output frame using the same timestamps as the input sample buffer:
    let presentationTimeStamp = CMSampleBufferGetPresentationTimeStamp(inputFrame!)
    let duration = CMSampleBufferGetDuration(inputFrame!)

    var sampleTimingInfo = CMSampleTimingInfo.init(
        duration: duration,
        presentationTimeStamp: presentationTimeStamp,
        decodeTimeStamp: CMTime.invalid)

    var formatDescription: CMFormatDescription? = nil
    CMVideoFormatDescriptionCreateForImageBuffer(
        allocator: kCFAllocatorDefault,
        imageBuffer: outputPixelBuffer,
        formatDescriptionOut: &formatDescription)

    var outputFrame: CMSampleBuffer? = nil
    CMSampleBufferCreateReadyWithImageBuffer(
        allocator: kCFAllocatorDefault,
        imageBuffer: outputPixelBuffer,
        formatDescription: formatDescription!,
        sampleTiming: &sampleTimingInfo,
        sampleBufferOut: &outputFrame)

    frameNotification?.write(outputFrame)
  }
}

// 2) Example showing how we can just directly write output frames from a custom source without the need to
//    read the incoming frame:
userMediaStream.setFrameReadyCallback(videoTrack) { (frameNotification: PhenixFrameNotification?) in
  // Assume we have a custom source that is able to provide CMSampleBuffer:
  let outputFrame: CMSampleBuffer? = ...

  frameNotification?.write(outputFrame)
}

// 3) Example showing how we can stop frames from getting propagated by instructing the notification
//    to drop them:
userMediaStream.setFrameReadyCallback(videoTrack) { (frameNotification: PhenixFrameNotification?) in
  // We want to prevent frames from getting propagated further:
  frameNotification?.drop()
}

// 4) Example showing how to read and convert incoming frames to a specific pixel format:
userMediaStream.setFrameReadyCallback(videoTrack) { (frameNotification) in
  frameNotification?.read(with: .BGRA) { (inputFrame: CMSampleBuffer?) in
    let bgraPixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(inputFrame!)!

    // ...
  }
}

Example Code for processing audio raw frames

import PhenixSdk

let userMediaStream: PhenixUserMediaStream = ... // previously obtained

// Assume there is at least one audio track:
let audioTrack = userMediaStream.mediaStream.getAudioTracks()[0]

// 1) Example showing how read incoming audio frame and produce and outgoing one:
userMediaStream.setFrameReadyCallback(audioTrack) { (frameNotification: PhenixFrameNotification?) in
  frameNotification?.read { (inputFrame: CMSampleBuffer?) in
    let blockBuffer = CMSampleBufferGetDataBuffer(inputFrame!)

    var totalLength = Int()
    var lengthAtOffset = Int()
    var dataPointer: UnsafeMutablePointer<Int8>? = nil

    guard CMBlockBufferGetDataPointer(
        blockBuffer!,
        atOffset: 0,
        lengthAtOffsetOut: &lengthAtOffset,
        totalLengthOut: &totalLength,
        dataPointerOut: &dataPointer) == kCMBlockBufferNoErr && lengthAtOffset == totalLength else {
      // Handle error, unexpected buffer length
      return
    }

    // Verify audio format:
    guard let formatDescription: CMAudioFormatDescription =
        CMSampleBufferGetFormatDescription(inputFrame!) else {
      return
    }
    guard let audioStreamBasicDescription =
        CMAudioFormatDescriptionGetStreamBasicDescription(formatDescription) else {
      return
    }
    guard (audioStreamBasicDescription.pointee.mFormatFlags & kAudioFormatFlagIsSignedInteger)
            == kAudioFormatFlagIsSignedInteger &&
          audioStreamBasicDescription.pointee.mBitsPerChannel == 16 else {
      return
    }

    // Raw samples are contained in `dataPointer` as Int16 values
    ...

    // Assume we generate an output CMBlockBuffer, which may or may not be based
    // on the incoming audio frame, and which as the same audio format and same
    // length as the incoming frame:
    let outputAudioBuffer: CMBlockBuffer = ...

    let presentationTimeStamp = CMSampleBufferGetPresentationTimeStamp(inputFrame!)
    let numberOfSamples =
        totalLength /
        Int(audioStreamBasicDescription.pointee.mChannelsPerFrame * audioStreamBasicDescription.pointee.mBitsPerChannel / 8)

    var outputFrame: CMSampleBuffer? = nil
    CMAudioSampleBufferCreateReadyWithPacketDescriptions(
        allocator: kCFAllocatorDefault,
        dataBuffer: outputAudioBuffer,
        formatDescription: formatDescription,
        sampleCount: numberOfSamples,
        presentationTimeStamp: presentationTimeStamp,
        packetDescriptions: nil,
        sampleBufferOut: &outputFrame)

    frameNotification?.write(outputFrame)
  }
}

// Other examples would look very similar to video

Frame Ready Callback Arguments

This callback gets invoked for each frame that is passing through.

Name Type Description
frameNotification PhenixFrameNotification Object representing the current frame

Frame Notification

Represents the current frame. Allows for reading, writing, and dropping.

Name Signature Returns Description
read (ReadFrameCallback) void Retrieves current raw frame in form of a CMSampleBufferRef
readWithFormat (PhenixMediaFormat, ReadFrameCallback) void Retrieves current raw frame in form of a CMSampleBufferRef
write (CMSampleBufferRef) void Writes back a processed or newly generated frame. The frame can have a different resolution and timestamps (the buffer attributes are expected to be set correctly)
drop () void Instructs stream to drop the current frame

Read Frame Callback Arguments

Receives the current raw frame.

Name Type Description
frame CMSampleBufferRef The raw audio or video frame

Media Format

Strategy Description
PhenixMediaFormatI420 FourCC planar pixel format I420, corresponds to Apple kCVPixelFormatType_420YpCbCr8Planar
PhenixMediaFormatNV12 FourCC planar pixel format NV12, corresponds to Apple kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
PhenixMediaFormatBGRA FourCC pixel format ARGB, corresponds to Apple kCVPixelFormatType_32BGRA

Subscriber side

The Frame Ready API on the subscriber side can be accessed via the renderer, which offers an analoguous setFrameReadyCallback method to the Publisher Side.

When using the Express API, you can access the stream tracks directly via the Express Subscriber. Otherwise, tracks can be accessed via the PhenixMediaStream object.

The example code is somewhat abbreviated as the contents of the Frame Ready callbacks would look the same as for the Publisher Side.

Example Code for hooking up frame-ready callback with a PhenixMediaStream object

import PhenixSdk

let mediaStream: PhenixMediaStream = ... // previously obtained
let renderer: PhenixRenderer = ... // previously obtained

// Assume there is at least one video track:
let videoTrack = mediaStream.getVideoTracks()[0]

renderer.setFrameReadyCallback(videoTrack, ...
// Remainder of code identical to Publish Side

Example Code for hooking up frame-ready callback with a PhenixExpressSubscriber object

import PhenixSdk

let expressSubscriber: PhenixExpressSubscriber = ... // previously obtained
let renderer: PhenixRenderer = ... // previously obtained

// Assume there is at least one video track:
let videoTrack = expressSubscriber.getVideoTracks()[0]

renderer.setFrameReadyCallback(videoTrack, ...
// Remainder of code identical to Publish Side

Limit Bandwidth

The outgoing or incoming video bandwidth can be limited on the publisher and subscriber side.

Publisher side

The published video bitrate can be limited temporarily if needed. The returned disposable allows to control for how long the limitation should stay in effect. If limitBandwidth is called multiple times before any of the previous disposables are released, then only the most recent override will remain in effect until its disposable is released. Relasing any of the disposables from earlier limitBandwidth calls will have no effect.

Example Code for limiting video bandwidth with a PhenixPublisher object

import PhenixSdk

// Previously obtained
let publisher: PhenixPublisher = ...

// Limit video bitrate to 200kbps for 10 seconds
var disposable = publisher.limitBandwidth(200000)

DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
  // Dropping the disposable will undo the bandwidth limitation
  disposable = nil
}

Example Code for limiting video bandwidth with a PhenixExpressPublisher object

import PhenixSdk

// Previously obtained
let expressPublisher: PhenixExpressPublisher = ...

// Limit video bitrate to 200kbps for 10 seconds
var disposable = expressPublisher.limitBandwidth(200000)

DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
  // Dropping the disposable will undo the bandwidth limitation
  disposable = nil
}

Parameters

Name Type Description
bandwidthLimitInBps (required) UInt64 Maximum bitrate limit in bps for video

Subscriber side

Invoking limitBandwidth on a subscriber will inform the publishing side to lower the video bandwidth to try to match the requested value. The API is attached to PhenixMediaStreamTrack. The semantics with regards to the disposable returned by the API are identical to the publisher side.

Notes:

Example Code for limiting video bandwidth with a PhenixMediaStream object

import PhenixSdk

// Previously obtained
let subscriber: PhenixMediaStream = ...

// Limit video bitrate to 200kbps for 10 seconds
// We assume there is at least one video track on this stream
var disposable = subscriber.getVideoTracks()[0].limitBandwidth(200000)

DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
  // Dropping the disposable will undo the bandwidth limitation
  disposable = nil
}

Example Code for limiting video bandwidth with a PhenixExpressSubscriber object

import PhenixSdk

// Previously obtained
let subscriber: PhenixExpressSubscriber = ...

// Limit video bitrate to 200kbps for 10 seconds
// We assume there is at least one video track on this stream
var disposable = subscriber.getVideoTracks()[0].limitBandwidth(200000)

DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
  // Dropping the disposable will undo the bandwidth limitation
  disposable = nil
}

Parameters

Name Type Description
bandwidthLimitInBps (required) UInt64 Maximum bitrate limit in bps for video

Override Playout Delay

Example Code for overriding the playout delay via a PhenixMediaStream object

import PhenixSdk

class SomeClass {
  private var currentRendererPlayoutDelayOverride: PhenixDisposable?
  private var currentRenderer: PhenixRenderer?

  private func setPlayoutDelayOverrideFor10Seconds() {
    // Previously obtained
    let mediaStream: PhenixMediaStream = ...

    self.currentRenderer = mediaStream.createRenderer()

    // Override playout delay to 900ms for 10 seconds
    let playoutDelay: TimeInterval = 0.9

    self.currentRendererPlayoutDelayOverride = self.currentRenderer?.overridePlayoutDelay(playoutDelay)

    DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
      // Dropping the disposable will undo the playout delay override
      self.currentRendererPlayoutDelayOverride = nil
    }
  }
}

Example Code for limiting video bandwidth with a PhenixExpressSubscriber object

import PhenixSdk

class SomeClass {
  private var currentRendererPlayoutDelayOverride: PhenixDisposable?
  private var currentRenderer: PhenixRenderer?

  private func setPlayoutDelayOverrideFor10Seconds() {
    // Previously obtained
    let subscriber: PhenixExpressSubscriber = ...

    self.currentRenderer = subscriber.createRenderer()

    // Override playout delay to 900ms for 10 seconds
    let playoutDelay: TimeInterval = 0.9

    self.currentRendererPlayoutDelayOverride = self.currentRenderer?.overridePlayoutDelay(playoutDelay)

    DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
      // Dropping the disposable will undo the playout delay override
      self.currentRendererPlayoutDelayOverride = nil
    }
  }
}

The playout delay represents the amount of time by which audio and video are delayed when content is rendered by a subscriber; i.e. it works as a buffer. The delay adds to the overall end-to-end latency experienced by the user (on top of capture, encoding, and network latencies). It is necessary to handle network-related fluctuations (such as jitter or data loss). By default, the playout delay for real-time streams is set to 230ms. The following API allows app developers to override this default value.

In order to access the API, a reference to a PhenixRenderer is needed. It can be obtained either by creating it from PhenixMediaStream or PhenixExpressSubscriber, or via several of the Express APIs, which can return renderers (joinChannel, subscribeToMemberStream, subscribe).

The returned disposable allows control over how long the override should stay in effect; it therefore needs to be held onto via a strong reference. If overridePlayoutDelay is called multiple times before any of the previous disposables are released, then only the most recent override will remain in effect until its disposable is released. Releasing any of the disposables from earlier overridePlayoutDelay calls will have no effect.

Notes:

Parameters

Name Type Description
desiredPlayoutDelay (required) TimeInterval Desired playout delay

Returns

Type Description
PhenixDisposable Ensures override is kept in effect for as long as a strong reference is held

iOS Examples

You can find our iOS examples on GitHub

Frame Ready API

A simple yet powerful example that composes a camera feed with an external video source in real-time!

WebView

A simple example that integrates WebView to load a web page using the Phenix WebSDK.