@npaw/forge-react-native 9.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +10 -0
- package/NPAWForgeReactNative.podspec +44 -0
- package/README.md +51 -0
- package/android/build.gradle +77 -0
- package/android/src/main/AndroidManifest.xml +14 -0
- package/android/src/main/java/com/npaw/forge/reactnative/ForgeDtoMapper.java +295 -0
- package/android/src/main/java/com/npaw/forge/reactnative/ForgeInstanceRegistry.java +61 -0
- package/android/src/main/java/com/npaw/forge/reactnative/NPAWForgeModule.java +735 -0
- package/android/src/main/java/com/npaw/forge/reactnative/NPAWForgePackage.java +53 -0
- package/android/src/test/java/com/npaw/forge/reactnative/ForgeDtoMapperTest.java +133 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/Info.plist +82 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/ForgeKernelFFI.h +17 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/forge_kernelFFI.h +873 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/module.modulemap +7 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/libForgeKernelFFI.a +0 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/ForgeKernelFFI.h +17 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/forge_kernelFFI.h +873 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/module.modulemap +7 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/libForgeKernelFFI.a +0 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/ForgeKernelFFI.h +17 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/forge_kernelFFI.h +873 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/module.modulemap +7 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/libForgeKernelFFI.a +0 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/ForgeKernelFFI.h +17 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/forge_kernelFFI.h +873 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/module.modulemap +7 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/libForgeKernelFFI.a +0 -0
- package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernel.swift +3931 -0
- package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernelFFI.h +873 -0
- package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernelFFI.modulemap +7 -0
- package/ios/ForgeApple/Sources/NPAWForgeAVPlayerAdapter/AVPlayerAccessLogFacts.swift +152 -0
- package/ios/ForgeApple/Sources/NPAWForgeAVPlayerAdapter/AVPlayerAdapter.swift +877 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/AttributionCapture.swift +158 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/CapturePlanGate.swift +189 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/CrashCapture.swift +120 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/ForgeAutomaticCapture.swift +639 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsCapture.swift +1176 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsEngine.swift +674 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsMarkers.swift +105 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsSwiftUI.swift +284 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/SwiftUICapture.swift +44 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/AdAdapter.swift +199 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/AdSemantics.swift +136 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeApple.swift +596 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeCapturePlan.swift +297 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeClock.swift +29 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeLogging.swift +109 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeOutcome.swift +89 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeRuntime.swift +291 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeStorage.swift +70 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ManualAdReporter.swift +180 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/NPAWForge.swift +453 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/NativeUniFFIRuntime.swift +594 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/NetworkRequestRecord.swift +191 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ObjectiveCWrappers.swift +191 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/PlayerAdapter.swift +299 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/PlayerSemantics.swift +266 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/PrivacySanitizer.swift +59 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/VideoAdapter.swift +478 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/VideoOptions.swift +431 -0
- package/ios/ForgeApple/Sources/NPAWForgeNetworkCapture/ForgeNetworkCapture.swift +116 -0
- package/ios/ForgeApple/Sources/NPAWForgeNetworkCaptureObjC/NPAWForgeNetworkSwizzler.m +293 -0
- package/ios/ForgeApple/Sources/NPAWForgeNetworkCaptureObjC/include/NPAWForgeNetworkSwizzler.h +33 -0
- package/ios/ForgeModuleImpl.swift +1131 -0
- package/ios/NPAWForgeTurboModule.mm +399 -0
- package/package.json +33 -0
- package/src/ForgeAdReporter.ts +101 -0
- package/src/ForgeVideo.ts +169 -0
- package/src/NPAWForge.ts +177 -0
- package/src/capture.ts +192 -0
- package/src/events.ts +40 -0
- package/src/index.ts +37 -0
- package/src/specs/NativeNPAWForge.ts +168 -0
- package/src/types.ts +114 -0
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) NPAW - All Rights Reserved
|
|
3
|
+
*
|
|
4
|
+
* This source code is protected under international copyright law. All rights
|
|
5
|
+
* reserved and protected by the copyright holders.
|
|
6
|
+
* This file is confidential and only available to authorized individuals with the
|
|
7
|
+
* permission of the copyright holders. If you encounter this file and do not have
|
|
8
|
+
* permission, please contact the copyright holders and delete this file.
|
|
9
|
+
*
|
|
10
|
+
*
|
|
11
|
+
* @author: Victor Soto <victor.soto@nicepeopleatwork.com>
|
|
12
|
+
* @description: Apple video adapter lifecycle and runtime view owner.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import Foundation
|
|
16
|
+
|
|
17
|
+
public final class VideoBuilder {
|
|
18
|
+
private weak var forge: NPAWForge?
|
|
19
|
+
private var identifier = NPAWForge.defaultVideoAdapterIdentifier
|
|
20
|
+
private var options = VideoOptions()
|
|
21
|
+
private var playerAdapter: PlayerAdapter?
|
|
22
|
+
private var adAdapter: AdAdapter?
|
|
23
|
+
|
|
24
|
+
init(forge: NPAWForge) {
|
|
25
|
+
self.forge = forge
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@discardableResult public func setIdentifier(_ identifier: String?) -> VideoBuilder {
|
|
29
|
+
if let identifier = identifier?.trimmingCharacters(in: .whitespacesAndNewlines), !identifier.isEmpty {
|
|
30
|
+
self.identifier = identifier
|
|
31
|
+
}
|
|
32
|
+
return self
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@discardableResult public func setOptions(_ options: VideoOptions?) -> VideoBuilder {
|
|
36
|
+
self.options = options ?? self.options
|
|
37
|
+
return self
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@discardableResult public func setPlayerAdapter(_ playerAdapter: PlayerAdapter?) -> VideoBuilder {
|
|
41
|
+
self.playerAdapter = playerAdapter
|
|
42
|
+
return self
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@discardableResult public func setAdAdapter(_ adAdapter: AdAdapter?) -> VideoBuilder {
|
|
46
|
+
self.adAdapter = adAdapter
|
|
47
|
+
return self
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public func build() -> VideoAdapter {
|
|
51
|
+
guard let forge else {
|
|
52
|
+
return VideoAdapter.unavailable(identifier: identifier, options: options)
|
|
53
|
+
}
|
|
54
|
+
return forge.buildVideoAdapter(
|
|
55
|
+
identifier: identifier,
|
|
56
|
+
options: options,
|
|
57
|
+
playerAdapter: playerAdapter,
|
|
58
|
+
adAdapter: adAdapter
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public final class VideoAdapter: RuntimeContentMetadataProvider {
|
|
64
|
+
weak var forge: NPAWForge?
|
|
65
|
+
public let identifier: String
|
|
66
|
+
public let options: VideoOptions
|
|
67
|
+
/// Options used for runtime event recording: the integrator options plus
|
|
68
|
+
/// the bound player adapter's `defaultWaitForMetadata` when the integrator
|
|
69
|
+
/// did not choose explicitly (mirrors the Android `effectiveOptions`).
|
|
70
|
+
private var effectiveOptions: VideoOptions
|
|
71
|
+
private let runtimeView: RuntimeVideoView
|
|
72
|
+
private var destroyed = false
|
|
73
|
+
private var willSendRequestListener: ((String, VideoAdapter, inout [String: String]) -> Void)?
|
|
74
|
+
|
|
75
|
+
public private(set) var playerAdapter: PlayerAdapter?
|
|
76
|
+
public private(set) var adAdapter: AdAdapter?
|
|
77
|
+
|
|
78
|
+
public var isDestroyed: Bool { destroyed }
|
|
79
|
+
|
|
80
|
+
init(forge: NPAWForge, identifier: String, options: VideoOptions) {
|
|
81
|
+
self.forge = forge
|
|
82
|
+
self.identifier = identifier
|
|
83
|
+
self.options = options
|
|
84
|
+
self.effectiveOptions = options
|
|
85
|
+
self.runtimeView = forge.kernel.createVideoView(timestampMs: 0, options: options)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private init(identifier: String, options: VideoOptions) {
|
|
89
|
+
self.identifier = identifier
|
|
90
|
+
self.options = options
|
|
91
|
+
self.effectiveOptions = options
|
|
92
|
+
self.runtimeView = RuntimeVideoView(id: -1)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static func unavailable(identifier: String, options: VideoOptions) -> VideoAdapter {
|
|
96
|
+
VideoAdapter(identifier: identifier, options: options)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public func setPlayerAdapter(_ playerAdapter: PlayerAdapter?) -> ForgeOutcome {
|
|
100
|
+
replacePlayerAdapter(playerAdapter, checkDuplicates: true)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public func setAdAdapter(_ adAdapter: AdAdapter?) -> ForgeOutcome {
|
|
104
|
+
replaceAdAdapter(adAdapter, checkDuplicates: true)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public func setWillSendRequestListener(_ listener: ((String, VideoAdapter, inout [String: String]) -> Void)?) -> ForgeOutcome {
|
|
108
|
+
guard !destroyed else { return .ignoredInvalidState }
|
|
109
|
+
willSendRequestListener = listener
|
|
110
|
+
let hook = listener.map { callback in
|
|
111
|
+
VideoPreSendHook { [weak self] eventName, _, _, params in
|
|
112
|
+
guard let self else { return }
|
|
113
|
+
callback(eventName, self, ¶ms)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return forge?.kernel.registerVideoPreSendHook(view: runtimeView, hook: hook) ?? .ignoredInvalidState
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public func fireInit(
|
|
120
|
+
metadata: PlayerMetadataSnapshot? = nil,
|
|
121
|
+
params: [String: String]? = [:],
|
|
122
|
+
timestampMs: Int64 = 0
|
|
123
|
+
) -> ForgeOutcome {
|
|
124
|
+
recordPlayerEvent(event: .initEvent, timestampMs: timestampMs, metadata: metadata ?? playerMetadataFromParams(params))
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public func fireStart(
|
|
128
|
+
forceInit: Bool? = false,
|
|
129
|
+
metadata: PlayerMetadataSnapshot? = nil,
|
|
130
|
+
params: [String: String]? = [:],
|
|
131
|
+
timestampMs: Int64 = 0
|
|
132
|
+
) -> ForgeOutcome {
|
|
133
|
+
var snapshot = metadata ?? playerMetadataFromParams(params)
|
|
134
|
+
snapshot.forceInit = forceInit ?? false
|
|
135
|
+
return recordPlayerEvent(event: .start, timestampMs: timestampMs, metadata: snapshot)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public func fireJoin(
|
|
139
|
+
metadata: PlayerMetadataSnapshot? = nil,
|
|
140
|
+
params: [String: String]? = [:],
|
|
141
|
+
timestampMs: Int64 = 0
|
|
142
|
+
) -> ForgeOutcome {
|
|
143
|
+
recordPlayerEvent(event: .join, timestampMs: timestampMs, metadata: metadata ?? playerMetadataFromParams(params))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public func firePause(
|
|
147
|
+
metadata: PlayerMetadataSnapshot? = nil,
|
|
148
|
+
params: [String: String]? = [:],
|
|
149
|
+
timestampMs: Int64 = 0
|
|
150
|
+
) -> ForgeOutcome {
|
|
151
|
+
recordPlayerEvent(event: .pause, timestampMs: timestampMs, metadata: metadata ?? playerMetadataFromParams(params))
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
public func fireResume(
|
|
155
|
+
metadata: PlayerMetadataSnapshot? = nil,
|
|
156
|
+
params: [String: String]? = [:],
|
|
157
|
+
timestampMs: Int64 = 0
|
|
158
|
+
) -> ForgeOutcome {
|
|
159
|
+
recordPlayerEvent(event: .resume, timestampMs: timestampMs, metadata: metadata ?? playerMetadataFromParams(params))
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
public func fireSeekBegin(
|
|
163
|
+
convertFromBuffer: Bool? = true,
|
|
164
|
+
metadata: PlayerMetadataSnapshot? = nil,
|
|
165
|
+
params: [String: String]? = [:],
|
|
166
|
+
timestampMs: Int64 = 0
|
|
167
|
+
) -> ForgeOutcome {
|
|
168
|
+
var snapshot = metadata ?? playerMetadataFromParams(params)
|
|
169
|
+
snapshot.convertFromBuffer = convertFromBuffer ?? true
|
|
170
|
+
return recordPlayerEvent(event: .seekBegin, timestampMs: timestampMs, metadata: snapshot)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
public func fireSeekEnd(
|
|
174
|
+
metadata: PlayerMetadataSnapshot? = nil,
|
|
175
|
+
params: [String: String]? = [:],
|
|
176
|
+
timestampMs: Int64 = 0
|
|
177
|
+
) -> ForgeOutcome {
|
|
178
|
+
recordPlayerEvent(event: .seekEnd, timestampMs: timestampMs, metadata: metadata ?? playerMetadataFromParams(params))
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
public func fireBufferBegin(
|
|
182
|
+
convertFromSeek: Bool? = false,
|
|
183
|
+
metadata: PlayerMetadataSnapshot? = nil,
|
|
184
|
+
params: [String: String]? = [:],
|
|
185
|
+
timestampMs: Int64 = 0
|
|
186
|
+
) -> ForgeOutcome {
|
|
187
|
+
var snapshot = metadata ?? playerMetadataFromParams(params)
|
|
188
|
+
snapshot.convertFromSeek = convertFromSeek ?? false
|
|
189
|
+
return recordPlayerEvent(event: .bufferBegin, timestampMs: timestampMs, metadata: snapshot)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
public func fireBufferEnd(
|
|
193
|
+
metadata: PlayerMetadataSnapshot? = nil,
|
|
194
|
+
params: [String: String]? = [:],
|
|
195
|
+
timestampMs: Int64 = 0
|
|
196
|
+
) -> ForgeOutcome {
|
|
197
|
+
recordPlayerEvent(event: .bufferEnd, timestampMs: timestampMs, metadata: metadata ?? playerMetadataFromParams(params))
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
public func fireStop(
|
|
201
|
+
metadata: PlayerMetadataSnapshot? = nil,
|
|
202
|
+
params: [String: String]? = [:],
|
|
203
|
+
timestampMs: Int64 = 0
|
|
204
|
+
) -> ForgeOutcome {
|
|
205
|
+
recordPlayerEvent(event: .stop, timestampMs: timestampMs, metadata: metadata ?? playerMetadataFromParams(params))
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
public func fireError(
|
|
209
|
+
errorCode: String? = nil,
|
|
210
|
+
errorMessage: String? = nil,
|
|
211
|
+
errorMetadata: String? = nil,
|
|
212
|
+
timestampMs: Int64 = 0
|
|
213
|
+
) -> ForgeOutcome {
|
|
214
|
+
var metadata = manualPlayerMetadata()
|
|
215
|
+
metadata.errorCode = errorCode
|
|
216
|
+
metadata.errorMessage = errorMessage
|
|
217
|
+
metadata.errorMetadata = errorMetadata
|
|
218
|
+
return recordPlayerEvent(event: .error, timestampMs: timestampMs, metadata: metadata)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
public func fireFatalError(
|
|
222
|
+
errorCode: String? = nil,
|
|
223
|
+
errorMessage: String? = nil,
|
|
224
|
+
errorMetadata: String? = nil,
|
|
225
|
+
timestampMs: Int64 = 0
|
|
226
|
+
) -> ForgeOutcome {
|
|
227
|
+
var metadata = manualPlayerMetadata()
|
|
228
|
+
metadata.errorCode = errorCode
|
|
229
|
+
metadata.errorMessage = errorMessage
|
|
230
|
+
metadata.errorMetadata = errorMetadata
|
|
231
|
+
metadata.errorLevel = "fatal"
|
|
232
|
+
return recordPlayerEvent(event: .fatalError, timestampMs: timestampMs, metadata: metadata)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
public func fireDestroy(timestampMs: Int64 = 0) -> ForgeOutcome {
|
|
236
|
+
recordPlayerEvent(event: .destroy, timestampMs: timestampMs, metadata: manualPlayerMetadata())
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
public func fireEvent(
|
|
240
|
+
eventName: String = "",
|
|
241
|
+
dimensions: [String: String] = [:],
|
|
242
|
+
values: [String: Double] = [:],
|
|
243
|
+
topLevelDimensions: [String: String] = [:],
|
|
244
|
+
hasEndDatetime: Bool = false,
|
|
245
|
+
timestampMs: Int64 = 0
|
|
246
|
+
) -> ForgeOutcome {
|
|
247
|
+
var metadata = manualPlayerMetadata()
|
|
248
|
+
metadata.customEvent = CustomVideoEventPayload(
|
|
249
|
+
name: eventName,
|
|
250
|
+
dimensions: dimensions,
|
|
251
|
+
values: values,
|
|
252
|
+
topLevel: topLevelDimensions,
|
|
253
|
+
hasEndDatetime: hasEndDatetime
|
|
254
|
+
)
|
|
255
|
+
return recordPlayerEvent(event: .customVideoEvent, timestampMs: timestampMs, metadata: metadata)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
public func fireEventEnd(
|
|
259
|
+
eventName: String = "",
|
|
260
|
+
dimensions: [String: String] = [:],
|
|
261
|
+
values: [String: Double] = [:],
|
|
262
|
+
topLevelDimensions: [String: String] = [:],
|
|
263
|
+
timestampMs: Int64 = 0
|
|
264
|
+
) -> ForgeOutcome {
|
|
265
|
+
var metadata = manualPlayerMetadata()
|
|
266
|
+
metadata.customEvent = CustomVideoEventPayload(
|
|
267
|
+
name: eventName,
|
|
268
|
+
dimensions: dimensions,
|
|
269
|
+
values: values,
|
|
270
|
+
topLevel: topLevelDimensions
|
|
271
|
+
)
|
|
272
|
+
return recordPlayerEvent(event: .customVideoEventEnd, timestampMs: timestampMs, metadata: metadata)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
public func fireMetadataUpdate(
|
|
276
|
+
metadata: PlayerMetadataSnapshot? = nil,
|
|
277
|
+
params: [String: String]? = [:],
|
|
278
|
+
timestampMs: Int64 = 0
|
|
279
|
+
) -> ForgeOutcome {
|
|
280
|
+
recordPlayerEvent(event: .metadataUpdate, timestampMs: timestampMs, metadata: metadata ?? playerMetadataFromParams(params))
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
public func createAdReporter(adapterVersion: String? = nil) -> ManualAdReporter {
|
|
284
|
+
ManualAdReporter(videoAdapter: self, adapterVersion: adapterVersion)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/// Remote behavior hints from the latest kernel capture plan (`bh.v`),
|
|
288
|
+
/// or `nil` before the first plan. Player adapters snapshot these at view
|
|
289
|
+
/// start; hints are never hot-swapped mid-view.
|
|
290
|
+
public func capturePlanBehavior() -> ForgeCapturePlan.Behavior? {
|
|
291
|
+
guard !destroyed else { return nil }
|
|
292
|
+
return forge?.capturePlan()?.behavior
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
public func destroy() -> ForgeOutcome {
|
|
296
|
+
guard !destroyed else { return .ignoredDuplicate }
|
|
297
|
+
let destroyOutcome = forge?.kernel.recordPlayerEvent(
|
|
298
|
+
view: runtimeView,
|
|
299
|
+
event: .destroy,
|
|
300
|
+
timestampMs: 0,
|
|
301
|
+
metadata: manualPlayerMetadata(),
|
|
302
|
+
options: effectiveOptions
|
|
303
|
+
) ?? .ignoredInvalidState
|
|
304
|
+
destroyed = true
|
|
305
|
+
playerAdapter?.unbindFromVideoAdapter()
|
|
306
|
+
adAdapter?.unbindFromVideoAdapter()
|
|
307
|
+
playerAdapter = nil
|
|
308
|
+
adAdapter = nil
|
|
309
|
+
willSendRequestListener = nil
|
|
310
|
+
_ = forge?.kernel.registerContentMetadataProvider(view: runtimeView, provider: nil)
|
|
311
|
+
_ = forge?.kernel.registerVideoPreSendHook(view: runtimeView, hook: nil)
|
|
312
|
+
_ = forge?.kernel.destroyVideoView(runtimeView)
|
|
313
|
+
forge?.onVideoAdapterDestroyed(self)
|
|
314
|
+
return destroyOutcome.isAccepted ? .accepted : destroyOutcome
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
func replacePlayerAdapter(_ playerAdapter: PlayerAdapter?, checkDuplicates: Bool) -> ForgeOutcome {
|
|
318
|
+
guard !destroyed else { return .ignoredInvalidState }
|
|
319
|
+
if checkDuplicates {
|
|
320
|
+
forge?.onPlayerAdapterRegistered(owner: self, playerAdapter: playerAdapter)
|
|
321
|
+
}
|
|
322
|
+
self.playerAdapter?.unbindFromVideoAdapter()
|
|
323
|
+
self.playerAdapter = playerAdapter
|
|
324
|
+
effectiveOptions = playerAdapter?.defaultWaitForMetadata == true
|
|
325
|
+
? options.withAdapterWaitForMetadataDefault()
|
|
326
|
+
: options
|
|
327
|
+
playerAdapter?.bindToVideoAdapter(self)
|
|
328
|
+
return forge?.kernel.registerContentMetadataProvider(view: runtimeView, provider: self) ?? .ignoredInvalidState
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
func replaceAdAdapter(_ adAdapter: AdAdapter?, checkDuplicates: Bool) -> ForgeOutcome {
|
|
332
|
+
guard !destroyed else { return .ignoredInvalidState }
|
|
333
|
+
if checkDuplicates {
|
|
334
|
+
forge?.onAdAdapterRegistered(owner: self, adAdapter: adAdapter)
|
|
335
|
+
}
|
|
336
|
+
self.adAdapter?.unbindFromVideoAdapter()
|
|
337
|
+
self.adAdapter = adAdapter
|
|
338
|
+
adAdapter?.bindToVideoAdapter(self)
|
|
339
|
+
return .accepted
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
func recordPlayerEvent(event: PlayerEvent, timestampMs: Int64, metadata: PlayerMetadataSnapshot) -> ForgeOutcome {
|
|
343
|
+
guard !destroyed else { return .ignoredInvalidState }
|
|
344
|
+
return forge?.kernel.recordPlayerEvent(
|
|
345
|
+
view: runtimeView,
|
|
346
|
+
event: event,
|
|
347
|
+
timestampMs: timestampMs,
|
|
348
|
+
metadata: metadata,
|
|
349
|
+
options: effectiveOptions
|
|
350
|
+
) ?? .ignoredInvalidState
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
func recordAdEvent(
|
|
354
|
+
event: AdEvent,
|
|
355
|
+
timestampMs: Int64,
|
|
356
|
+
metadata: AdMetadataSnapshot,
|
|
357
|
+
adapterVersion: String? = nil
|
|
358
|
+
) -> ForgeOutcome {
|
|
359
|
+
guard !destroyed else { return .ignoredInvalidState }
|
|
360
|
+
return forge?.kernel.recordAdEvent(
|
|
361
|
+
view: runtimeView,
|
|
362
|
+
event: event,
|
|
363
|
+
timestampMs: timestampMs,
|
|
364
|
+
metadata: metadata,
|
|
365
|
+
options: effectiveOptions,
|
|
366
|
+
adapterVersion: adapterVersion ?? adAdapter?.descriptor.version
|
|
367
|
+
) ?? .ignoredInvalidState
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
func usesAdapterOrPlayer(_ candidate: AnyObject) -> Bool {
|
|
371
|
+
if playerAdapter === candidate || adAdapter === candidate { return true }
|
|
372
|
+
if let playerAdapter, playerAdapter.playerObject === candidate { return true }
|
|
373
|
+
if let adAdapter, adAdapter.playerObject === candidate { return true }
|
|
374
|
+
return false
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
public func metadataSnapshot(timemarkMs: Int64, elapsedRealtimeMs: Int64) -> MetadataSnapshot? {
|
|
378
|
+
guard !destroyed,
|
|
379
|
+
let snapshot = playerAdapter?.snapshotMetadata(timemarkMs: timemarkMs, elapsedRealtimeMs: elapsedRealtimeMs) else {
|
|
380
|
+
return nil
|
|
381
|
+
}
|
|
382
|
+
return MetadataSnapshot(
|
|
383
|
+
content: snapshot,
|
|
384
|
+
qos: playerAdapter?.snapshotQos(timemarkMs: timemarkMs, elapsedRealtimeMs: elapsedRealtimeMs)
|
|
385
|
+
)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
private func manualPlayerMetadata() -> PlayerMetadataSnapshot {
|
|
389
|
+
PlayerMetadataSnapshot(playerName: "Manual Player")
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
private func playerMetadataFromParams(_ params: [String: String]?) -> PlayerMetadataSnapshot {
|
|
393
|
+
PlayerMetadataSnapshot(
|
|
394
|
+
playerName: params?["player"] ?? "Manual Player",
|
|
395
|
+
playerVersion: params?["playerVersion"],
|
|
396
|
+
resourceUrl: params?["resource"],
|
|
397
|
+
parsedResource: params?["parsedResource"] ?? params?["urlToParse"],
|
|
398
|
+
title: params?["title"],
|
|
399
|
+
contentType: (params?["contentType"] ?? params?["content_type"]).flatMap(ContentType.fromAdapterlessValue) ?? .unknown,
|
|
400
|
+
playheadMs: params?["playhead"].flatMap(Self.secondsToMillis),
|
|
401
|
+
durationMs: params?["mediaDuration"].flatMap(Self.secondsToMillis),
|
|
402
|
+
bitrate: params?["bitrate"].flatMap(Int64.init),
|
|
403
|
+
rendition: params?["rendition"],
|
|
404
|
+
videoCodec: params?["videoCodec"],
|
|
405
|
+
audioCodec: params?["audioCodec"],
|
|
406
|
+
codecProfile: params?["codecProfile"],
|
|
407
|
+
containerFormat: params?["containerFormat"],
|
|
408
|
+
errorMetadata: params?["errorMetadata"],
|
|
409
|
+
errorLevel: params?["errorLevel"],
|
|
410
|
+
program: params?["program"]
|
|
411
|
+
)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
func adMetadataFromParams(_ params: [String: String]?) -> AdMetadataSnapshot {
|
|
415
|
+
AdMetadataSnapshot(
|
|
416
|
+
adId: params?["adId"] ?? params?["ad_id"],
|
|
417
|
+
creativeId: params?["adCreativeId"] ?? params?["creative_id"],
|
|
418
|
+
adUrl: params?["adUrl"] ?? params?["ad_url"],
|
|
419
|
+
adTitle: params?["adTitle"],
|
|
420
|
+
adDurationMs: params?["adDurationMs"].flatMap(Int64.init) ?? params?["adDuration"].flatMap(Self.secondsToMillis),
|
|
421
|
+
adPlayheadMs: params?["adPlayheadMs"].flatMap(Int64.init) ?? params?["adPlayhead"].flatMap(Self.secondsToMillis),
|
|
422
|
+
adBitrate: params?["adBitrate"].flatMap(Int64.init),
|
|
423
|
+
position: params?["position"].flatMap(AdPosition.fromAdapterlessValue),
|
|
424
|
+
quartile: params?["quartile"].flatMap(Int.init),
|
|
425
|
+
insertionType: (params?["adInsertionType"] ?? params?["insertion_type"]).flatMap(AdInsertionType.fromAdapterlessValue) ?? .unknown,
|
|
426
|
+
errorMetadata: params?["errorMetadata"],
|
|
427
|
+
errorLevel: params?["errorLevel"]
|
|
428
|
+
)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
private static func secondsToMillis(_ value: String) -> Int64? {
|
|
432
|
+
Double(value).map { Int64($0 * 1000.0) }
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
private extension ContentType {
|
|
437
|
+
static func fromAdapterlessValue(_ value: String) -> ContentType {
|
|
438
|
+
switch value.lowercased() {
|
|
439
|
+
case "live": return .live
|
|
440
|
+
case "vod": return .vod
|
|
441
|
+
case "audio": return .audio
|
|
442
|
+
default: return .unknown
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
private extension AdPosition {
|
|
448
|
+
static func fromAdapterlessValue(_ value: String) -> AdPosition {
|
|
449
|
+
switch value.lowercased() {
|
|
450
|
+
case "pre", "preroll": return .pre
|
|
451
|
+
case "mid", "midroll": return .mid
|
|
452
|
+
case "post", "postroll": return .post
|
|
453
|
+
default: return .unknown
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
private extension AdInsertionType {
|
|
459
|
+
static func fromAdapterlessValue(_ value: String) -> AdInsertionType {
|
|
460
|
+
switch value.lowercased() {
|
|
461
|
+
case "csai", "client", "client_side": return .clientSide
|
|
462
|
+
case "ssai", "server", "server_side": return .serverSide
|
|
463
|
+
default: return .unknown
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
private final class VideoPreSendHook: RuntimeVideoPreSendHook {
|
|
469
|
+
private let callback: (String, RuntimeVideoView, String, inout [String: String]) -> Void
|
|
470
|
+
|
|
471
|
+
init(callback: @escaping (String, RuntimeVideoView, String, inout [String: String]) -> Void) {
|
|
472
|
+
self.callback = callback
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
func willSendRequest(eventName: String, view: RuntimeVideoView, serviceName: String, params: inout [String: String]) {
|
|
476
|
+
callback(eventName, view, serviceName, ¶ms)
|
|
477
|
+
}
|
|
478
|
+
}
|