Develop iOS Sender App with Cast v2

This overview shows how to build Google Cast sender applications for iOS using the Google Cast SDK v2.

In this document, sender application refers to an iOS app running on a mobile device (the sender device), and receiver application refers to the receiver application running on the Cast device.

Setup

The Google Cast SDK supports iOS version 6 and later.

Library

Download the Google Cast iOS Sender API library. See the Google Cast iOS API Reference for descriptions of all classes and methods.

Xcode setup

In your Xcode project, set the Other Linker Flags to -ObjC .

 

In your Xcode project, add the following framework libraries (linked, not embedded):

  • Accelerate.framework
  • AudioToolbox.framework
  • AVFoundation.framework
  • CoreBluetooth.framework
  • CoreText.framework
  • GoogleCast.framework
  • libc++.dylib
  • MediaAccessibility.framework
  • MediaPlayer.framework
  • MediaToolbox.framework
  • SystemConfiguration.framework

Add the GoogleCastResource.bundle to your app. In the Build Phases for your target, add a new entry in the Copy Bundle Resources section, and select Add Other.

Browse to the framework, and select GoogleCastResources.bundle inside.

Development

The Google Cast SDK uses the delegation pattern to inform the application of events and to move between various states of the Cast app life cycle.

Application flow

The following sections cover the details of the typical execution flow for a sender application:

Scan for devices

In this step, the sender application searches the WiFi network for Google Cast receiver devices. This involves instantiating a device scanner, a delegate, and starting the scan. As the scanner discovers devices, it notifies the application via the delegate.

In order to cast, the application must know what receivers are available. Receivers are discovered through scanning, which is done by a GCKDeviceScanner object. While a scan is in progress, the GCKDeviceScanner regularly polls the network to maintain a list of available Cast devices.

A Google Cast receiver device is represented by a GCKDevice object, which contains attributes like the device's IP address, friendly display name, model and manufacturer, and a URL to the device's display icon.

An application should run a scan while a Cast button is visible. The scanner increases battery consumption and network traffic, so the app may turn it off in areas with no casting functionality. A scan is either in active mode or passive mode. Passive mode consumes less battery and produces less network traffic than active mode, but the results are less up-to-date. Passive mode is preferred while the Cast device menu is closed, and active mode is preferred while the Cast device menu is open.

To scan for Cast-enabled devices you must define a device scanner and register the delegate, then start scanning.

Device scanner

Initialize the device scanner and create filter criteria (GCKFilterCriteria) to show only devices that can run your app. This allows you to publish your app to the Apple App Store before publishing in the Cast console. Once the app is published in the Cast console the Cast icon will begin showing up on iOS devices. If an app is not published in the Cast console, the Cast icon will only appear for whitelisted devices.

Objective-C
Swift

After scanning begins, your delegate will be notified when devices are discovered or go offline.

Device scanner listener

The listener must be set as the delegate of the device scanner.

Objective-C
Swift

For convenience, the device scanner keeps track of all known active devices. This can be used to create an UIActionSheet for deploying devices to the user.

Showing devices in UIActionSheet

Objective-C
Swift

Device selection

Once the user has selected a device, you can connect to it. Start by creating a device manager and give it the selected device. Next, you register a delegate to listen for the connection. Finally, you connect to the device.

Objective-C
Swift

Launch application

Once you are connected to the receiver you will be notified. After connecting successfully, you can launch your application.

Objective-C
Swift

Media channels

Media channels provide the means by which your sender app controls the playback on the receiver. You can also define custom channels to send custom messages to the receiver.

Media control channel

The media control channel plays, pauses, seeks, and stops the media on a receiver application. The media channel has a well-known namespace of urn:x-cast:com.google.cast.media.

To use a media channel you must create an instance of GCKMediaControlChannel after you connect to the Cast application.

Objective-C
Swift

Next, you must define the media you would like to cast by using the GCKMediaMetadata class. See Media metadata, below.

Custom channel

Your sender can use a custom channel to send String messages to the receiver. Each custom channel is defined by a unique namespace and must start with the prefix urn:x-cast:, for example, urn:x-cast:com.example.custom. It is possible to have multiple custom channels, each with a unique namespace.

The sender can work with a custom channel by creating a class that extends GCKCastChannel, and implements the didReceiveTextMessage method, as in the example below.

Objective-C
Swift

The sender can send a message to the receiver by using GCKCastChannel.sendTextMessage.

Objective-C
Swift

Often, an application will encode JSON messages as strings, and have the receiver decode the strings.

Media metadata

Define the media you would like to cast by using the GCKMediaMetadata class.

Objective-C
Swift

Load media

Finally, you are ready to load and cast the media. You must create a GCKMediaInformation that can be used to cast the media on the media control channel.

Objective-C
Swift

Using the Tracks APIs

A track can be a text (subtitle or caption), an audio, or a video stream object. The Tracks APIs let you work with these objects in your application.

A GCKMediaTrack object represents a track in the SDK. The following example shows how to configure and assign a unique ID to a track.

Objective-C
  GCKMediaTrack *captionsTrack =
      [[GCKMediaTrack alloc] initWithIdentifier:1
                              contentIdentifier:@"https://commondatastorage.googleapis.com/"
                                                "gtv-videos-bucket/sample/"
                                                "DesigningForGoogleCast-en.vtt"
                                    contentType:@"text/vtt"
                                           type:GCKMediaTrackTypeText
                                    textSubtype:GCKMediaTextTrackSubtypeCaptions
                                           name:@"English Captions"
                                   languageCode:@"en"
                                     customData:nil];
      
Swift
    let captionsTrack = GCKMediaTrack(identifier: 1, contentIdentifier:
        "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/" +
        "DesigningForGoogleCast-en.vtt", contentType: "text/vtt", type: GCKMediaTrackType.Text,
        textSubtype: GCKMediaTextTrackSubtype.Captions, name: "English Captions",
        languageCode: "en", customData: nil)
      

A media item may have multiple tracks; for example, it can have multiple subtitles (each for a different language) or multiple alternative audio streams (for different languages).

GCKMediaInformation is the class that models a media item. To associate a collection of GCKMediaTrack objects with a media item, you update its mediaTracks property. This association needs to be made before the media is loaded to the receiver, as in the code below.

Objective-C
  NSArray *tracks = @[captionsTrack];

  GCKMediaInformation *mediaInformation =
      [[GCKMediaInformation alloc] initWithContentID:@"https://commondatastorage.googleapis.com/"
                                                     "gtv-videos-bucket/sample/"
                                                     "DesigningForGoogleCast.mp4"
                                          streamType:GCKMediaStreamTypeNone
                                         contentType:@"video/mp4"
                                            metadata:metadata
                                      streamDuration:0
                                         mediaTracks:tracks
                                      textTrackStyle:nil
                                          customData:nil];
      
Swift
    let tracks = [captionsTrack]
    let mediaInformation = GCKMediaInformation(contentID:
        "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/" +
        "DesigningForGoogleCast.mp4", streamType: GCKMediaStreamType.None, contentType: "video/mp4",
        metadata: metadata, streamDuration: 0, mediaTracks: tracks, textTrackStyle: nil,
        customData: nil)
      

Activate one or more tracks that were associated with the media item (after the media is loaded) by calling GCKMediaControlChannel setActiveTrackIDs and passing the IDs of the tracks to be activated. For example, the following code activates the captions track created above.

Objective-C
    [_mediaControlChannel setActiveTrackIDs:@[@1]];
      
Swift
      mediaControlChannel?.setActiveTrackIDs([1])
      

To deactivate a track on the current media item, call setActiveTrackIDs without the track's ID. The following code disables the captions track.

Objective-C
    [_mediaControlChannel setActiveTrackIDs: @[]];
      
Swift
      mediaControlChannel?.setActiveTrackIDs([])
      

Styling text tracks

The GCKMediaTextTrackStyle class encapsulates a text track's style. After creating or updating an existing GCKMediaTextTrackStyle object, request it be applied to the currently playing media item by calling GCKMediaControlChannel setTextTrackStyle. The track style created in the code below turns text red and sets a seriffed font.

Objective-C
    GCKMediaTextTrackStyle *textTrackStyle = [GCKMediaTextTrackStyle createDefault];
    [textTrackStyle setForegroundColor:[[GCKColor alloc] initWithCSSString:@"#FF000080"]];
    [textTrackStyle setFontFamily:@"serif"];
    // Save the Request Id if you want to check its status later
    _styleChangeRequestId = [_mediaControlChannel setTextTrackStyle:textTrackStyle];
      
Swift
      let textTrackStyle = GCKMediaTextTrackStyle.createDefault()
      textTrackStyle.foregroundColor = GCKColor(CSSString: "#FF000080")
      textTrackStyle.fontFamily = "serif"
      styleChangeRequestID = mediaControlChannel?.setTextTrackStyle(textTrackStyle)
      

You can track the status of the request with the GCKMediaControllerDelegate requestDidCompleteWIthID method, which receives request IDs that you can compare to the one returned from the setTextTrackStyle call, as in the code below.

Objective-C
#pragma mark - GCKMediaControlChannelDelegate
- (void)mediaControlChannel:(GCKMediaControlChannel *)mediaControlChannel
   requestDidCompleteWithID:(NSInteger)requestID {
  if (requestID == kGCKInvalidRequestID) {
    NSLog(@"An invalid request ID was issued.");
  } else if (requestID == _styleChangeRequestId) {  // Saved from setting the style.
    NSLog(@"Style update completed.");
  } else {
    NSLog(@"Other update completed.");
  }
}
      
Swift
  func mediaControlChannel(mediaControlChannel: GCKMediaControlChannel!,
      requestDidCompleteWithID requestID: Int) {
    if (requestID == kGCKInvalidRequestID) {
      print("An invalid request ID was issued.")
    } else if (requestID == styleChangeRequestID) {
      print("Style update completed.")
    } else {
      print("Other update completed.")
    }
  }
      

See Status updates below for more information.

Applications should allow users to update the style for text tracks, either using the settings provided by the system or by the application itself. There is a default style provided (in iOS 7 and later), which can be retrieved via the static method GCKMediaTextTrackStyle createDefault.

The following text track style elements can be changed:

  • Foreground (text) color and opacity
  • Background color and opacity
  • Edge type
  • Edge Color
  • Font Scale
  • Font Family
  • Font Style

Volume control

The sender app should adhere to the following guidelines for controlling volume:

  • The sender application must synchronize with the receiver so that the sender UI always reports the volume per the receiver. Use the volumeDidChangeToLevel callback to maintain the volume on the sender. See Status updates for more information.
  • Sender apps must not set the volume level at a specific, pre-defined level or set the volume level to the sender device's ringer/media volume when the app loads on the receiver.

See Sender volume controls in the Design Checklist for more specific UI guidelines.

Status updates

When multiple senders are connected to the same receiver, it is important for each sender to be aware of the changes on the receiver even if those changes were initiated from other senders.

To this end, your application should register a GCKMediaControlChannelDelegate. If the GCKMediaTextTrackStyle of the current media changes, then all of the connected senders will be notified through both the mediaControlChannelDidUpdateMetadata: and the mediaControlChannelDidUpdateStatus: callbacks. In this case, the receiver SDK does not verify whether the new style is different from the previous one and notifies all the connected senders regardless. If, however, the list of active tracks is updated, only the mediaControlChannelDidUpdateStatus in connected senders will be notified.

Progress indicator

Showing the playback location with a progress indicator on the sender is a requirement for most apps. The Cast APIs use the Cast media protocol, which optimizes bandwidth consumption for this and other scenarios, so you do not need to implement your own status synchronization. For the proper implementation of a progress indicator for media playback using the APIs, see the CastVideos-ios sample app.

CORS requirements

For adaptive media streaming, Google Cast requires the presence of CORS headers, but even simple mp4 media streams require CORS if they include Tracks. If you want to enable Tracks for any media, you must enable CORS for both your track streams and your media streams. So, if you do not have CORS headers available for your simple mp4 media on your server, and you then add a simple subtitle track, you will not be able to stream your media unless you update your server to include the appropriate CORS header. In addition, you need to allow at least the following headers: Content-Type, Accept-Encoding, and Range. Note that the last two headers are additional headers that you may not have needed previously.

Reconnection

Your sender application may become implicitly disconnected from an active Cast session due to a network problem or any number of reasons beyond the application's control. When this happens, the sender should reconnect automatically to the active Cast session so that the user can continue to control the playback.

To learn how to reconnect your app, see Reconnection for Cast SDK v2.

Logging

Use the GCKLoggerDelegate to customize how you handle log messages.

Objective-C
Swift

Sample apps

Several Google Cast sample apps have been open sourced on GitHub. It is highly recommended that you import these apps into your development environment and get them running on your devices. This will ensure that your development environment is ready to create your own Cast apps.