This overview shows how to build Google Cast sender applications for iOS using the Google Cast SDK.
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):
- GoogleCast.framework
- SystemConfiguration.framework
- MediaAccessibility.framework (for iOS 7 and later)
- CoreText.framework (for iOS 7 and later)
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.
A Google Cast receiver device is represented by a GCKDevice class, which contains attributes like the device's IP address, friendly display name, model and manufacturer, and a URL to the device's display icon.
Typically, an application will run a scan for a fixed amount of time (for example, 5 seconds), and display a list of discovered devices to the user. The user will then select the device they wish to interact with from this list.
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.
self.deviceScanner = [[GCKDeviceScanner alloc] init]; GCKFilterCriteria *filterCriteria = [[GCKFilterCriteria alloc] init]; filterCriteria = [GCKFilterCriteria criteriaForAvailableApplicationWithID:@"YOUR_APP_ID_HERE"]; self.deviceScanner.filterCriteria = filterCriteria; [self.deviceScanner addListener:self]; [self.deviceScanner startScan];
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.
#pragma mark - GCKDeviceScannerListener
- (void)deviceDidComeOnline:(GCKDevice *)device {
NSLog(@"device found!!!");
}
- (void)deviceDidGoOffline:(GCKDevice *)device {
NSLog(@"device disappeared!!!");
}
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
UIActionSheet *sheet = ...;
for ( GCKDevice* device in deviceScanner.devices ){
[sheet addButtonWithTitle:device.friendlyName];
}
[sheet showInView:myView];
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.
GCKDevice *selectedDevice = ...; deviceManager = [[GCKDeviceManager alloc] initWithDevice:selectedDevice clientPackageName:[info objectForKey:@"CFBundleIdentifier"]]; deviceManager.delegate = self; [self.deviceManager connect];
Launch application
Once you are connected to the receiver you will be notified. After successful connection you can launch your application.
#pragma mark - GCKDeviceManagerDelegate
- (void)deviceManagerDidConnect:(GCKDeviceManager *)deviceManager {
NSLog(@"connected!!");
[self.deviceManager launchApplication:@"APP_ID_HERE"];
}
Media channels
Media channels provide the means by which your sender app controls the playback on the receiver. You can also define a 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.
#pragma mark - GCKDeviceManagerDelegate
- (void)deviceManager:(GCKDeviceManager *)deviceManager
didConnectToCastApplication:(GCKApplicationMetadata *)applicationMetadata
sessionID:(NSString *)sessionID
launchedApplication:(BOOL)launchedApp {
self.mediaControlChannel = [[GCKMediaControlChannel alloc] init];
self.mediaControlChannel.delegate = self;
[self.deviceManager addChannel:self.mediaControlChannel];
}
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:
//Custom Channel class that extends GCKCastChannel
@implementation MyCustomChannel
- (void)didReceiveTextMessage:(NSString*)message {
NSLog(@"received message: %@",message);
}
@end
The sender can send a message to the receiver by using GCKCastChannel.sendTextMessage.
MyCustomChannel *textChannel = ...; [self.textChannel sendTextMessage:[@"my text message"]];
Often an application will encode JSON messages into a String, then the receiver can decode the JSON string.
Media metadata
Define the media you would like to cast by using the GCKMediaMetadata class.
GCKMediaMetadata *metadata = [[GCKMediaMetadata alloc] init]; [metadata setString:title forKey:kGCKMetadataKeyTitle]; [metadata setString:subtitle forKey:kGCKMetadataKeySubtitle]; [metadata addImage:[[GCKImage alloc] initWithURL:thumbnailURL width:200 height:100]];
Finally you are ready to cast the media. You must create a GCKMediaInformation that can be used to cast the media on the media control channel.
Load media
To load the media, do the following.
GCKMediaInformation *mediaInformation =
[[GCKMediaInformation alloc] initWithContentID:[url absoluteString]
streamType:GCKMediaStreamTypeNone
contentType:mimeType
metadata:metaData
streamDuration:123
customData:nil];
[_mediaControlChannel loadMedia:mediaInformation autoplay:autoPlay playPosition:startTime];
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. You can configure a track and assign a unique ID to it like this:
[[GCKMediaTrack alloc] initWithIdentifier:1
contentIdentifier:@"https://some-url/caption_en.vtt"
contentType:@"text/vtt" type:GCKMediaTrackTypeText
textSubtype:GCKMediaTextTrackSubtypeSubtitles
name:@"English Subtitles"
languageCode:@"en-US"
customData:nil];
[[GCKMediaTrack alloc] initWithIdentifier:2
contentIdentifier:@"https://some-url/caption_fr.vtt"
contentType:@"text/vtt" type:GCKMediaTrackTypeText
textSubtype:GCKMediaTextTrackSubtypeSubtitles
name:@"French Subtitles"
languageCode:@"fr"
customData:nil];
[[GCKMediaTrack alloc] initWithIdentifier:3
contentIdentifier:@"trk0001"
contentType:@"audio/mp3" type:GCKMediaTrackTypeAudio
textSubtype:nil
name:@"French Audio"
languageCode:@"fr"
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:
NSArray *tracks = @[englishSubtitle, frenchSubtitle]; GCKMediaInfo *mediaInfo = [[GCKMediaInformation alloc] initWithContentID:@"URL" streamType:GCKMediaStreamTypeBuffered contentType:@"content type" metadata:metadata streamDuration:0 mediaTracks:tracks textTrackStyle:[GCKMediaTextTrackStyle createDefault] customData:nil];
You 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, here is how to activate the French subtitle (@2) and French audio (@3):
[_mediaControlChannel setActiveTrackIds:@[@2, @3]]
To remove all the tracks from the current media (for example turning off subtitles), call setActiveTrackIDs with no IDs.
Styling text tracks
GCKMediaTextTrackStyle is the object that encapsulates the styling information of a text track. After creating or updating an existing GCKMediaTextTrackStyle object, you can apply that to the currently playing media item by calling GCKMediaControlChannel setTextTrackStyle, like this:
NSInteger requestId = [_mediaControlChannel setTextTrackStyle:style];
You can track the status of the request with the GCKMediaControllerDelegate requestDidCompleteWIthID method, which passes a request ID that you can compare to the one returned from the setTextTrackStyle call, as follows:
- (void)mediaControlChannel:(GCKMediaControlChannel *)mediaControlChannel
requestDidCompleteWithID:(NSInteger)requestID {
if (requestID == _pendingStyleChange) {
NSLog(@"Style update completed");
} else {
NSLog(@"Other request 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, like this:
GCKMediaTextTrackStyle *textTrackStyle = [GCKMediaTextTrackStyle createDefault];
You can style the following text track style elements:
- Foreground (text) color and opacity
- Background color and opacity
- Edge type
- Edge Color
- Font Scale
- Font Family
- Font Style
For example, set the text color to red with 75% opacity as follows:
GCKMediaTextTrackStyle *style = [GCKMediaTextTrackStyle createDefault]; [style setForegroundColor:[[GCKColor alloc] initWithCSSString:@"#FF000080"]];
Status updates
When multiple senders are connected to the same receiver, it is important for each sender to be aware of the changes in 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 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.
Logging
Use the GCKLoggerDelegate to customize how you handle log messages.
@interface AppDelegate(){ ... } @end @implementation AppDelegate - (id)init { if (self = [super init]) { [GCKLogger sharedInstance].delegate = self; .... } return self; } ,,, #pragma mark - GCKLoggerDelegate - (void)logFromFunction:(const char *)function message:(NSString *)message { if (self.debugLoggingEnabled) { NSLog(@"%s - %@", function, message); } } @end
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.
- Hello Video
- Hello Text
- Cast Videos (complies with the UX Guidelines and Design Checklist)