Skip to content
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### Added

- Added support on iOS to push the initial state of the player from the iOS bridge to the React native adapter.

### Fixed

- Fixed an issue on iOS where the integrationParameters from the contentProtection section of the source were not processed correctly, resulting in failures for DRM connectors that depend on them.
Expand Down
6 changes: 5 additions & 1 deletion example/ios/ReactNativeTHEOplayer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,7 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_BITCODE = NO;
ENABLE_TESTABILITY = YES;
GCC_NO_COMMON_BLOCKS = YES;
Expand Down Expand Up @@ -699,6 +699,8 @@
);
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
OTHER_LDFLAGS = "$(inherited)";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
Expand Down Expand Up @@ -766,6 +768,8 @@
"\"$(inherited)\"",
);
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_CFLAGS = "$(inherited)";
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
OTHER_LDFLAGS = "$(inherited)";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
Expand Down
8 changes: 4 additions & 4 deletions ios/THEOplayerRCTMainEventHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class THEOplayerRCTMainEventHandler {
// MARK: Members
private weak var player: THEOplayer?
private weak var presentationModeContext: THEOplayerRCTPresentationModeContext?
private var loadedMetadataAndChapterTracksInfo: [[String:Any]] = []
private(set) var loadedMetadataAndChapterTracksInfo: [[String:Any]] = []

// MARK: Events
var onNativePlay: RCTDirectEventBlock?
Expand Down Expand Up @@ -274,9 +274,9 @@ public class THEOplayerRCTMainEventHandler {
self.loadedMetadataListener = player.addEventListener(type: PlayerEventTypes.LOADED_META_DATA) { [weak self, weak player] event in
if DEBUG_THEOPLAYER_EVENTS { PrintUtils.printLog(logText: "[NATIVE] Received LOADED_META_DATA event from THEOplayer") }
if let wplayer = player,
let welf = self,
let forwardedLoadedMetadataEvent = self?.onNativeLoadedMetadata {
let metadata = THEOplayerRCTTrackMetadataAggregator.aggregateTrackMetadata(player: wplayer, metadataTracksInfo: welf.loadedMetadataAndChapterTracksInfo)
let self,
let forwardedLoadedMetadataEvent = self.onNativeLoadedMetadata {
let metadata = THEOplayerRCTTrackMetadataAggregator.aggregateTrackInfo(player: wplayer, metadataTracksInfo: self.loadedMetadataAndChapterTracksInfo)
forwardedLoadedMetadataEvent(metadata)
}
}
Expand Down
100 changes: 100 additions & 0 deletions ios/THEOplayerRCTPlayerStateBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import Foundation
import THEOplayerSDK

let STATE_SOURCE = "source"
let STATE_CURRENT_TIME = "currentTime"
let STATE_CURRENT_PROGRAM_DATE_TIME = "currentProgramDateTime"
let STATE_PAUSED = "paused"
let STATE_PLAYBACK_RATE = "playbackRate"
let STATE_DURATION = "duration"
let STATE_VOLUME = "volume"
let STATE_MUTED = "muted"
let STATE_SEEKABLE = "seekable"
let STATE_BUFFERED = "buffered"
let STATE_START = "start"
let STATE_END = "end"

class THEOplayerRCTPlayerStateBuilder {
private var playerState: [String: Any] = [:]

func build() -> [String: Any] {
return playerState
}

func source(_ sourceDescription: SourceDescription?) -> Self {
if let sourceDesc = sourceDescription {
playerState[STATE_SOURCE] = THEOplayerRCTSourceDescriptionAggregator.aggregateSourceDescription(sourceDescription: sourceDesc)
}
return self
}

func currentTime(_ timeInSec: Double) -> Self {
playerState[STATE_CURRENT_TIME] = timeInSec * 1000 // sec -> msec
return self
}

func currentProgramDateTime(_ programDataTime: Date?) -> Self {
if let date = programDataTime {
playerState[STATE_CURRENT_PROGRAM_DATE_TIME] = date.timeIntervalSince1970 * 1000 // sec -> msec
}
return self
}

func paused(_ paused: Bool) -> Self {
playerState[STATE_PAUSED] = paused
return self
}

func playbackRate(_ rate: Double) -> Self {
playerState[STATE_PLAYBACK_RATE] = rate
return self
}

func duration(_ durationInSec: Double?) -> Self {
if let durationInSec = durationInSec {
playerState[PROP_DURATION] = THEOplayerRCTTypeUtils.encodeInfNan(durationInSec * 1000) // sec -> msec
}
return self
}

func volume(_ volume: Float) -> Self {
playerState[STATE_VOLUME] = Double(volume)
return self
}

func muted(_ muted: Bool) -> Self {
playerState[STATE_MUTED] = muted
return self
}

func seekable(_ seekableRanges: [THEOplayerSDK.TimeRange]?) -> Self {
playerState[STATE_SEEKABLE] = fromTimeRanges(seekableRanges)
return self
}

func buffered(_ bufferedRanges: [THEOplayerSDK.TimeRange]?) -> Self {
playerState[STATE_BUFFERED] = fromTimeRanges(bufferedRanges)
return self
}

func trackInfo(_ trackInfo: [String: Any]) -> Self {
playerState.merge(trackInfo) { (current, _) in current }
return self
}

private func fromTimeRanges(_ ranges: [THEOplayerSDK.TimeRange]?) -> [[String: Double]] {
guard let inRanges = ranges else { return [] }

var outRanges: [[String:Double]] = []
inRanges.forEach({ timeRange in
outRanges.append(
[
STATE_START: timeRange.start * 1000, // sec -> msec
STATE_END: timeRange.end * 1000 // sec -> msec
]
)
})
return outRanges
}
}

19 changes: 12 additions & 7 deletions ios/THEOplayerRCTSourceDescriptionAggregator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,30 @@ import Foundation
import THEOplayerSDK
import UIKit

#if os(iOS)
class THEOplayerRCTSourceDescriptionAggregator {
class func aggregateCacheTaskSourceDescription(sourceDescription: SourceDescription, cachingTaskId: String) -> [String:Any]? {
class func aggregateSourceDescription(sourceDescription: SourceDescription) -> [String:Any]? {
do {
let jsonEncoder = JSONEncoder()
let data = try jsonEncoder.encode(sourceDescription)
if let result = try? JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] {
let srcDescription = THEOplayerRCTSourceDescriptionAggregator.sanitiseSourceDescriptionMetadata(input: result)
let extendedSrcDescription = THEOplayerRCTSourceDescriptionAggregator.addCachingTaskIdToMetadata(input: srcDescription, cachingTaskId: cachingTaskId)
return extendedSrcDescription
return THEOplayerRCTSourceDescriptionAggregator.sanitiseSourceDescriptionMetadata(input: result)
}
} catch {
if DEBUG { PrintUtils.printLog(logText: "[NATIVE] Could not aggregate sourceDescription for caching task: \(error.localizedDescription)")}
if DEBUG { PrintUtils.printLog(logText: "[NATIVE] Could not aggregate sourceDescription: \(error.localizedDescription)")}
return nil
}
return nil
}

class func aggregateCacheTaskSourceDescription(sourceDescription: SourceDescription, cachingTaskId: String) -> [String:Any]? {
if let result = THEOplayerRCTSourceDescriptionAggregator.aggregateSourceDescription(sourceDescription: sourceDescription) {
let srcDescription = THEOplayerRCTSourceDescriptionAggregator.sanitiseSourceDescriptionMetadata(input: result)
let extendedSrcDescription = THEOplayerRCTSourceDescriptionAggregator.addCachingTaskIdToMetadata(input: srcDescription, cachingTaskId: cachingTaskId)
return extendedSrcDescription
}
return nil
}

private class func sanitiseSourceDescriptionMetadata(input: [String:Any]) -> [String:Any] {
var output: [String:Any] = input
if let metadata = output[SD_PROP_METADATA] as? [String:Any],
Expand Down Expand Up @@ -53,4 +59,3 @@ class THEOplayerRCTSourceDescriptionAggregator {
return output
}
}
#endif
2 changes: 1 addition & 1 deletion ios/THEOplayerRCTTrackMetadataAggregator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ let PROP_END_ON_NEXT: String = "endOnNext"

class THEOplayerRCTTrackMetadataAggregator {

class func aggregateTrackMetadata(player: THEOplayer, metadataTracksInfo: [[String:Any]]) -> [String:Any] {
class func aggregateTrackInfo(player: THEOplayer, metadataTracksInfo: [[String:Any]]) -> [String:Any] {
let textTracks: TextTrackList = player.textTracks
let audioTracks: AudioTrackList = player.audioTracks
let videoTracks: VideoTrackList = player.videoTracks
Expand Down
41 changes: 34 additions & 7 deletions ios/THEOplayerRCTView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -210,16 +210,43 @@ public class THEOplayerRCTView: UIView {
}
}

private func notifyNativePlayerReady() {
public func notifyNativePlayerReady() {
DispatchQueue.main.async {
let versionString = THEOplayer.version
if let forwardedNativeReady = self.onNativePlayerReady {
forwardedNativeReady([
"version": [
"version" : versionString,
"playerSuiteVersion": versionString
],
])
var payload: [String: Any] = [:]

// pass initial player state
if let player = self.player {
// collect stored track metadata
let trackInfo = THEOplayerRCTTrackMetadataAggregator.aggregateTrackInfo(
player: player,
metadataTracksInfo: self.mainEventHandler.loadedMetadataAndChapterTracksInfo
)

// build state
payload["state"] = THEOplayerRCTPlayerStateBuilder()
.source(player.source)
.currentTime(player.currentTime)
.currentProgramDateTime(player.currentProgramDateTime)
.paused(player.paused)
.playbackRate(player.playbackRate)
.duration(player.duration)
.volume(player.volume)
.muted(player.muted)
.seekable(player.seekable)
.buffered(player.buffered)
.trackInfo(trackInfo)
.build()
}

// pass version onfo
payload["version"] = [
"version": versionString,
"playerSuiteVersion": versionString
]

forwardedNativeReady(payload)
}
}
}
Expand Down
Loading