fix(texttracks): unable to disable sideloaded texttracks in the AVPlayer
This commit is contained in:
parent
d681e5505f
commit
c9ecc8e9e0
@ -65,7 +65,7 @@ static int const RCTVideoUnset = -1;
|
|||||||
BOOL _paused;
|
BOOL _paused;
|
||||||
BOOL _repeat;
|
BOOL _repeat;
|
||||||
BOOL _allowsExternalPlayback;
|
BOOL _allowsExternalPlayback;
|
||||||
NSArray * _textTracks;
|
NSMutableArray * _textTracks;
|
||||||
NSDictionary * _selectedTextTrack;
|
NSDictionary * _selectedTextTrack;
|
||||||
NSDictionary * _selectedAudioTrack;
|
NSDictionary * _selectedAudioTrack;
|
||||||
BOOL _playbackStalled;
|
BOOL _playbackStalled;
|
||||||
@ -418,16 +418,18 @@ static int const RCTVideoUnset = -1;
|
|||||||
_drm = drm;
|
_drm = drm;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSURL*) urlFilePath:(NSString*) filepath {
|
- (NSURL*) urlFilePath:(NSString*) filepath searchPath:(enum NSSearchPathDirectory) searchPath {
|
||||||
if ([filepath containsString:@"file://"]) {
|
if ([filepath containsString:@"file://"]) {
|
||||||
return [NSURL URLWithString:filepath];
|
return [NSURL URLWithString:filepath];
|
||||||
}
|
}
|
||||||
|
|
||||||
// if no file found, check if the file exists in the Document directory
|
// if no file found, check if the file exists in the given searchPath
|
||||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(searchPath, NSUserDomainMask, YES);
|
||||||
NSString* relativeFilePath = [filepath lastPathComponent];
|
NSString* relativeFilePath = [filepath lastPathComponent];
|
||||||
// the file may be multiple levels below the documents directory
|
|
||||||
NSArray* fileComponents = [filepath componentsSeparatedByString:@"Documents/"];
|
// the file may be multiple levels below the in the given searchPath
|
||||||
|
NSString *directoryString = searchPath == NSCachesDirectory ? @"Library/Caches/" : @"Documents/";
|
||||||
|
NSArray *fileComponents = [filepath componentsSeparatedByString:directoryString];
|
||||||
if (fileComponents.count > 1) {
|
if (fileComponents.count > 1) {
|
||||||
relativeFilePath = [fileComponents objectAtIndex:1];
|
relativeFilePath = [fileComponents objectAtIndex:1];
|
||||||
}
|
}
|
||||||
@ -467,13 +469,22 @@ static int const RCTVideoUnset = -1;
|
|||||||
error:nil];
|
error:nil];
|
||||||
|
|
||||||
NSMutableArray* validTextTracks = [NSMutableArray array];
|
NSMutableArray* validTextTracks = [NSMutableArray array];
|
||||||
|
|
||||||
|
NSMutableDictionary *emptyVttTrack = [self createDisabledVttFile];
|
||||||
|
if (emptyVttTrack) [_textTracks addObject:emptyVttTrack];
|
||||||
|
|
||||||
for (int i = 0; i < _textTracks.count; ++i) {
|
for (int i = 0; i < _textTracks.count; ++i) {
|
||||||
AVURLAsset *textURLAsset;
|
AVURLAsset *textURLAsset;
|
||||||
NSString *textUri = [_textTracks objectAtIndex:i][@"uri"];
|
NSString *textUri = [_textTracks objectAtIndex:i][@"uri"];
|
||||||
if ([[textUri lowercaseString] hasPrefix:@"http"]) {
|
if ([[textUri lowercaseString] hasPrefix:@"http"]) {
|
||||||
textURLAsset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:textUri] options:assetOptions];
|
textURLAsset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:textUri] options:assetOptions];
|
||||||
} else {
|
} else {
|
||||||
textURLAsset = [AVURLAsset URLAssetWithURL:[self urlFilePath:textUri] options:nil];
|
BOOL isDisabledTrack = [[_textTracks objectAtIndex:i][@"language"] isEqualToString:@"disabled"];
|
||||||
|
|
||||||
|
// Search the track to disabled subtitles in the Caches directory, search every other local file in the Documents directory
|
||||||
|
NSSearchPathDirectory searchPath = isDisabledTrack ? NSCachesDirectory : NSDocumentDirectory;
|
||||||
|
NSURL *assetUrl = [self urlFilePath:textUri searchPath:searchPath];
|
||||||
|
textURLAsset = [AVURLAsset URLAssetWithURL:assetUrl options:nil];
|
||||||
}
|
}
|
||||||
AVAssetTrack *textTrackAsset = [textURLAsset tracksWithMediaType:AVMediaTypeText].firstObject;
|
AVAssetTrack *textTrackAsset = [textURLAsset tracksWithMediaType:AVMediaTypeText].firstObject;
|
||||||
if (!textTrackAsset) continue; // fix when there's no textTrackAsset
|
if (!textTrackAsset) continue; // fix when there's no textTrackAsset
|
||||||
@ -493,6 +504,36 @@ static int const RCTVideoUnset = -1;
|
|||||||
handler([AVPlayerItem playerItemWithAsset:mixComposition]);
|
handler([AVPlayerItem playerItemWithAsset:mixComposition]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create an useless / almost empty VTT file in the list with available tracks. This track gets selected when you give type: "disabled" as the selectedTextTrack
|
||||||
|
* This is needed because there is a bug where sideloaded texttracks cannot be disabled in the AVPlayer. Loading this VTT file instead solves that problem.
|
||||||
|
* For more info see: https://github.com/react-native-community/react-native-video/issues/1144
|
||||||
|
*/
|
||||||
|
- (NSMutableDictionary*)createDisabledVttFile {
|
||||||
|
NSError *error;
|
||||||
|
NSMutableDictionary *emptyVTTDictionary = nil;
|
||||||
|
|
||||||
|
// Write the file into the Caches directory because that directory is available on iOS and tvOS
|
||||||
|
NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"empty.vtt"];
|
||||||
|
|
||||||
|
if (![[NSFileManager defaultManager] isReadableFileAtPath:filePath]){
|
||||||
|
// WebVTT should have at least 1 cue to be valid. That's why a small dot is visible for 1 ms at the 99:59:59.000 timestamp
|
||||||
|
NSString *stringToWrite = @"WEBVTT\n\n1\n99:59:59.000 --> 99:59:59.001\n.";
|
||||||
|
|
||||||
|
[stringToWrite writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!error){
|
||||||
|
emptyVTTDictionary = [[NSMutableDictionary alloc] init];
|
||||||
|
emptyVTTDictionary[@"language"] = @"disabled";
|
||||||
|
emptyVTTDictionary[@"title"] = @"EmptyVttFile";
|
||||||
|
emptyVTTDictionary[@"uri"] = filePath;
|
||||||
|
emptyVTTDictionary[@"type"] = @"text/vtt";
|
||||||
|
}
|
||||||
|
|
||||||
|
return emptyVTTDictionary;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)playerItemForSource:(NSDictionary *)source withCallback:(void(^)(AVPlayerItem *))handler
|
- (void)playerItemForSource:(NSDictionary *)source withCallback:(void(^)(AVPlayerItem *))handler
|
||||||
{
|
{
|
||||||
bool isNetwork = [RCTConvert BOOL:[source objectForKey:@"isNetwork"]];
|
bool isNetwork = [RCTConvert BOOL:[source objectForKey:@"isNetwork"]];
|
||||||
@ -1185,7 +1226,8 @@ static int const RCTVideoUnset = -1;
|
|||||||
int selectedTrackIndex = RCTVideoUnset;
|
int selectedTrackIndex = RCTVideoUnset;
|
||||||
|
|
||||||
if ([type isEqualToString:@"disabled"]) {
|
if ([type isEqualToString:@"disabled"]) {
|
||||||
// Do nothing. We want to ensure option is nil
|
// Enable the empty texttrack
|
||||||
|
[_player.currentItem.tracks[_player.currentItem.tracks.count - 1] setEnabled:YES];
|
||||||
} else if ([type isEqualToString:@"language"]) {
|
} else if ([type isEqualToString:@"language"]) {
|
||||||
NSString *selectedValue = _selectedTextTrack[@"value"];
|
NSString *selectedValue = _selectedTextTrack[@"value"];
|
||||||
for (int i = 0; i < textTracks.count; ++i) {
|
for (int i = 0; i < textTracks.count; ++i) {
|
||||||
@ -1324,17 +1366,22 @@ static int const RCTVideoUnset = -1;
|
|||||||
mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
|
mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
|
||||||
for (int i = 0; i < group.options.count; ++i) {
|
for (int i = 0; i < group.options.count; ++i) {
|
||||||
AVMediaSelectionOption *currentOption = [group.options objectAtIndex:i];
|
AVMediaSelectionOption *currentOption = [group.options objectAtIndex:i];
|
||||||
|
NSString *language = [currentOption extendedLanguageTag] ? [currentOption extendedLanguageTag] : @"";
|
||||||
|
|
||||||
|
// Ignore the texttrack with language disabled. This is not a real texttrack and can be ignored
|
||||||
|
if ([language isEqualToString:@"disabled"]) continue;
|
||||||
|
|
||||||
NSString *title = @"";
|
NSString *title = @"";
|
||||||
NSArray *values = [[currentOption commonMetadata] valueForKey:@"value"];
|
NSArray *values = [[currentOption commonMetadata] valueForKey:@"value"];
|
||||||
if (values.count > 0) {
|
if (values.count > 0) {
|
||||||
title = [values objectAtIndex:0];
|
title = [values objectAtIndex:0];
|
||||||
}
|
}
|
||||||
NSString *language = [currentOption extendedLanguageTag] ? [currentOption extendedLanguageTag] : @"";
|
|
||||||
NSDictionary *textTrack = @{
|
NSDictionary *textTrack = @{
|
||||||
@"index": [NSNumber numberWithInt:i],
|
@"index": [NSNumber numberWithInt:i],
|
||||||
@"title": title,
|
@"title": title,
|
||||||
@"language": language
|
@"language": language
|
||||||
};
|
};
|
||||||
|
|
||||||
[textTracks addObject:textTrack];
|
[textTracks addObject:textTrack];
|
||||||
}
|
}
|
||||||
return textTracks;
|
return textTracks;
|
||||||
|
Loading…
Reference in New Issue
Block a user