Add and customize captions (iOS v4 | tvOS)

Add embedded or sidecar captions to your iOS or tvOS app.





If you have captions or subtitles for your video content, you can use the following sections to add embedded or sidecar captions to your app.

Although there are differences between the intended purposes of captions and subtitles, you use the same processes to add or to customize either type of synchronized text to your app. For simplicity, both captions and subtitles are referred to as captions in our documentation.



Add captions to your app

Embedded captions

  1. Copy the URL of your stream. The stream must contain CEA-608, CEA-708, or in-manifest WebVTT captions.
  2. Build a JWPlayerItem passing the URL of your stream as the argument for the file method of the item builder.
let urlString = "https://cdn.mydomain.com/manifests/A3Bc9z5.m3u8"
let url = URL(string:urlString)!
do {
    let jwItem = try JWPlayerItemBuider().file(url).build()
} catch is JWError {
    // Handle error
}
NSString *urlString = @"https://cdn.mydomain.com/manifests/A3Bc9z5.m3u8";
NSURL *url = [NSURL URLWithString:urlString];
NSError *error;
JWPlayerItemBuilder *builder = [[JWPlayerItemBuilder alloc]init];
[builder file:url];
JWPlayerItem *item = [builder buildAndReturnError:&error];

Sidecar Captions

  1. Build a JWMediaTrack for each caption.
  2. Create an array of JWMediaTrack objects.

📘

If you define a single track, the label value is ignored and not shown.

// Build the JWMediaTrack
var captions = [JWMediaTrack]()
let urlString = "https://content.jwplatform.com/tracks/sample01"
let url = URL(string: urlString)!
let builder = JWCaptionTrackBuilder()
    .file(url)
    .label("English")
    .defaultTrack(true)

do {
    let englishTrack = try builder.build()
    captions.append(englishTrack)
} catch {
    // Handle error
}
// Build the JWMediaTrack
NSString *urlString = @”https://content.jwplatform.com/tracks/sample01.vtt”;
NSURL *url = [NSURL URLWithString:urlString];
NSString *label = @"English";
NSError *error;
JWCaptionTrackBuilder *builder = [[JWCaptionTrackBuilder alloc] init];
[builder file:url];
[builder label:label];
[builder defaultTrack:YES]; // In the case that the track is the default

JWMediaTrack *englishTrack = [builder buildAndReturnError:&error];
// Create array with tracks
NSArray<JWMediaTrack *> *tracks = @[englishTrack];


Customize your captions

Using the JWCaptionStyle class, you can customize the font, font color, window color, background color, and edge style of the captions in your app. If you do not define any styling, your captions are styled based upon the Settings > Accessibility > Subtitles & Captioning settings on the viewer's device.

📘

Any caption styling that you define applies only to in-manifest WebVTT (embedded) captions and sidecar captions. These captions inherent your styling only when a viewer enables Video Override in his or her device's Settings > Accessibility > Subtitles & Captioning settings.

CEA-608 and CEA-708 captions do not inherit caption styling defined by JWCaptionStyle.


  1. Build a JWCaptionStyle object.
  2. Use the following examples to set the properties of the JWCaptionStyle object that the builder will output.
let builder = JWCaptionStyleBuilder()
    .font(UIFont(name: "Zapfino", size: 20))
    .fontColor(.blue)
    .highlightColor(.white)
    .backgroundColor(UIColor(red: 0.3, green: 0.6, blue: 0.3, alpha: 0.7))
    .edgeStyle(.raised)
do {
  let style = try builder.build() 
} catch {
  // Handle error
}
JWCaptionStyleBuilder *builder = [[JWCaptionStyleBuilder alloc] init];
[builder font:[UIFont fontWithName:@"Zapfino" size:20]];
[builder fontColor: [UIColor blueColor]];
[builder highlightColor: [UIColor orangeColor]];
[builder backgroundColor: [UIColor colorWithRed:0.3 green:0.6 blue:0.3 alpha:0.7]];
[builder edgeStyle: JWCaptionEdgeStyleRaised];

NSError *error;
JWCaptionStyle *style = [builder buildAndReturnError:&error];


iOS settings mapping

The following tables map the specific iOS Settings > Accessibility > Subtitles & Captioning settings to the iOS SDK property.

📘

Remember that viewers must enable Video Override in each of these areas for your customizations to render when they use your app.


Text

iOS Setting JWCaptionStyle Property
Color fontColor (UIColor)
Font font (UIFont)
Size font (UIFont)

Background

iOS Setting JWCaptionStyle Property
Color backgroundColor (UIColor)
Opacity backgroundColor (UIColor:alpha)

Advanced

iOS Setting JWCaptionStyle Property
Text Edge Style edgeStyle (JWCaptionEdgeStyle)
Text Highlight highlightColor (UIColor)
Text Opacity color (UIColor:alpha)


Listening for caption events

The following table describes the JWAVDelegate methods that correspond to captions events.

MethodDescription
jwplayer(_ player: JWPlayer, updatedCaptionList options: [JWMediaSelectionOption])When captions are loaded for the content reports what captions are available.
jwplayer(_ player: JWPlayer, captionTrackChanged index: Int)Reports what caption was selected, represented as an index into the list of available captions.
jwplayer(_ player: JWPlayer, captionPresented caption: [String], at time: JWTimeData)Reports what strings were displayed and what time they are being presented on screen.
override func jwplayer(_ player: JWPlayer, updatedCaptionList options: [JWMediaSelectionOption]) {
    super.jwplayer(player, updatedCaptionList: options)
}

override func jwplayer(_ player: JWPlayer, captionTrackChanged index: Int) {
    super.jwplayer(player, captionTrackChanged: index)
}

override func jwplayer(_ player: JWPlayer, captionPresented caption: [String], at time: JWTimeData) {
    super.jwplayer(player, updatedCues: cues)
}
- (void)jwplayer:(id<JWPlayer> _Nonnull)player updatedCaptionList:(NSArray<JWMediaSelectionOption *> * _Nonnull)options {
}
- (void)jwplayer:(id<JWPlayer> _Nonnull)player captionTrackChanged:(NSInteger)index 
{
}
- (void)jwplayer:(id<JWPlayer> _Nonnull)player captionPresented:(NSArray<NSString *> * _Nonnull)caption at:(JWTimeData * _Nonnull)time 
{
}


Setting a caption track programmatically

Setting a caption track programmatically has many benefits, including

  • Knowing and setting the language your user prefers
  • Mirroring a user’s language selection outside of the UI in JWPlayerViewController or JWCinematicViewController
  • Use in conjunction with your UI implementation when the user selects a language if you are only using JWPlayerView

To set the captions track programmatically, use the index of the captions according to the captions list or with the locale that was set for the captions.

MethodDescription
func setCaptionTrack(index: Int) throwsSet the caption by using a valid index into the list of available captions. Using -1 will set to no captions.
func setCaptionTrack(locale: String?) throwsSet captions using an RFC-5646 language tag—e.g, “en”.
try { 
    player.setCaptionTrack(index: 2)
} catch {
    // handle error
}

try { 
    player.setCaptionTrack(locale: “en”)
} catch {
    // handle error
}
NSError *setWithIndexError;
 [player setCaptionTrackWithIndex:1 error: &setWithIndexError];
    
 if (setWithIndexError) {
     // handle error if not null
 }
    
 NSError *setWithLocaleError;
 [player setCaptionTrackWithLocale:@"en" error: &setWithLocaleError];
    
 if (setWithLocaleError) {
    // handle error if not null
 }


Caption Rendering

Our SDK renders all side-loaded captions. For embedded captions, such as in an HLS stream, AVPlayer renders the captions. Specifying values within JWCaptionStyle will only affect side-loaded captions.


Disabling Rendering

For some uses of our player, it may be desired that the player not render the captions. Instead, perhaps you wish to render the captions into a different view, elsewhere on the screen.

In JWPlayer, there is a property called suppressesCaptionRendering. If false, the player will not render captions on the screen if a caption track is selected, however, you will still receive caption events, including func jwplayer(_ player: JWPlayer, captionPresented caption: [String], at time: JWTimeData). By default, this flag is false.

class CustomPlayerViewController: JWPlayerViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
    
    // Suppress caption rendering
    player.suppressesCaptionRendering = true

    // Initialize configuration and the player below.
  }
}


Setting Caption Insets

We can set the amount of space from the edges of the player to the captions container using the JWPlayerView.captionInsets API. This property is a UIEdgeInsets, which lets us specify the top, bottom, left, and right distance from the player edges you require.

// The new insets that will be used
let newInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

// Set them to the JWPlayerView instance
playerView.captionInsets = newInsets
// The new insets that will be used
UIEdgeInsets newInsets = UIEdgeInsetsMake(0, 0, 0, 0);

// Set them to your JWPlayerView instance
self.playerView.captionInsets = newInsets;