unified-video-framework 1.0.0
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/.github/workflows/ci.yml +253 -0
- package/ANDROID_TV_IMPLEMENTATION.md +313 -0
- package/COMPLETION_STATUS.md +165 -0
- package/CONTRIBUTING.md +376 -0
- package/FINAL_STATUS_REPORT.md +170 -0
- package/FRAMEWORK_REVIEW.md +247 -0
- package/IMPROVEMENTS_SUMMARY.md +168 -0
- package/LICENSE +21 -0
- package/NATIVE_APP_INTEGRATION_GUIDE.md +903 -0
- package/PAYWALL_RENTAL_FLOW.md +499 -0
- package/PLATFORM_SETUP_GUIDE.md +1636 -0
- package/README.md +315 -0
- package/RUN_LOCALLY.md +151 -0
- package/apps/demo/cast-sender-min.html +173 -0
- package/apps/demo/custom-player.html +883 -0
- package/apps/demo/demo.html +990 -0
- package/apps/demo/enhanced-player.html +3556 -0
- package/apps/demo/index.html +159 -0
- package/apps/rental-api/.env.example +24 -0
- package/apps/rental-api/README.md +23 -0
- package/apps/rental-api/migrations/001_init.sql +35 -0
- package/apps/rental-api/migrations/002_videos.sql +10 -0
- package/apps/rental-api/migrations/003_add_gateway_subref.sql +4 -0
- package/apps/rental-api/migrations/004_update_gateways.sql +4 -0
- package/apps/rental-api/migrations/005_seed_demo_video.sql +5 -0
- package/apps/rental-api/package-lock.json +2045 -0
- package/apps/rental-api/package.json +33 -0
- package/apps/rental-api/scripts/run-migration.js +42 -0
- package/apps/rental-api/scripts/update-video-currency.js +21 -0
- package/apps/rental-api/scripts/update-video-price.js +19 -0
- package/apps/rental-api/src/config.ts +14 -0
- package/apps/rental-api/src/db.ts +10 -0
- package/apps/rental-api/src/routes/cashfree.ts +167 -0
- package/apps/rental-api/src/routes/pesapal.ts +92 -0
- package/apps/rental-api/src/routes/rentals.ts +242 -0
- package/apps/rental-api/src/routes/webhooks.ts +73 -0
- package/apps/rental-api/src/server.ts +41 -0
- package/apps/rental-api/src/services/entitlements.ts +45 -0
- package/apps/rental-api/src/services/payments.ts +22 -0
- package/apps/rental-api/tsconfig.json +17 -0
- package/check-urls.ps1 +74 -0
- package/comparison-report.md +181 -0
- package/docs/PAYWALL.md +95 -0
- package/docs/PLAYER_UI_VISIBILITY.md +431 -0
- package/docs/README.md +7 -0
- package/docs/SYSTEM_ARCHITECTURE.md +612 -0
- package/docs/VDOCIPHER_CLONE_REQUIREMENTS.md +403 -0
- package/examples/android/JavaSampleApp/MainActivity.java +641 -0
- package/examples/android/JavaSampleApp/activity_main.xml +226 -0
- package/examples/android/SampleApp/MainActivity.kt +430 -0
- package/examples/ios/SampleApp/ViewController.swift +337 -0
- package/examples/ios/SwiftUISampleApp/ContentView.swift +304 -0
- package/iOS_IMPLEMENTATION_OPTIONS.md +470 -0
- package/ios/UnifiedVideoPlayer/UnifiedVideoPlayer.podspec +33 -0
- package/jest.config.js +33 -0
- package/jitpack.yml +5 -0
- package/lerna.json +35 -0
- package/package.json +69 -0
- package/packages/PLATFORM_STATUS.md +163 -0
- package/packages/android/build.gradle +135 -0
- package/packages/android/src/main/AndroidManifest.xml +36 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/PlayerConfiguration.java +221 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/UnifiedVideoPlayer.java +1037 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/UnifiedVideoPlayer.kt +707 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/analytics/AnalyticsProvider.java +9 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/cast/CastManager.java +141 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/cast/CastOptionsProvider.java +29 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/overlay/WatermarkOverlayView.java +88 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/pip/PipActionReceiver.java +33 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/services/PlaybackService.java +110 -0
- package/packages/android/src/main/java/com/unifiedvideo/player/services/PlayerHolder.java +19 -0
- package/packages/core/package.json +34 -0
- package/packages/core/src/BasePlayer.ts +250 -0
- package/packages/core/src/VideoPlayer.ts +237 -0
- package/packages/core/src/VideoPlayerFactory.ts +145 -0
- package/packages/core/src/index.ts +20 -0
- package/packages/core/src/interfaces/IVideoPlayer.ts +184 -0
- package/packages/core/src/interfaces.ts +240 -0
- package/packages/core/src/utils/EventEmitter.ts +66 -0
- package/packages/core/src/utils/PlatformDetector.ts +300 -0
- package/packages/core/tsconfig.json +20 -0
- package/packages/enact/package.json +51 -0
- package/packages/enact/src/VideoPlayer.js +365 -0
- package/packages/enact/src/adapters/TizenAdapter.js +354 -0
- package/packages/enact/src/index.js +82 -0
- package/packages/ios/BUILD_INSTRUCTIONS.md +108 -0
- package/packages/ios/FIX_EMBED_ISSUE.md +142 -0
- package/packages/ios/GETTING_STARTED.md +100 -0
- package/packages/ios/Package.swift +35 -0
- package/packages/ios/README.md +84 -0
- package/packages/ios/Sources/UnifiedVideoPlayer/Analytics/AnalyticsEmitter.swift +26 -0
- package/packages/ios/Sources/UnifiedVideoPlayer/DRM/FairPlayDRMManager.swift +102 -0
- package/packages/ios/Sources/UnifiedVideoPlayer/Info.plist +24 -0
- package/packages/ios/Sources/UnifiedVideoPlayer/Remote/RemoteCommandCenter.swift +109 -0
- package/packages/ios/Sources/UnifiedVideoPlayer/UnifiedVideoPlayer.swift +811 -0
- package/packages/ios/Sources/UnifiedVideoPlayer/UnifiedVideoPlayerView.swift +640 -0
- package/packages/ios/Sources/UnifiedVideoPlayer/Utilities/Color+Hex.swift +36 -0
- package/packages/ios/UnifiedVideoPlayer.podspec +27 -0
- package/packages/ios/UnifiedVideoPlayer.xcodeproj/project.pbxproj +385 -0
- package/packages/ios/build_framework.sh +55 -0
- package/packages/react-native/android/src/main/java/com/unifiedvideo/UnifiedVideoPlayerModule.kt +482 -0
- package/packages/react-native/ios/UnifiedVideoPlayer.swift +436 -0
- package/packages/react-native/package.json +51 -0
- package/packages/react-native/src/ReactNativePlayer.tsx +423 -0
- package/packages/react-native/src/VideoPlayer.tsx +224 -0
- package/packages/react-native/src/index.ts +28 -0
- package/packages/react-native/src/utils/EventEmitter.ts +66 -0
- package/packages/react-native/tsconfig.json +31 -0
- package/packages/roku/components/UnifiedVideoPlayer.brs +400 -0
- package/packages/roku/package.json +44 -0
- package/packages/roku/source/VideoPlayer.brs +231 -0
- package/packages/roku/source/main.brs +28 -0
- package/packages/web/GETTING_STARTED.md +292 -0
- package/packages/web/jest.config.js +28 -0
- package/packages/web/jest.setup.ts +110 -0
- package/packages/web/package.json +50 -0
- package/packages/web/src/SecureVideoPlayer.ts +1164 -0
- package/packages/web/src/WebPlayer.ts +3110 -0
- package/packages/web/src/__tests__/WebPlayer.test.ts +314 -0
- package/packages/web/src/index.ts +14 -0
- package/packages/web/src/paywall/PaywallController.ts +215 -0
- package/packages/web/src/react/WebPlayerView.tsx +177 -0
- package/packages/web/tsconfig.json +23 -0
- package/packages/web/webpack.config.js +45 -0
- package/server.js +131 -0
- package/server.py +84 -0
- package/test-urls.ps1 +97 -0
- package/test-video-urls.ps1 +87 -0
- package/tsconfig.json +39 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @unified-video/enact
|
|
3
|
+
* Enact TV implementation for Samsung Tizen and LG webOS
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Export main video player
|
|
7
|
+
export { default as VideoPlayer } from './VideoPlayer';
|
|
8
|
+
|
|
9
|
+
// Export TV-specific adapters
|
|
10
|
+
export { default as TizenAdapter } from './adapters/TizenAdapter';
|
|
11
|
+
|
|
12
|
+
// Export TV utilities
|
|
13
|
+
export const TVUtils = {
|
|
14
|
+
/**
|
|
15
|
+
* Check if running on Samsung Tizen TV
|
|
16
|
+
*/
|
|
17
|
+
isTizen: () => {
|
|
18
|
+
return typeof window !== 'undefined' && window.tizen !== undefined;
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Check if running on LG webOS TV
|
|
23
|
+
*/
|
|
24
|
+
isWebOS: () => {
|
|
25
|
+
return typeof window !== 'undefined' && window.webOS !== undefined;
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get TV platform info
|
|
30
|
+
*/
|
|
31
|
+
getPlatformInfo: () => {
|
|
32
|
+
if (TVUtils.isTizen()) {
|
|
33
|
+
return {
|
|
34
|
+
platform: 'tizen',
|
|
35
|
+
version: window.tizen?.systeminfo?.getCapability('http://tizen.org/feature/platform.version')
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (TVUtils.isWebOS()) {
|
|
39
|
+
return {
|
|
40
|
+
platform: 'webos',
|
|
41
|
+
version: window.webOS?.platform?.tv?.version
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
platform: 'unknown',
|
|
46
|
+
version: null
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Register TV remote key handlers
|
|
52
|
+
*/
|
|
53
|
+
registerRemoteKeys: () => {
|
|
54
|
+
if (TVUtils.isTizen()) {
|
|
55
|
+
// Register Tizen remote control keys
|
|
56
|
+
if (window.tizen && window.tizen.tvinputdevice) {
|
|
57
|
+
const keys = [
|
|
58
|
+
'MediaPlayPause',
|
|
59
|
+
'MediaPlay',
|
|
60
|
+
'MediaPause',
|
|
61
|
+
'MediaStop',
|
|
62
|
+
'MediaFastForward',
|
|
63
|
+
'MediaRewind'
|
|
64
|
+
];
|
|
65
|
+
keys.forEach(key => {
|
|
66
|
+
try {
|
|
67
|
+
window.tizen.tvinputdevice.registerKey(key);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
console.warn(`Failed to register key: ${key}`);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// webOS handles keys automatically
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Export version
|
|
79
|
+
export const VERSION = '1.0.0';
|
|
80
|
+
|
|
81
|
+
// Export platform identifier
|
|
82
|
+
export const PLATFORM = 'tv';
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# iOS Build Instructions - Fixing Architecture Issues
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
The error "UnifiedVideoPlayer.swiftmodule is not built for arm64" occurs when the framework isn't compiled for the correct architecture.
|
|
5
|
+
|
|
6
|
+
## Solution
|
|
7
|
+
|
|
8
|
+
### Option 1: Using Swift Package Manager (Recommended)
|
|
9
|
+
|
|
10
|
+
1. In Xcode, go to **File > Add Package Dependencies**
|
|
11
|
+
2. Click "Add Local..." and navigate to `/packages/ios/`
|
|
12
|
+
3. Select the package and add it to your target
|
|
13
|
+
4. Swift Package Manager will automatically build for the correct architecture
|
|
14
|
+
|
|
15
|
+
### Option 2: Build Universal Framework
|
|
16
|
+
|
|
17
|
+
1. Open Terminal and navigate to `/packages/ios/`
|
|
18
|
+
2. Run the build script:
|
|
19
|
+
```bash
|
|
20
|
+
chmod +x build_framework.sh
|
|
21
|
+
./build_framework.sh
|
|
22
|
+
```
|
|
23
|
+
3. This will create a universal XCFramework in `Output/UnifiedVideoPlayer.xcframework`
|
|
24
|
+
4. Drag the `.xcframework` into your Xcode project
|
|
25
|
+
|
|
26
|
+
### Option 3: Direct Xcode Project Build
|
|
27
|
+
|
|
28
|
+
1. Open `/packages/ios/UnifiedVideoPlayer.xcodeproj` in Xcode
|
|
29
|
+
2. Select your target device/simulator from the scheme selector
|
|
30
|
+
3. Go to **Product > Build** (⌘B)
|
|
31
|
+
4. The framework will be built for the selected architecture
|
|
32
|
+
|
|
33
|
+
### Option 4: Manual Architecture Configuration
|
|
34
|
+
|
|
35
|
+
If you're still experiencing issues:
|
|
36
|
+
|
|
37
|
+
1. In your app's Xcode project, select your target
|
|
38
|
+
2. Go to **Build Settings**
|
|
39
|
+
3. Search for "Architectures"
|
|
40
|
+
4. Ensure these settings:
|
|
41
|
+
- **Architectures**: Standard Architectures (arm64, arm64e)
|
|
42
|
+
- **Valid Architectures**: arm64 arm64e x86_64
|
|
43
|
+
- **Build Active Architecture Only**:
|
|
44
|
+
- Debug: Yes (for faster builds)
|
|
45
|
+
- Release: No (for distribution)
|
|
46
|
+
- **Excluded Architectures**: Leave empty or remove any entries
|
|
47
|
+
|
|
48
|
+
### Architecture Reference
|
|
49
|
+
|
|
50
|
+
- **arm64**: Physical iOS devices (iPhone 6s and later)
|
|
51
|
+
- **arm64e**: Newer iOS devices (iPhone XS and later)
|
|
52
|
+
- **x86_64**: Intel-based Mac simulators
|
|
53
|
+
- **arm64** (simulator): Apple Silicon Mac simulators
|
|
54
|
+
|
|
55
|
+
### Troubleshooting
|
|
56
|
+
|
|
57
|
+
1. **Clean Build Folder**: Product > Clean Build Folder (⇧⌘K)
|
|
58
|
+
2. **Delete Derived Data**:
|
|
59
|
+
- Xcode > Settings > Locations
|
|
60
|
+
- Click arrow next to Derived Data path
|
|
61
|
+
- Delete the folder for your project
|
|
62
|
+
3. **Reset Package Caches**: File > Packages > Reset Package Caches
|
|
63
|
+
4. **Check Deployment Target**: Ensure iOS deployment target is 13.0 or higher
|
|
64
|
+
|
|
65
|
+
### Using in Your Project
|
|
66
|
+
|
|
67
|
+
```swift
|
|
68
|
+
import UnifiedVideoPlayer
|
|
69
|
+
|
|
70
|
+
struct ContentView: View {
|
|
71
|
+
var body: some View {
|
|
72
|
+
UnifiedVideoPlayerView(url: URL(string: "https://example.com/video.mp4")!)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Build from Command Line
|
|
78
|
+
|
|
79
|
+
For CI/CD or command-line builds:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Build for simulator
|
|
83
|
+
xcodebuild -scheme UnifiedVideoPlayer \
|
|
84
|
+
-sdk iphonesimulator \
|
|
85
|
+
-configuration Release \
|
|
86
|
+
-arch x86_64 \
|
|
87
|
+
-arch arm64 \
|
|
88
|
+
build
|
|
89
|
+
|
|
90
|
+
# Build for device
|
|
91
|
+
xcodebuild -scheme UnifiedVideoPlayer \
|
|
92
|
+
-sdk iphoneos \
|
|
93
|
+
-configuration Release \
|
|
94
|
+
-arch arm64 \
|
|
95
|
+
build
|
|
96
|
+
|
|
97
|
+
# Create XCFramework (universal)
|
|
98
|
+
xcodebuild -create-xcframework \
|
|
99
|
+
-framework path/to/device.framework \
|
|
100
|
+
-framework path/to/simulator.framework \
|
|
101
|
+
-output UnifiedVideoPlayer.xcframework
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Notes
|
|
105
|
+
|
|
106
|
+
- Always use `.xcframework` for distributing binary frameworks as it supports multiple architectures
|
|
107
|
+
- When archiving for App Store, ensure "Build Active Architecture Only" is set to NO
|
|
108
|
+
- For development, you can use "Build Active Architecture Only = YES" for faster builds
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Fix for "Library not loaded" Runtime Error
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
The error `Library not loaded: @rpath/UnifiedVideoPlayer.framework/UnifiedVideoPlayer` occurs when the framework is linked but not embedded in the app bundle.
|
|
5
|
+
|
|
6
|
+
## Solution Steps
|
|
7
|
+
|
|
8
|
+
### Step 1: Check Framework Embedding in Xcode
|
|
9
|
+
|
|
10
|
+
1. **Select your app target** in Xcode
|
|
11
|
+
2. Go to **General** tab
|
|
12
|
+
3. Scroll to **Frameworks, Libraries, and Embedded Content**
|
|
13
|
+
4. Find `UnifiedVideoPlayer` in the list
|
|
14
|
+
5. **IMPORTANT**: Change the "Embed" setting from "Do Not Embed" to **"Embed & Sign"**
|
|
15
|
+
|
|
16
|
+

|
|
17
|
+
|
|
18
|
+
### Step 2: Verify Build Phases
|
|
19
|
+
|
|
20
|
+
1. Select your app target
|
|
21
|
+
2. Go to **Build Phases** tab
|
|
22
|
+
3. Expand **Embed Frameworks** section (create if it doesn't exist)
|
|
23
|
+
4. Ensure `UnifiedVideoPlayer.framework` is listed
|
|
24
|
+
5. Check "Code Sign On Copy" checkbox
|
|
25
|
+
|
|
26
|
+
If the framework is missing from Embed Frameworks:
|
|
27
|
+
- Click the "+" button
|
|
28
|
+
- Add `UnifiedVideoPlayer.framework`
|
|
29
|
+
- Enable "Code Sign On Copy"
|
|
30
|
+
|
|
31
|
+
### Step 3: Clean and Rebuild
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# In Xcode
|
|
35
|
+
1. Product > Clean Build Folder (⇧⌘K)
|
|
36
|
+
2. Close Xcode
|
|
37
|
+
3. Delete DerivedData:
|
|
38
|
+
rm -rf ~/Library/Developer/Xcode/DerivedData/Flicknexs-*
|
|
39
|
+
4. Reopen Xcode
|
|
40
|
+
5. Product > Build (⌘B)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Step 4: Alternative - Manual Framework Setup
|
|
44
|
+
|
|
45
|
+
If Swift Package Manager continues to have issues, use the pre-built framework:
|
|
46
|
+
|
|
47
|
+
1. Remove the Swift Package:
|
|
48
|
+
- Select your project in navigator
|
|
49
|
+
- Select project (not target) in editor
|
|
50
|
+
- Go to "Package Dependencies" tab
|
|
51
|
+
- Select UnifiedVideoPlayer and click "-" to remove
|
|
52
|
+
|
|
53
|
+
2. Build the framework manually:
|
|
54
|
+
```bash
|
|
55
|
+
cd packages/ios
|
|
56
|
+
xcodebuild -scheme UnifiedVideoPlayer \
|
|
57
|
+
-configuration Release \
|
|
58
|
+
-derivedDataPath build \
|
|
59
|
+
-arch arm64 \
|
|
60
|
+
-sdk iphoneos \
|
|
61
|
+
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
3. Add the built framework:
|
|
65
|
+
- Drag `build/Build/Products/Release-iphoneos/UnifiedVideoPlayer.framework` to your project
|
|
66
|
+
- Select "Copy items if needed"
|
|
67
|
+
- Add to your app target
|
|
68
|
+
- **Set to "Embed & Sign"**
|
|
69
|
+
|
|
70
|
+
### Step 5: Verify Runtime Search Paths
|
|
71
|
+
|
|
72
|
+
1. Select your app target
|
|
73
|
+
2. Go to **Build Settings**
|
|
74
|
+
3. Search for "Runpath Search Paths"
|
|
75
|
+
4. Ensure it includes:
|
|
76
|
+
```
|
|
77
|
+
@executable_path/Frameworks
|
|
78
|
+
@loader_path/Frameworks
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Step 6: Check Code Signing
|
|
82
|
+
|
|
83
|
+
1. Select your app target
|
|
84
|
+
2. Go to **Signing & Capabilities**
|
|
85
|
+
3. Ensure "Automatically manage signing" is enabled
|
|
86
|
+
4. Or manually select your development team and provisioning profile
|
|
87
|
+
|
|
88
|
+
## Quick Checklist
|
|
89
|
+
|
|
90
|
+
- [ ] Framework is set to "Embed & Sign" in General > Frameworks, Libraries, and Embedded Content
|
|
91
|
+
- [ ] Framework appears in Build Phases > Embed Frameworks
|
|
92
|
+
- [ ] "Code Sign On Copy" is checked
|
|
93
|
+
- [ ] Runtime Search Paths includes @executable_path/Frameworks
|
|
94
|
+
- [ ] Clean Build Folder performed
|
|
95
|
+
- [ ] DerivedData deleted
|
|
96
|
+
- [ ] Code signing configured properly
|
|
97
|
+
|
|
98
|
+
## If Still Not Working
|
|
99
|
+
|
|
100
|
+
Try creating a simple test to verify the framework loads:
|
|
101
|
+
|
|
102
|
+
```swift
|
|
103
|
+
// In AppDelegate.swift or App.swift
|
|
104
|
+
import UnifiedVideoPlayer
|
|
105
|
+
|
|
106
|
+
func testFrameworkLoad() {
|
|
107
|
+
print("UnifiedVideoPlayer loaded successfully")
|
|
108
|
+
let player = UnifiedVideoPlayer()
|
|
109
|
+
print("Player created: \(player)")
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Call this in application:didFinishLaunchingWithOptions
|
|
113
|
+
testFrameworkLoad()
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Common Causes
|
|
117
|
+
|
|
118
|
+
1. **Wrong Embed Setting**: Most common - framework not set to "Embed & Sign"
|
|
119
|
+
2. **Missing from Embed Frameworks**: Framework linked but not copied to app bundle
|
|
120
|
+
3. **Architecture Mismatch**: Framework not built for device architecture
|
|
121
|
+
4. **Code Signing Issues**: Framework not properly signed
|
|
122
|
+
5. **Corrupt DerivedData**: Old build artifacts causing issues
|
|
123
|
+
|
|
124
|
+
## Terminal Commands for Debugging
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# Check if framework is in the app bundle (after building)
|
|
128
|
+
ls -la ~/Library/Developer/Xcode/DerivedData/Flicknexs-*/Build/Products/Debug-iphoneos/Flicknexs.app/Frameworks/
|
|
129
|
+
|
|
130
|
+
# Check framework architectures
|
|
131
|
+
lipo -info ~/Library/Developer/Xcode/DerivedData/Flicknexs-*/Build/Products/Debug-iphoneos/PackageFrameworks/UnifiedVideoPlayer.framework/UnifiedVideoPlayer
|
|
132
|
+
|
|
133
|
+
# Verify code signing
|
|
134
|
+
codesign -dv ~/Library/Developer/Xcode/DerivedData/Flicknexs-*/Build/Products/Debug-iphoneos/Flicknexs.app/Frameworks/UnifiedVideoPlayer.framework
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Contact Support
|
|
138
|
+
|
|
139
|
+
If none of these solutions work, the issue might be specific to your project configuration. Check:
|
|
140
|
+
- Your minimum deployment target (should be iOS 13.0+)
|
|
141
|
+
- Any custom build scripts that might interfere
|
|
142
|
+
- Other third-party frameworks that might conflict
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Getting Started with UnifiedVideoPlayer (iOS)
|
|
2
|
+
|
|
3
|
+
This guide shows how to install, configure dynamic theme color, and play a video using the iOS SDK under `packages/ios`.
|
|
4
|
+
|
|
5
|
+
## 1) Install the SDK
|
|
6
|
+
|
|
7
|
+
- CocoaPods (public repo suggested)
|
|
8
|
+
- Ensure `UnifiedVideoPlayer.podspec` points to your Git repo + tag.
|
|
9
|
+
- Podfile:
|
|
10
|
+
```ruby
|
|
11
|
+
platform :ios, '13.0'
|
|
12
|
+
use_frameworks!
|
|
13
|
+
|
|
14
|
+
target 'YourApp' do
|
|
15
|
+
pod 'UnifiedVideoPlayer', :git => 'https://github.com/yourcompany/unified-video-ios.git', :tag => '1.0.0'
|
|
16
|
+
end
|
|
17
|
+
```
|
|
18
|
+
- Run `pod install`
|
|
19
|
+
|
|
20
|
+
- Swift Package Manager
|
|
21
|
+
- In Xcode: File > Add Packages… > Add Local… and choose `packages/ios` or use the hosted repo URL.
|
|
22
|
+
|
|
23
|
+
## 2) Enable Capabilities (recommended)
|
|
24
|
+
- Background Modes: enable "Audio, AirPlay, and Picture in Picture"
|
|
25
|
+
- If your DRM or CDN endpoints require it, configure ATS exceptions in your app's Info.plist.
|
|
26
|
+
|
|
27
|
+
## 3) Configure Dynamic Color and Video URL
|
|
28
|
+
|
|
29
|
+
Dynamic color is set via `PlayerConfiguration.themeColorHex` (supports `#RRGGBB` or `#AARRGGBB`).
|
|
30
|
+
The video URL is set on `MediaSource(url:)` or by using the SwiftUI helper with a URL.
|
|
31
|
+
|
|
32
|
+
### UIKit example
|
|
33
|
+
```swift
|
|
34
|
+
import UnifiedVideoPlayer
|
|
35
|
+
|
|
36
|
+
let player = UnifiedVideoPlayer()
|
|
37
|
+
let config = PlayerConfiguration()
|
|
38
|
+
config.autoPlay = true
|
|
39
|
+
config.themeColorHex = "#FF5722" // dynamic brand color
|
|
40
|
+
|
|
41
|
+
player.initialize(container: playerContainerView, configuration: config)
|
|
42
|
+
|
|
43
|
+
let source = MediaSource(url: "https://example.com/stream.m3u8")
|
|
44
|
+
source.metadata = ["title": "My Demo Video"]
|
|
45
|
+
// Optional DRM
|
|
46
|
+
// let drm = DRMConfiguration(type: "fairplay", licenseUrl: "https://license.example.com/fps")
|
|
47
|
+
// drm.certificateUrl = "https://license.example.com/cert"
|
|
48
|
+
// drm.headers = ["X-Tenant-ID": "default"]
|
|
49
|
+
// source.drm = drm
|
|
50
|
+
|
|
51
|
+
player.load(source: source)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### SwiftUI example
|
|
55
|
+
```swift
|
|
56
|
+
import UnifiedVideoPlayer
|
|
57
|
+
|
|
58
|
+
let cfg = PlayerConfiguration()
|
|
59
|
+
cfg.autoPlay = true
|
|
60
|
+
cfg.themeColorHex = "#4CAF50"
|
|
61
|
+
|
|
62
|
+
// In your View body:
|
|
63
|
+
CustomUnifiedVideoPlayer(
|
|
64
|
+
url: "https://example.com/stream.m3u8",
|
|
65
|
+
configuration: cfg
|
|
66
|
+
)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
What does the theme color affect?
|
|
70
|
+
- UIKit: tint color of the default play/pause control the SDK overlays on top of the video.
|
|
71
|
+
- SwiftUI: tint/accent color for control icons and sliders.
|
|
72
|
+
|
|
73
|
+
## 4) Subtitles, AirPlay, PiP (optional)
|
|
74
|
+
- Subtitles: provide `source.subtitles = [SubtitleTrack(...)]` (full parsing depends on your format).
|
|
75
|
+
- AirPlay: use `let airPlayView = player.makeAirPlayPickerView()` and add to your UI.
|
|
76
|
+
- PiP: call `player.startPictureInPicture()` and `player.stopPictureInPicture()`.
|
|
77
|
+
|
|
78
|
+
## 5) FairPlay DRM (optional)
|
|
79
|
+
```swift
|
|
80
|
+
let drm = DRMConfiguration(type: "fairplay", licenseUrl: "https://license.example.com/fps")
|
|
81
|
+
drm.certificateUrl = "https://license.example.com/cert"
|
|
82
|
+
drm.headers = ["X-Tenant-ID": "default"]
|
|
83
|
+
source.drm = drm
|
|
84
|
+
player.load(source: source)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 6) Lock Screen Controls & Background Audio
|
|
88
|
+
- Enabled automatically; SDK configures AVAudioSession and Remote Command Center.
|
|
89
|
+
- Provide `source.metadata["title"]` to display track title on the lock screen.
|
|
90
|
+
|
|
91
|
+
## 7) Troubleshooting
|
|
92
|
+
- If you see "not built for arm64": follow `BUILD_INSTRUCTIONS.md` in `packages/ios`.
|
|
93
|
+
- If you get "Library not loaded": follow `FIX_EMBED_ISSUE.md` in `packages/ios`.
|
|
94
|
+
|
|
95
|
+
## 8) Publishing to CocoaPods
|
|
96
|
+
- Ensure `UnifiedVideoPlayer.podspec`:
|
|
97
|
+
- `s.source` points to a public Git repo URL
|
|
98
|
+
- `s.version` matches your Git tag
|
|
99
|
+
- Run `pod lib lint` and `pod trunk push` as appropriate.
|
|
100
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// swift-tools-version:5.5
|
|
2
|
+
import PackageDescription
|
|
3
|
+
|
|
4
|
+
let package = Package(
|
|
5
|
+
name: "UnifiedVideoPlayer",
|
|
6
|
+
platforms: [
|
|
7
|
+
.iOS(.v13),
|
|
8
|
+
.tvOS(.v13),
|
|
9
|
+
.macCatalyst(.v13)
|
|
10
|
+
],
|
|
11
|
+
products: [
|
|
12
|
+
.library(
|
|
13
|
+
name: "UnifiedVideoPlayer",
|
|
14
|
+
type: .dynamic,
|
|
15
|
+
targets: ["UnifiedVideoPlayer"]
|
|
16
|
+
)
|
|
17
|
+
],
|
|
18
|
+
dependencies: [],
|
|
19
|
+
targets: [
|
|
20
|
+
.target(
|
|
21
|
+
name: "UnifiedVideoPlayer",
|
|
22
|
+
dependencies: [],
|
|
23
|
+
path: "Sources/UnifiedVideoPlayer",
|
|
24
|
+
swiftSettings: [
|
|
25
|
+
.unsafeFlags(["-enable-library-evolution"])
|
|
26
|
+
]
|
|
27
|
+
),
|
|
28
|
+
.testTarget(
|
|
29
|
+
name: "UnifiedVideoPlayerTests",
|
|
30
|
+
dependencies: ["UnifiedVideoPlayer"],
|
|
31
|
+
path: "Tests/UnifiedVideoPlayerTests"
|
|
32
|
+
)
|
|
33
|
+
],
|
|
34
|
+
swiftLanguageVersions: [.v5]
|
|
35
|
+
)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# UnifiedVideoPlayer (iOS SDK)
|
|
2
|
+
|
|
3
|
+
A unified iOS video player SDK with:
|
|
4
|
+
- HLS playback (AVPlayer)
|
|
5
|
+
- FairPlay DRM (SPC/CKC)
|
|
6
|
+
- Subtitles/audio track selection (AVMediaSelection)
|
|
7
|
+
- Picture-in-Picture (AVPictureInPictureController)
|
|
8
|
+
- AirPlay (AVRoutePickerView)
|
|
9
|
+
- Remote Command Center + Now Playing (lock screen controls)
|
|
10
|
+
- Background audio (AVAudioSession)
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
### CocoaPods
|
|
15
|
+
1) Ensure this SDK is in a public Git repository. Update the podspec `s.source` to point at that repo and tag.
|
|
16
|
+
2) In your Podfile:
|
|
17
|
+
```ruby
|
|
18
|
+
platform :ios, '13.0'
|
|
19
|
+
use_frameworks!
|
|
20
|
+
|
|
21
|
+
target 'YourApp' do
|
|
22
|
+
pod 'UnifiedVideoPlayer', :git => 'https://github.com/yourcompany/unified-video-ios.git', :tag => '1.0.0'
|
|
23
|
+
end
|
|
24
|
+
```
|
|
25
|
+
3) Run:
|
|
26
|
+
```bash
|
|
27
|
+
pod install
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Swift Package Manager
|
|
31
|
+
Add the package at the repository URL or use Add Local Package pointing to `packages/ios`.
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
```swift
|
|
35
|
+
import UnifiedVideoPlayer
|
|
36
|
+
|
|
37
|
+
let containerView = UIView(frame: .zero)
|
|
38
|
+
let player = UnifiedVideoPlayer()
|
|
39
|
+
let config = PlayerConfiguration()
|
|
40
|
+
config.autoPlay = true
|
|
41
|
+
|
|
42
|
+
player.initialize(container: containerView, configuration: config)
|
|
43
|
+
|
|
44
|
+
let source = MediaSource(url: "https://example.com/stream.m3u8")
|
|
45
|
+
source.metadata = ["title": "Demo Stream"]
|
|
46
|
+
player.onReady = { print("ready") }
|
|
47
|
+
player.onQualityChange = { br in print("bitrate: \(br)") }
|
|
48
|
+
|
|
49
|
+
player.load(source: source)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### FairPlay DRM
|
|
53
|
+
```swift
|
|
54
|
+
let drm = DRMConfiguration(type: "fairplay", licenseUrl: "https://license.example.com/fps")
|
|
55
|
+
drm.certificateUrl = "https://license.example.com/cert"
|
|
56
|
+
drm.headers = ["X-Tenant-ID": "default"]
|
|
57
|
+
|
|
58
|
+
let source = MediaSource(url: "https://cdn.example.com/protected/playlist.m3u8")
|
|
59
|
+
source.drm = drm
|
|
60
|
+
player.load(source: source)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Tracks
|
|
64
|
+
```swift
|
|
65
|
+
let audios = player.audioTracks() // [String]
|
|
66
|
+
let subs = player.subtitleTracks() // [String]
|
|
67
|
+
player.selectAudioTrack(index: 0)
|
|
68
|
+
player.selectSubtitleTrack(index: -1) // off
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### PiP & AirPlay
|
|
72
|
+
```swift
|
|
73
|
+
player.startPictureInPicture()
|
|
74
|
+
player.stopPictureInPicture()
|
|
75
|
+
let airPlay = player.makeAirPlayPickerView()
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Capabilities
|
|
79
|
+
- Enable Background Modes > Audio
|
|
80
|
+
- For DRM endpoints, configure ATS exceptions if necessary.
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
MIT
|
|
84
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import AVFoundation
|
|
3
|
+
|
|
4
|
+
final class AnalyticsEmitter {
|
|
5
|
+
private var observer: NSObjectProtocol?
|
|
6
|
+
var onBitrate: ((Double) -> Void)?
|
|
7
|
+
|
|
8
|
+
func startObserving(item: AVPlayerItem) {
|
|
9
|
+
stopObserving()
|
|
10
|
+
observer = NotificationCenter.default.addObserver(
|
|
11
|
+
forName: .AVPlayerItemNewAccessLogEntry,
|
|
12
|
+
object: item,
|
|
13
|
+
queue: .main
|
|
14
|
+
) { [weak self] _ in
|
|
15
|
+
guard let event = item.accessLog()?.events.last else { return }
|
|
16
|
+
let br = event.indicatedBitrate > 0 ? event.indicatedBitrate : event.observedBitrate
|
|
17
|
+
self?.onBitrate?(br)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
func stopObserving() {
|
|
22
|
+
if let obs = observer { NotificationCenter.default.removeObserver(obs) }
|
|
23
|
+
observer = nil
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import AVFoundation
|
|
3
|
+
|
|
4
|
+
final class FairPlayDRMManager: NSObject, AVAssetResourceLoaderDelegate {
|
|
5
|
+
private let drm: DRMConfiguration
|
|
6
|
+
private var certificateData: Data?
|
|
7
|
+
private let queue = DispatchQueue(label: "uvp.fairplay")
|
|
8
|
+
|
|
9
|
+
init(drm: DRMConfiguration) {
|
|
10
|
+
self.drm = drm
|
|
11
|
+
super.init()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// MARK: AVAssetResourceLoaderDelegate
|
|
15
|
+
func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
|
|
16
|
+
queue.async { [weak self] in
|
|
17
|
+
self?.handle(loadingRequest: loadingRequest)
|
|
18
|
+
}
|
|
19
|
+
return true
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
func resourceLoader(_ resourceLoader: AVAssetResourceLoader, didCancel loadingRequest: AVAssetResourceLoadingRequest) {
|
|
23
|
+
// No-op
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private func handle(loadingRequest: AVAssetResourceLoadingRequest) {
|
|
27
|
+
guard let requestURL = loadingRequest.request.url, requestURL.scheme == "skd" else {
|
|
28
|
+
loadingRequest.finishLoading(with: NSError(domain: "UnifiedVideoPlayer.FairPlay", code: -10, userInfo: [NSLocalizedDescriptionKey: "Invalid or missing SKD URL"]))
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
do {
|
|
33
|
+
let cert = try fetchCertificate()
|
|
34
|
+
let contentIdData: Data
|
|
35
|
+
if let cid = requestURL.host?.data(using: .utf8) ?? requestURL.absoluteString.data(using: .utf8) {
|
|
36
|
+
contentIdData = cid
|
|
37
|
+
} else {
|
|
38
|
+
throw NSError(domain: "UnifiedVideoPlayer.FairPlay", code: -11, userInfo: [NSLocalizedDescriptionKey: "Unable to derive content ID"])
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let spcData = try loadingRequest.streamingContentKeyRequestData(forApp: cert, contentIdentifier: contentIdData, options: nil)
|
|
42
|
+
requestCKC(spc: spcData) { [weak loadingRequest] result in
|
|
43
|
+
guard let lr = loadingRequest else { return }
|
|
44
|
+
switch result {
|
|
45
|
+
case .success(let ckc):
|
|
46
|
+
lr.dataRequest?.respond(with: ckc)
|
|
47
|
+
lr.finishLoading()
|
|
48
|
+
case .failure(let error):
|
|
49
|
+
lr.finishLoading(with: error)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
53
|
+
loadingRequest.finishLoading(with: error)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private func fetchCertificate() throws -> Data {
|
|
58
|
+
if let cached = certificateData { return cached }
|
|
59
|
+
guard let certURLString = drm.certificateUrl, let certURL = URL(string: certURLString) else {
|
|
60
|
+
throw NSError(domain: "UnifiedVideoPlayer.FairPlay", code: -12, userInfo: [NSLocalizedDescriptionKey: "certificateUrl is missing"])
|
|
61
|
+
}
|
|
62
|
+
var request = URLRequest(url: certURL)
|
|
63
|
+
if let headers = drm.headers { headers.forEach { request.addValue($0.value, forHTTPHeaderField: $0.key) } }
|
|
64
|
+
let sem = DispatchSemaphore(value: 0)
|
|
65
|
+
var resultData: Data?
|
|
66
|
+
var resultError: Error?
|
|
67
|
+
URLSession.shared.dataTask(with: request) { data, _, error in
|
|
68
|
+
resultData = data
|
|
69
|
+
resultError = error
|
|
70
|
+
sem.signal()
|
|
71
|
+
}.resume()
|
|
72
|
+
_ = sem.wait(timeout: .now() + 30)
|
|
73
|
+
if let e = resultError { throw e }
|
|
74
|
+
guard let data = resultData else {
|
|
75
|
+
throw NSError(domain: "UnifiedVideoPlayer.FairPlay", code: -13, userInfo: [NSLocalizedDescriptionKey: "Empty certificate response"])
|
|
76
|
+
}
|
|
77
|
+
certificateData = data
|
|
78
|
+
return data
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private func requestCKC(spc: Data, completion: @escaping (Result<Data, Error>) -> Void) {
|
|
82
|
+
guard let licenseURL = URL(string: drm.licenseUrl) else {
|
|
83
|
+
completion(.failure(NSError(domain: "UnifiedVideoPlayer.FairPlay", code: -14, userInfo: [NSLocalizedDescriptionKey: "Invalid licenseUrl"])) )
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
var request = URLRequest(url: licenseURL)
|
|
87
|
+
request.httpMethod = "POST"
|
|
88
|
+
request.addValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
|
|
89
|
+
if let headers = drm.headers { headers.forEach { request.addValue($0.value, forHTTPHeaderField: $0.key) } }
|
|
90
|
+
request.httpBody = spc
|
|
91
|
+
|
|
92
|
+
URLSession.shared.dataTask(with: request) { data, _, error in
|
|
93
|
+
if let e = error { completion(.failure(e)); return }
|
|
94
|
+
guard let data = data else {
|
|
95
|
+
completion(.failure(NSError(domain: "UnifiedVideoPlayer.FairPlay", code: -15, userInfo: [NSLocalizedDescriptionKey: "Empty CKC response"])) )
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
completion(.success(data))
|
|
99
|
+
}.resume()
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|