react-native-media3-player 1.0.0 → 2.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/README.md CHANGED
@@ -5,14 +5,16 @@
5
5
  ---
6
6
 
7
7
  ## Table of Contents
8
- - [Installation](#installation)
9
- - [Usage](#usage)
10
- - [Props](#props)
11
- - [Events](#events)
12
- - [TypeScript Support](#typescript-support)
13
- - [Examples](#examples)
14
- - [Screenshots](#screenshots)
15
- - [Contributing](#contributing)
8
+
9
+ - [Installation](#installation)
10
+ - [Usage](#usage)
11
+ - [Props](#props)
12
+ - [DRM Support (Widevine)](#drm-support-widevine)
13
+ - [Events](#events)
14
+ - [TypeScript Support](#typescript-support)
15
+ - [Examples](#examples)
16
+ - [Screenshots](#screenshots)
17
+ - [Contributing](#contributing)
16
18
  - [License](#license)
17
19
 
18
20
  ---
@@ -20,38 +22,85 @@
20
22
  ## Installation
21
23
 
22
24
  **Install via npm:**
25
+
23
26
  ```bash
24
27
  npm install react-native-media3-player
25
28
  ```
26
29
 
27
30
  **Or yarn:**
31
+
28
32
  ```bash
29
33
  yarn add react-native-media3-player
30
34
  ```
31
35
 
32
- > Requires **React Native >= 0.70**
36
+ > Requires **React Native >= 0.70**
37
+
38
+ ---
39
+
40
+ **Note:** To use a specific Media3 version, add or update the following in your **android/build.gradle** (Project-level) file:
41
+
42
+ ```groovy
43
+ buildscript {
44
+ ext {
45
+ media3Version = "1.4.1" // Set your desired Media3 version
46
+ }
47
+ }
48
+ ```
49
+
50
+ Be sure to sync your project after making this change.
33
51
 
34
52
  ---
35
53
 
36
54
  ## Usage
37
55
 
56
+ **Basic (non-DRM):**
57
+
38
58
  ```jsx
39
59
  import React from 'react';
40
- import { View } from 'react-native';
60
+ import {View} from 'react-native';
41
61
  import Media3Player from 'react-native-media3-player';
42
62
 
43
63
  export default function App() {
44
64
  return (
45
- <View style={{ flex: 1 }}>
65
+ <View style={{flex: 1}}>
46
66
  <Media3Player
47
- style={{ width: '100%', height: 250 }}
48
- source={{ uri: 'https://example.com/video.mp4' }}
67
+ style={{width: '100%', height: 250}}
68
+ source={{uri: 'https://example.com/video.mp4'}}
49
69
  autoplay={true}
50
70
  play={true}
51
71
  mute={false}
52
72
  onReady={() => console.log('Player is ready')}
53
73
  onEnd={() => console.log('Video ended')}
54
- onError={(error) => console.log('Player error:', error.message)}
74
+ onError={error => console.log('Player error:', error.message)}
75
+ />
76
+ </View>
77
+ );
78
+ }
79
+ ```
80
+
81
+ **DRM-protected content (Widevine):**
82
+
83
+ ```jsx
84
+ import React from 'react';
85
+ import {View} from 'react-native';
86
+ import Media3Player from 'react-native-media3-player';
87
+
88
+ export default function App() {
89
+ return (
90
+ <View style={{flex: 1}}>
91
+ <Media3Player
92
+ style={{width: '100%', height: 250}}
93
+ source={{
94
+ uri: 'https://storage.googleapis.com/shaka-demo-assets/angel-one-widevine/dash.mpd',
95
+ drm: {
96
+ licenseUrl: 'https://cwip-shaka-proxy.appspot.com/no_auth',
97
+ },
98
+ }}
99
+ autoplay={true}
100
+ play={true}
101
+ onReady={() => console.log('Player is ready')}
102
+ onEnd={() => console.log('Video ended')}
103
+ onError={error => console.log('Player error:', error.message)}
55
104
  />
56
105
  </View>
57
106
  );
@@ -62,23 +111,87 @@ export default function App() {
62
111
 
63
112
  ## Props
64
113
 
65
- | Prop | Type | Default | Description |
66
- |------------|-----------------------------|----------|-------------|
67
- | `source` | `{ uri: string }` | required | Video source object. Must include a valid URI. |
68
- | `autoplay` | `boolean` | `false` | Automatically start playback when the video is ready. |
69
- | `play` | `boolean` | `false` | Controls whether the player is playing. Overrides autoplay. |
70
- | `mute` | `boolean` | `false` | Mutes or unmutes the video. |
71
- | `style` | `ViewStyle` or `ViewStyle[]` | `{ width: '100%', height: 250 }` | Styling for the player container. |
114
+ | Prop | Type | Default | Description |
115
+ | ---------- | ---------------------------- | -------------------------------- | ------------------------------------------------------------------------ |
116
+ | `source` | `Source` | required | Video source object. Must include a valid `uri`. Supports optional `drm` config. |
117
+ | `autoplay` | `boolean` | `false` | Automatically start playback when the video is ready. |
118
+ | `play` | `boolean` | `false` | Controls whether the player is playing. Overrides autoplay. |
119
+ | `mute` | `boolean` | `false` | Mutes or unmutes the video. |
120
+ | `style` | `ViewStyle` or `ViewStyle[]` | `{ width: '100%', height: 250 }` | Styling for the player container. |
121
+
122
+ ### `Source` Object
123
+
124
+ | Property | Type | Required | Description |
125
+ | -------- | ----------- | -------- | ---------------------------------------------------- |
126
+ | `uri` | `string` | Yes | The URI of the media to play (MP4, DASH, HLS, etc.). |
127
+ | `drm` | `DRMConfig` | No | DRM configuration for protected content. |
128
+
129
+ ### `DRMConfig` Object
130
+
131
+ | Property | Type | Required | Description |
132
+ | ------------ | --------------------------------- | -------- | ----------------------------------------------------- |
133
+ | `licenseUrl` | `string` | Yes | The Widevine license server URL. |
134
+ | `headers` | `Array<{ key: string, value: string }>` | No | Custom headers to include in the DRM license request. |
135
+
136
+ ---
137
+
138
+ ## DRM Support (Widevine)
139
+
140
+ This library supports **Widevine DRM** for playing protected DASH streams on Android. To enable DRM, pass a `drm` object inside the `source` prop.
141
+
142
+ ### Basic DRM Playback
143
+
144
+ ```jsx
145
+ <Media3Player
146
+ source={{
147
+ uri: 'https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd',
148
+ drm: {
149
+ licenseUrl: 'https://proxy.uat.widevine.com/proxy?provider=widevine_test',
150
+ },
151
+ }}
152
+ autoplay
153
+ play
154
+ />
155
+ ```
156
+
157
+ ### DRM with Custom License Headers
158
+
159
+ Some license servers require authentication tokens or custom headers. Pass them as an array of `{ key, value }` objects:
160
+
161
+ ```jsx
162
+ <Media3Player
163
+ source={{
164
+ uri: 'https://example.com/protected-stream/manifest.mpd',
165
+ drm: {
166
+ licenseUrl: 'https://license.example.com/widevine',
167
+ headers: [
168
+ {key: 'Authorization', value: 'Bearer <your-token>'},
169
+ {key: 'X-Custom-Header', value: 'custom-value'},
170
+ ],
171
+ },
172
+ }}
173
+ autoplay
174
+ play
175
+ onError={e => console.error('DRM error:', e.message)}
176
+ />
177
+ ```
178
+
179
+ ### Notes
180
+
181
+ - DRM is currently supported on **Android only** (Widevine L1/L3 depending on device).
182
+ - The `source.uri` should point to a DASH (`.mpd`) stream encrypted with Widevine.
183
+ - Multi-session DRM is enabled by default for streams that require it.
184
+ - If the license request fails, the `onError` callback will fire with the error details.
72
185
 
73
186
  ---
74
187
 
75
188
  ## Events
76
189
 
77
- | Event | Callback Signature | Description |
78
- |------------|----------------------------------|-------------|
79
- | `onReady` | `() => void` | Fired when the player is ready to play. |
80
- | `onEnd` | `() => void` | Fired when the video reaches the end. |
81
- | `onError` | `(error: { message: string }) => void` | Fired when the player encounters an error. Error object includes a `message` property. |
190
+ | Event | Callback Signature | Description |
191
+ | --------- | -------------------------------------- | -------------------------------------------------------------------------------------- |
192
+ | `onReady` | `() => void` | Fired when the player is ready to play. |
193
+ | `onEnd` | `() => void` | Fired when the video reaches the end. |
194
+ | `onError` | `(error: { message: string }) => void` | Fired when the player encounters an error. Error object includes a `message` property. |
82
195
 
83
196
  ---
84
197
 
@@ -88,18 +201,38 @@ This library includes TypeScript definitions. Example:
88
201
 
89
202
  ```ts
90
203
  import React from 'react';
91
- import { ViewStyle } from 'react-native';
92
- import Media3Player, { Media3PlayerProps } from 'react-native-media3-player';
204
+ import {ViewStyle} from 'react-native';
205
+ import Media3Player, {Media3PlayerProps} from 'react-native-media3-player';
93
206
 
94
207
  const props: Media3PlayerProps = {
95
- source: { uri: 'https://example.com/video.mp4' },
208
+ source: {uri: 'https://example.com/video.mp4'},
96
209
  autoplay: true,
97
210
  play: true,
98
211
  mute: false,
99
- style: { width: '100%', height: 250 },
212
+ style: {width: '100%', height: 250},
100
213
  onReady: () => console.log('Ready'),
101
214
  onEnd: () => console.log('End'),
102
- onError: (err) => console.log(err.message),
215
+ onError: err => console.log(err.message),
216
+ };
217
+ ```
218
+
219
+ **With DRM:**
220
+
221
+ ```ts
222
+ const drmProps: Media3PlayerProps = {
223
+ source: {
224
+ uri: 'https://storage.googleapis.com/shaka-demo-assets/angel-one-widevine/dash.mpd',
225
+ drm: {
226
+ licenseUrl: 'https://cwip-shaka-proxy.appspot.com/no_auth',
227
+ headers: [
228
+ {key: 'Authorization', value: 'Bearer my-token'},
229
+ ],
230
+ },
231
+ },
232
+ autoplay: true,
233
+ play: true,
234
+ style: {width: '100%', height: 250},
235
+ onError: err => console.log('DRM Error:', err.message),
103
236
  };
104
237
  ```
105
238
 
@@ -108,26 +241,59 @@ const props: Media3PlayerProps = {
108
241
  ## Examples
109
242
 
110
243
  **Basic Player:**
244
+
111
245
  ```jsx
112
- <Media3Player source={{ uri: 'https://example.com/video.mp4' }} />
246
+ <Media3Player source={{uri: 'https://example.com/video.mp4'}} />
113
247
  ```
114
248
 
115
249
  **Autoplay & Mute:**
250
+
116
251
  ```jsx
117
- <Media3Player
118
- source={{ uri: 'https://example.com/video.mp4' }}
119
- autoplay
120
- mute
121
- />
252
+ <Media3Player source={{uri: 'https://example.com/video.mp4'}} autoplay mute />
122
253
  ```
123
254
 
124
255
  **Event Handling:**
256
+
125
257
  ```jsx
126
258
  <Media3Player
127
- source={{ uri: 'https://example.com/video.mp4' }}
259
+ source={{uri: 'https://example.com/video.mp4'}}
128
260
  onReady={() => console.log('Ready')}
129
261
  onEnd={() => console.log('End')}
130
- onError={(err) => console.error(err.message)}
262
+ onError={err => console.error(err.message)}
263
+ />
264
+ ```
265
+
266
+ **Widevine DRM Stream:**
267
+
268
+ ```jsx
269
+ <Media3Player
270
+ source={{
271
+ uri: 'https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd',
272
+ drm: {
273
+ licenseUrl: 'https://proxy.uat.widevine.com/proxy?provider=widevine_test',
274
+ },
275
+ }}
276
+ autoplay
277
+ play
278
+ />
279
+ ```
280
+
281
+ **DRM with License Headers:**
282
+
283
+ ```jsx
284
+ <Media3Player
285
+ source={{
286
+ uri: 'https://example.com/protected/manifest.mpd',
287
+ drm: {
288
+ licenseUrl: 'https://license.example.com/widevine',
289
+ headers: [
290
+ {key: 'Authorization', value: 'Bearer my-token'},
291
+ ],
292
+ },
293
+ }}
294
+ autoplay
295
+ play
296
+ onError={err => console.error('DRM Error:', err.message)}
131
297
  />
132
298
  ```
133
299
 
@@ -135,13 +301,13 @@ const props: Media3PlayerProps = {
135
301
 
136
302
  ## Contributing
137
303
 
138
- We welcome contributions!
304
+ We welcome contributions!
139
305
 
140
- 1. Fork the repo
141
- 2. Create a branch (`git checkout -b feature/new-feature`)
142
- 3. Commit your changes (`git commit -m 'Add feature'`)
143
- 4. Push to the branch (`git push origin feature/new-feature`)
144
- 5. Open a Pull Request
306
+ 1. Fork the repo
307
+ 2. Create a branch (`git checkout -b feature/new-feature`)
308
+ 3. Commit your changes (`git commit -m 'Add feature'`)
309
+ 4. Push to the branch (`git push origin feature/new-feature`)
310
+ 5. Open a Pull Request
145
311
 
146
312
  > Please follow [Semantic Versioning](https://semver.org/) for commits.
147
313
 
@@ -150,4 +316,3 @@ We welcome contributions!
150
316
  ## License
151
317
 
152
318
  MIT © [Mohammad Rehan](https://github.com/mdRehan991)
153
-
@@ -5,6 +5,11 @@ apply plugin: "org.jetbrains.kotlin.android"
5
5
  // Apply the React Native plugin to enable React Native integration
6
6
  apply plugin: "com.facebook.react"
7
7
 
8
+ // Use app-defined version if available, otherwise fallback
9
+ def media3Version = rootProject.ext.has("media3Version")
10
+ ? rootProject.ext.media3Version
11
+ : "1.4.1"
12
+
8
13
  android {
9
14
  // Set the namespace for the generated R class and manifest
10
15
  namespace "com.rnmedia3playerdemo"
@@ -46,9 +51,17 @@ dependencies {
46
51
  implementation "org.jetbrains.kotlin:kotlin-stdlib"
47
52
 
48
53
  // AndroidX Media3 ExoPlayer, UI components, and Common classes
49
- implementation "androidx.media3:media3-exoplayer:1.4.1"
50
- implementation "androidx.media3:media3-ui:1.4.1"
51
- implementation "androidx.media3:media3-common:1.4.1"
54
+ implementation "androidx.media3:media3-exoplayer:$media3Version"
55
+ implementation "androidx.media3:media3-ui:$media3Version"
56
+ implementation "androidx.media3:media3-common:$media3Version"
57
+
58
+ // streaming formats
59
+ implementation "androidx.media3:media3-exoplayer-dash:$media3Version"
60
+ implementation "androidx.media3:media3-exoplayer-hls:$media3Version"
61
+ implementation "androidx.media3:media3-exoplayer-smoothstreaming:$media3Version"
62
+
63
+ // OkHttp data source for Media3 (DRM/auth/advanced networking)
64
+ implementation "androidx.media3:media3-datasource-okhttp:$media3Version"
52
65
 
53
66
  // Android core utilities and extensions (KTX)
54
67
  implementation "androidx.core:core-ktx:1.13.1"
@@ -14,6 +14,7 @@ import com.facebook.react.bridge.ReactContext
14
14
  import com.facebook.react.bridge.WritableMap
15
15
  import com.facebook.react.uimanager.UIManagerHelper
16
16
  import com.facebook.react.uimanager.events.Event
17
+ import androidx.media3.common.C
17
18
 
18
19
  /**
19
20
  * Custom view that wraps ExoPlayer and PlayerView, integrating with React Native.
@@ -83,19 +84,47 @@ class Media3PlayerView(context: Context) : FrameLayout(context) {
83
84
  }
84
85
 
85
86
  /**
86
- * Sets the video source from a URI string and prepares the player.
87
- * @param uriString Video source URI as a string
87
+ * Sets the video source (with optional DRM support) and prepares the player.
88
+ * Called from the ViewManager when the JS "source" prop changes.
89
+ *
90
+ * @param uriString The URI of the media source to play.
91
+ * @param licenseUrl If provided, enables DRM playback with this license URL.
92
+ * @param headers Optional headers to add to DRM license requests.
88
93
  */
89
- fun setSource(uriString: String?) {
94
+ fun setSource(
95
+ uriString: String?,
96
+ licenseUrl: String?,
97
+ headers: Map<String, String>?
98
+ ) {
99
+ // Return early if no valid URI is provided
90
100
  if (uriString.isNullOrEmpty()) return
91
101
  sourceUri = uriString
92
102
 
93
- // Ensure player is initialized
103
+ // Ensure the ExoPlayer instance is initialized before use
94
104
  initializePlayer()
95
105
 
96
- // Set and prepare the media item for playback
97
- val item = MediaItem.fromUri(Uri.parse(uriString))
98
- exoPlayer?.setMediaItem(item)
106
+ val uri = Uri.parse(uriString)
107
+ // Build the MediaItem, adding DRM configuration if a license URL is given
108
+ val mediaItem =
109
+ if (!licenseUrl.isNullOrEmpty()) {
110
+ val drmBuilder = MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
111
+ .setLicenseUri(licenseUrl) // set the license URL
112
+ .setMultiSession(true) // allow multiple DRM sessions if the stream requires them
113
+
114
+ headers?.let {
115
+ drmBuilder.setLicenseRequestHeaders(it)
116
+ }
117
+
118
+ MediaItem.Builder()
119
+ .setUri(uri)
120
+ .setDrmConfiguration(drmBuilder.build())
121
+ .build()
122
+ } else {
123
+ // No DRM: simple MediaItem from URI
124
+ MediaItem.fromUri(uri)
125
+ }
126
+ // Set the media item on the player and prepare for playback
127
+ exoPlayer?.setMediaItem(mediaItem)
99
128
  exoPlayer?.prepare()
100
129
  }
101
130
 
@@ -45,14 +45,35 @@ class Media3PlayerViewManager :
45
45
  }
46
46
 
47
47
  /**
48
- * Sets the "source" property on the Media3PlayerView.
49
- * Reads the "uri" string from the passed ReadableMap and loads it into the native view.
48
+ * Handles the "source" prop for Media3PlayerView.
49
+ * Extracts the "uri", optional DRM license URL, and headers from the ReadableMap and forwards
50
+ * them to the view for loading the media source and DRM configuration.
50
51
  */
51
52
  @ReactProp(name = "source")
52
53
  override fun setSource(view: Media3PlayerView, source: ReadableMap?) {
53
54
  val uri = source?.getString("uri")
55
+ var licenseUrl: String? = null
56
+ var headers: Map<String, String>? = null
57
+ val drmMap = source?.getMap("drm")
58
+
59
+ if (drmMap != null) {
60
+ licenseUrl = drmMap.getString("licenseUrl")
61
+ val headersArray = drmMap.getArray("headers")
62
+ if (headersArray != null) {
63
+ val map = mutableMapOf<String, String>()
64
+ for (i in 0 until headersArray.size()) {
65
+ val entry = headersArray.getMap(i)
66
+ val key = entry?.getString("key")
67
+ val value = entry?.getString("value")
68
+ if (key != null && value != null) {
69
+ map[key] = value
70
+ }
71
+ }
72
+ headers = map
73
+ }
74
+ }
54
75
  if (!uri.isNullOrEmpty()) {
55
- view.setSource(uri)
76
+ view.setSource(uri, licenseUrl, headers)
56
77
  }
57
78
  }
58
79
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-media3-player",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "React Native Media3 Player component for Android (ExoPlayer 3 integration).",
5
5
  "main": "index.js",
6
6
  "react-native": "index.js",
@@ -5,6 +5,22 @@ import type {DirectEventHandler} from 'react-native/Libraries/Types/CodegenTypes
5
5
  // Import codegenNativeComponent to register the native UI component for use in React Native
6
6
  import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
7
7
 
8
+ type HeaderEntry = Readonly<{
9
+ key: string;
10
+ value: string;
11
+ }>;
12
+
13
+ type DRMConfig = Readonly<{
14
+ licenseUrl: string;
15
+ headers?: ReadonlyArray<HeaderEntry>;
16
+ }>;
17
+ // Source defines the media source for the player component.
18
+ // - uri: The URI of the media to be played (required).
19
+ // - drm: (Optional) DRM configuration for protected content.
20
+ type Source = Readonly<{
21
+ uri: string;
22
+ drm?: DRMConfig;
23
+ }>;
8
24
  // Event type emitted when the player is ready
9
25
  type OnReadyEvent = Readonly<{}>;
10
26
  // Event type emitted when playback reaches the end
@@ -17,7 +33,7 @@ type OnErrorEvent = Readonly<{
17
33
  // Props supported by the native Media3PlayerView component
18
34
  export interface NativeProps extends ViewProps {
19
35
  // Source URI for the media to play
20
- source?: Readonly<{uri: string}>;
36
+ source?: Source;
21
37
  // Whether playback should start automatically when ready
22
38
  autoplay?: boolean;
23
39
  // Whether playback should currently be running (true = play, false = pause)