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.
Files changed (129) hide show
  1. package/.github/workflows/ci.yml +253 -0
  2. package/ANDROID_TV_IMPLEMENTATION.md +313 -0
  3. package/COMPLETION_STATUS.md +165 -0
  4. package/CONTRIBUTING.md +376 -0
  5. package/FINAL_STATUS_REPORT.md +170 -0
  6. package/FRAMEWORK_REVIEW.md +247 -0
  7. package/IMPROVEMENTS_SUMMARY.md +168 -0
  8. package/LICENSE +21 -0
  9. package/NATIVE_APP_INTEGRATION_GUIDE.md +903 -0
  10. package/PAYWALL_RENTAL_FLOW.md +499 -0
  11. package/PLATFORM_SETUP_GUIDE.md +1636 -0
  12. package/README.md +315 -0
  13. package/RUN_LOCALLY.md +151 -0
  14. package/apps/demo/cast-sender-min.html +173 -0
  15. package/apps/demo/custom-player.html +883 -0
  16. package/apps/demo/demo.html +990 -0
  17. package/apps/demo/enhanced-player.html +3556 -0
  18. package/apps/demo/index.html +159 -0
  19. package/apps/rental-api/.env.example +24 -0
  20. package/apps/rental-api/README.md +23 -0
  21. package/apps/rental-api/migrations/001_init.sql +35 -0
  22. package/apps/rental-api/migrations/002_videos.sql +10 -0
  23. package/apps/rental-api/migrations/003_add_gateway_subref.sql +4 -0
  24. package/apps/rental-api/migrations/004_update_gateways.sql +4 -0
  25. package/apps/rental-api/migrations/005_seed_demo_video.sql +5 -0
  26. package/apps/rental-api/package-lock.json +2045 -0
  27. package/apps/rental-api/package.json +33 -0
  28. package/apps/rental-api/scripts/run-migration.js +42 -0
  29. package/apps/rental-api/scripts/update-video-currency.js +21 -0
  30. package/apps/rental-api/scripts/update-video-price.js +19 -0
  31. package/apps/rental-api/src/config.ts +14 -0
  32. package/apps/rental-api/src/db.ts +10 -0
  33. package/apps/rental-api/src/routes/cashfree.ts +167 -0
  34. package/apps/rental-api/src/routes/pesapal.ts +92 -0
  35. package/apps/rental-api/src/routes/rentals.ts +242 -0
  36. package/apps/rental-api/src/routes/webhooks.ts +73 -0
  37. package/apps/rental-api/src/server.ts +41 -0
  38. package/apps/rental-api/src/services/entitlements.ts +45 -0
  39. package/apps/rental-api/src/services/payments.ts +22 -0
  40. package/apps/rental-api/tsconfig.json +17 -0
  41. package/check-urls.ps1 +74 -0
  42. package/comparison-report.md +181 -0
  43. package/docs/PAYWALL.md +95 -0
  44. package/docs/PLAYER_UI_VISIBILITY.md +431 -0
  45. package/docs/README.md +7 -0
  46. package/docs/SYSTEM_ARCHITECTURE.md +612 -0
  47. package/docs/VDOCIPHER_CLONE_REQUIREMENTS.md +403 -0
  48. package/examples/android/JavaSampleApp/MainActivity.java +641 -0
  49. package/examples/android/JavaSampleApp/activity_main.xml +226 -0
  50. package/examples/android/SampleApp/MainActivity.kt +430 -0
  51. package/examples/ios/SampleApp/ViewController.swift +337 -0
  52. package/examples/ios/SwiftUISampleApp/ContentView.swift +304 -0
  53. package/iOS_IMPLEMENTATION_OPTIONS.md +470 -0
  54. package/ios/UnifiedVideoPlayer/UnifiedVideoPlayer.podspec +33 -0
  55. package/jest.config.js +33 -0
  56. package/jitpack.yml +5 -0
  57. package/lerna.json +35 -0
  58. package/package.json +69 -0
  59. package/packages/PLATFORM_STATUS.md +163 -0
  60. package/packages/android/build.gradle +135 -0
  61. package/packages/android/src/main/AndroidManifest.xml +36 -0
  62. package/packages/android/src/main/java/com/unifiedvideo/player/PlayerConfiguration.java +221 -0
  63. package/packages/android/src/main/java/com/unifiedvideo/player/UnifiedVideoPlayer.java +1037 -0
  64. package/packages/android/src/main/java/com/unifiedvideo/player/UnifiedVideoPlayer.kt +707 -0
  65. package/packages/android/src/main/java/com/unifiedvideo/player/analytics/AnalyticsProvider.java +9 -0
  66. package/packages/android/src/main/java/com/unifiedvideo/player/cast/CastManager.java +141 -0
  67. package/packages/android/src/main/java/com/unifiedvideo/player/cast/CastOptionsProvider.java +29 -0
  68. package/packages/android/src/main/java/com/unifiedvideo/player/overlay/WatermarkOverlayView.java +88 -0
  69. package/packages/android/src/main/java/com/unifiedvideo/player/pip/PipActionReceiver.java +33 -0
  70. package/packages/android/src/main/java/com/unifiedvideo/player/services/PlaybackService.java +110 -0
  71. package/packages/android/src/main/java/com/unifiedvideo/player/services/PlayerHolder.java +19 -0
  72. package/packages/core/package.json +34 -0
  73. package/packages/core/src/BasePlayer.ts +250 -0
  74. package/packages/core/src/VideoPlayer.ts +237 -0
  75. package/packages/core/src/VideoPlayerFactory.ts +145 -0
  76. package/packages/core/src/index.ts +20 -0
  77. package/packages/core/src/interfaces/IVideoPlayer.ts +184 -0
  78. package/packages/core/src/interfaces.ts +240 -0
  79. package/packages/core/src/utils/EventEmitter.ts +66 -0
  80. package/packages/core/src/utils/PlatformDetector.ts +300 -0
  81. package/packages/core/tsconfig.json +20 -0
  82. package/packages/enact/package.json +51 -0
  83. package/packages/enact/src/VideoPlayer.js +365 -0
  84. package/packages/enact/src/adapters/TizenAdapter.js +354 -0
  85. package/packages/enact/src/index.js +82 -0
  86. package/packages/ios/BUILD_INSTRUCTIONS.md +108 -0
  87. package/packages/ios/FIX_EMBED_ISSUE.md +142 -0
  88. package/packages/ios/GETTING_STARTED.md +100 -0
  89. package/packages/ios/Package.swift +35 -0
  90. package/packages/ios/README.md +84 -0
  91. package/packages/ios/Sources/UnifiedVideoPlayer/Analytics/AnalyticsEmitter.swift +26 -0
  92. package/packages/ios/Sources/UnifiedVideoPlayer/DRM/FairPlayDRMManager.swift +102 -0
  93. package/packages/ios/Sources/UnifiedVideoPlayer/Info.plist +24 -0
  94. package/packages/ios/Sources/UnifiedVideoPlayer/Remote/RemoteCommandCenter.swift +109 -0
  95. package/packages/ios/Sources/UnifiedVideoPlayer/UnifiedVideoPlayer.swift +811 -0
  96. package/packages/ios/Sources/UnifiedVideoPlayer/UnifiedVideoPlayerView.swift +640 -0
  97. package/packages/ios/Sources/UnifiedVideoPlayer/Utilities/Color+Hex.swift +36 -0
  98. package/packages/ios/UnifiedVideoPlayer.podspec +27 -0
  99. package/packages/ios/UnifiedVideoPlayer.xcodeproj/project.pbxproj +385 -0
  100. package/packages/ios/build_framework.sh +55 -0
  101. package/packages/react-native/android/src/main/java/com/unifiedvideo/UnifiedVideoPlayerModule.kt +482 -0
  102. package/packages/react-native/ios/UnifiedVideoPlayer.swift +436 -0
  103. package/packages/react-native/package.json +51 -0
  104. package/packages/react-native/src/ReactNativePlayer.tsx +423 -0
  105. package/packages/react-native/src/VideoPlayer.tsx +224 -0
  106. package/packages/react-native/src/index.ts +28 -0
  107. package/packages/react-native/src/utils/EventEmitter.ts +66 -0
  108. package/packages/react-native/tsconfig.json +31 -0
  109. package/packages/roku/components/UnifiedVideoPlayer.brs +400 -0
  110. package/packages/roku/package.json +44 -0
  111. package/packages/roku/source/VideoPlayer.brs +231 -0
  112. package/packages/roku/source/main.brs +28 -0
  113. package/packages/web/GETTING_STARTED.md +292 -0
  114. package/packages/web/jest.config.js +28 -0
  115. package/packages/web/jest.setup.ts +110 -0
  116. package/packages/web/package.json +50 -0
  117. package/packages/web/src/SecureVideoPlayer.ts +1164 -0
  118. package/packages/web/src/WebPlayer.ts +3110 -0
  119. package/packages/web/src/__tests__/WebPlayer.test.ts +314 -0
  120. package/packages/web/src/index.ts +14 -0
  121. package/packages/web/src/paywall/PaywallController.ts +215 -0
  122. package/packages/web/src/react/WebPlayerView.tsx +177 -0
  123. package/packages/web/tsconfig.json +23 -0
  124. package/packages/web/webpack.config.js +45 -0
  125. package/server.js +131 -0
  126. package/server.py +84 -0
  127. package/test-urls.ps1 +97 -0
  128. package/test-video-urls.ps1 +87 -0
  129. package/tsconfig.json +39 -0
@@ -0,0 +1,1636 @@
1
+ # ๐Ÿ“š Unified Video Framework - Complete Platform Setup Guide
2
+
3
+ This guide provides step-by-step instructions for setting up the Unified Video Framework on all supported platforms.
4
+
5
+ ## Table of Contents
6
+ 1. [Web Platform](#1-web-platform-setup)
7
+ 2. [iOS Platform](#2-ios-platform-setup)
8
+ 3. [Android Platform](#3-android-platform-setup)
9
+ 4. [Samsung Tizen TV](#4-samsung-tizen-tv-setup)
10
+ 5. [LG webOS TV](#5-lg-webos-tv-setup)
11
+ 6. [Roku Platform](#6-roku-platform-setup)
12
+ 7. [Android TV](#7-android-tv-setup)
13
+ 8. [React Native (Mobile)](#8-react-native-mobile-setup)
14
+
15
+ ---
16
+
17
+ ## 1. Web Platform Setup
18
+
19
+ ### Prerequisites
20
+ - Node.js 16+ and npm 8+
21
+ - Modern web browser (Chrome, Firefox, Safari, Edge)
22
+
23
+ ### Step-by-Step Installation
24
+
25
+ #### Step 1: Install Dependencies
26
+ ```bash
27
+ npm install @unified-video/core @unified-video/web
28
+ ```
29
+
30
+ #### Step 2: Create HTML Structure
31
+ ```html
32
+ <!DOCTYPE html>
33
+ <html>
34
+ <head>
35
+ <title>Video Player</title>
36
+ <style>
37
+ #video-container {
38
+ width: 100%;
39
+ max-width: 800px;
40
+ margin: 0 auto;
41
+ }
42
+ </style>
43
+ </head>
44
+ <body>
45
+ <div id="video-container"></div>
46
+ <script type="module" src="app.js"></script>
47
+ </body>
48
+ </html>
49
+ ```
50
+
51
+ #### Step 3: Initialize Player (app.js)
52
+ ```javascript
53
+ import { WebPlayer } from '@unified-video/web';
54
+
55
+ async function initializePlayer() {
56
+ // Create player instance
57
+ const player = new WebPlayer();
58
+
59
+ // Initialize with container
60
+ await player.initialize('#video-container', {
61
+ controls: true,
62
+ autoPlay: false,
63
+ muted: false,
64
+ debug: true
65
+ });
66
+
67
+ // Load video source
68
+ await player.load({
69
+ url: 'https://example.com/video.mp4',
70
+ type: 'mp4',
71
+ subtitles: [{
72
+ url: 'https://example.com/subs.vtt',
73
+ language: 'en',
74
+ label: 'English',
75
+ kind: 'subtitles'
76
+ }]
77
+ });
78
+
79
+ // Event handling
80
+ player.on('onReady', () => console.log('Player ready'));
81
+ player.on('onPlay', () => console.log('Playback started'));
82
+ player.on('onError', (error) => console.error('Player error:', error));
83
+
84
+ // Start playback
85
+ await player.play();
86
+ }
87
+
88
+ // Initialize when DOM is ready
89
+ document.addEventListener('DOMContentLoaded', initializePlayer);
90
+ ```
91
+
92
+ #### Step 4: Build for Production
93
+ ```bash
94
+ # Using Webpack
95
+ npm install --save-dev webpack webpack-cli
96
+ npx webpack app.js --output bundle.js
97
+
98
+ # Or using Vite
99
+ npm install --save-dev vite
100
+ npx vite build
101
+ ```
102
+
103
+ #### Step 5: Deploy
104
+ ```bash
105
+ # Test locally
106
+ npx http-server
107
+
108
+ # Deploy to production (examples)
109
+ # - Upload to CDN
110
+ # - Deploy to Netlify/Vercel
111
+ # - Integrate with existing web app
112
+ ```
113
+
114
+ ---
115
+
116
+ ## 2. iOS Platform Setup
117
+
118
+ ### Prerequisites
119
+ - macOS with Xcode 14+
120
+ - iOS 13.0+ deployment target
121
+ - Swift 5.0+ or Objective-C
122
+ - SwiftUI 2.0+ (for SwiftUI implementation)
123
+
124
+ ### โš ๏ธ Architecture Build Fix
125
+ If you encounter "UnifiedVideoPlayer.swiftmodule is not built for arm64" error:
126
+
127
+ ```bash
128
+ # Quick Fix - Use Swift Package Manager (Recommended)
129
+ 1. In Xcode: File > Add Package Dependencies
130
+ 2. Click "Add Local..." and navigate to packages/ios/
131
+ 3. Add the package to your target
132
+
133
+ # Alternative - Build Universal Framework
134
+ cd packages/ios
135
+ chmod +x build_framework.sh
136
+ ./build_framework.sh
137
+ # This creates Output/UnifiedVideoPlayer.xcframework
138
+ ```
139
+
140
+ For detailed architecture troubleshooting, see [BUILD_INSTRUCTIONS.md](packages/ios/BUILD_INSTRUCTIONS.md)
141
+
142
+ ### Choose Your Integration Method:
143
+
144
+ ## Option A: Native iOS (Swift/SwiftUI) - For Modern iOS Apps โญ RECOMMENDED
145
+
146
+ ### Step 1: Add Framework to Your Project
147
+
148
+ #### Using Swift Package Manager (Auto-builds for correct architecture):
149
+ ```swift
150
+ // In Xcode: File > Add Package Dependencies
151
+ // Add Local Package: packages/ios/
152
+ // Or Remote: https://github.com/yourcompany/unified-video-ios.git
153
+ ```
154
+
155
+ #### Using CocoaPods:
156
+ ```ruby
157
+ # Podfile
158
+ pod 'UnifiedVideoPlayer', '~> 1.0'
159
+ ```
160
+
161
+ #### Using XCFramework (Universal Binary):
162
+ ```bash
163
+ cd packages/ios
164
+ ./build_framework.sh
165
+ # Drag Output/UnifiedVideoPlayer.xcframework into your project
166
+ ```
167
+
168
+ ### Quick Start - iOS (Just 3 lines!):
169
+ ```swift
170
+ // UIKit
171
+ let player = UnifiedVideoPlayer()
172
+ player.initialize(container: yourView)
173
+ player.load(url: "video.mp4")
174
+
175
+ // SwiftUI
176
+ UnifiedVideoPlayerView(url: URL(string: "video.mp4")!)
177
+ .frame(height: 200)
178
+ .onAppear { /* auto-plays */ }
179
+ ```
180
+
181
+ ### Step 2: Configure Info.plist
182
+ ```xml
183
+ <key>NSAppTransportSecurity</key>
184
+ <dict>
185
+ <key>NSAllowsArbitraryLoads</key>
186
+ <true/>
187
+ </dict>
188
+
189
+ <!-- For background playback -->
190
+ <key>UIBackgroundModes</key>
191
+ <array>
192
+ <string>audio</string>
193
+ </array>
194
+ ```
195
+
196
+ ### Step 3A: SwiftUI Implementation (Modern Approach) ๐Ÿ†•
197
+ ```swift
198
+ import SwiftUI
199
+ import UnifiedVideoPlayer
200
+
201
+ struct VideoPlayerView: View {
202
+ @StateObject private var playerViewModel = VideoPlayerViewModel()
203
+ @State private var isPlaying = false
204
+ @State private var showControls = true
205
+
206
+ var body: some View {
207
+ VStack {
208
+ // Video Player
209
+ UnifiedVideoPlayerView(
210
+ url: URL(string: "https://example.com/video.m3u8")!,
211
+ configuration: .init(
212
+ autoPlay: true,
213
+ controls: false, // Custom controls
214
+ muted: false
215
+ )
216
+ )
217
+ .frame(height: 250)
218
+ .overlay(
219
+ Group {
220
+ if showControls {
221
+ PlayerControlsOverlay(
222
+ isPlaying: $isPlaying,
223
+ onPlayPause: { playerViewModel.togglePlayPause() },
224
+ onSeek: { playerViewModel.seek(to: $0) }
225
+ )
226
+ }
227
+ }
228
+ )
229
+ .onTapGesture {
230
+ withAnimation { showControls.toggle() }
231
+ }
232
+
233
+ // Custom Controls
234
+ HStack {
235
+ Button(action: { playerViewModel.skipBackward(10) }) {
236
+ Image(systemName: "gobackward.10")
237
+ }
238
+
239
+ Button(action: { playerViewModel.togglePlayPause() }) {
240
+ Image(systemName: isPlaying ? "pause.fill" : "play.fill")
241
+ .font(.title)
242
+ }
243
+
244
+ Button(action: { playerViewModel.skipForward(10) }) {
245
+ Image(systemName: "goforward.10")
246
+ }
247
+ }
248
+ .padding()
249
+
250
+ // Progress Bar
251
+ ProgressView(value: playerViewModel.currentTime,
252
+ total: playerViewModel.duration)
253
+ .padding(.horizontal)
254
+ }
255
+ .onAppear {
256
+ playerViewModel.load(url: "https://example.com/video.m3u8")
257
+ }
258
+ }
259
+ }
260
+
261
+ // View Model for SwiftUI
262
+ class VideoPlayerViewModel: ObservableObject {
263
+ @Published var currentTime: Double = 0
264
+ @Published var duration: Double = 100
265
+ @Published var isPlaying = false
266
+
267
+ private var player: UnifiedVideoPlayer?
268
+
269
+ func load(url: String) {
270
+ player = UnifiedVideoPlayer()
271
+ player?.load(url: url)
272
+ player?.onTimeUpdate = { [weak self] time in
273
+ self?.currentTime = time
274
+ }
275
+ player?.onDurationChange = { [weak self] duration in
276
+ self?.duration = duration
277
+ }
278
+ }
279
+
280
+ func togglePlayPause() {
281
+ isPlaying.toggle()
282
+ isPlaying ? player?.play() : player?.pause()
283
+ }
284
+
285
+ func seek(to time: Double) {
286
+ player?.seek(to: time)
287
+ }
288
+
289
+ func skipForward(_ seconds: Double) {
290
+ player?.seek(to: currentTime + seconds)
291
+ }
292
+
293
+ func skipBackward(_ seconds: Double) {
294
+ player?.seek(to: max(0, currentTime - seconds))
295
+ }
296
+ }
297
+ ```
298
+
299
+ ### Step 3B: UIKit Implementation (Traditional Approach)
300
+ ```swift
301
+ import UIKit
302
+ import AVFoundation
303
+ import UnifiedVideoPlayer
304
+
305
+ class VideoViewController: UIViewController {
306
+
307
+ private var videoPlayer: UnifiedVideoPlayer?
308
+ @IBOutlet weak var playerContainer: UIView!
309
+
310
+ override func viewDidLoad() {
311
+ super.viewDidLoad()
312
+ setupVideoPlayer()
313
+ }
314
+
315
+ private func setupVideoPlayer() {
316
+ // Initialize player
317
+ videoPlayer = UnifiedVideoPlayer()
318
+
319
+ // Configure
320
+ let config = PlayerConfiguration(
321
+ autoPlay: true,
322
+ controls: true,
323
+ muted: false
324
+ )
325
+
326
+ // Add to your view
327
+ videoPlayer?.initialize(
328
+ container: playerContainer,
329
+ configuration: config
330
+ )
331
+
332
+ // Load video
333
+ videoPlayer?.load(url: "https://example.com/video.m3u8")
334
+
335
+ // Handle events
336
+ videoPlayer?.onReady = { [weak self] in
337
+ print("Player ready")
338
+ }
339
+
340
+ videoPlayer?.onError = { [weak self] error in
341
+ self?.showError(error)
342
+ }
343
+ }
344
+
345
+ // Control methods
346
+ @IBAction func playButtonTapped() {
347
+ videoPlayer?.play()
348
+ }
349
+
350
+ @IBAction func pauseButtonTapped() {
351
+ videoPlayer?.pause()
352
+ }
353
+ }
354
+ ```
355
+
356
+ ### Step 4: Objective-C Implementation (Alternative)
357
+ ```objc
358
+ // VideoViewController.m
359
+ #import "UnifiedVideoPlayer.h"
360
+
361
+ @interface VideoViewController ()
362
+ @property (nonatomic, strong) UnifiedVideoPlayer *videoPlayer;
363
+ @property (weak, nonatomic) IBOutlet UIView *playerContainer;
364
+ @end
365
+
366
+ @implementation VideoViewController
367
+
368
+ - (void)viewDidLoad {
369
+ [super viewDidLoad];
370
+ [self setupVideoPlayer];
371
+ }
372
+
373
+ - (void)setupVideoPlayer {
374
+ // Initialize player
375
+ self.videoPlayer = [[UnifiedVideoPlayer alloc] init];
376
+
377
+ // Configure
378
+ NSDictionary *config = @{
379
+ @"autoPlay": @YES,
380
+ @"controls": @YES,
381
+ @"muted": @NO
382
+ };
383
+
384
+ // Add to view
385
+ [self.videoPlayer initializeWithContainer:self.playerContainer
386
+ configuration:config];
387
+
388
+ // Load video
389
+ [self.videoPlayer loadWithUrl:@"https://example.com/video.m3u8"];
390
+
391
+ // Handle events
392
+ self.videoPlayer.onReady = ^{
393
+ NSLog(@"Player ready");
394
+ };
395
+ }
396
+
397
+ - (IBAction)playButtonTapped:(id)sender {
398
+ [self.videoPlayer play];
399
+ }
400
+
401
+ @end
402
+ ```
403
+
404
+ ## Option B: React Native - For Cross-Platform Apps
405
+
406
+ ### Step 1: Install Dependencies
407
+ ```bash
408
+ npx react-native init MyVideoApp
409
+ cd MyVideoApp
410
+ npm install @unified-video/core @unified-video/react-native
411
+ npm install react-native-video
412
+ cd ios && pod install
413
+ ```
414
+
415
+ ### Step 2: React Native Implementation
416
+ ```jsx
417
+ import React, { useRef } from 'react';
418
+ import { View, Button } from 'react-native';
419
+ import { ReactNativePlayer } from '@unified-video/react-native';
420
+
421
+ export default function VideoScreen() {
422
+ const playerRef = useRef(null);
423
+
424
+ return (
425
+ <View style={{ flex: 1 }}>
426
+ <ReactNativePlayer
427
+ ref={playerRef}
428
+ style={{ flex: 1 }}
429
+ config={{ autoPlay: true }}
430
+ />
431
+ </View>
432
+ );
433
+ }
434
+ ```
435
+
436
+ ## Option C: Flutter - For Dart-based Cross-Platform
437
+
438
+ ### Step 1: Add Dependencies
439
+ ```yaml
440
+ # pubspec.yaml
441
+ dependencies:
442
+ unified_video_player: ^1.0.0
443
+ video_player: ^2.7.0
444
+ ```
445
+
446
+ ### Step 2: Flutter Implementation
447
+ ```dart
448
+ import 'package:flutter/material.dart';
449
+ import 'package:unified_video_player/unified_video_player.dart';
450
+
451
+ class VideoScreen extends StatefulWidget {
452
+ @override
453
+ _VideoScreenState createState() => _VideoScreenState();
454
+ }
455
+
456
+ class _VideoScreenState extends State<VideoScreen> {
457
+ UnifiedVideoPlayer? _player;
458
+
459
+ @override
460
+ void initState() {
461
+ super.initState();
462
+ _player = UnifiedVideoPlayer();
463
+ _player!.initialize();
464
+ _player!.load('https://example.com/video.m3u8');
465
+ }
466
+
467
+ @override
468
+ Widget build(BuildContext context) {
469
+ return Scaffold(
470
+ body: UnifiedVideoPlayerView(
471
+ player: _player!,
472
+ ),
473
+ );
474
+ }
475
+ }
476
+ ```
477
+
478
+ ### Build and Run
479
+ ```bash
480
+ # Native iOS (UIKit/SwiftUI)
481
+ open MyApp.xcodeproj
482
+ # Or: xcodebuild -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 14'
483
+
484
+ # Fix Architecture Issues Before Building
485
+ # In Xcode: Product > Clean Build Folder (โ‡งโŒ˜K)
486
+ # Then: Product > Build (โŒ˜B)
487
+
488
+ # React Native
489
+ npx react-native run-ios
490
+ # Or specific simulator: npx react-native run-ios --simulator="iPhone 14 Pro"
491
+
492
+ # Flutter
493
+ flutter run -d ios
494
+ ```
495
+
496
+ ### Troubleshooting iOS Build Issues
497
+
498
+ #### Architecture Error Fix:
499
+ ```bash
500
+ # If you see: "not built for arm64"
501
+ # Solution 1: Clean and rebuild
502
+ xcodebuild clean -workspace MyApp.xcworkspace -scheme MyApp
503
+ xcodebuild build -workspace MyApp.xcworkspace -scheme MyApp -destination 'generic/platform=iOS'
504
+
505
+ # Solution 2: Reset Swift Package Manager cache
506
+ rm -rf ~/Library/Caches/org.swift.swiftpm
507
+ rm -rf ~/Library/Developer/Xcode/DerivedData
508
+
509
+ # Solution 3: Build universal framework
510
+ cd packages/ios
511
+ ./build_framework.sh
512
+ ```
513
+
514
+ ---
515
+
516
+ ## 3. Android Platform Setup
517
+
518
+ ### Prerequisites
519
+ - Android Studio Arctic Fox+
520
+ - Android SDK 21+ (Lollipop)
521
+ - Java 11 / Kotlin 1.6+
522
+
523
+ ### Choose Your Integration Method:
524
+
525
+ ## Option A: Native Android (Kotlin/Java) - For Existing Android Apps โญ RECOMMENDED
526
+
527
+ ### Step 1: Add Library to Your Project
528
+
529
+ cd packages/android
530
+ ./gradlew assembleRelease
531
+ # This creates an AAR file in build/outputs/aar/
532
+
533
+ Android (Just 3 lines!):
534
+ val player = UnifiedVideoPlayer(context)
535
+ player.initialize(container)
536
+ player.load("video.mp4")
537
+
538
+
539
+ #### Using Gradle:
540
+ ```gradle
541
+ // app/build.gradle
542
+ dependencies {
543
+ implementation 'com.unifiedvideo:player:1.0.0'
544
+
545
+ // Or local AAR
546
+ implementation files('libs/unified-video-player.aar')
547
+
548
+ // Required dependencies
549
+ implementation 'com.google.android.exoplayer:exoplayer:2.18.5'
550
+ implementation 'com.google.android.exoplayer:exoplayer-hls:2.18.5'
551
+ }
552
+ ```
553
+
554
+ ### Step 2: Configure AndroidManifest.xml
555
+ ```xml
556
+ <uses-permission android:name="android.permission.INTERNET" />
557
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
558
+
559
+ <application
560
+ android:usesCleartextTraffic="true"
561
+ android:largeHeap="true">
562
+
563
+ <!-- For background playback -->
564
+ <service android:name="com.unifiedvideo.PlaybackService" />
565
+ </application>
566
+ ```
567
+
568
+ ### Step 3: Implement in Your Activity (Kotlin)
569
+ ```kotlin
570
+ // VideoActivity.kt
571
+ import android.os.Bundle
572
+ import androidx.appcompat.app.AppCompatActivity
573
+ import com.unifiedvideo.player.UnifiedVideoPlayer
574
+ import com.unifiedvideo.player.PlayerConfiguration
575
+
576
+ class VideoActivity : AppCompatActivity() {
577
+
578
+ private lateinit var videoPlayer: UnifiedVideoPlayer
579
+
580
+ override fun onCreate(savedInstanceState: Bundle?) {
581
+ super.onCreate(savedInstanceState)
582
+ setContentView(R.layout.activity_video)
583
+
584
+ setupVideoPlayer()
585
+ }
586
+
587
+ private fun setupVideoPlayer() {
588
+ // Find your container view
589
+ val playerContainer = findViewById<FrameLayout>(R.id.player_container)
590
+
591
+ // Create and initialize player
592
+ videoPlayer = UnifiedVideoPlayer(this).apply {
593
+ initialize(
594
+ container = playerContainer,
595
+ configuration = PlayerConfiguration(
596
+ autoPlay = true,
597
+ controls = true,
598
+ muted = false
599
+ )
600
+ )
601
+
602
+ // Set event listeners
603
+ onReady = {
604
+ Log.d("Player", "Ready")
605
+ }
606
+
607
+ onError = { error ->
608
+ showError(error)
609
+ }
610
+
611
+ // Load video
612
+ load("https://example.com/video.m3u8")
613
+ }
614
+ }
615
+
616
+ // Control methods
617
+ fun onPlayButtonClick() {
618
+ videoPlayer.play()
619
+ }
620
+
621
+ fun onPauseButtonClick() {
622
+ videoPlayer.pause()
623
+ }
624
+
625
+ override fun onPause() {
626
+ super.onPause()
627
+ videoPlayer.pause()
628
+ }
629
+
630
+ override fun onDestroy() {
631
+ super.onDestroy()
632
+ videoPlayer.release()
633
+ }
634
+ }
635
+ ```
636
+
637
+ ### Step 4: Java Implementation (Alternative)
638
+ ```java
639
+ // VideoActivity.java
640
+ import android.os.Bundle;
641
+ import androidx.appcompat.app.AppCompatActivity;
642
+ import com.unifiedvideo.player.UnifiedVideoPlayer;
643
+ import com.unifiedvideo.player.PlayerConfiguration;
644
+
645
+ public class VideoActivity extends AppCompatActivity {
646
+
647
+ private UnifiedVideoPlayer videoPlayer;
648
+
649
+ @Override
650
+ protected void onCreate(Bundle savedInstanceState) {
651
+ super.onCreate(savedInstanceState);
652
+ setContentView(R.layout.activity_video);
653
+
654
+ setupVideoPlayer();
655
+ }
656
+
657
+ private void setupVideoPlayer() {
658
+ // Find container
659
+ FrameLayout playerContainer = findViewById(R.id.player_container);
660
+
661
+ // Create player
662
+ videoPlayer = new UnifiedVideoPlayer(this);
663
+
664
+ // Configure
665
+ PlayerConfiguration config = new PlayerConfiguration.Builder()
666
+ .setAutoPlay(true)
667
+ .setControls(true)
668
+ .build();
669
+
670
+ // Initialize
671
+ videoPlayer.initialize(playerContainer, config);
672
+
673
+ // Set listeners
674
+ videoPlayer.setOnReadyListener(() -> {
675
+ Log.d("Player", "Ready");
676
+ });
677
+
678
+ // Load video
679
+ videoPlayer.load("https://example.com/video.mp4");
680
+ }
681
+
682
+ @Override
683
+ protected void onDestroy() {
684
+ super.onDestroy();
685
+ if (videoPlayer != null) {
686
+ videoPlayer.release();
687
+ }
688
+ }
689
+ }
690
+ ```
691
+
692
+ ## Option B: React Native - For Cross-Platform Apps
693
+
694
+ ### Step 1: Install Dependencies
695
+ ```bash
696
+ npx react-native init MyVideoApp
697
+ cd MyVideoApp
698
+ npm install @unified-video/core @unified-video/react-native
699
+ npm install react-native-video
700
+ ```
701
+
702
+ ### Step 2: React Native Implementation
703
+ ```jsx
704
+ import React from 'react';
705
+ import { View } from 'react-native';
706
+ import { ReactNativePlayer } from '@unified-video/react-native';
707
+
708
+ export default function VideoScreen() {
709
+ return (
710
+ <View style={{ flex: 1 }}>
711
+ <ReactNativePlayer
712
+ style={{ flex: 1 }}
713
+ config={{ autoPlay: true }}
714
+ />
715
+ </View>
716
+ );
717
+ }
718
+ ```
719
+
720
+ ## Option C: Flutter - For Dart-based Cross-Platform
721
+
722
+ ### Step 1: Add Dependencies
723
+ ```yaml
724
+ # pubspec.yaml
725
+ dependencies:
726
+ unified_video_player: ^1.0.0
727
+ ```
728
+
729
+ ### Step 2: Flutter Implementation
730
+ ```dart
731
+ import 'package:unified_video_player/unified_video_player.dart';
732
+
733
+ class VideoScreen extends StatelessWidget {
734
+ @override
735
+ Widget build(BuildContext context) {
736
+ return UnifiedVideoPlayer(
737
+ url: 'https://example.com/video.mp4',
738
+ autoPlay: true,
739
+ );
740
+ }
741
+ }
742
+ ```
743
+
744
+ ### Build and Run
745
+ ```bash
746
+ # Native Android
747
+ # Open in Android Studio and run
748
+
749
+ # React Native
750
+ npx react-native run-android
751
+
752
+ # Flutter
753
+ flutter run -d android
754
+ ```
755
+
756
+ ---
757
+
758
+ ## 4. Samsung Tizen TV Setup
759
+
760
+ ### Prerequisites
761
+ - Tizen Studio 5.0+
762
+ - Samsung TV (2017+ model) or Emulator
763
+ - Developer Certificate
764
+
765
+ ### Step-by-Step Installation
766
+
767
+ #### Step 1: Create Tizen Project
768
+ ```bash
769
+ # Install Tizen CLI
770
+ npm install -g @tizen/cli
771
+
772
+ # Create new project
773
+ tizen create web-project -n VideoPlayerApp -t BasicProject
774
+ cd VideoPlayerApp
775
+ ```
776
+
777
+ #### Step 2: Install Framework
778
+ ```bash
779
+ npm install @unified-video/core @unified-video/web
780
+ ```
781
+
782
+ #### Step 3: Configure config.xml
783
+ ```xml
784
+ <?xml version="1.0" encoding="UTF-8"?>
785
+ <widget xmlns="http://www.w3.org/ns/widgets"
786
+ xmlns:tizen="http://tizen.org/ns/widgets"
787
+ id="http://yourdomain/VideoPlayerApp"
788
+ version="1.0.0">
789
+ <name>VideoPlayerApp</name>
790
+ <tizen:application id="ABC123.VideoPlayerApp"
791
+ package="ABC123"
792
+ required_version="5.0"/>
793
+ <content src="index.html"/>
794
+ <feature name="http://tizen.org/feature/screen.size.all"/>
795
+ <access origin="*" subdomains="true"/>
796
+ <tizen:privilege name="http://tizen.org/privilege/internet"/>
797
+ <tizen:privilege name="http://tizen.org/privilege/tv.inputdevice"/>
798
+ <tizen:setting screen-orientation="landscape"/>
799
+ </widget>
800
+ ```
801
+
802
+ #### Step 4: Implement TV Controls
803
+ ```javascript
804
+ // js/app.js
805
+ import { WebPlayer } from '@unified-video/web';
806
+
807
+ let player;
808
+
809
+ async function initTizenPlayer() {
810
+ player = new WebPlayer();
811
+
812
+ await player.initialize('#player-container', {
813
+ controls: false, // Use custom TV controls
814
+ autoPlay: true
815
+ });
816
+
817
+ // Register TV remote control events
818
+ registerKeyHandler();
819
+
820
+ // Load video
821
+ await player.load({
822
+ url: 'https://example.com/video.m3u8',
823
+ type: 'hls'
824
+ });
825
+ }
826
+
827
+ function registerKeyHandler() {
828
+ document.addEventListener('keydown', function(e) {
829
+ switch(e.keyCode) {
830
+ case 13: // Enter
831
+ player.isPlaying() ? player.pause() : player.play();
832
+ break;
833
+ case 37: // Left
834
+ player.seek(player.getCurrentTime() - 10);
835
+ break;
836
+ case 39: // Right
837
+ player.seek(player.getCurrentTime() + 10);
838
+ break;
839
+ case 38: // Up
840
+ player.setVolume(player.getState().volume + 0.1);
841
+ break;
842
+ case 40: // Down
843
+ player.setVolume(player.getState().volume - 0.1);
844
+ break;
845
+ case 10009: // Back button
846
+ tizen.application.getCurrentApplication().exit();
847
+ break;
848
+ }
849
+ });
850
+ }
851
+
852
+ // Initialize on load
853
+ window.onload = initTizenPlayer;
854
+ ```
855
+
856
+ #### Step 5: Build and Deploy
857
+ ```bash
858
+ # Build package
859
+ tizen build-web
860
+
861
+ # Package as WGT
862
+ tizen package -t wgt -s yourCertificate -- .buildResult
863
+
864
+ # Install on TV
865
+ tizen install -n VideoPlayerApp.wgt -t <TV_IP>
866
+
867
+ # Run on TV
868
+ tizen run -p ABC123.VideoPlayerApp -t <TV_IP>
869
+ ```
870
+
871
+ ---
872
+
873
+ ## 5. LG webOS TV Setup
874
+
875
+ ### Prerequisites
876
+ - webOS TV SDK 6.0+
877
+ - LG TV (2018+ model) or Emulator
878
+ - Developer Account
879
+
880
+ ### Step-by-Step Installation
881
+
882
+ #### Step 1: Create webOS Project
883
+ ```bash
884
+ # Install webOS CLI
885
+ npm install -g @webos-tools/cli
886
+
887
+ # Create new project
888
+ ares-generate -t webapp VideoPlayerApp
889
+ cd VideoPlayerApp
890
+ ```
891
+
892
+ #### Step 2: Configure appinfo.json
893
+ ```json
894
+ {
895
+ "id": "com.yourdomain.videoplayerapp",
896
+ "version": "1.0.0",
897
+ "vendor": "Your Company",
898
+ "type": "web",
899
+ "main": "index.html",
900
+ "title": "Video Player App",
901
+ "icon": "icon.png",
902
+ "largeIcon": "largeIcon.png",
903
+ "requiredPermissions": ["internet", "media.playback"]
904
+ }
905
+ ```
906
+
907
+ #### Step 3: Implement webOS Player
908
+ ```javascript
909
+ // js/app.js
910
+ import { WebPlayer } from '@unified-video/web';
911
+
912
+ class WebOSVideoPlayer {
913
+ constructor() {
914
+ this.player = new WebPlayer();
915
+ this.initializePlayer();
916
+ this.registerRemoteControl();
917
+ }
918
+
919
+ async initializePlayer() {
920
+ await this.player.initialize('#video-container', {
921
+ controls: false,
922
+ autoPlay: true
923
+ });
924
+
925
+ // Load video
926
+ await this.player.load({
927
+ url: 'https://example.com/video.m3u8',
928
+ type: 'hls',
929
+ drm: {
930
+ type: 'playready',
931
+ licenseUrl: 'https://license.server.com'
932
+ }
933
+ });
934
+ }
935
+
936
+ registerRemoteControl() {
937
+ // Register webOS specific remote control
938
+ webOS.registerKeys(['red', 'green', 'yellow', 'blue', 'back']);
939
+
940
+ document.addEventListener('webOSRelaunch', (e) => {
941
+ if (e.detail.parameters.contentId) {
942
+ this.loadContent(e.detail.parameters.contentId);
943
+ }
944
+ });
945
+
946
+ document.addEventListener('keydown', (e) => {
947
+ switch(e.key) {
948
+ case 'Enter':
949
+ this.togglePlayPause();
950
+ break;
951
+ case 'ArrowLeft':
952
+ this.seek(-10);
953
+ break;
954
+ case 'ArrowRight':
955
+ this.seek(10);
956
+ break;
957
+ case 'Back':
958
+ webOS.platformBack();
959
+ break;
960
+ }
961
+ });
962
+ }
963
+
964
+ togglePlayPause() {
965
+ this.player.isPlaying() ?
966
+ this.player.pause() :
967
+ this.player.play();
968
+ }
969
+
970
+ seek(seconds) {
971
+ const currentTime = this.player.getCurrentTime();
972
+ this.player.seek(currentTime + seconds);
973
+ }
974
+ }
975
+
976
+ // Initialize on app launch
977
+ window.addEventListener('load', () => {
978
+ new WebOSVideoPlayer();
979
+ });
980
+ ```
981
+
982
+ #### Step 4: Build and Deploy
983
+ ```bash
984
+ # Package app
985
+ ares-package . -o dist/
986
+
987
+ # Install on TV
988
+ ares-install dist/com.yourdomain.videoplayerapp_1.0.0_all.ipk -t <TV_NAME>
989
+
990
+ # Launch app
991
+ ares-launch com.yourdomain.videoplayerapp -t <TV_NAME>
992
+
993
+ # Debug app
994
+ ares-inspect com.yourdomain.videoplayerapp -t <TV_NAME>
995
+ ```
996
+
997
+ ---
998
+
999
+ ## 6. Roku Platform Setup
1000
+
1001
+ ### Prerequisites
1002
+ - Roku Device (or Emulator)
1003
+ - Roku Developer Account
1004
+ - Developer Mode enabled on device
1005
+
1006
+ ### Step-by-Step Installation
1007
+
1008
+ #### Step 1: Project Structure
1009
+ ```
1010
+ VideoPlayerApp/
1011
+ โ”œโ”€โ”€ manifest
1012
+ โ”œโ”€โ”€ source/
1013
+ โ”‚ โ””โ”€โ”€ main.brs
1014
+ โ”œโ”€โ”€ components/
1015
+ โ”‚ โ”œโ”€โ”€ UnifiedVideoPlayer.xml
1016
+ โ”‚ โ””โ”€โ”€ UnifiedVideoPlayer.brs
1017
+ โ””โ”€โ”€ images/
1018
+ โ”œโ”€โ”€ splash_hd.png
1019
+ โ””โ”€โ”€ splash_sd.png
1020
+ ```
1021
+
1022
+ #### Step 2: Configure Manifest
1023
+ ```
1024
+ title=Video Player App
1025
+ major_version=1
1026
+ minor_version=0
1027
+ build_version=0
1028
+ mm_icon_focus_hd=pkg:/images/icon_hd.png
1029
+ mm_icon_focus_sd=pkg:/images/icon_sd.png
1030
+ splash_screen_hd=pkg:/images/splash_hd.png
1031
+ splash_screen_sd=pkg:/images/splash_sd.png
1032
+ ui_resolutions=fhd
1033
+ bs_libs_required=roku_ads_lib
1034
+ ```
1035
+
1036
+ #### Step 3: Main Scene Component
1037
+ Create `components/MainScene.xml`:
1038
+ ```xml
1039
+ <?xml version="1.0" encoding="utf-8" ?>
1040
+ <component name="MainScene" extends="Scene">
1041
+ <interface>
1042
+ <field id="videoUrl" type="string" />
1043
+ </interface>
1044
+
1045
+ <script type="text/brightscript" uri="MainScene.brs" />
1046
+
1047
+ <children>
1048
+ <UnifiedVideoPlayer
1049
+ id="videoPlayer"
1050
+ width="1920"
1051
+ height="1080" />
1052
+ </children>
1053
+ </component>
1054
+ ```
1055
+
1056
+ Create `components/MainScene.brs`:
1057
+ ```brightscript
1058
+ sub init()
1059
+ m.videoPlayer = m.top.findNode("videoPlayer")
1060
+ m.top.setFocus(true)
1061
+
1062
+ ' Initialize with sample content
1063
+ loadContent()
1064
+ end sub
1065
+
1066
+ sub loadContent()
1067
+ content = CreateObject("roSGNode", "ContentNode")
1068
+ content.url = "https://example.com/video.m3u8"
1069
+ content.streamFormat = "hls"
1070
+ content.title = "Sample Video"
1071
+
1072
+ ' DRM configuration
1073
+ content.drmParams = {
1074
+ type: "widevine",
1075
+ licenseUrl: "https://license.server.com"
1076
+ }
1077
+
1078
+ ' Load into player
1079
+ m.videoPlayer.callFunc("loadContent", content)
1080
+ end sub
1081
+
1082
+ function onKeyEvent(key as String, press as Boolean) as Boolean
1083
+ if press then
1084
+ if key = "play"
1085
+ m.videoPlayer.callFunc("play")
1086
+ return true
1087
+ else if key = "pause"
1088
+ m.videoPlayer.callFunc("pause")
1089
+ return true
1090
+ else if key = "back"
1091
+ return true
1092
+ end if
1093
+ end if
1094
+ return false
1095
+ end function
1096
+ ```
1097
+
1098
+ #### Step 4: Build and Deploy
1099
+ ```bash
1100
+ # Create ZIP package
1101
+ zip -r VideoPlayerApp.zip manifest source components images
1102
+
1103
+ # Enable developer mode on Roku (Settings > System > Advanced > Developer Mode)
1104
+
1105
+ # Upload via browser
1106
+ # Navigate to http://<ROKU_IP> and upload the ZIP
1107
+
1108
+ # Or use Roku Deploy CLI
1109
+ npm install -g roku-deploy
1110
+ roku-deploy --host <ROKU_IP> --password <DEV_PASSWORD>
1111
+ ```
1112
+
1113
+ ---
1114
+
1115
+ ## 7. Android TV Setup
1116
+
1117
+ ### Prerequisites
1118
+ - Android Studio with Android TV SDK
1119
+ - Android TV device or emulator
1120
+ - Leanback library
1121
+
1122
+ ### Step-by-Step Installation
1123
+
1124
+ #### Step 1: Create Android TV Project
1125
+ ```bash
1126
+ # In Android Studio: New Project > TV > Empty Activity
1127
+ ```
1128
+
1129
+ #### Step 2: Configure build.gradle
1130
+ ```gradle
1131
+ // app/build.gradle
1132
+ android {
1133
+ compileSdk 33
1134
+
1135
+ defaultConfig {
1136
+ applicationId "com.yourdomain.videoplayer"
1137
+ minSdk 21
1138
+ targetSdk 33
1139
+ versionCode 1
1140
+ versionName "1.0"
1141
+ }
1142
+ }
1143
+
1144
+ dependencies {
1145
+ implementation 'androidx.leanback:leanback:1.2.0'
1146
+ implementation 'com.google.android.exoplayer:exoplayer:2.18.5'
1147
+ implementation 'com.google.android.exoplayer:exoplayer-hls:2.18.5'
1148
+ implementation 'com.google.android.exoplayer:extension-leanback:2.18.5'
1149
+ }
1150
+ ```
1151
+
1152
+ #### Step 3: TV Manifest Configuration
1153
+ ```xml
1154
+ <!-- AndroidManifest.xml -->
1155
+ <uses-feature
1156
+ android:name="android.software.leanback"
1157
+ android:required="true" />
1158
+
1159
+ <uses-feature
1160
+ android:name="android.hardware.touchscreen"
1161
+ android:required="false" />
1162
+
1163
+ <application
1164
+ android:banner="@drawable/tv_banner"
1165
+ android:theme="@style/Theme.Leanback">
1166
+
1167
+ <activity
1168
+ android:name=".MainActivity"
1169
+ android:label="@string/app_name"
1170
+ android:screenOrientation="landscape">
1171
+ <intent-filter>
1172
+ <action android:name="android.intent.action.MAIN" />
1173
+ <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
1174
+ </intent-filter>
1175
+ </activity>
1176
+
1177
+ <activity
1178
+ android:name=".PlayerActivity"
1179
+ android:configChanges="orientation|screenSize" />
1180
+ </application>
1181
+ ```
1182
+
1183
+ #### Step 4: Implement Player Activity
1184
+ ```kotlin
1185
+ // PlayerActivity.kt
1186
+ import android.os.Bundle
1187
+ import androidx.fragment.app.FragmentActivity
1188
+ import com.google.android.exoplayer2.ExoPlayer
1189
+ import com.google.android.exoplayer2.MediaItem
1190
+ import com.google.android.exoplayer2.ext.leanback.LeanbackPlayerAdapter
1191
+ import androidx.leanback.app.VideoSupportFragment
1192
+ import androidx.leanback.app.VideoSupportFragmentGlueHost
1193
+ import androidx.leanback.media.PlaybackTransportControlGlue
1194
+
1195
+ class PlayerActivity : FragmentActivity() {
1196
+ private lateinit var exoPlayer: ExoPlayer
1197
+ private lateinit var playerGlue: PlaybackTransportControlGlue<LeanbackPlayerAdapter>
1198
+
1199
+ override fun onCreate(savedInstanceState: Bundle?) {
1200
+ super.onCreate(savedInstanceState)
1201
+ setContentView(R.layout.activity_player)
1202
+
1203
+ val videoFragment = supportFragmentManager
1204
+ .findFragmentById(R.id.video_fragment) as VideoSupportFragment
1205
+
1206
+ // Initialize ExoPlayer
1207
+ exoPlayer = ExoPlayer.Builder(this).build()
1208
+
1209
+ // Create Leanback adapter
1210
+ val playerAdapter = LeanbackPlayerAdapter(this, exoPlayer, 16)
1211
+
1212
+ // Create playback control glue
1213
+ playerGlue = PlaybackTransportControlGlue(this, playerAdapter)
1214
+ playerGlue.host = VideoSupportFragmentGlueHost(videoFragment)
1215
+ playerGlue.title = "Video Title"
1216
+ playerGlue.subtitle = "Video Subtitle"
1217
+
1218
+ // Load video
1219
+ val videoUrl = intent.getStringExtra("video_url")
1220
+ val mediaItem = MediaItem.fromUri(videoUrl!!)
1221
+ exoPlayer.setMediaItem(mediaItem)
1222
+ exoPlayer.prepare()
1223
+ exoPlayer.play()
1224
+ }
1225
+
1226
+ override fun onStop() {
1227
+ super.onStop()
1228
+ playerGlue.pause()
1229
+ }
1230
+
1231
+ override fun onDestroy() {
1232
+ super.onDestroy()
1233
+ exoPlayer.release()
1234
+ }
1235
+ }
1236
+ ```
1237
+
1238
+ #### Step 5: Build and Deploy
1239
+ ```bash
1240
+ # Build APK
1241
+ ./gradlew assembleDebug
1242
+
1243
+ # Install on Android TV
1244
+ adb connect <TV_IP>
1245
+ adb install app/build/outputs/apk/debug/app-debug.apk
1246
+
1247
+ # Launch app
1248
+ adb shell am start -n com.yourdomain.videoplayer/.MainActivity
1249
+ ```
1250
+
1251
+ ---
1252
+
1253
+ ## 8. React Native Mobile Setup (iOS + Android Combined)
1254
+
1255
+ ### Prerequisites
1256
+ - Node.js 16+
1257
+ - React Native CLI
1258
+ - Xcode (for iOS)
1259
+ - Android Studio (for Android)
1260
+
1261
+ ### Step-by-Step Installation
1262
+
1263
+ #### Step 1: Create React Native Project
1264
+ ```bash
1265
+ npx react-native init VideoPlayerApp --template react-native-template-typescript
1266
+ cd VideoPlayerApp
1267
+ ```
1268
+
1269
+ #### Step 2: Install Dependencies
1270
+ ```bash
1271
+ npm install @unified-video/core @unified-video/react-native
1272
+ npm install react-native-video react-native-orientation-locker react-native-slider
1273
+ npm install --save-dev @types/react-native-video
1274
+ ```
1275
+
1276
+ #### Step 3: Platform-specific Setup
1277
+ ```bash
1278
+ # iOS
1279
+ cd ios && pod install && cd ..
1280
+
1281
+ # Android - Already configured via autolinking
1282
+ ```
1283
+
1284
+ #### Step 4: Create Video Player Screen
1285
+ ```typescript
1286
+ // src/screens/VideoPlayerScreen.tsx
1287
+ import React, { useRef, useState, useEffect } from 'react';
1288
+ import {
1289
+ View,
1290
+ StyleSheet,
1291
+ TouchableOpacity,
1292
+ Text,
1293
+ StatusBar,
1294
+ Platform,
1295
+ SafeAreaView
1296
+ } from 'react-native';
1297
+ import { ReactNativePlayer, ReactNativePlayerRef } from '@unified-video/react-native';
1298
+ import Orientation from 'react-native-orientation-locker';
1299
+ import Slider from '@react-native-community/slider';
1300
+
1301
+ interface VideoPlayerScreenProps {
1302
+ route: {
1303
+ params: {
1304
+ videoUrl: string;
1305
+ title?: string;
1306
+ };
1307
+ };
1308
+ }
1309
+
1310
+ export const VideoPlayerScreen: React.FC<VideoPlayerScreenProps> = ({ route }) => {
1311
+ const playerRef = useRef<ReactNativePlayerRef>(null);
1312
+ const [isPlaying, setIsPlaying] = useState(false);
1313
+ const [currentTime, setCurrentTime] = useState(0);
1314
+ const [duration, setDuration] = useState(0);
1315
+ const [showControls, setShowControls] = useState(true);
1316
+
1317
+ useEffect(() => {
1318
+ // Lock to landscape
1319
+ Orientation.lockToLandscape();
1320
+
1321
+ // Hide status bar
1322
+ StatusBar.setHidden(true);
1323
+
1324
+ // Load video
1325
+ loadVideo();
1326
+
1327
+ return () => {
1328
+ Orientation.unlockAllOrientations();
1329
+ StatusBar.setHidden(false);
1330
+ };
1331
+ }, []);
1332
+
1333
+ const loadVideo = async () => {
1334
+ const { videoUrl } = route.params;
1335
+
1336
+ await playerRef.current?.load({
1337
+ url: videoUrl,
1338
+ type: videoUrl.includes('.m3u8') ? 'hls' : 'mp4',
1339
+ metadata: {
1340
+ title: route.params.title || 'Video'
1341
+ }
1342
+ });
1343
+
1344
+ // Start playback
1345
+ await playerRef.current?.play();
1346
+ setIsPlaying(true);
1347
+ };
1348
+
1349
+ const togglePlayPause = () => {
1350
+ if (isPlaying) {
1351
+ playerRef.current?.pause();
1352
+ } else {
1353
+ playerRef.current?.play();
1354
+ }
1355
+ setIsPlaying(!isPlaying);
1356
+ };
1357
+
1358
+ const onSliderValueChange = (value: number) => {
1359
+ playerRef.current?.seek(value);
1360
+ };
1361
+
1362
+ const formatTime = (seconds: number): string => {
1363
+ const mins = Math.floor(seconds / 60);
1364
+ const secs = Math.floor(seconds % 60);
1365
+ return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
1366
+ };
1367
+
1368
+ return (
1369
+ <SafeAreaView style={styles.container}>
1370
+ <TouchableOpacity
1371
+ activeOpacity={1}
1372
+ style={styles.videoContainer}
1373
+ onPress={() => setShowControls(!showControls)}
1374
+ >
1375
+ <ReactNativePlayer
1376
+ ref={playerRef}
1377
+ style={styles.video}
1378
+ config={{
1379
+ autoPlay: false,
1380
+ controls: false, // Use custom controls
1381
+ muted: false
1382
+ }}
1383
+ onReady={() => console.log('Player ready')}
1384
+ onPlay={() => setIsPlaying(true)}
1385
+ onPause={() => setIsPlaying(false)}
1386
+ onTimeUpdate={(time) => setCurrentTime(time)}
1387
+ onLoadedMetadata={(metadata) => {
1388
+ setDuration(metadata.duration || 0);
1389
+ }}
1390
+ onError={(error) => console.error('Player error:', error)}
1391
+ />
1392
+
1393
+ {showControls && (
1394
+ <View style={styles.controls}>
1395
+ <View style={styles.topControls}>
1396
+ <Text style={styles.title}>{route.params.title}</Text>
1397
+ </View>
1398
+
1399
+ <View style={styles.centerControls}>
1400
+ <TouchableOpacity onPress={togglePlayPause}>
1401
+ <Text style={styles.playButton}>
1402
+ {isPlaying ? 'โธ' : 'โ–ถ'}
1403
+ </Text>
1404
+ </TouchableOpacity>
1405
+ </View>
1406
+
1407
+ <View style={styles.bottomControls}>
1408
+ <Text style={styles.time}>{formatTime(currentTime)}</Text>
1409
+ <Slider
1410
+ style={styles.slider}
1411
+ minimumValue={0}
1412
+ maximumValue={duration}
1413
+ value={currentTime}
1414
+ onSlidingComplete={onSliderValueChange}
1415
+ minimumTrackTintColor="#FFFFFF"
1416
+ maximumTrackTintColor="rgba(255,255,255,0.3)"
1417
+ />
1418
+ <Text style={styles.time}>{formatTime(duration)}</Text>
1419
+ </View>
1420
+ </View>
1421
+ )}
1422
+ </TouchableOpacity>
1423
+ </SafeAreaView>
1424
+ );
1425
+ };
1426
+
1427
+ const styles = StyleSheet.create({
1428
+ container: {
1429
+ flex: 1,
1430
+ backgroundColor: '#000',
1431
+ },
1432
+ videoContainer: {
1433
+ flex: 1,
1434
+ },
1435
+ video: {
1436
+ position: 'absolute',
1437
+ top: 0,
1438
+ left: 0,
1439
+ right: 0,
1440
+ bottom: 0,
1441
+ },
1442
+ controls: {
1443
+ position: 'absolute',
1444
+ top: 0,
1445
+ left: 0,
1446
+ right: 0,
1447
+ bottom: 0,
1448
+ backgroundColor: 'rgba(0,0,0,0.5)',
1449
+ },
1450
+ topControls: {
1451
+ position: 'absolute',
1452
+ top: 20,
1453
+ left: 20,
1454
+ right: 20,
1455
+ },
1456
+ title: {
1457
+ color: '#FFF',
1458
+ fontSize: 18,
1459
+ fontWeight: 'bold',
1460
+ },
1461
+ centerControls: {
1462
+ flex: 1,
1463
+ justifyContent: 'center',
1464
+ alignItems: 'center',
1465
+ },
1466
+ playButton: {
1467
+ fontSize: 60,
1468
+ color: '#FFF',
1469
+ },
1470
+ bottomControls: {
1471
+ position: 'absolute',
1472
+ bottom: 20,
1473
+ left: 20,
1474
+ right: 20,
1475
+ flexDirection: 'row',
1476
+ alignItems: 'center',
1477
+ },
1478
+ time: {
1479
+ color: '#FFF',
1480
+ fontSize: 14,
1481
+ },
1482
+ slider: {
1483
+ flex: 1,
1484
+ height: 40,
1485
+ marginHorizontal: 10,
1486
+ },
1487
+ });
1488
+ ```
1489
+
1490
+ #### Step 5: Build and Deploy
1491
+
1492
+ **iOS:**
1493
+ ```bash
1494
+ # Development
1495
+ npx react-native run-ios
1496
+
1497
+ # Production
1498
+ cd ios
1499
+ xcodebuild archive \
1500
+ -workspace VideoPlayerApp.xcworkspace \
1501
+ -scheme VideoPlayerApp \
1502
+ -archivePath ~/Desktop/VideoPlayerApp.xcarchive
1503
+
1504
+ # Upload to App Store
1505
+ xcrun altool --upload-app \
1506
+ -f ~/Desktop/VideoPlayerApp.ipa \
1507
+ -u your@email.com \
1508
+ -p app-specific-password
1509
+ ```
1510
+
1511
+ **Android:**
1512
+ ```bash
1513
+ # Development
1514
+ npx react-native run-android
1515
+
1516
+ # Production
1517
+ cd android
1518
+ ./gradlew bundleRelease
1519
+
1520
+ # Sign and upload to Google Play Console
1521
+ ```
1522
+
1523
+ ---
1524
+
1525
+ ## ๐Ÿš€ Quick Testing Commands
1526
+
1527
+ ### Web
1528
+ ```bash
1529
+ npm run serve:demo
1530
+ open http://localhost:3000/apps/demo/demo.html
1531
+ ```
1532
+
1533
+ ### React Native
1534
+ ```bash
1535
+ # iOS Simulator
1536
+ npx react-native run-ios --simulator="iPhone 14 Pro"
1537
+
1538
+ # Android Emulator
1539
+ npx react-native run-android --deviceId=emulator-5554
1540
+ ```
1541
+
1542
+ ### Smart TV
1543
+ ```bash
1544
+ # Samsung Tizen
1545
+ tizen emulator --name TV-5.0
1546
+
1547
+ # LG webOS
1548
+ ares-launch-simulator TV_23
1549
+ ```
1550
+
1551
+ ### Roku
1552
+ ```bash
1553
+ # Use browser
1554
+ open http://<ROKU_IP>
1555
+ ```
1556
+
1557
+ ---
1558
+
1559
+ ## ๐Ÿ“ Common Issues and Solutions
1560
+
1561
+ ### Issue: iOS - UnifiedVideoPlayer.swiftmodule not built for arm64
1562
+ **Solution:** The framework needs to be built for the correct architecture:
1563
+ ```bash
1564
+ # Option 1: Use Swift Package Manager (auto-builds)
1565
+ File > Add Package Dependencies > Add Local Package
1566
+
1567
+ # Option 2: Build universal framework
1568
+ cd packages/ios
1569
+ ./build_framework.sh
1570
+
1571
+ # Option 3: Clean Xcode build
1572
+ Product > Clean Build Folder (โ‡งโŒ˜K)
1573
+ File > Packages > Reset Package Caches
1574
+ ```
1575
+
1576
+ ### Issue: iOS - Module 'UnifiedVideoPlayer' not found
1577
+ **Solution:** Ensure the framework is properly added:
1578
+ ```bash
1579
+ # Check target membership in Xcode
1580
+ 1. Select UnifiedVideoPlayer.framework
1581
+ 2. File Inspector > Target Membership
1582
+ 3. Check your app target
1583
+
1584
+ # Verify Build Phases
1585
+ 1. Target > Build Phases
1586
+ 2. Link Binary With Libraries should include UnifiedVideoPlayer
1587
+ 3. Embed Frameworks should include it with "Embed & Sign"
1588
+ ```
1589
+
1590
+ ### Issue: DRM Content Not Playing
1591
+ **Solution:** Ensure proper DRM configuration and certificates are in place for each platform.
1592
+
1593
+ ### Issue: CORS Errors in Web
1594
+ **Solution:** Configure proper CORS headers on your video server or use a proxy.
1595
+
1596
+ ### Issue: React Native Build Fails
1597
+ **Solution:** Clean and rebuild:
1598
+ ```bash
1599
+ cd ios && pod deintegrate && pod install
1600
+ cd android && ./gradlew clean
1601
+ npx react-native start --reset-cache
1602
+ ```
1603
+
1604
+ ### Issue: TV Remote Not Working
1605
+ **Solution:** Register proper key codes for each TV platform and test in actual device.
1606
+
1607
+ ---
1608
+
1609
+ ## ๐ŸŽฏ Production Checklist
1610
+
1611
+ - [ ] Configure proper DRM licenses for protected content
1612
+ - [ ] Set up CDN for video delivery
1613
+ - [ ] Implement analytics tracking
1614
+ - [ ] Add error reporting (Sentry, Crashlytics)
1615
+ - [ ] Configure app signing certificates
1616
+ - [ ] Test on actual devices
1617
+ - [ ] Implement adaptive bitrate streaming
1618
+ - [ ] Add offline download capability (mobile)
1619
+ - [ ] Configure background playback (mobile)
1620
+ - [ ] Implement Chromecast support
1621
+
1622
+ ---
1623
+
1624
+ ## ๐Ÿ“š Additional Resources
1625
+
1626
+ - [Web Platform Docs](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement)
1627
+ - [iOS AVPlayer Docs](https://developer.apple.com/documentation/avfoundation/avplayer)
1628
+ - [Android ExoPlayer Docs](https://exoplayer.dev/)
1629
+ - [Samsung Tizen Docs](https://developer.samsung.com/smarttv)
1630
+ - [LG webOS Docs](https://webostv.developer.lge.com/)
1631
+ - [Roku SDK Docs](https://developer.roku.com/docs/developer-program/getting-started/roku-dev-prog.md)
1632
+ - [Android TV Docs](https://developer.android.com/tv)
1633
+
1634
+ ---
1635
+
1636
+ This guide provides everything needed to implement the Unified Video Framework on any supported platform. Choose your target platform and follow the step-by-step instructions to get started!