fix(ios): fix external text tracks crashes with m3u8 files (#3330)

* fix(ios): fix crash when using textTracks with m3u8

* docs: add warning to `textTracks` prop

* chore: apply code review

* chore: remove indents
This commit is contained in:
Krzysztof Moch 2023-11-17 08:19:39 +01:00 committed by GitHub
parent 8777b0a35e
commit 782e7e0df1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 67 additions and 70 deletions

View File

@ -708,6 +708,7 @@ subtitleStyle={{ paddingBottom: 50, fontSize: 20 }}
### `textTracks`
Load one or more "sidecar" text tracks. This takes an array of objects representing each track. Each object should have the format:
> ⚠️ This feature does not work with HLS playlists (e.g m3u8) on iOS
Property | Description
--- | ---

View File

@ -184,24 +184,24 @@ enum RCTVideoUtils {
let mixComposition:AVMutableComposition = AVMutableComposition()
let videoAsset:AVAssetTrack! = asset.tracks(withMediaType: AVMediaType.video).first
// we need videoAsset asset to be not null to get durration later
if videoAsset == nil {
return mixComposition
}
let videoCompTrack:AVMutableCompositionTrack! = mixComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID:kCMPersistentTrackID_Invalid)
do {
try videoCompTrack.insertTimeRange(
try? videoCompTrack.insertTimeRange(
CMTimeRangeMake(start: .zero, duration: videoAsset.timeRange.duration),
of: videoAsset,
at: .zero)
} catch {
}
let audioAsset:AVAssetTrack! = asset.tracks(withMediaType: AVMediaType.audio).first
let audioCompTrack:AVMutableCompositionTrack! = mixComposition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID:kCMPersistentTrackID_Invalid)
do {
try audioCompTrack.insertTimeRange(
CMTimeRangeMake(start: .zero, duration: videoAsset.timeRange.duration),
try? audioCompTrack.insertTimeRange(
CMTimeRangeMake(start: .zero, duration: audioAsset.timeRange.duration),
of: audioAsset,
at: .zero)
} catch {
}
return mixComposition
}
@ -226,12 +226,11 @@ enum RCTVideoUtils {
validTextTracks.append(textTracks[i])
let textCompTrack:AVMutableCompositionTrack! = mixComposition.addMutableTrack(withMediaType: AVMediaType.text,
preferredTrackID:kCMPersistentTrackID_Invalid)
do {
try textCompTrack.insertTimeRange(
CMTimeRangeMake(start: .zero, duration: videoAsset.timeRange.duration),
if videoAsset != nil {
try? textCompTrack.insertTimeRange(
CMTimeRangeMake(start: .zero, duration: videoAsset!.timeRange.duration),
of: textTrackAsset,
at: .zero)
} catch {
}
}
}

View File

@ -305,14 +305,14 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
if let uri = source.uri, uri.starts(with: "ph://") {
return Promise {
RCTVideoUtils.preparePHAsset(uri: uri).then { asset in
return self.playerItemPrepareText(asset:asset, assetOptions:nil)
return self.playerItemPrepareText(asset:asset, assetOptions:nil, uri: source.uri ?? "")
}
}
}
guard let assetResult = RCTVideoUtils.prepareAsset(source: source),
let asset = assetResult.asset,
let assetOptions = assetResult.assetOptions else {
DebugLog("Could not find video URL in source '\(self._source)'")
DebugLog("Could not find video URL in source '\(String(describing: self._source))'")
throw NSError(domain: "", code: 0, userInfo: nil)
}
@ -332,7 +332,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
reactTag: self.reactTag
)
}
return Promise{self.playerItemPrepareText(asset: asset, assetOptions:assetOptions)}
return Promise{self.playerItemPrepareText(asset: asset, assetOptions:assetOptions, uri: source.uri ?? "")}
}.then{[weak self] (playerItem:AVPlayerItem!) in
guard let self = self else {throw NSError(domain: "", code: 0, userInfo: nil)}
@ -390,8 +391,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
_localSourceEncryptionKeyScheme = keyScheme
}
func playerItemPrepareText(asset:AVAsset!, assetOptions:NSDictionary?) -> AVPlayerItem {
if (_textTracks == nil) || _textTracks?.count==0 {
func playerItemPrepareText(asset:AVAsset!, assetOptions:NSDictionary?, uri: String) -> AVPlayerItem {
if (_textTracks == nil) || _textTracks?.count==0 || (uri.hasSuffix(".m3u8")) {
return self.playerItemPropegateMetadata(AVPlayerItem(asset: asset))
}
@ -678,7 +679,6 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
}
}
func applyModifiers() {
if let video = _player?.currentItem,
video == nil || video.status != AVPlayerItem.Status.readyToPlay {
@ -719,8 +719,6 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
_repeat = `repeat`
}
@objc
func setSelectedAudioTrack(_ selectedAudioTrack:NSDictionary?) {
setSelectedAudioTrack(SelectedTrackCriteria(selectedAudioTrack))
@ -934,7 +932,6 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
}
}
func videoPlayerViewControllerDidDismiss(playerViewController:AVPlayerViewController) {
if _playerViewController == playerViewController && _fullscreenPlayerPresented {
_fullscreenPlayerPresented = false

View File

@ -6,7 +6,7 @@ import Promises
class RCTVideoCachingHandler: NSObject, DVAssetLoaderDelegatesDelegate {
private var _videoCache:RCTVideoCache! = RCTVideoCache.sharedInstance()
var playerItemPrepareText: ((AVAsset?, NSDictionary?) -> AVPlayerItem)?
var playerItemPrepareText: ((AVAsset?, NSDictionary?) -> AVPlayerItem, uri: String)?
override init() {
super.init()
@ -33,12 +33,12 @@ class RCTVideoCachingHandler: NSObject, DVAssetLoaderDelegatesDelegate {
case .missingFileExtension:
DebugLog("Could not generate cache key for uri '\(uri)'. It is currently not supported to cache urls that do not include a file extension. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md")
let asset:AVURLAsset! = AVURLAsset(url: url!, options:options as! [String : Any])
return playerItemPrepareText(asset, options)
return playerItemPrepareText(asset, options, "")
case .unsupportedFileExtension:
DebugLog("Could not generate cache key for uri '\(uri)'. The file extension of that uri is currently not supported. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md")
let asset:AVURLAsset! = AVURLAsset(url: url!, options:options as! [String : Any])
return playerItemPrepareText(asset, options)
return playerItemPrepareText(asset, options, "")
default:
if let cachedAsset = cachedAsset {