diff --git a/ios/Video/Features/RCTVideoTVUtils.swift b/ios/Video/Features/RCTVideoTVUtils.swift new file mode 100644 index 00000000..71f2a4a4 --- /dev/null +++ b/ios/Video/Features/RCTVideoTVUtils.swift @@ -0,0 +1,49 @@ +import Foundation +import AVFoundation +import AVKit + +/*! + * Collection of helper functions for tvOS specific features + */ + +#if os(tvOS) +enum RCTVideoTVUtils { + static func makeNavigationMarkerGroups(_ chapters: [Chapter]) -> [AVNavigationMarkersGroup] { + var metadataGroups = [AVTimedMetadataGroup]() + + // Iterate over the defined chapters and build a timed metadata group object for each. + chapters.forEach { chapter in + metadataGroups.append(makeTimedMetadataGroup(for: chapter)) + } + + return [AVNavigationMarkersGroup(title: nil, timedNavigationMarkers: metadataGroups)] + } + + static func makeTimedMetadataGroup(for chapter: Chapter) -> AVTimedMetadataGroup { + var metadata = [AVMetadataItem]() + + // Create a metadata item that contains the chapter title. + let titleItem = RCTVideoUtils.createMetadataItem(for: .commonIdentifierTitle, value: chapter.title) + metadata.append(titleItem) + + // Create a time range for the metadata group. + let timescale: Int32 = 600 + let startTime = CMTime(seconds: chapter.startTime, preferredTimescale: timescale) + let endTime = CMTime(seconds: chapter.endTime, preferredTimescale: timescale) + let timeRange = CMTimeRangeFromTimeToTime(start: startTime, end: endTime) + + // Image + if let imgUri = chapter.uri, + let uri = URL(string: imgUri), + let imgData = try? Data(contentsOf: uri), + let image = UIImage(data: imgData), + let pngData = image.pngData() + { + let imageItem = RCTVideoUtils.createMetadataItem(for: .commonIdentifierArtwork, value: pngData) + metadata.append(imageItem) + } + + return AVTimedMetadataGroup(items: metadata, timeRange: timeRange) + } +} +#endif diff --git a/ios/Video/Features/RCTVideoUtils.swift b/ios/Video/Features/RCTVideoUtils.swift index 9916db37..ce87afe1 100644 --- a/ios/Video/Features/RCTVideoUtils.swift +++ b/ios/Video/Features/RCTVideoUtils.swift @@ -316,4 +316,18 @@ enum RCTVideoUtils { } return (asset, assetOptions) } + + static func createMetadataItems(for mapping: [AVMetadataIdentifier: Any]) -> [AVMetadataItem] { + return mapping.compactMap { createMetadataItem(for:$0, value:$1) } + } + + static func createMetadataItem(for identifier: AVMetadataIdentifier, + value: Any) -> AVMetadataItem { + let item = AVMutableMetadataItem() + item.identifier = identifier + item.value = value as? NSCopying & NSObjectProtocol + // Specify "und" to indicate an undefined language. + item.extendedLanguageTag = "und" + return item.copy() as! AVMetadataItem + } } diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index 6ea8cf89..2820a8e6 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -391,71 +391,17 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } if #available(iOS 12.2, *), !mapping.isEmpty { - playerItem.externalMetadata = createMetadataItems(for: mapping) + playerItem.externalMetadata = RCTVideoUtils.createMetadataItems(for: mapping) } #if os(tvOS) if let chapters = _chapters { - playerItem.navigationMarkerGroups = makeNavigationMarkerGroups(chapters) + playerItem.navigationMarkerGroups = RCTVideoTVUtils.makeNavigationMarkerGroups(chapters) } #endif return playerItem } - - func createMetadataItems(for mapping: [AVMetadataIdentifier: Any]) -> [AVMetadataItem] { - return mapping.compactMap { createMetadataItem(for:$0, value:$1) } - } - - #if os(tvOS) - private func makeNavigationMarkerGroups(_ chapters: [Chapter]) -> [AVNavigationMarkersGroup] { - var metadataGroups = [AVTimedMetadataGroup]() - - // Iterate over the defined chapters and build a timed metadata group object for each. - chapters.forEach { chapter in - metadataGroups.append(makeTimedMetadataGroup(for: chapter)) - } - - return [AVNavigationMarkersGroup(title: nil, timedNavigationMarkers: metadataGroups)] - } - - private func makeTimedMetadataGroup(for chapter: Chapter) -> AVTimedMetadataGroup { - var metadata = [AVMetadataItem]() - - // Create a metadata item that contains the chapter title. - let titleItem = createMetadataItem(for: .commonIdentifierTitle, value: chapter.title) - metadata.append(titleItem) - - // Create a time range for the metadata group. - let timescale: Int32 = 600 - let startTime = CMTime(seconds: chapter.startTime, preferredTimescale: timescale) - let endTime = CMTime(seconds: chapter.endTime, preferredTimescale: timescale) - let timeRange = CMTimeRangeFromTimeToTime(start: startTime, end: endTime) - - // Image - if let imgUri = chapter.uri, - let uri = URL(string: imgUri), - let imgData = try? Data(contentsOf: uri), - let image = UIImage(data: imgData), - let pngData = image.pngData() - { - let imageItem = createMetadataItem(for: .commonIdentifierArtwork, value: pngData) - metadata.append(imageItem) - } - - return AVTimedMetadataGroup(items: metadata, timeRange: timeRange) - } - #endif - - private func createMetadataItem(for identifier: AVMetadataIdentifier, - value: Any) -> AVMetadataItem { - let item = AVMutableMetadataItem() - item.identifier = identifier - item.value = value as? NSCopying & NSObjectProtocol - // Specify "und" to indicate an undefined language. - item.extendedLanguageTag = "und" - return item.copy() as! AVMetadataItem - } // MARK: - Prop setters