UNPKG

@jwplayer/jwplayer-react-native

Version:

React-native Android/iOS plugin for JWPlayer SDK (https://www.jwplayer.com/)

694 lines (565 loc) 16.1 kB
# Platform Differences Guide This guide documents the differences between iOS and Android platforms when using JWPlayer React Native. ## Table of Contents - [Overview](#overview) - [Configuration Naming Differences](#configuration-naming-differences) - [iOS-Specific Features](#ios-specific-features) - [Android-Specific Features](#android-specific-features) - [Cross-Platform Best Practices](#cross-platform-best-practices) - [Common Gotchas](#common-gotchas) --- ## Overview The JWPlayer React Native wrapper provides a **unified configuration interface** that works across both platforms. However, some features are platform-specific due to native SDK differences. ### Platform Detection ```typescript import { Platform } from 'react-native'; const config: JWPlayerConfig = { license: Platform.OS === 'ios' ? 'IOS_LICENSE' : 'ANDROID_LICENSE', file: 'https://example.com/video.m3u8', // Platform-specific feature ...(Platform.OS === 'ios' && { styling: { colors: { buttons: '#FF0000' } } }), ...(Platform.OS === 'android' && { uiConfig: { hasControlbar: true, hasOverlay: true } }) }; ``` --- ## Configuration Naming Differences ### IMA DAI Settings **Field Name Differences:** | Feature | iOS Parser | Android Parser | **Recommended** | |---------|-----------|----------------|-----------------| | DAI Settings Object | `googimadai` | `imaDaiSettings` | `imaDaiSettings` | | DAI Settings (alternate) | `googleimadaisettings` | `imaDaiSettings` | `imaDaiSettings` | | IMA Settings Object | `imaSettings` | `imaSdkSettings` | `imaSdkSettings` | | Video ID | `videoID` | `videoId` | `videoId` | | CMS ID | `cmsID` | `cmsId` | `cmsId` | **✅ Recommended Cross-Platform Config:** ```typescript const config: JWPlayerConfig = { license: 'YOUR_LICENSE_KEY', file: 'https://example.com/video.m3u8', advertising: { client: 'dai', // Works on both platforms imaDaiSettings: { // Preferred naming videoId: 'tears-of-steel', // camelCase cmsId: '2528370', // camelCase streamType: 'hls' }, imaSdkSettings: { // Preferred naming language: 'en' } } }; ``` **iOS-Specific Naming (also supported):** ```typescript // iOS can also use these alternate names const iosConfig = { advertising: { client: 'GoogleIMADAI', // iOS naming googimadai: { // iOS naming videoID: '...', // Uppercase ID cmsID: '...' // Uppercase ID }, imaSettings: { // iOS uses "imaSettings" locale: 'en' } } }; ``` ### Ad Client Values | Ad Type | iOS Values | Android Values | **Recommended** | |---------|-----------|----------------|-----------------| | VAST | `'vast'`, `'VAST'` | `'vast'`, `'VAST'` | `'vast'` | | Google IMA | `'googima'` | `'googima'`, `'IMA'`, `'GOOGIMA'` | `'googima'` | | Google DAI | `'dai'`, `'GoogleIMADAI'` | `'IMA_DAI'` | `'dai'` | **Example:** ```typescript // Cross-platform advertising: { client: 'dai' } // Platform-specific advertising: { client: 'GoogleIMADAI' } // iOS only advertising: { client: 'IMA_DAI' } // Android only ``` ### Playlist Item Fields | Field | iOS | Android | **Recommended** | |-------|-----|---------|-----------------| | Media ID | `mediaid` | `mediaid` | `mediaId` | | Ad Schedule | `adschedule` | `adschedule` | `adSchedule` | | Feed ID | `feedid` | `feedid` | `feedId` | | DAI Settings | `daiSetting` | `imaDaiSettings` | `imaDaiSettings` | | Start Time | `starttime` | `starttime` | `startTime` | --- ## iOS-Specific Features ### 1. Styling Configuration **iOS Only** - Android uses XML styling ```typescript const config: JWPlayerConfig = { license: 'YOUR_LICENSE_KEY', file: 'https://example.com/video.m3u8', styling: { // ⚠️ iOS ONLY colors: { buttons: '#FF0000', backgroundColor: '#000000', fontColor: '#FFFFFF', timeslider: { thumb: '#FF0000', rail: '#808080', slider: '#FF0000' } }, font: { name: 'Helvetica', size: 14 }, showTitle: true, showDesc: true } }; ``` **Android Alternative:** Use XML styling instead. See [Android Styling Guide](https://docs.jwplayer.com/players/docs/android-styling-guide). ### 2. Audio Session Configuration **iOS Only** - Controls background audio behavior ```typescript const config: JWPlayerConfig = { license: 'YOUR_LICENSE_KEY', file: 'https://example.com/video.m3u8', backgroundAudioEnabled: true, // ⚠️ iOS ONLY category: 'Playback', // ⚠️ iOS ONLY categoryOptions: [ // ⚠️ iOS ONLY 'MixWithOthers', 'DuckOthers' ], mode: 'MoviePlayback' // ⚠️ iOS ONLY }; ``` **Audio Session Categories:** - `Ambient`: Mix with other audio, silence when locked - `SoloAmbient`: Default, silences other audio - `Playback`: For media playback - `Record`: For recording - `PlayAndRecord`: For VoIP - `MultiRoute`: Multiple audio routes **Category Options:** - `MixWithOthers`: Mix with other audio - `DuckOthers`: Lower volume of other audio - `AllowBluetooth`: Allow Bluetooth A2DP - `DefaultToSpeaker`: Route to speaker by default - `InterruptSpokenAudioAndMix`: Interrupt spoken audio - `AllowAirPlay`: Allow AirPlay ### 3. Caption Styling **iOS Only** - Customize caption appearance ```typescript const config: JWPlayerConfig = { license: 'YOUR_LICENSE_KEY', file: 'https://example.com/video.m3u8', styling: { // ⚠️ iOS ONLY captionsStyle: { fontColor: '#FFFF00', fontSize: 16, backgroundColor: '#000000', backgroundOpacity: 75, windowColor: '#000000', windowOpacity: 0, edgeStyle: 'dropshadow', // 'none' | 'dropshadow' | 'raised' | 'depressed' | 'uniform' fontFamily: 'Helvetica' } } }; ``` **Android:** Uses system accessibility settings for captions. ### 4. Interface Control **iOS Only** - Control UI behavior ```typescript const config: JWPlayerConfig = { license: 'YOUR_LICENSE_KEY', file: 'https://example.com/video.m3u8', interfaceBehavior: 'normal', // ⚠️ iOS ONLY: 'normal' | 'hidden' | 'onscreen' interfaceFadeDelay: 3, // ⚠️ iOS ONLY: Seconds before UI fades hideUIGroups: [ // ⚠️ iOS ONLY 'settings_menu', 'casting_menu', 'quality_submenu' ] }; ``` ### 5. FairPlay DRM **iOS Only** - FairPlay DRM ```typescript const config: JWPlayerConfig = { license: 'YOUR_LICENSE_KEY', sources: [{ file: 'https://example.com/video.m3u8', drm: { fairplay: { // ⚠️ iOS ONLY processSpcUrl: 'https://drm-server.com/spc', certificateUrl: 'https://drm-server.com/cert' } } }], // Global FairPlay settings (iOS only) processSpcUrl: 'https://drm-server.com/spc', fairplayCertUrl: 'https://drm-server.com/cert', contentUUID: 'unique-content-id' }; ``` ### 6. External Playback (AirPlay) **iOS Only** ```typescript const config: JWPlayerConfig = { license: 'YOUR_LICENSE_KEY', file: 'https://example.com/video.m3u8', externalPlaybackSettings: { // ⚠️ iOS ONLY playbackEnabled: true, usesExternalPlaybackWhileExternalScreenIsActive: true, videoGravity: 'resizeAspect' } }; ``` --- ## Android-Specific Features ### 1. UI Configuration **Android Only** - Granular UI control ```typescript const config: JWPlayerConfig = { license: 'YOUR_LICENSE_KEY', file: 'https://example.com/video.m3u8', uiConfig: { // ⚠️ ANDROID ONLY hasOverlay: true, hasControlbar: true, hasCenterControls: true, hasNextUp: true, hasQualitySubMenu: true, hasCaptionsSubMenu: true, hasPlaybackRatesSubMenu: true, hasMenu: true, hasAds: true } }; ``` **⚠️ Important:** When using `uiConfig`, ALL unspecified elements default to `false`. You must explicitly enable each element you want visible. **iOS Alternative:** Use `hideUIGroups` or `interfaceBehavior`. ### 2. TextureView **Android Only** ```typescript const config: JWPlayerConfig = { license: 'YOUR_LICENSE_KEY', file: 'https://example.com/video.m3u8', useTextureView: true // ⚠️ ANDROID ONLY }; ``` **Use Case:** Required for applying transformations (rotation, scaling) to the video view. ### 3. Ad Rules **Android Primary** (iOS has limited support) ```typescript const config: JWPlayerConfig = { license: 'YOUR_LICENSE_KEY', playlist: [...], advertising: { client: 'vast', schedule: [...], rules: { startOn: 2, // Both platforms frequency: 2, // Both platforms timeBetweenAds: 300, // ⚠️ ANDROID ONLY startOnSeek: 'pre' // ⚠️ ANDROID ONLY } } }; ``` **Platform Support:** - `startOn`: iOS, Android - `frequency`: iOS, Android - `timeBetweenAds`: iOS, Android - `startOnSeek`: iOS, Android ### 4. Widevine DRM **Android Only** - Widevine DRM ```typescript const config: JWPlayerConfig = { license: 'YOUR_LICENSE_KEY', sources: [{ file: 'https://example.com/video.mpd', drm: { widevine: { // ⚠️ ANDROID ONLY url: 'https://license-server.com/license', keySetId: 'optional-key-id' } } }] }; ``` ### 5. HTTP Headers **Android Only** ```typescript const playlistItem: JWPlaylistItem = { file: 'https://example.com/video.m3u8', httpheaders: { // ⚠️ ANDROID ONLY 'Authorization': 'Bearer token', 'Custom-Header': 'value' } }; ``` ### 6. Cross-Protocol Redirects **Android Only** ```typescript const config: JWPlayerConfig = { license: 'YOUR_LICENSE_KEY', file: 'https://example.com/video.m3u8', allowCrossProtocolRedirectsSupport: true // ⚠️ ANDROID ONLY }; ``` ### 7. Display Title/Description **Android Only** (iOS always shows based on `showTitle`/`showDesc` in styling) ```typescript const config: JWPlayerConfig = { license: 'YOUR_LICENSE_KEY', file: 'https://example.com/video.m3u8', displaytitle: true, // ⚠️ ANDROID ONLY displaydescription: true // ⚠️ ANDROID ONLY }; ``` --- ## Cross-Platform Best Practices ### 1. Use Conditional Configuration ```typescript import { Platform } from 'react-native'; const config: JWPlayerConfig = { license: Platform.OS === 'ios' ? IOS_LICENSE : ANDROID_LICENSE, file: 'https://example.com/video.m3u8', autostart: true, // iOS-specific features ...(Platform.OS === 'ios' && { styling: { colors: { buttons: '#FF0000' } }, backgroundAudioEnabled: true }), // Android-specific features ...(Platform.OS === 'android' && { uiConfig: { hasControlbar: true, hasOverlay: true }, useTextureView: true }) }; ``` ### 2. Unified Naming Convention Always use the **recommended** cross-platform naming: ```typescript // GOOD - Cross-platform const config: JWPlayerConfig = { advertising: { client: 'dai', imaDaiSettings: { videoId: 'tears-of-steel', cmsId: '2528370' }, imaSdkSettings: { language: 'en' } } }; // AVOID - Platform-specific naming const config: JWPlayerConfig = { advertising: { client: 'GoogleIMADAI', // iOS-specific googimadai: { // iOS-specific videoID: '...', // Uppercase (iOS) cmsID: '...' // Uppercase (iOS) } } }; ``` ### 3. Handle DRM Properly ```typescript import { Platform } from 'react-native'; const getDrmConfig = () => { if (Platform.OS === 'ios') { return { fairplay: { processSpcUrl: 'https://drm.example.com/fps', certificateUrl: 'https://drm.example.com/cert' } }; } else { return { widevine: { url: 'https://drm.example.com/widevine' } }; } }; const config: JWPlayerConfig = { license: Platform.OS === 'ios' ? IOS_LICENSE : ANDROID_LICENSE, sources: [{ file: Platform.OS === 'ios' ? 'https://example.com/fairplay.m3u8' : 'https://example.com/widevine.mpd', label: '1080p', drm: getDrmConfig() }] }; ``` ### 4. Test on Both Platforms Always test configurations on both iOS and Android devices: ```typescript // Helper function for testing export const validateConfig = (config: JWPlayerConfig): string[] => { const warnings: string[] = []; if (Platform.OS === 'android' && config.styling) { warnings.push('`styling` is iOS-only. Use XML styling on Android.'); } if (Platform.OS === 'ios' && config.uiConfig) { warnings.push('`uiConfig` is Android-only. iOS uses native UI.'); } if (Platform.OS === 'ios' && config.useTextureView) { warnings.push('`useTextureView` is Android-only.'); } return warnings; }; ``` --- ## Common Gotchas ### 1. IMA DAI Naming Confusion **Wrong:** ```typescript // Mixing iOS and Android naming advertising: { client: 'GoogleIMADAI', // iOS naming imaDaiSettings: { // Android naming videoID: '...', // iOS casing cmsId: '...' // Android casing } } ``` **Correct:** ```typescript advertising: { client: 'dai', // Cross-platform imaDaiSettings: { // Preferred videoId: '...', // camelCase cmsId: '...' // camelCase } } ``` ### 2. UI Configuration Defaults **Wrong (Android):** ```typescript // Only specifying some UI elements uiConfig: { hasControlbar: true // Oops! All other elements are now hidden } ``` **Correct (Android):** ```typescript // Specify ALL elements you want visible uiConfig: { hasOverlay: true, hasControlbar: true, hasCenterControls: true, hasNextUp: true, hasQualitySubMenu: true, hasCaptionsSubMenu: true, hasMenu: true, hasAds: true } ``` ### 3. License Keys **Wrong:** ```typescript // Using same license for both platforms const config = { license: 'SINGLE_LICENSE_KEY' // Won't work! }; ``` **Correct:** ```typescript import { Platform } from 'react-native'; const config = { license: Platform.OS === 'ios' ? process.env.IOS_LICENSE_KEY : process.env.ANDROID_LICENSE_KEY }; ``` ### 4. Styling vs UI Config **Wrong:** ```typescript // Trying to use both const config = { styling: { ... }, // iOS uiConfig: { ... } // Android // Will be ignored on wrong platform }; ``` **Correct:** ```typescript const config = { ...(Platform.OS === 'ios' && { styling: { ... } }), ...(Platform.OS === 'android' && { uiConfig: { ... } }) }; ``` ### 5. Ad Rules Assumptions **Wrong:** ```typescript // Assuming full ad rules support on iOS advertising: { client: 'vast', rules: { startOn: 2, timeBetweenAds: 300 // iOS doesn't support this! } } ``` **Correct:** ```typescript advertising: { client: 'vast', rules: { startOn: 2, frequency: 2, ...(Platform.OS === 'android' && { timeBetweenAds: 300, startOnSeek: 'pre' }) } } ``` --- ## Summary Table | Feature | iOS | Android | Recommendation | |---------|-----|---------|----------------| | **Styling** | Full | Use XML | Platform-specific | | **UI Config** | Use hideUIGroups | Full | Platform-specific | | **Background Audio** | Full | | iOS only | | **Caption Styling** | Full | System settings | iOS only | | **FairPlay DRM** | | | iOS only | | **Widevine DRM** | | | Android only | | **Ad Rules (full)** | ⚠️ Limited | Full | Platform-aware | | **HTTP Headers** | | | Android only | | **TextureView** | | | Android only | | **AirPlay** | | | iOS only | | **IMA DAI** | | | Use `imaDaiSettings` | | **VAST/IMA** | | | Fully cross-platform | --- ## See Also - [Configuration Reference](./CONFIG-REFERENCE.md) - Complete config documentation - [Migration Guide](./MIGRATION-GUIDE.md) - Upgrading from legacy configs - [TypeScript Example](../Example/app/jsx/screens/TypeScriptExample.tsx) - Example with platform-specific configurations --- *Last Updated: October 2025* *SDK Version: JWPlayer React Native*