unified-video-framework 1.4.382 → 1.4.384
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/package.json +1 -1
- package/packages/core/dist/interfaces.d.ts +28 -2
- package/packages/core/dist/interfaces.d.ts.map +1 -1
- package/packages/core/dist/interfaces.js +0 -1
- package/packages/core/dist/interfaces.js.map +1 -1
- package/packages/core/src/interfaces.ts +53 -3
- package/packages/react-native/dist/drm/AndroidDRMProtection.js +1 -1
- package/packages/react-native/dist/drm/iOSDRMProtection.js +1 -1
- package/packages/web/dist/WebPlayer.d.ts +2 -0
- package/packages/web/dist/WebPlayer.d.ts.map +1 -1
- package/packages/web/dist/WebPlayer.js +9731 -9644
- package/packages/web/dist/WebPlayer.js.map +1 -1
- package/packages/web/dist/drm/DRMHelper.d.ts +28 -0
- package/packages/web/dist/drm/DRMHelper.d.ts.map +1 -0
- package/packages/web/dist/drm/DRMHelper.js +117 -0
- package/packages/web/dist/drm/DRMHelper.js.map +1 -0
- package/packages/web/dist/index.d.ts +0 -1
- package/packages/web/dist/index.d.ts.map +1 -1
- package/packages/web/dist/index.js +6 -7
- package/packages/web/dist/index.js.map +1 -1
- package/packages/web/dist/react/WebPlayerView.d.ts +21 -0
- package/packages/web/dist/react/WebPlayerView.d.ts.map +1 -1
- package/packages/web/dist/react/WebPlayerView.js.map +1 -1
- package/packages/web/dist/security/CanvasVideoRenderer.d.ts +26 -0
- package/packages/web/dist/security/CanvasVideoRenderer.d.ts.map +1 -0
- package/packages/web/dist/security/CanvasVideoRenderer.js +143 -0
- package/packages/web/dist/security/CanvasVideoRenderer.js.map +1 -0
- package/packages/web/dist/security/ScreenProtectionController.d.ts +39 -0
- package/packages/web/dist/security/ScreenProtectionController.d.ts.map +1 -0
- package/packages/web/dist/security/ScreenProtectionController.js +278 -0
- package/packages/web/dist/security/ScreenProtectionController.js.map +1 -0
- package/packages/web/src/WebPlayer.ts +120 -2
- package/packages/web/src/drm/DRMHelper.ts +203 -0
- package/packages/web/src/index.ts +0 -3
- package/packages/web/src/react/WebPlayerView.tsx +24 -0
- package/packages/web/src/security/CanvasVideoRenderer.ts +246 -0
- package/packages/web/src/security/ScreenProtectionController.ts +391 -0
- package/scripts/fix-imports.js +18 -16
- package/packages/core/src/interfaces/IDRMProtection.ts +0 -285
- package/packages/react-native/src/drm/AndroidDRMProtection.ts +0 -419
- package/packages/react-native/src/drm/iOSDRMProtection.ts +0 -415
- package/packages/web/src/drm/WebDRMProtection.ts +0 -596
|
@@ -1,415 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* iOS DRM Protection Implementation
|
|
3
|
-
*
|
|
4
|
-
* Implements Netflix-like content protection for iOS using:
|
|
5
|
-
* - FairPlay Streaming DRM
|
|
6
|
-
* - Screen capture detection via UIScreen.isCaptured
|
|
7
|
-
* - AirPlay support (DRM playback, not mirroring)
|
|
8
|
-
* - External display detection
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { NativeModules, NativeEventEmitter } from 'react-native';
|
|
12
|
-
import {
|
|
13
|
-
IDRMProtection,
|
|
14
|
-
IDRMProtectionConfig,
|
|
15
|
-
IDRMProtectionStatus,
|
|
16
|
-
CastDevice,
|
|
17
|
-
DRMErrorCode,
|
|
18
|
-
} from '@unified-video/core';
|
|
19
|
-
|
|
20
|
-
const { DRMProtectionModule } = NativeModules;
|
|
21
|
-
const drmEventEmitter = new NativeEventEmitter(DRMProtectionModule);
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* iOS DRM Protection
|
|
25
|
-
*/
|
|
26
|
-
export class iOSDRMProtection implements IDRMProtection {
|
|
27
|
-
private config: IDRMProtectionConfig;
|
|
28
|
-
private status: IDRMProtectionStatus;
|
|
29
|
-
private listeners: any[] = [];
|
|
30
|
-
|
|
31
|
-
constructor() {
|
|
32
|
-
this.config = { enabled: false };
|
|
33
|
-
this.status = {
|
|
34
|
-
isProtected: false,
|
|
35
|
-
drmSystem: 'none',
|
|
36
|
-
isScreenRecordingBlocked: false,
|
|
37
|
-
isAudioCaptureBlocked: false,
|
|
38
|
-
isScreenshotBlocked: false,
|
|
39
|
-
isCasting: false,
|
|
40
|
-
screenRecordingDetected: false,
|
|
41
|
-
mirroringDetected: false,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Initialize DRM protection
|
|
47
|
-
*/
|
|
48
|
-
async initialize(config: IDRMProtectionConfig): Promise<void> {
|
|
49
|
-
this.config = {
|
|
50
|
-
blockScreenRecording: true,
|
|
51
|
-
blockAudioCapture: true,
|
|
52
|
-
blockScreenshots: true,
|
|
53
|
-
allowCasting: true,
|
|
54
|
-
blockMirroring: true,
|
|
55
|
-
...config,
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
if (!this.config.enabled) {
|
|
59
|
-
console.log('[DRM-iOS] Protection disabled');
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
console.log('[DRM-iOS] Initializing...', this.config);
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
// Initialize FairPlay DRM
|
|
67
|
-
if (this.config.licenseServerUrl && this.config.certificateUrl) {
|
|
68
|
-
await this.initializeFairPlay();
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Start screen capture detection
|
|
72
|
-
if (this.config.blockScreenRecording) {
|
|
73
|
-
await this.startScreenCaptureDetection();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Initialize AirPlay
|
|
77
|
-
if (this.config.allowCasting) {
|
|
78
|
-
await this.initializeAirPlay();
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Setup event listeners
|
|
82
|
-
this.setupEventListeners();
|
|
83
|
-
|
|
84
|
-
this.status.isProtected = true;
|
|
85
|
-
console.log('[DRM-iOS] Protection initialized');
|
|
86
|
-
} catch (error) {
|
|
87
|
-
console.error('[DRM-iOS] Initialization failed:', error);
|
|
88
|
-
throw error;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Initialize FairPlay Streaming
|
|
94
|
-
*/
|
|
95
|
-
private async initializeFairPlay(): Promise<void> {
|
|
96
|
-
console.log('[DRM-iOS] Initializing FairPlay...');
|
|
97
|
-
|
|
98
|
-
const fairPlayConfig = {
|
|
99
|
-
licenseServerUrl: this.config.licenseServerUrl,
|
|
100
|
-
certificateUrl: this.config.certificateUrl,
|
|
101
|
-
licenseHeaders: this.config.licenseHeaders,
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const result = await DRMProtectionModule.initializeFairPlay(fairPlayConfig);
|
|
105
|
-
|
|
106
|
-
if (result.success) {
|
|
107
|
-
this.status.drmSystem = 'fairplay';
|
|
108
|
-
console.log('[DRM-iOS] FairPlay initialized successfully');
|
|
109
|
-
} else {
|
|
110
|
-
throw {
|
|
111
|
-
code: DRMErrorCode.FAIRPLAY_NOT_AVAILABLE,
|
|
112
|
-
message: 'FairPlay initialization failed',
|
|
113
|
-
platform: 'ios',
|
|
114
|
-
recoverable: false,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Start screen capture detection using UIScreen.isCaptured
|
|
121
|
-
*/
|
|
122
|
-
private async startScreenCaptureDetection(): Promise<void> {
|
|
123
|
-
console.log('[DRM-iOS] Starting screen capture detection...');
|
|
124
|
-
|
|
125
|
-
await DRMProtectionModule.startScreenCaptureDetection();
|
|
126
|
-
this.status.isScreenRecordingBlocked = true;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Initialize AirPlay
|
|
131
|
-
*/
|
|
132
|
-
private async initializeAirPlay(): Promise<void> {
|
|
133
|
-
console.log('[DRM-iOS] Initializing AirPlay...');
|
|
134
|
-
|
|
135
|
-
await DRMProtectionModule.initializeAirPlay({
|
|
136
|
-
allowMirroring: false, // Only allow native AirPlay, not screen mirroring
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Setup event listeners
|
|
142
|
-
*/
|
|
143
|
-
private setupEventListeners(): void {
|
|
144
|
-
// Screen capture detected
|
|
145
|
-
this.listeners.push(
|
|
146
|
-
drmEventEmitter.addListener('onScreenCaptureDetected', () => {
|
|
147
|
-
console.warn('[DRM-iOS] ⚠️ Screen capture detected!');
|
|
148
|
-
this.status.screenRecordingDetected = true;
|
|
149
|
-
this.config.onScreenRecordingDetected?.();
|
|
150
|
-
})
|
|
151
|
-
);
|
|
152
|
-
|
|
153
|
-
// External display detected (mirroring)
|
|
154
|
-
this.listeners.push(
|
|
155
|
-
drmEventEmitter.addListener('onExternalDisplayDetected', (event: any) => {
|
|
156
|
-
console.warn('[DRM-iOS] ⚠️ External display detected:', event);
|
|
157
|
-
this.status.mirroringDetected = true;
|
|
158
|
-
this.config.onMirroringDetected?.();
|
|
159
|
-
})
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
// AirPlay started
|
|
163
|
-
this.listeners.push(
|
|
164
|
-
drmEventEmitter.addListener('onAirPlayStarted', (event: any) => {
|
|
165
|
-
console.log('[DRM-iOS] AirPlay started:', event.deviceName);
|
|
166
|
-
this.status.isCasting = true;
|
|
167
|
-
this.status.castDevice = {
|
|
168
|
-
name: event.deviceName,
|
|
169
|
-
type: 'airplay',
|
|
170
|
-
};
|
|
171
|
-
this.config.onCastingStarted?.(event.deviceName, 'airplay');
|
|
172
|
-
})
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
// AirPlay ended
|
|
176
|
-
this.listeners.push(
|
|
177
|
-
drmEventEmitter.addListener('onAirPlayEnded', () => {
|
|
178
|
-
console.log('[DRM-iOS] AirPlay ended');
|
|
179
|
-
this.status.isCasting = false;
|
|
180
|
-
this.status.castDevice = undefined;
|
|
181
|
-
this.config.onCastingEnded?.();
|
|
182
|
-
})
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
// FairPlay license acquired
|
|
186
|
-
this.listeners.push(
|
|
187
|
-
drmEventEmitter.addListener('onLicenseAcquired', () => {
|
|
188
|
-
console.log('[DRM-iOS] FairPlay license acquired');
|
|
189
|
-
this.config.onLicenseAcquired?.();
|
|
190
|
-
})
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
// DRM error
|
|
194
|
-
this.listeners.push(
|
|
195
|
-
drmEventEmitter.addListener('onDRMError', (error: any) => {
|
|
196
|
-
console.error('[DRM-iOS] DRM Error:', error);
|
|
197
|
-
this.config.onDRMError?.(error);
|
|
198
|
-
})
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Get current status
|
|
204
|
-
*/
|
|
205
|
-
getStatus(): IDRMProtectionStatus {
|
|
206
|
-
return { ...this.status };
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Enable/disable protection
|
|
211
|
-
*/
|
|
212
|
-
setEnabled(enabled: boolean): void {
|
|
213
|
-
this.config.enabled = enabled;
|
|
214
|
-
DRMProtectionModule.setEnabled(enabled);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Set specific feature
|
|
219
|
-
*/
|
|
220
|
-
setFeature(feature: keyof IDRMProtectionConfig, enabled: boolean): void {
|
|
221
|
-
(this.config as any)[feature] = enabled;
|
|
222
|
-
|
|
223
|
-
switch (feature) {
|
|
224
|
-
case 'blockScreenRecording':
|
|
225
|
-
if (enabled) {
|
|
226
|
-
this.startScreenCaptureDetection();
|
|
227
|
-
}
|
|
228
|
-
break;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Start casting (AirPlay)
|
|
234
|
-
*/
|
|
235
|
-
async startCasting(deviceId: string): Promise<void> {
|
|
236
|
-
await DRMProtectionModule.startAirPlay(deviceId);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Stop casting
|
|
241
|
-
*/
|
|
242
|
-
async stopCasting(): Promise<void> {
|
|
243
|
-
await DRMProtectionModule.stopAirPlay();
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Get available cast devices
|
|
248
|
-
*/
|
|
249
|
-
async getAvailableCastDevices(): Promise<CastDevice[]> {
|
|
250
|
-
const devices = await DRMProtectionModule.getAvailableAirPlayDevices();
|
|
251
|
-
return devices.map((device: any) => ({
|
|
252
|
-
id: device.id,
|
|
253
|
-
name: device.name,
|
|
254
|
-
type: 'airplay',
|
|
255
|
-
isAvailable: device.isAvailable,
|
|
256
|
-
capabilities: {
|
|
257
|
-
supportsVideo: true,
|
|
258
|
-
supportsAudio: true,
|
|
259
|
-
supportsDRM: device.supportsFairPlay,
|
|
260
|
-
maxResolution: device.maxResolution,
|
|
261
|
-
},
|
|
262
|
-
}));
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Renew license
|
|
267
|
-
*/
|
|
268
|
-
async renewLicense(): Promise<void> {
|
|
269
|
-
await DRMProtectionModule.renewFairPlayLicense();
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Cleanup
|
|
274
|
-
*/
|
|
275
|
-
dispose(): void {
|
|
276
|
-
this.listeners.forEach((listener) => listener.remove());
|
|
277
|
-
this.listeners = [];
|
|
278
|
-
DRMProtectionModule.dispose();
|
|
279
|
-
this.status.isProtected = false;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* NATIVE iOS MODULE (Swift)
|
|
285
|
-
*
|
|
286
|
-
* File: ios/YourApp/DRMProtectionModule.swift
|
|
287
|
-
*
|
|
288
|
-
* ```swift
|
|
289
|
-
* import Foundation
|
|
290
|
-
* import AVFoundation
|
|
291
|
-
* import React
|
|
292
|
-
*
|
|
293
|
-
* @objc(DRMProtectionModule)
|
|
294
|
-
* class DRMProtectionModule: RCTEventEmitter {
|
|
295
|
-
*
|
|
296
|
-
* private var screenCaptureObserver: NSObjectProtocol?
|
|
297
|
-
* private var externalDisplayObserver: NSObjectProtocol?
|
|
298
|
-
* private var fairPlayResourceLoader: FairPlayResourceLoader?
|
|
299
|
-
*
|
|
300
|
-
* override static func requiresMainQueueSetup() -> Bool {
|
|
301
|
-
* return true
|
|
302
|
-
* }
|
|
303
|
-
*
|
|
304
|
-
* override func supportedEvents() -> [String]! {
|
|
305
|
-
* return [
|
|
306
|
-
* "onScreenCaptureDetected",
|
|
307
|
-
* "onExternalDisplayDetected",
|
|
308
|
-
* "onAirPlayStarted",
|
|
309
|
-
* "onAirPlayEnded",
|
|
310
|
-
* "onLicenseAcquired",
|
|
311
|
-
* "onDRMError"
|
|
312
|
-
* ]
|
|
313
|
-
* }
|
|
314
|
-
*
|
|
315
|
-
* @objc
|
|
316
|
-
* func initializeFairPlay(_ config: NSDictionary, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
317
|
-
* guard let licenseServerUrl = config["licenseServerUrl"] as? String,
|
|
318
|
-
* let certificateUrl = config["certificateUrl"] as? String else {
|
|
319
|
-
* rejecter("FAIRPLAY_ERROR", "Invalid configuration", nil)
|
|
320
|
-
* return
|
|
321
|
-
* }
|
|
322
|
-
*
|
|
323
|
-
* fairPlayResourceLoader = FairPlayResourceLoader(
|
|
324
|
-
* licenseServerUrl: licenseServerUrl,
|
|
325
|
-
* certificateUrl: certificateUrl
|
|
326
|
-
* )
|
|
327
|
-
*
|
|
328
|
-
* resolver(["success": true])
|
|
329
|
-
* }
|
|
330
|
-
*
|
|
331
|
-
* @objc
|
|
332
|
-
* func startScreenCaptureDetection(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
333
|
-
* if #available(iOS 11.0, *) {
|
|
334
|
-
* // Monitor UIScreen.isCaptured
|
|
335
|
-
* screenCaptureObserver = NotificationCenter.default.addObserver(
|
|
336
|
-
* forName: UIScreen.capturedDidChangeNotification,
|
|
337
|
-
* object: nil,
|
|
338
|
-
* queue: .main
|
|
339
|
-
* ) { [weak self] _ in
|
|
340
|
-
* if UIScreen.main.isCaptured {
|
|
341
|
-
* print("[DRM] Screen capture detected!")
|
|
342
|
-
* self?.sendEvent(withName: "onScreenCaptureDetected", body: ["isCaptured": true])
|
|
343
|
-
* }
|
|
344
|
-
* }
|
|
345
|
-
*
|
|
346
|
-
* // Monitor external displays
|
|
347
|
-
* externalDisplayObserver = NotificationCenter.default.addObserver(
|
|
348
|
-
* forName: UIScreen.didConnectNotification,
|
|
349
|
-
* object: nil,
|
|
350
|
-
* queue: .main
|
|
351
|
-
* ) { [weak self] notification in
|
|
352
|
-
* if let screen = notification.object as? UIScreen {
|
|
353
|
-
* print("[DRM] External display connected")
|
|
354
|
-
* self?.sendEvent(withName: "onExternalDisplayDetected", body: [
|
|
355
|
-
* "screenName": screen.debugDescription
|
|
356
|
-
* ])
|
|
357
|
-
* }
|
|
358
|
-
* }
|
|
359
|
-
*
|
|
360
|
-
* resolve(true)
|
|
361
|
-
* } else {
|
|
362
|
-
* reject("NOT_SUPPORTED", "Screen capture detection requires iOS 11+", nil)
|
|
363
|
-
* }
|
|
364
|
-
* }
|
|
365
|
-
*
|
|
366
|
-
* @objc
|
|
367
|
-
* func initializeAirPlay(_ config: NSDictionary, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
368
|
-
* // Configure AirPlay
|
|
369
|
-
* let allowMirroring = config["allowMirroring"] as? Bool ?? false
|
|
370
|
-
* resolver(true)
|
|
371
|
-
* }
|
|
372
|
-
*
|
|
373
|
-
* deinit {
|
|
374
|
-
* if let observer = screenCaptureObserver {
|
|
375
|
-
* NotificationCenter.default.removeObserver(observer)
|
|
376
|
-
* }
|
|
377
|
-
* if let observer = externalDisplayObserver {
|
|
378
|
-
* NotificationCenter.default.removeObserver(observer)
|
|
379
|
-
* }
|
|
380
|
-
* }
|
|
381
|
-
* }
|
|
382
|
-
*
|
|
383
|
-
* // FairPlay Resource Loader
|
|
384
|
-
* class FairPlayResourceLoader: NSObject, AVAssetResourceLoaderDelegate {
|
|
385
|
-
* let licenseServerUrl: String
|
|
386
|
-
* let certificateUrl: String
|
|
387
|
-
*
|
|
388
|
-
* init(licenseServerUrl: String, certificateUrl: String) {
|
|
389
|
-
* self.licenseServerUrl = licenseServerUrl
|
|
390
|
-
* self.certificateUrl = certificateUrl
|
|
391
|
-
* }
|
|
392
|
-
*
|
|
393
|
-
* func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
|
|
394
|
-
* guard let url = loadingRequest.request.url else {
|
|
395
|
-
* return false
|
|
396
|
-
* }
|
|
397
|
-
*
|
|
398
|
-
* // Handle FairPlay license request
|
|
399
|
-
* // 1. Get certificate from certificateUrl
|
|
400
|
-
* // 2. Create SPC (Server Playback Context)
|
|
401
|
-
* // 3. Send to license server
|
|
402
|
-
* // 4. Get CKC (Content Key Context)
|
|
403
|
-
* // 5. Provide to AVPlayer
|
|
404
|
-
*
|
|
405
|
-
* return true
|
|
406
|
-
* }
|
|
407
|
-
* }
|
|
408
|
-
* ```
|
|
409
|
-
*
|
|
410
|
-
* Info.plist requirements:
|
|
411
|
-
* ```xml
|
|
412
|
-
* <key>NSMicrophoneUsageDescription</key>
|
|
413
|
-
* <string>Required for content protection</string>
|
|
414
|
-
* ```
|
|
415
|
-
*/
|