Skip to content

ChromeCast Documentation

ChromeCast Integration

Cast serves as a reference implementation of a ChromeCast sender interface for communicating with the EMP Receiver. Client applications should define their own custom sender library.

Getting Started

EMP ChromeCast layer, Cast extends the default GoogleCast framework to work with the EMP ChromeCast Receiver and is fully compliant with the default version unless otherwise stated. The framework is not intended to be a replacement for *Google*s implementaion. Please have a look at the EMP ChromeCast Receiver for in depth documentation.

Cast does not alter the general ChromeCast workflow as described by Google in any significant way.

iOS Permissions Changes

With recent updates to iOS, the operating system will now enforce new restrictions and permissions that affect the Cast user experience. It will also affect how you build the Cast SDK into your app. For your app to maintain Cast functionality with the latest versions of iOS, you must make updates to handle these permissions changes.

Cast library uses the Google Chrome cast sdk which requires Bluetooth® permissions.

Please follow the guidelines in this document to support the iOS version your app supports . Google Cast

Sample Integration

Developers can find a sample implementation of the cast sdk here. iOSClientSDKSampleApp

Version 3.0

Loading Media

Loading media onto the receiver requires client applications to supply several things. First of all, a valid SessionToken and an Exposure Environment is required as the receiver will perform an entitlements request prior to starting playback. Secondly, media identifiers in the form of EMP asset Id.

From the version 2.0 & onwards developers do not need to create CastEnvironment. They can directly pass the customer & businessUnit values as CustomData in to the cast receiver.

let customData = CustomData(customer: environment.customer, businessUnit: environment.businessUnit, locale: "fr").toJson

Then developers can create the GCKMediaInformationBuilder & assign the EMP asset Id value as contentID value.

let mediaInfoBuilder = GCKMediaInformationBuilder()
mediaInfoBuilder.contentID = playable.assetId

Finally you need to assign the SessionToken value as the GCKMediaLoadRequestDataBuilder.credentials value to make an authenticated GCKRequest.

let mediaInfo = mediaInfoBuilder.build()

if let remoteMediaClient = session.remoteMediaClient {

   let mediaQueueItemBuilder = GCKMediaQueueItemBuilder()
   mediaQueueItemBuilder.mediaInformation = mediaInfo
   let mediaQueueItem = mediaQueueItemBuilder.build()
   let queueDataBuilder = GCKMediaQueueDataBuilder(queueType: .generic)
   queueDataBuilder.items = [mediaQueueItem]
   queueDataBuilder.repeatMode = remoteMediaClient.mediaStatus?.queueRepeatMode ?? .off



   let mediaLoadRequestDataBuilder = GCKMediaLoadRequestDataBuilder()
   mediaLoadRequestDataBuilder.credentials = "SessionToken"
   mediaLoadRequestDataBuilder.queueData = queueDataBuilder.build()
   mediaLoadRequestDataBuilder.customData = customData

   let request = remoteMediaClient.loadMedia(with: mediaLoadRequestDataBuilder.build())

}

Server-Side Ad Insertion (SSAI) when casting

if you are planning to use server side ads insertion & required to pass adParameters to the cast reciever, you can create CastAdsOptions & add it to the CustomData.

let adsOptions = CastAdsOptions(latitude: "18.000", longitude: "18.000", mute: true, consent: "consent", deviceMake: "deviceMake", ifa: "ifa", gdprOptin: true)
let customData = CustomData(customer: environment.customer, businessUnit: environment.businessUnit, adParameters: adsOptions).toJson

Integrate Primetime authentication when casting

if you are planning to use adobe primetime authentication & required to pass Adobe Primetime Token when casting, you can add it to the CustomData as well.

let customData = CustomData(customer: environment.customer, businessUnit: environment.businessUnit, adobePrimetimeToken: "base64EncodedToken").toJson

Add support for default cast subtitle & audio language

You can pass subtitle & audio language values when start casting by passing them in the CustomData.

let customData = CustomData(customer: environment.customer, businessUnit: environment.businessUnit, subtitleLanguage: "en", audioLanguage: "fr").toJson

Version 2.2.00 or below

Loading Media

Loading media onto the receiver requires client applications to supply several things. First of all, a valid SessionToken and an Exposure Environment is required as the receiver will perform an entitlements request prior to starting playback. Secondly, media identifiers in the form of EMP asset Id (and optionally a program id) for the asset in question.

let environment = CastEnvironment(baseUrl: exposureBaseUrl,
customer: "someCustomer",
businessUnit: "someBusinessUnit",
sessionToken: validSessionToken)

let properties = PlaybackProperties(playFrom: "bookmark")

PlaybackProperties specify startTime behavior. The following options apply:

  • defaultBehaviour (default) Start at beginning of the program if programId is included otherwise start at live edge
  • startTime Start at a Unix startTime
  • beginning Start at the beginning of the program
  • bookmark Start at the bookmark returned by EMP backend. If there is no bookmark, it falls back to the defaultBehaviour

Finally, each load request may be complimented with CustomData that issues special instructions to the receiver.

let assetPlayback = CustomData(environment: environment, assetId: "anAssetId", playbackProperties: properties)

// Old versions
let vodPlayback = CustomData(environment: environment, assetId: "anAssetId", playbackProperties: properties)

let channelPlayback = CustomData(environment: environment, channelId: "aChannelId", playbackProperties: properties)

let programPlaybacl = CustomData(environment: environment, channelId: "aChannelId", programId: "aSpecificProgramId", playbackProperties: properties)

These include, but are not limited to:

  • Audio and text language
  • Start time manipulation
  • Timeshift override
  • Maximum bitrate selection
  • Autoplay behavior
  • Session shift configuration
  • Language that can be used for mediainfo in control bar.

  • When the subtitle parameter ( textLanguage ) is not specified , Cast will pass None to the receiver so it will start the chrome cast playback without the subtitles.

The actual loading of the configured media is done through *API*s provided by Google. An initial set of metadata can be supplied before loading the media. This will show up on the mini controller and the expanded controller. Once the receiver has loaded the media, the metadata will be updated continuously. It is important to use the correct contentID when configuring the GCKMediaInformation.

  • program playback: use programId
  • channel playback: use channelId
  • assets or vods: use assetId

Please consult the Google docmentation for more information.

let mediaInfoBuilder = GCKMediaInformationBuilder()
mediaInfoBuilder.contentID = contentId
mediaInfoBuilder.streamType = .none
mediaInfoBuilder.contentType = "video/mp4"
mediaInfoBuilder.metadata = nil
mediaInfoBuilder.streamDuration = 0
mediaInfoBuilder.mediaTracks = nil
mediaInfoBuilder.textTrackStyle = nil

let mediaInfo = mediaInfoBuilder.build()

let mediaLoadOptions = GCKMediaLoadOptions()

// Provide the custom data to the receiver.
mediaLoadOptions.customData = customData.toJson

GCKCastContext
    .sharedInstance()
    .sessionManager
    .currentCastSession
    .remoteMediaClient?
    .loadMedia(mediaInfo, with: mediaLoadOptions)

Responding to Events

The EMP Receiver has been customized to publish several non-standard events. These can be listened to through by creating, registering and then observing Channels.

A Channel is an Cast specific extension of the Google provided GCKCastChannel and governs the sender-to-receiver communication.

Embedded tracks are broadcasted to all connected senders when the tracks or the period change. Client applications should listen to the onTracksUpdated message and update UI accordingly.

let channel = Channel()

channel.onTracksUpdated { tracksUpdated in
    // Update UI
}

tracksUpdated contains information about subtiles and audio along with information about the currently active track(s). Selecting a different track can be done by supplying the Channel with a Track struct.

let englishSubs = tracksUpdated
    .subtitles
    .filter{ $0.language == "en" }
    .first

if let subs = englishSubs {
    channel.use(textTrack: subs)
}

Or by specifying the 2 letter code for the desired language.

channel.use(subtitle: "en")

The same proceedure applies for audio tracks.

Finally, it is possible to hide the subtitle track.

channel.hideSubtitles()

A user who joins a session in progress may wish to receive updated status of the ChromeCast controls. This can be done by calling pull() which will force an onTracksUpdated event.

channel.pull()

There is also several general information events detailing how the stream in question behaves. onTimeshiftEnabled for example indicates if timeline UI should be present or not.

channel
    .onTimeshiftEnabled { timeshift in
        // Hide or show timeline in UI
    }
    .onVolumeChanged { volumeChanged in
        // Update the volume indicator
    }
    .onAutoplay { autoplay in
        // Indicates if playback during the cast session was started automatically
    }

The following methods declare event listeners that may be especially useful for managing live playback.

channel
    .onEntitlementChange{ entitlement in
        // Indicages the playback session takes place under a new entitlement
    }
    .onStartTimeLive{ startTime in
        // Indicates the start time in *unix epoch* time for the current live stream.
    }
    .onProgramUpdated{ program in
        // The program changed, update relevant media metadata
    }
    .onIsLive { isLive in
        // Update "live now" indicators
    }

Error Handling

Two event listeners declare events related to error handling. The first, onSegmentMissing, publishes information about seek cancellation due to missing segments. It can be useful to give users feedback when their intended seek failed. Beyond that, errors will be broadcasted through the onError event.

channel
    .onSegmentMissing{ segment in
        // Indicate the seek was cancelled due to missing segments
    }
    .onError{ error in
        // Handle or display the error in question
    }

Error handling revolves around three types of errors, ReceiverError, SenderError and GCKError. The former two are defined by Cast and the last one provided by Google.

SenderErrors mostly deal with serialization and message translation stemming from the sender-to-receiver communication. Errors in this domain indicate issues with either the Cast framework or it's integration with the EMP receiver.

ReceiverErrors come direcly from the EMP receiver. For more information with regards to debugging the receiver, please look at this documentation. Each ReceiverError has an associated code and message.

Finally, GCKError stem from GoogleCast framework. More information can be found here