Android SDK
The Android SDK APIs are split into two main categories: Express and Low-Level. We recommend that you start with the Express API as this:
- Simplifies integration
- Automatically recovers from stream failures
- 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 a standard Android library module in form of an .aar file. Please check our private Github repository for details.
Extended feature support
(Optional) For extended live streaming support (Dash), you also need to add ExoPlayer as a dependency to your project:
implementation 'com.google.android.exoplayer:exoplayer-dash:2.6.1'
implementation 'com.google.android.exoplayer:exoplayer-core:2.6.1'
Min SDK Requirements
Depending on required features, the minimum supported Android API levels are as follows:
- API Level 15: Real-time streaming
- API Level 19: ExoPlayer with DRM support
- API Level 21: Screen sharing
Known issues
Build crash: java.io.IOException: Failed to find byte code for …
Android Studio issue: Google Issue 76403146
Solution:
- Upgrade : gradle-wrapper.properties - gradle-4.6-all.zip or later
- Upgrade gradle plugin in project build.gradle - ‘com.android.tools.build:gradle:3.2.+’ or later
Or Disable instant run - Android Studio -> Settings -> Search Instant run -> Disable
IDE error: .GroovyFileImpl because: different providers: SingleRootFileViewProvider
Android Studio issue: Google Issue 77939622
Solution:
If directory for module was created (e.g. /phenix-sdk
), just add direcory name to settings.gradle file.
include ':app', ':phenix-sdk', ':phenix-utils', ':phenix-ui'
Debugging
You can retrieve the Phenix SDK logs programmatically using the collectLogMessages
PCast API.
import com.phenixrts.pcast.PCast;
import com.phenixrts.pcast.RequestStatus;
// Previously initialized
PCast pcast = ...;
pcast.collectLogMessages(
new PCast.LogMessageCollectionCallback() {
@Override
public void onEvent(
PCast pcast,
RequestStatus requestStatus,
String messages) {
if (requestStatus != RequestStatus.OK) {
// Handle error
return;
}
String messagesArray[]= messages.split("\n");
for (String message: messagesArray) {
// Do something with 'message'
}
}
});
Notes:
messages
string contains the most recent as well as initial logs (since your app started)- Each log entry in the string is separated by a newline
- Log entries are stored chronologically, newest logs last
General
All Phenix SDK objects provide a dispose
method, which allows for instant release of all its resources. To force release resources held on to by any object, just call dispose
.
Note: Once an object has been disposed, it should no longer be accessed or else it will throw an IllegalStateException
App Store
Google supports a split Android Bundle format as the replacement for the old APK format. We highly recommend using this new format, as it significantly minimizes user app download size without any additional configuration.
If you are using the APK app format, you can optimize your app by deploying multiple APKs, each specific to an ABI (i.e. processor architecture) by updating your Gradle configuration file.
// build.gradle
android {
...
splits {
// Configures multiple APKs based on ABI.
abi {
// Enables building multiple APKs per ABI.
enable true
// Resets the list of ABIs that Gradle should create APKs for to none.
reset()
// Specifies a list of ABIs that Gradle should create APKs for.
include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
// Specifies that we do not want to also generate a universal APK that includes all ABIs.
universalApk false
}
}
}
With this approach, the end user will automatically download the correct version for their device when installing the app from the Play Store. Their downloaded APK will only have the Phenix SDK version that their architecture requires, making the download size smaller.
In general, when using multiple APKs, you will want to make sure that they conform to the requirements described in the Google Android Developer documentation
Express Android SDK
Our Express APIs provide a simple, single-step API for the easiest integration of streaming into your application
Common Use Cases
- Channel (one to many) -> Channel
- Group Broadcast (few to many) -> Room
- Group Chat (many to many) -> Room
- One to One Chat -> Room
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:
- View a channel
- Publish to a channel
- Create a channel
Initializing
import android.content.Context;
import com.phenixrts.common.RequestStatus;
import com.phenixrts.environment.android.AndroidContext;
import com.phenixrts.express.ChannelExpressFactory;
import com.phenixrts.express.ChannelExpressOptions;
import com.phenixrts.express.PCastExpressFactory;
import com.phenixrts.express.PCastExpressOptions;
import com.phenixrts.express.RoomExpressFactory;
import com.phenixrts.express.RoomExpressOptions;
// IMPORTANT: Before accessing any of the static factories, make sure the context is passed to Phenix:
final Context context = ...; // e.g. Activity.getApplication();
AndroidContext.setContext(context);
final PCastExpressOptions pcastExpressOptions =
PCastExpressFactory.createPCastExpressOptionsBuilder()
.withBackendUri("https://example.yourdomain.com/phenix/")
.withUnrecoverableErrorCallback((status, description) -> {
// Best option is to try rebuilding the ChannelExpress instance and/or quit
// your app
})
.buildPCastExpressOptions();
final RoomExpressOptions roomExpressOptions =
RoomExpressFactory.createRoomExpressOptionsBuilder()
.withPCastExpressOptions(pcastExpressOptions)
.buildRoomExpressOptions();
final ChannelExpressOptions channelExpressOptions =
ChannelExpressFactory.createChannelExpressOptionsBuilder()
.withRoomExpressOptions(roomExpressOptions)
.buildChannelExpressOptions();
final ChannelExpress channelExpress = ChannelExpressFactory.createChannelExpress(channelExpressOptions);
ChannelExpressOptionsBuilder
Name | Type | Default | Description |
---|---|---|---|
withRoomExpressOptions (required) | RoomExpressOptions | See RoomExpressOptionsBuilder | |
buildChannelExpressOptions | <none> | Builds the ChannelExpressOptions |
View a Channel
Join a channel and automatically view the most recent content published to that channel.
import android.view.SurfaceView;
import com.phenixrts.common.RequestStatus;
import com.phenixrts.express.ChannelExpress;
import com.phenixrts.express.ChannelExpressFactory;
import com.phenixrts.express.JoinChannelOptions;
import com.phenixrts.express.JoinRoomOptions;
import com.phenixrts.express.RoomExpressFactory;
import com.phenixrts.pcast.RendererOptions;
import com.phenixrts.pcast.android.AndroidVideoRenderSurface;
import com.phenixrts.room.RoomService;
final ChannelExpress channelExpress = ...; // previously obtained
final SurfaceView view = ...; // previously obtained
final AndroidVideoRenderSurface renderSurface = new AndroidVideoRenderSurface(view.getHolder());
// Just an example (you can omit renderer options if defaults are ok)
final RendererOptions rendererOptions = new RendererOptions();
rendererOptions.aspectRatioMode = AspectRatioMode.LETTERBOX;
final JoinRoomOptions joinRoomOptions = RoomExpressFactory.createJoinRoomOptionsBuilder()
.withRoomId("us-central#xxxxx#channel")
.withCapabilities(new String[] { "real-time" })
.buildJoinRoomOptions();
final JoinChannelOptions joinChannelOptions =
ChannelExpressFactory.createJoinChannelOptionsBuilder()
.withJoinRoomOptions(joinRoomOptions)
.withRenderer(renderSurface)
.withRendererOptions(rendererOptions)
.buildJoinChannelOptions();
this.channelExpress.joinChannel(
joinChannelOptions,
(RequestStatus status, RoomService roomService) -> {
if (status != RequestStatus.OK) {
// Handle room join error
return;
}
// Important: Store room service reference, otherwise we will leave channel again
// as soon as this RoomService instance is garbage collected:
OuterClass.this.currentRoomService = roomService;
},
(RequestStatus status, ExpressSubscriber subscriber, Renderer renderer) -> {
switch (status) {
case OK:
// Successfully subscribed to a stream. No need to hold on to any references
break;
case NO_STREAM_PLAYING:
// No stream playing in channel, update UI accordingy
break;
default:
// We failed to subscribe and retry attempts must have failed
break;
}
});
Parameters
Name | Type | Description |
---|---|---|
options (required) | Options | Options to join channel with |
joinChannelCallback (required) | ChannelExpress.JoinChannelCallback | Function to call on success/failure of joining the channel. |
subscriberCallback (required) | PCastExpress.SubscribeCallback | Function to call on when the most recent presenter changes. |
JoinChannelOptionsBuilder
Name | Type | Default | Description |
---|---|---|---|
withJoinRoomOptions (required) | JoinRoomOptions | See JoinRoomOptionsBuilder. | |
withRenderer (optional) | AndroidVideoRenderSurface | Render layer on which to display stream. If none of the withRenderer... methods are called, no renderer will be instantiated. |
|
withRenderer (optional) | Will trigger instantiation of renderer. Useful for audio only type streams that do not require a render surface. | ||
withRendererOptions (optional) | RendererOptions | Options passed to renderer. Will trigger instantiation of renderer. | |
withStreamSelectionStrategy (optional) | StreamSelectionStrategy | StreamSelectionStrategy.MOST_RECENT | Determines how member streams are selected for subscriptions. |
buildJoinChannelOptions | Builds the JoinChannelOptions |
Stream Selection Strategy
Strategy | Description |
---|---|
StreamSelectionStrategy.MOST_RECENT | Select the most recent stream. Viewing stream changes any time a stream starts or is updated in the room. |
StreamSelectionStrategy.HIGH_AVAILABILITY | 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 | RoomService | Room service object |
View Channel Subscriber Callback Status Codes
Status | Description |
---|---|
ok | Successfully subscribed to presenter |
no-stream-playing | No presenter in room to subscribe to. Wait for presenter to join. |
<varies> | Subscribe to presenter failed for other reasons |
Publish to a Channel
Publish a local or remote media to a channel. Users that are viewing the channel will see your media.
import android.view.SurfaceView;
import com.phenixrts.common.RequestStatus;
import com.phenixrts.express.ChannelExpress;
import com.phenixrts.express.ChannelExpressFactory;
import com.phenixrts.express.PCastExpressFactory;
import com.phenixrts.express.PublishOptions;
import com.phenixrts.express.PublishToChannelOptions;
import com.phenixrts.express.RoomExpressFactory;
import com.phenixrts.pcast.DeviceCapability;
import com.phenixrts.pcast.FacingMode;
import com.phenixrts.pcast.RendererOptions;
import com.phenixrts.pcast.UserMediaOptions
import com.phenixrts.pcast.android.AndroidVideoRenderSurface;
import com.phenixrts.room.ChannelOptions;
import com.phenixrts.room.RoomServiceFactory;
final ChannelExpress channelExpress = ...; // previously obtained
final SurfaceView view = ...; // previously obtained
final AndroidVideoRenderSurface renderSurface = new AndroidVideoRenderSurface(view.getHolder());
// 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 `PublishToChannelOptions` below
final ChannelOptions channelOptions = RoomServiceFactory.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
final UserMediaOptions mediaConstraints = new UserMediaOptions();
mediaConstraints.getVideoOptions().capabilityConstraints.put(
DeviceCapability.FACING_MODE, Arrays.asList(new DeviceConstraint(FacingMode.USER)));
mediaConstraints.getVideoOptions().capabilityConstraints.put(
DeviceCapability.FRAME_RATE, Arrays.asList(new DeviceConstraint(15)));
mediaConstraints.getVideoOptions().capabilityConstraints.put(
DeviceCapability.HEIGHT, Arrays.asList(new DeviceConstraint(720)));
mediaConstraints.getVideoOptions().capabilityConstraints.put(
DeviceCapability.WIDTH, Arrays.asList(new DeviceConstraint(1280)));
final PublishOptions publishOptions = PCastExpressFactory.createPublishOptionsBuilder()
.withCapabilities(new String[]{"hd", "streaming"})
.withMediaConstraints(mediaConstraints)
.withPreviewRenderer(renderSurface)
.buildPublishOptions();
final PublishToChannelOptions publishToChannelOptions =
ChannelExpressFactory.createPublishToChannelOptionsBuilder()
.withChannelOptions(channelOptions)
.withPublishOptions(publishOptions)
.buildPublishToChannelOptions();
channelExpress.publishToChannel(
publishToChannelOptions,
(publishStatus, roomService, publisher, previewRenderer) -> {
if (publishStatus != RequestStatus.OK) {
// Handle channel publish error
return;
}
// Important: Store publisher reference, otherwise we will stop publishing again immediately:
currentPublisher = publisher;
}
);
// OR (without a preview):
channelExpress.publishToChannel(
publishToChannelOptions,
(publishStatus, roomService, publisher) -> {
if (publishStatus != RequestStatus.OK) {
// Handle channel publish error
return;
}
// Important: Store publisher reference, otherwise we will stop publishing again immediately:
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 |
PublishToChannelOptionsBuilder
Name | Type | Default | Description |
---|---|---|---|
withChannelOptions (required) | ChannelOptions | See ChannelOptionsBuilder. 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) | PublishOptions | Either provide this or remote publish options | |
withPublishRemoteOptions (required) | RemotePublishOptions | Either provide this or publish options. | |
withMemberRole (optional) | MemberRole | MemberRole.PRESENTER | Role of member to join channel as (used if not already in channel). |
withStreamType (optional) | StreamType | StreamType.PRESENTATION | Type of stream to publish. |
withScreenName (optional) | String | <random unique string> | Screen name of self member |
withViewerStreamSelectionStrategy (optional) | StreamSelectionStrategy | StreamSelectionStrategy.MOST_RECENT | This has to match the strategy channel viewers are using. When StreamSelectionStrategy.HIGH_AVAILABILITY 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 PublishToChannelOptions |
Notes: * Wildcard token generation is always enabled when publishing to a channel
ChannelOptionsBuilder
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 ChannelOptions |
Publish To Channel Callback Arguments
Name | Type | Description |
---|---|---|
status | RequestStatus | The status of the operation |
roomService | RoomService | Phenix room service |
publisher | ExpressPublisher | Publisher object |
Create a Channel
import com.phenixrts.common.RequestStatus;
import com.phenixrts.express.ChannelExpress;
import com.phenixrts.room.ChannelOptions;
import com.phenixrts.room.RoomServiceFactory;
final ChannelExpress channelExpress = ...; // previously obtained
final ChannelOptions channelOptions = RoomServiceFactory.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();
this.channelExpress.createChannel(
channelOptions,
(status, channel) -> {
if (status != RequestStatus.OK) {
// Handle room create error
return;
}
// use `channel` to e.g. join
});
Parameters
Name | Type | Description |
---|---|---|
options (required) | ChannelOptions | 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 | RequestStatus | The status of the operation. |
channel | ImmutableRoom | Immutable 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:
- Join, create, and publish to a room
- Subscribe to streams of members in a room
Initializing
import android.content.Context;
import com.phenixrts.common.RequestStatus;
import com.phenixrts.environment.android.AndroidContext;
import com.phenixrts.express.PCastExpress;
import com.phenixrts.express.PCastExpressFactory;
import com.phenixrts.express.PCastExpressOptions;
import com.phenixrts.express.RoomExpress;
import com.phenixrts.express.RoomExpressFactory;
import com.phenixrts.express.RoomExpressOptions;
import com.phenixrts.pcast.PCastInitializeOptions;
// IMPORTANT: Before accessing any of the static factories, make sure the context is passed to Phenix:
final Context context = ...; // e.g. Activity.getApplication();
AndroidContext.setContext(context);
final PCastExpressOptions pcastExpressOptions =
PCastExpressFactory.createPCastExpressOptionsBuilder()
.withBackendUri("https://example.yourdomain.com/phenix/")
.withUnrecoverableErrorCallback((RequestStatus status, String description) -> {
// Best to restart app, or attempt to re-create PCastExpress
})
.buildPCastExpressOptions();
final RoomExpressOptions roomExpressOptions =
RoomExpressFactory.createRoomExpressOptionsBuilder()
.withPCastExpressOptions(pcastExpressOptions)
.buildPCastExpressOptions();
final RoomExpress roomExpress = RoomExpressFactory.createRoomExpress(roomExpressOptions);
RoomExpressOptionsBuilder
Name | Type | Default | Description |
---|---|---|---|
withPCastExpressOptions (required) | PCastExpressOptions | See PCastExpressOptionsBuilder | |
buildRoomExpressOptions | Builds the RoomExpressOptions |
Join a Room
Join a room and optionally, automatically subscribe to member changes.
import com.phenixrts.common.RequestStatus;
import com.phenixrts.express.JoinRoomOptions;
import com.phenixrts.express.RoomExpress;
import com.phenixrts.express.RoomExpressFactory;
import com.phenixrts.room.Member;
import com.phenixrts.room.RoomService;
final RoomExpress roomExpress = ...; // previously obtained
final JoinRoomOptions joinRoomOptions = RoomExpressFactory.createJoinRoomOptionsBuilder()
.withRoomAlias("myRoom42")
.withCapabilities(new String[] {"streaming"})
.buildJoinRoomOptions();
roomExpress.joinRoom(joinRoomOptions, (RequestStatus status, RoomService roomService) -> {
if (status == RequestStatus.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:
this.roomExpress.joinRoom(
joinRoomOptions,
(RequestStatus status, RoomService roomService) -> {
if (status == RequestStatus.OK) {
// Hold on to roomService reference for as long as you wish to stay in the room
} else {
// Handle error
}
},
(Member[] members) -> {
// Do something with room members
});
Parameters
Name | Type | Description |
---|---|---|
options (required) | JoinRoomOptions | Options to join room with |
joinRoomCallback (required) | RoomExpress.JoinRoomCallback | Function to call on success/failure of joining the room |
membersChangedCallback (optional) | RoomExpress.MembersChangedCallback | 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. |
JoinRoomOptionsBuilder
Name | Type | Default | Description |
---|---|---|---|
withRoomId (required) | String | Id of channel to view | |
withRoomAlias (optional) | String | Alias, alternative to ID | |
withCapabilities (required) | String[] | The list of all capabilities to subscribe with. | |
withRole (optional) | MemberRole | MemberRole.AUDIENCE | The Member Role to join with |
withScreenName (optional) | String | The member screen name to join with. A random string will be generated if not provided. | |
withStreams (optional) | Stream[] | [] | The member streams to join with. Empty if not provided |
buildJoinRoomOptions | Builds the JoinRoomOptions |
Express Join Room Callback Arguments
Name | Type | Description |
---|---|---|
status | RequestStatus | The status of the operation. |
roomService | RoomService | 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 android.view.SurfaceView;
import com.phenixrts.common.RequestStatus;
import com.phenixrts.express.RoomExpress;
import com.phenixrts.express.RoomExpressFactory;
import com.phenixrts.express.SubscribeToMemberStreamOptions;
import com.phenixrts.room.ImmutableRoom;
import com.phenixrts.room.Member;
import com.phenixrts.room.Stream;
import com.phenixrts.pcast.android.AndroidVideoRenderSurface;
final RoomExpress roomExpress = ...; // previously obtained
final ImmutableRoom room = ...; // previously obtained
final SurfaceView view = ...; // previously obtained
final AndroidVideoRenderSurface renderSurface = new AndroidVideoRenderSurface(view.getHolder());
// 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.
final Member member = room.getObservableMembers().getValue()[0];
final Stream memberStream = member.getObservableStreams().getValue()[0];
final SubscribeToMemberStreamOptions options =
RoomExpressFactory.createSubscribeToMemberStreamOptionsBuilder()
.withRenderer(renderSurface)
.buildSubscribeToMemberStreamOptions();
roomExpress.subscribeToMemberStream(
memberStream,
options,
(status, subscriber, renderer) -> {
if (status != RequestStatus.OK) {
// Handle subscribe error
return;
}
// Important: Store subscriber reference, otherwise we will stop subscription immediately:
//currentSubscriber = subscriber;
});
Parameters
Name | Type | Description |
---|---|---|
memberStream (required) | Stream | 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 |
SubscribeToMemberStreamOptionsBuilder
Name | Type | Default | Description |
---|---|---|---|
withCapabilities (optional) | String[] | [] | The list of all capabilities to subscribe with |
withRenderer (optional) | AndroidVideoRenderSurface | <none> | Render surface 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) | RendererOptions | <none> | Options passed to renderer. Will trigger instantiation of renderer |
withMonitor (optional) | MonitorOptions.SetupFailedCallback, MonitorOptions.StreamEndedCallback, MonitorOptions | <none> | Options for monitoring a subscriber for failure |
withConnectOptions (optional) | String[] | [] | List of options for subscribing |
withTags (optional) | String[] | [] | Tags for the stream |
buildSubscribeToMemberStreamOptions | <none> | Builds the SubscribeToMemberStreamOptions |
Subscribe To Member Stream Callback Arguments
Name | Type | Description |
---|---|---|
status | RequestStatus | 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 android.view.SurfaceView;
import com.phenixrts.common.RequestStatus;
import com.phenixrts.express.PCastExpressFactory;
import com.phenixrts.express.PublishOptions;
import com.phenixrts.express.PublishRemoteOptions;
import com.phenixrts.express.PublishToRoomOptions;
import com.phenixrts.express.RoomExpress;
import com.phenixrts.express.RoomExpressFactory;
import com.phenixrts.pcast.DeviceCapability;
import com.phenixrts.pcast.FacingMode;
import com.phenixrts.pcast.RendererOptions;
import com.phenixrts.pcast.UserMediaOptions
import com.phenixrts.pcast.android.AndroidVideoRenderSurface;
import com.phenixrts.room.MemberRole;
import com.phenixrts.room.RoomOptions;
import com.phenixrts.room.RoomServiceFactory;
import com.phenixrts.room.StreamType;
final RoomExpress roomExpress = ...; // previously obtained
final SurfaceView view = ...; // previously obtained
final AndroidVideoRenderSurface renderSurface = new AndroidVideoRenderSurface(view.getHolder());
final UserMediaOptions mediaConstraints = new UserMediaOptions();
// Customize constraints if needed
final PublishOptions publishOptions = PCastExpressFactory.createPublishOptionsBuilder()
.withCapabilities(new String[]{"hd", "streaming"})
.withMediaConstraints(mediaConstraints)
.withPreviewRenderer(renderSurface)
.buildPublishOptions();
// Using RoomOptions means that the room may or may not already exist.
// If the room ID is known in advance, it is recommended to use `withRoomId` instead
// of `withRoomOptions` when assembling the `PublishToRoomOptions` below
final RoomOptions roomOptions = RoomServiceFactory.createRoomOptionsBuilder()
.withName("MyAwesomeRoom")
.buildRoomOptions();
final PublishToRoomOptions localPublishToRoomOptions =
RoomExpressFactory.createPublishToRoomOptionsBuilder()
.withStreamType(StreamType.USER)
.withMemberRole(MemberRole.PARTICIPANT)
.withRoomOptions(roomOptions)
.withPublishOptions(publishOptions)
.buildPublishToRoomOptions();
roomExpress.publishToRoom(
localPublishToRoomOptions,
(publishStatus, roomService, publisher, previewRenderer) -> {
if (publishStatus != RequestStatus.OK) {
// Handle channel publish error
return;
}
// Important: Store publisher reference, otherwise we will stop publishing again immediately:
currentPublisher = publisher;
}
);
// OR for a remote stream:
final PublishRemoteOptions remotePublishOptions = PCastExpressFactory
.createPublishRemoteOptionsBuilder()
.withStreamUri("http://example.com/mystream.mp4")
.buildPublishRemoteOptions();
final PublishToRoomOptions remotePublishToRoomOptions =
RoomExpressFactory.createPublishToRoomOptionsBuilder()
.withStreamType(StreamType.USER)
.withMemberRole(MemberRole.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 |
PublishToRoomOptionsBuilder
Name | Type | Default | Description |
---|---|---|---|
withRoomOptions (required) | RoomOptions) | If omitted, then withRoomId needs to be provided. |
|
withRoomId (required) | String | ID of room to publish to. If omitted, then withRoomOptions needs to be provided. |
|
withPublishOptions (required) | PublishOptions | Either local or remote publish options are required | |
withPublishRemoteOptions (required) | PublishRemoteOptions | Either local or remote publish options are required | |
withMemberRole (required) | MemberRole | Role of member to join room as (used if not already in room). See Member Roles | |
withStreamType (required) | StreamType | Type of stream to publish. See Stream Types | |
withScreenName (optional) | String | <automatically generated> | Screen name of self member |
withViewerStreamSelectionStrategy (optional) | StreamSelectionStrategy | StreamSelectionStrategy.MOST_RECENT | 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 PublishToRoomOptions |
Publish To Room Callback Arguments
Name | Type | Description |
---|---|---|
status | RequestStatus | 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 com.phenixrts.express.PCastExpress;
import com.phenixrts.express.RoomExpress;
final RoomExpress roomExpress = ...; // previously obtained
final PCastExpress pcastExpress = roomExpress.getPCastExpress();
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) an do not call dispose
on them. Once those references as well as any reference to the room express instance itself have been released (or dispose
has been called), 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:
- Publishing local media
- Publishing remote media (ingest)
- Subscribing to published streams
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 android.content.Context;
import com.phenixrts.common.RequestStatus;
import com.phenixrts.environment.android.AndroidContext;
import com.phenixrts.express.PCastExpress;
import com.phenixrts.express.PCastExpressFactory;
import com.phenixrts.express.PCastExpressOptions;
import com.phenixrts.pcast.PCastInitializeOptions;
// IMPORTANT: Before accessing any of the static factories, make sure the context is passed to Phenix:
final Context context = ...; // e.g. Activity.getApplication();
AndroidContext.setContext(context);
final PCastExpressOptions pcastExpressOptions =
PCastExpressFactory.createPCastExpressOptionsBuilder()
.withBackendUri("https://example.yourdomain.com/phenix/")
.withUnrecoverableErrorCallback((RequestStatus status, String description) -> {
// Best to restart app, or attempt to re-create PCastExpress
})
.buildPCastExpressOptions();
final PCastExpress pcastExpress = PCastExpressFactory.createPCastExpress(pcastExpressOptions);
PCastExpressOptionsBuilder
Name | Type | Default | Description |
---|---|---|---|
withBackendUri (required) | String | Url to your backend. We send requests here to authenticate and get streaming tokens. | |
withAuthenticationData (optional) | String | 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) | String | 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) | PCastExpressOptions.UnrecoverableErrorCallback | Function to be called when authentication fails or a failure occurs that is unrecoverable. | |
withPCastUri (optional) | String | Allows overriding default PCast URI. | |
withPCastInitializationOptions (optional) | PCastInitializeOptions | Use custom options when initializing PCast. | |
withAuthenticationRouteOverride (optional) | String | auth | Allows override of default route for authentication tokens |
withStreamRouteOverride (optional) | String | stream | Allows override of default route for stream tokens |
buildPCastExpressOptions | Builds the PCastExpressOptions |
Publishing Local Media
Publish local user media:
- Camera
- Microphone
- Screen (on supported devices)
import android.view.SurfaceView;
import com.phenixrts.common.RequestStatus;
import com.phenixrts.express.ExpressPublisher;
import com.phenixrts.express.PCastExpress;
import com.phenixrts.express.PCastExpressFactory;
import com.phenixrts.express.PublishOptions;
import com.phenixrts.pcast.AudioEchoCancelationMode;
import com.phenixrts.pcast.DeviceCapability;
import com.phenixrts.pcast.DeviceConstraint;
import com.phenixrts.pcast.FacingMode;
import com.phenixrts.pcast.Renderer;
import com.phenixrts.pcast.UserMediaOptions;
import com.phenixrts.pcast.android.AndroidVideoRenderSurface;
import java.util.Arrays;
final PCastExpress pcastExpress = ...; // previously obtained
final UserMediaOptions userMediaConstraints = new UserMediaOptions();
userMediaConstraints.getVideoOptions().enabled = true;
userMediaConstraints.getVideoOptions().capabilityConstraints.put(
DeviceCapability.FACING_MODE,
Arrays.asList(new DeviceConstraint(FacingMode.USER)));
userMediaConstraints.getAudioOptions().enabled = true;
userMediaConstraints.getAudioOptions().capabilityConstraints.put(
DeviceCapability.AUDIO_ECHO_CANCELATION_MODE,
Arrays.asList(new DeviceConstraint(AudioEchoCancelationMode.ON)));
final PublishOptions publishOptions = PCastExpressFactory.createPublishOptionsBuilder()
.withCapabilities(new String[] {"real-time"})
.withMediaConstraints(userMediaConstraints)
.buildPublishOptions();
pcastExpress.publish(publishOptions, (RequestStatus status, ExpressPublisher publisher) -> {
if (status == RequestStatus.OK) {
// Do something with publisher
} else {
// Handle error
}
});
// Create a publisher with an automatically started preview renderer
final SurfaceView view = ...; // previously obtained
final AndroidVideoRenderSurface renderSurface = new AndroidVideoRenderSurface(view.getHolder());
final PublishOptions publishOptionsWithPreview = PCastExpressFactory.createPublishOptionsBuilder()
.withCapabilities(new String[] {"real-time"})
.withMediaConstraints(userMediaConstraints)
.withPreviewRenderer(renderSurface)
.buildPublishOptions();
pcastExpress.publish(
publishOptions, (RequestStatus status, ExpressPublisher publisher, Renderer preview) -> {
if (status == RequestStatus.OK) {
// Do something with publisher and preview renderer
} else {
// Handle error
}
});
Notes:
- The preview renderer will already have been started by the time it is received by your callback
- The publisher will remain active for as long as you keep a reference to it
- You can force release of the publisher by invoking
dispose
on it
Parameters
Name | Type | Description |
---|---|---|
options (required) | PublishOptions | Publish options |
callback (required) | PCastExpress.PublishCallback or PCastExpress.PublishWithPreviewCallback | Callback for error/success handling |
PublishOptionsBuilder
Name | Type | Default | Description |
---|---|---|---|
withMediaConstraints (required) | UserMediaOptions | getUserMedia options Constraints to get the user media. | |
withUserMedia (optional) | UserMediaStream | alternative to withMediaConstraints - you can pass user media stream returned from getUserMedia. |
|
withCapabilities (optional) | String[] | The list of all capabilities to publish with. Default is empty array. | |
withPreviewRenderer (optional) | AndroidVideoRenderSurface | Render layer on which to display local preview. If none of the withPreview... methods are called, no preview renderer will be instantiated. |
|
withPreviewRenderer (optional) | Will trigger instantiation of preview renderer. Useful for audio only type streams that do not require a render surface. | ||
withPreviewRendererOptions (optional) | RendererOptions | Options passed to preview renderer. Will trigger instantiation of preview renderer. | |
withMonitor (optional) | MonitorOptions.SetupFailedCallback, MonitorOptions.StreamEndedCallback, MonitorOptions | Options for monitoring a publisher for failure. | |
withConnectOptions (optional) | Strings[] | List of options for publishing. | |
withTags (optional) | Strings[] | Tags for the stream. | |
withStreamToken (optional) | String | 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 | Builds the PublishOptions |
Callback Arguments
Name | Type | Description |
---|---|---|
status | RequestStatus | The status of the operation |
publisher | ExpressPublisher | Publisher object |
previewRenderer | Renderer | Optionally provided if any of the withPreview... methods were called on the options builder and publish is invoked with withPreview . |
ExpressPublisher
Shares most methods with regular Publisher returned by PCast, 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 StreamEndedReason.CUSTOM 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) | Disposable | Temporarily limit published video bitrate, see Limit Bitrate |
getStreamId | () | String | 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 com.phenixrts.common.RequestStatus;
import com.phenixrts.express.ExpressPublisher;
import com.phenixrts.express.PCastExpress;
import com.phenixrts.express.PCastExpressFactory;
import com.phenixrts.express.PublishRemoteOptions;
final PCastExpress pcastExpress = ...; // previously obtained
final PublishRemoteOptions publishRemoteOptions = PCastExpressFactory.createPublishRemoteOptionsBuilder()
.withStreamUri("http://mycdn.example.com/mystream.mp4")
.withCapabilities(new String[] {})
.buildPublishRemoteOptions();
pcastExpress.publishRemote(publishRemoteOptions, (RequestStatus status, ExpressPublisher publisher) -> {
if (status == RequestStatus.OK) {
// Do something with publisher
} else {
// Handle error
}
});
Parameters
Name | Type | Description |
---|---|---|
options (required) | PublishRemoteOptions | Publish Remote options |
callback (required) | PCastExpress.PublishCallback | Callback for error/success handling |
PublishRemoteOptionsBuilder
Name | Type | Default | Description |
---|---|---|---|
withCapabilities (required) | String[] | The list of all capabilities to publish with. | |
withStreamUri (required) | String | Link to remote media (mp4, rtmp, etc.) | |
withStreamToken (optional) | String | 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) | Strings[] | List of options for publishing from a remote source. | |
withTags (optional) | Strings[] | Tags for the stream | |
withMaximumFrameRateConstraint (optional) | double | Maximum frame rate constraint. | |
withExactFrameRateConstraint (optional) | double | Exact frame rate constraint. | |
withPrerollSkipDuration (optional) | long | 500 | The amount of time to skip at the beginning of the media in milliseconds. |
buildPublishRemoteOptions | Builds the PublishRemoteOptions |
Callback Arguments
Name | Type | Description |
---|---|---|
status | RequestStatus | The status of the operation. See Publish Callback Status Codes |
publisher | ExpressPublisher | Phenix publisher object |
Subscribing to Published Media
Subscribe to streams published with the Phenix platform
import android.view.SurfaceView;
import com.phenixrts.common.RequestStatus;
import com.phenixrts.express.ExpressSubscriber;
import com.phenixrts.express.PCastExpress;
import com.phenixrts.express.PCastExpressFactory;
import com.phenixrts.express.SubscribeOptions;
import com.phenixrts.pcast.Renderer;
import com.phenixrts.pcast.android.AndroidVideoRenderSurface;
final PCastExpress pcastExpress = ...; // previously obtained
final SurfaceView view = ...; // previously obtained
final AndroidVideoRenderSurface renderSurface = new AndroidVideoRenderSurface(view.getHolder());
final SubscribeOptions subscribeOptions = PCastExpressFactory.createSubscribeOptionsBuilder()
.withStreamId("us-west#us-west1-b.zzzzzzzz.20000000.xxxxxxxx")
.withCapabilities(new String[] {"streaming"})
.withRenderer(renderSurface)
.buildSubscribeOptions();
pcastExpress.subscribe(
subscribeOptions,
(RequestStatus status, ExpressSubscriber subscriber, Renderer renderer) -> {
if (status == RequestStatus.OK) {
// Do something with subscriber
if (renderer != null) {
// Returned if `withRenderer...` option was enabled - Do something with renderer
}
} else {
// Handle error
}
});
Notes:
- The renderer will already have been started by the time it is received by your callback
- If a renderer is provided, your ExpressSubscriber will be kept alive for as long as you are referencing that renderer. There is no need to also store a reference to the subscriber in that case
- Once subscriber and renderer references have been released, the renderer and subscription will automatically be stopped
- To force release renderer or subscriber, you can invoke
dispose
, which will dispose the underlying resources
Parameters
Name | Type | Description |
---|---|---|
options (required) | SubscribeOptions | Subscribe options |
callback (required) | PCastExpress.SubscribeCallback | Callback for error/success handling |
Subscribe Options
Name | Type | Default | Description |
---|---|---|---|
withStreamId (required) | String | The stream ID of the published stream | |
withCapabilities (required) | String[] | The list of all capabilities to subscribe with. | |
withStreamToken (optional) | String | 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) | AndroidVideoRenderSurface | Render layer on which to display stream. If none of the withRenderer... methods are called, no renderer will be instantiated. |
|
withRenderer (optional) | Will trigger instantiation of renderer. Useful for audio only type streams that do not require a render surface. | ||
withRendererOptions (optional) | RendererOptions | Options passed to renderer. Will trigger instantiation of renderer. | |
withMonitor (optional) | MonitorOptions.SetupFailedCallback, MonitorOptions.StreamEndedCallback, MonitorOptions | Options for monitoring a subscriber for failure. | |
withConnectOptions (optional) | String[] | List of options for subscribing. | |
withTags (optional) | NSString[] | Tags for the stream | |
buildSubscribeOptions | Builds the SubscribeOptions |
ExpressSubscriber
Shares most methods with regular MediaStream returned by PCast, see Subscribe to a Stream.
Name | Signature | Returns | Description |
---|---|---|---|
createRenderer | () | Renderer | Creates a new renderer. This should only be called if none of the withRenderer... builder methods were invoked. |
createRenderer | (RendererOptions) | Renderer | Creates a new renderer with RendererOptions. This should only be called if none of the withRenderer... builder methods were invoked. |
getAudioTracks | () | MediaStreamTrack[] | Returns all associated audio tracks of this stream |
getVideoTracks | () | MediaStreamTrack[] | Returns all associated video tracks of this stream |
getTracks | () | MediaStreamTrack[] | 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 Android, 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 OptionalAction 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 should call dismiss
on the retry action to dismiss it, dispose
has the same effect. You also may defer invoking the retry action.
Example Monitor for subscribing
import com.phenixrts.common.OptionalAction;
import com.phenixrts.common.RequestStatus;
import com.phenixrts.express.ExpressSubscriber;
import com.phenixrts.express.MonitorOptions;
import com.phenixrts.express.PCastExpressFactory;
import com.phenixrts.express.SubscribeOptions;
import com.phenixrts.pcast.StreamEndedReason;
final MonitorOptions monitorOptions = PCastExpressFactory.createMonitorOptionsBuilder()
.buildMonitorOptions();
final SubscribeOptions subscribeOptions = PCastExpressFactory.createSubscribeOptionsBuilder()
.withStreamId("us-west#us-west1-b.zzzzzzzz.20000000.xxxxxxxx")
.withCapabilities(new String[] {"real-time"})
.withMonitor(
(RequestStatus status, OptionalAction retry) -> {
if (retry.isPresent()) {
if (shouldRetry()) { // <- Your logic goes here
retry.perform();
} else {
retry.dismiss();
}
}
},
(StreamEndedReason reason, String description, OptionalAction retry) -> {
if (retry.isPresent()) {
if (reason == StreamEndedReason.FAILED) { // <- Just an example
retry.perform();
} else {
retry.dismiss();
}
}
},
monitorOptions)
.buildSubscribeOptions();
OptionalAction
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. Invoking dispose has same effect. |
isPresent | () | boolean | Indicates whether an action can be performed. |
MonitorSetupFailedCallback Callback Arguments
Name | Type | Description |
---|---|---|
status | RequestStatus | The status of the operation. |
retry | OptionalAction | Optionally allow retrying the failed stream. |
MonitorStreamEndedCallback Callback Arguments
Name | Type | Description |
---|---|---|
reason | StreamEndedReason | Reason for stream ended. |
description | String | Optional additional ended reason description. Carries custom message. |
retry | OptionalAction | 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 com.phenixrts.common.RequestStatus;
import com.phenixrts.express.PCastExpress;
import com.phenixrts.pcast.UserMediaOptions;
import com.phenixrts.pcast.UserMediaStream;
final PCastExpress pcastExpress = ...; // previously obtained
final UserMediaOptions userMediaOptions = new UserMediaOptions();
pcastExpress.getUserMedia(userMediaOptions, (RequestStatus status, UserMediaStream userMedia) -> {
if (status == RequestStatus.OK) {
// Do something with user media stream
} else {
// Handle error
}
});
Parameters
Name | Type | Description |
---|---|---|
options (required) | UserMediaOptions | User media options |
callback (required) | PCastExpress.GetUserMediaCallback | Callback for error/success handling |
Callback Arguments
Name | Type | Description |
---|---|---|
status | RequestStatus | The status of the operation. |
userMedia | UserMediaStream | 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 com.phenixrts.express.PCastExpress;
import com.phenixrts.pcast.PCast;
final PCastExpress pcastExpress = ...; // previously obtained
final PCast pcast = pcastExpress.getPCast();
Clean up
Subscribers and publishers are kept alive for as long as they are being referenced in your app. To force SDK objects to release their resources, you can call dispose
on them . PCastExpress will only shutdown once it is no longer being referenced or dispose
has been called on it.
Override Playout Delay
Example Code for overriding the playout delay via a MediaStream object
import com.phenixrts.common.Disposable;
import com.phenixrts.pcast.MediaStream;
import com.phenixrts.pcast.Renderer;
class SomeClass {
private Disposable currentRendererPlayoutDelayOverride;
private Renderer currentRenderer;
private void setPlayoutDelayOverrideFor10Seconds() {
final MediaStream mediaStream = ...; // Previously obtained
this.currentRenderer = mediaStream.createRenderer();
// Override playout delay to 900ms for 10 seconds
final long playoutDelayInMilliseconds = 900
this.currentRendererPlayoutDelayOverride = this.currentRenderer.overridePlayoutDelay(playoutDelayInMilliseconds);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
SomeClass.this.currentRendererPlayoutDelayOverride.dispose();
SomeClass.this.currentRendererPlayoutDelayOverride = null;
}
}, 10000);
}
}
Example Code for limiting video bandwidth with a ExpressSubscriber object
import com.phenixrts.common.Disposable;
import com.phenixrts.express.ExpressSubscriber;
import com.phenixrts.pcast.Renderer;
class SomeClass {
private Disposable currentRendererPlayoutDelayOverride;
private Renderer currentRenderer;
private void setPlayoutDelayOverrideFor10Seconds() {
final ExpressSubscriber subscriber = ...; // Previously obtained
this.currentRenderer = subscriber.createRenderer();
// Override playout delay to 900ms for 10 seconds
final long playoutDelayInMilliseconds = 900
this.currentRendererPlayoutDelayOverride = this.currentRenderer.overridePlayoutDelay(playoutDelayInMilliseconds);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
SomeClass.this.currentRendererPlayoutDelayOverride.dispose();
SomeClass.this.currentRendererPlayoutDelayOverride = null;
}
}, 10000);
}
}
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 Renderer
is needed. It can be obtained either by creating it from MediaStream
or ExpressSubscriber
, 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:
- The override represents an absolute value, not a delta.
- If an override increases the playout delay, it will result in content being paused. Example: changing a delay of 1 second to 5 seconds will cause the content to be paused for roughly 4 seconds.
- If an override decreases the playout delay, it will cause a jump where some of the content will be skipped.
- Very large override values will increase the amount of memory consumed. It is generally recommended to stay below 10 seconds.
Parameters
Name | Type | Description |
---|---|---|
desiredPlayoutDelayInMilliseconds (required) | long | Desired playout delay |
Returns
Type | Description |
---|---|
Disposable | Ensures override is kept in effect for as long as a strong reference is held |
Android Examples
You can find our Android examples on GitHub
WebView
A simple example that integrates WebView to load a web page using the Phenix Web SDK.