Loading ios/RCTVideo.m +50 −42 Changes for ios/RCTVideo.m: 50 added lines, 42 removed lines. Original line number Diff line number Diff line Loading @@ -47,7 +47,6 @@ static NSString *const timedMetadata = @"timedMetadata"; BOOL _allowsExternalPlayback; NSArray * _textTracks; NSDictionary * _selectedTextTrack; NSDictionary * _selectedText; BOOL _playbackStalled; BOOL _playInBackground; BOOL _playWhenInactive; Loading Loading @@ -352,66 +351,66 @@ static NSString *const timedMetadata = @"timedMetadata"; bool isNetwork = [RCTConvert BOOL:[source objectForKey:@"isNetwork"]]; bool isAsset = [RCTConvert BOOL:[source objectForKey:@"isAsset"]]; NSString *uri = [source objectForKey:@"uri"]; NSString *subtitleUri = _selectedText[@"uri"]; NSString *type = [source objectForKey:@"type"]; NSDictionary *headers = [source objectForKey:@"requestHeaders"]; AVURLAsset *asset; AVURLAsset *subAsset; NSMutableDictionary *assetOptions = [[NSMutableDictionary alloc] init]; if (isNetwork) { NSMutableDictionary *assetOptions = [[NSMutableDictionary alloc]init]; /* Per #1091, this is not a public API. We need to either get approval from Apple to use this * or use a different approach. NSDictionary *headers = [source objectForKey:@"requestHeaders"]; if ([headers count] > 0) { [assetOptions setObject:headers forKey:@"AVURLAssetHTTPHeaderFieldsKey"]; } */ NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]; [assetOptions setObject:cookies forKey:AVURLAssetHTTPCookiesKey]; asset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:uri] options:assetOptions]; subAsset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:subtitleUri] options:assetOptions]; } else if (isAsset) // assets on iOS have to be in the Documents folder { } else if (isAsset) { // assets on iOS have to be in the Documents folder asset = [AVURLAsset URLAssetWithURL:[self urlFilePath:uri] options:nil]; subAsset = [AVURLAsset URLAssetWithURL:[self urlFilePath:subtitleUri] options:nil]; } else // file passed in through JS, or an asset in the Xcode project { } else { // file passed in through JS, or an asset in the Xcode project asset = [AVURLAsset URLAssetWithURL:[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]] options:nil]; } if (!_textTracks || !_selectedText) return [AVPlayerItem playerItemWithAsset:asset]; // otherwise sideload text tracks AVAssetTrack *videoAsset = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject; AVAssetTrack *audioAsset = [asset tracksWithMediaType:AVMediaTypeAudio].firstObject; AVAssetTrack *textAsset = [subAsset tracksWithMediaType:AVMediaTypeText].firstObject; NSLog(@"assets: %@,%@,%@", videoAsset, audioAsset, textAsset); if (!_textTracks) { return [AVPlayerItem playerItemWithAsset:asset]; } // sideload text tracks AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init]; AVMutableCompositionTrack *videoCompTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; AVMutableCompositionTrack *audioCompTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; AVMutableCompositionTrack *subtitleCompTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeText preferredTrackID:kCMPersistentTrackID_Invalid]; AVAssetTrack *videoAsset = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject; AVMutableCompositionTrack *videoCompTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; [videoCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.timeRange.duration) ofTrack:videoAsset atTime:kCMTimeZero error:nil]; AVAssetTrack *audioAsset = [asset tracksWithMediaType:AVMediaTypeAudio].firstObject; AVMutableCompositionTrack *audioCompTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; [audioCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.timeRange.duration) ofTrack:audioAsset atTime:kCMTimeZero error:nil]; [subtitleCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.timeRange.duration) ofTrack:textAsset for (int i = 0; i < _textTracks.count; ++i) { AVURLAsset *textURLAsset; NSString *textUri = [_textTracks objectAtIndex:i][@"uri"]; if ([[textUri lowercaseString] hasPrefix:@"http"]) { textURLAsset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:textUri] options:assetOptions]; } else { textURLAsset = [AVURLAsset URLAssetWithURL:[self urlFilePath:textUri] options:nil]; } AVAssetTrack *textTrackAsset = [textURLAsset tracksWithMediaType:AVMediaTypeText].firstObject; AVMutableCompositionTrack *textCompTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeText preferredTrackID:kCMPersistentTrackID_Invalid]; [textCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.timeRange.duration) ofTrack:textTrackAsset atTime:kCMTimeZero error:nil]; } return [AVPlayerItem playerItemWithAsset:mixComposition]; } Loading @@ -425,7 +424,7 @@ static NSString *const timedMetadata = @"timedMetadata"; if (items && ![items isEqual:[NSNull null]] && items.count > 0) { NSMutableArray *array = [NSMutableArray new]; for (AVMetadataItem *item in items) { NSString *value = item.value; NSString *value = (NSString *)item.value; NSString *identifier = item.identifier; if (![value isEqual: [NSNull null]]) { Loading Loading @@ -730,29 +729,35 @@ static NSString *const timedMetadata = @"timedMetadata"; - (void)setSelectedTextTrack:(NSDictionary *)selectedTextTrack { _selectedTextTrack = selectedTextTrack; if (_textTracks) { [self setSideloadedText]; } else { [self setStreamingText]; } else [self setStreamingText]; } - (void) setSideloadedText { NSString *type = _selectedTextTrack[@"type"]; NSArray* textTracks = [self getTextTrackInfo]; _selectedText = nil; // The first few tracks will be audio & video track int firstTextIndex = 0; for (firstTextIndex = 0; firstTextIndex < _player.currentItem.tracks.count; ++firstTextIndex) { if ([_player.currentItem.tracks[firstTextIndex].assetTrack hasMediaCharacteristic:AVMediaCharacteristicLegible]) { break; } } int selectedTrackIndex = -1; if ([type isEqualToString:@"disabled"]) { // Do nothing. We want to ensure option is nil } else if ([type isEqualToString:@"language"]) { NSString *selectedValue = _selectedTextTrack[@"value"]; for (int i = 0; i < textTracks.count; ++i) { NSDictionary *currentTextTrack = [textTracks objectAtIndex:i]; if ([selectedValue isEqualToString:currentTextTrack[@"language"]]) { _selectedText = currentTextTrack; selectedTrackIndex = i; break; } } Loading @@ -760,9 +765,8 @@ static NSString *const timedMetadata = @"timedMetadata"; NSString *selectedValue = _selectedTextTrack[@"value"]; for (int i = 0; i < textTracks.count; ++i) { NSDictionary *currentTextTrack = [textTracks objectAtIndex:i]; if ([selectedValue isEqualToString:currentTextTrack[@"title"]]) { _selectedText = currentTextTrack; selectedTrackIndex = i; break; } } Loading @@ -770,30 +774,34 @@ static NSString *const timedMetadata = @"timedMetadata"; if ([_selectedTextTrack[@"value"] isKindOfClass:[NSNumber class]]) { int index = [_selectedTextTrack[@"value"] intValue]; if (textTracks.count > index) { _selectedText = [textTracks objectAtIndex:index]; selectedTrackIndex = index; } } } // user's selected language might not be available, or system defaults have captions enabled if (_selectedText==nil || [type isEqualToString:@"default"]) { if (selectedTrackIndex == -1 || [type isEqualToString:@"default"]) { CFArrayRef captioningMediaCharacteristics = MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics(kMACaptionAppearanceDomainUser); NSArray *captionSettings = (__bridge NSArray*)captioningMediaCharacteristics; if ([captionSettings containsObject: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility]) { // iterate through the textTracks to find a matching option, or default to the first object. _selectedText = textTracks.firstObject; selectedTrackIndex = firstTextIndex; NSString * systemLanguage = [[NSLocale preferredLanguages] firstObject]; for (int i = 0; i < textTracks.count; ++i) { NSDictionary *currentTextTrack = [textTracks objectAtIndex:i]; if ([systemLanguage isEqualToString:currentTextTrack[@"language"]]) { _selectedText = currentTextTrack; selectedTrackIndex = i; break; } } } } for (int i = firstTextIndex; i < _player.currentItem.tracks.count; ++i) { BOOL isEnabled = i == selectedTrackIndex + firstTextIndex; [_player.currentItem.tracks[i] setEnabled:isEnabled]; } } -(void) setStreamingText { Loading Loading
ios/RCTVideo.m +50 −42 Changes for ios/RCTVideo.m: 50 added lines, 42 removed lines. Original line number Diff line number Diff line Loading @@ -47,7 +47,6 @@ static NSString *const timedMetadata = @"timedMetadata"; BOOL _allowsExternalPlayback; NSArray * _textTracks; NSDictionary * _selectedTextTrack; NSDictionary * _selectedText; BOOL _playbackStalled; BOOL _playInBackground; BOOL _playWhenInactive; Loading Loading @@ -352,66 +351,66 @@ static NSString *const timedMetadata = @"timedMetadata"; bool isNetwork = [RCTConvert BOOL:[source objectForKey:@"isNetwork"]]; bool isAsset = [RCTConvert BOOL:[source objectForKey:@"isAsset"]]; NSString *uri = [source objectForKey:@"uri"]; NSString *subtitleUri = _selectedText[@"uri"]; NSString *type = [source objectForKey:@"type"]; NSDictionary *headers = [source objectForKey:@"requestHeaders"]; AVURLAsset *asset; AVURLAsset *subAsset; NSMutableDictionary *assetOptions = [[NSMutableDictionary alloc] init]; if (isNetwork) { NSMutableDictionary *assetOptions = [[NSMutableDictionary alloc]init]; /* Per #1091, this is not a public API. We need to either get approval from Apple to use this * or use a different approach. NSDictionary *headers = [source objectForKey:@"requestHeaders"]; if ([headers count] > 0) { [assetOptions setObject:headers forKey:@"AVURLAssetHTTPHeaderFieldsKey"]; } */ NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]; [assetOptions setObject:cookies forKey:AVURLAssetHTTPCookiesKey]; asset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:uri] options:assetOptions]; subAsset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:subtitleUri] options:assetOptions]; } else if (isAsset) // assets on iOS have to be in the Documents folder { } else if (isAsset) { // assets on iOS have to be in the Documents folder asset = [AVURLAsset URLAssetWithURL:[self urlFilePath:uri] options:nil]; subAsset = [AVURLAsset URLAssetWithURL:[self urlFilePath:subtitleUri] options:nil]; } else // file passed in through JS, or an asset in the Xcode project { } else { // file passed in through JS, or an asset in the Xcode project asset = [AVURLAsset URLAssetWithURL:[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]] options:nil]; } if (!_textTracks || !_selectedText) return [AVPlayerItem playerItemWithAsset:asset]; // otherwise sideload text tracks AVAssetTrack *videoAsset = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject; AVAssetTrack *audioAsset = [asset tracksWithMediaType:AVMediaTypeAudio].firstObject; AVAssetTrack *textAsset = [subAsset tracksWithMediaType:AVMediaTypeText].firstObject; NSLog(@"assets: %@,%@,%@", videoAsset, audioAsset, textAsset); if (!_textTracks) { return [AVPlayerItem playerItemWithAsset:asset]; } // sideload text tracks AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init]; AVMutableCompositionTrack *videoCompTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; AVMutableCompositionTrack *audioCompTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; AVMutableCompositionTrack *subtitleCompTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeText preferredTrackID:kCMPersistentTrackID_Invalid]; AVAssetTrack *videoAsset = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject; AVMutableCompositionTrack *videoCompTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; [videoCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.timeRange.duration) ofTrack:videoAsset atTime:kCMTimeZero error:nil]; AVAssetTrack *audioAsset = [asset tracksWithMediaType:AVMediaTypeAudio].firstObject; AVMutableCompositionTrack *audioCompTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; [audioCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.timeRange.duration) ofTrack:audioAsset atTime:kCMTimeZero error:nil]; [subtitleCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.timeRange.duration) ofTrack:textAsset for (int i = 0; i < _textTracks.count; ++i) { AVURLAsset *textURLAsset; NSString *textUri = [_textTracks objectAtIndex:i][@"uri"]; if ([[textUri lowercaseString] hasPrefix:@"http"]) { textURLAsset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:textUri] options:assetOptions]; } else { textURLAsset = [AVURLAsset URLAssetWithURL:[self urlFilePath:textUri] options:nil]; } AVAssetTrack *textTrackAsset = [textURLAsset tracksWithMediaType:AVMediaTypeText].firstObject; AVMutableCompositionTrack *textCompTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeText preferredTrackID:kCMPersistentTrackID_Invalid]; [textCompTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.timeRange.duration) ofTrack:textTrackAsset atTime:kCMTimeZero error:nil]; } return [AVPlayerItem playerItemWithAsset:mixComposition]; } Loading @@ -425,7 +424,7 @@ static NSString *const timedMetadata = @"timedMetadata"; if (items && ![items isEqual:[NSNull null]] && items.count > 0) { NSMutableArray *array = [NSMutableArray new]; for (AVMetadataItem *item in items) { NSString *value = item.value; NSString *value = (NSString *)item.value; NSString *identifier = item.identifier; if (![value isEqual: [NSNull null]]) { Loading Loading @@ -730,29 +729,35 @@ static NSString *const timedMetadata = @"timedMetadata"; - (void)setSelectedTextTrack:(NSDictionary *)selectedTextTrack { _selectedTextTrack = selectedTextTrack; if (_textTracks) { [self setSideloadedText]; } else { [self setStreamingText]; } else [self setStreamingText]; } - (void) setSideloadedText { NSString *type = _selectedTextTrack[@"type"]; NSArray* textTracks = [self getTextTrackInfo]; _selectedText = nil; // The first few tracks will be audio & video track int firstTextIndex = 0; for (firstTextIndex = 0; firstTextIndex < _player.currentItem.tracks.count; ++firstTextIndex) { if ([_player.currentItem.tracks[firstTextIndex].assetTrack hasMediaCharacteristic:AVMediaCharacteristicLegible]) { break; } } int selectedTrackIndex = -1; if ([type isEqualToString:@"disabled"]) { // Do nothing. We want to ensure option is nil } else if ([type isEqualToString:@"language"]) { NSString *selectedValue = _selectedTextTrack[@"value"]; for (int i = 0; i < textTracks.count; ++i) { NSDictionary *currentTextTrack = [textTracks objectAtIndex:i]; if ([selectedValue isEqualToString:currentTextTrack[@"language"]]) { _selectedText = currentTextTrack; selectedTrackIndex = i; break; } } Loading @@ -760,9 +765,8 @@ static NSString *const timedMetadata = @"timedMetadata"; NSString *selectedValue = _selectedTextTrack[@"value"]; for (int i = 0; i < textTracks.count; ++i) { NSDictionary *currentTextTrack = [textTracks objectAtIndex:i]; if ([selectedValue isEqualToString:currentTextTrack[@"title"]]) { _selectedText = currentTextTrack; selectedTrackIndex = i; break; } } Loading @@ -770,30 +774,34 @@ static NSString *const timedMetadata = @"timedMetadata"; if ([_selectedTextTrack[@"value"] isKindOfClass:[NSNumber class]]) { int index = [_selectedTextTrack[@"value"] intValue]; if (textTracks.count > index) { _selectedText = [textTracks objectAtIndex:index]; selectedTrackIndex = index; } } } // user's selected language might not be available, or system defaults have captions enabled if (_selectedText==nil || [type isEqualToString:@"default"]) { if (selectedTrackIndex == -1 || [type isEqualToString:@"default"]) { CFArrayRef captioningMediaCharacteristics = MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics(kMACaptionAppearanceDomainUser); NSArray *captionSettings = (__bridge NSArray*)captioningMediaCharacteristics; if ([captionSettings containsObject: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility]) { // iterate through the textTracks to find a matching option, or default to the first object. _selectedText = textTracks.firstObject; selectedTrackIndex = firstTextIndex; NSString * systemLanguage = [[NSLocale preferredLanguages] firstObject]; for (int i = 0; i < textTracks.count; ++i) { NSDictionary *currentTextTrack = [textTracks objectAtIndex:i]; if ([systemLanguage isEqualToString:currentTextTrack[@"language"]]) { _selectedText = currentTextTrack; selectedTrackIndex = i; break; } } } } for (int i = firstTextIndex; i < _player.currentItem.tracks.count; ++i) { BOOL isEnabled = i == selectedTrackIndex + firstTextIndex; [_player.currentItem.tracks[i] setEnabled:isEnabled]; } } -(void) setStreamingText { Loading