react-native-media3-player 0.0.1 → 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 +211 -46
- package/android/build.gradle +40 -9
- package/android/src/main/java/com/rnmedia3playerdemo/Media3Package.kt +17 -5
- package/android/src/main/java/com/rnmedia3playerdemo/Media3PlayerView.kt +113 -28
- package/android/src/main/java/com/rnmedia3playerdemo/Media3PlayerViewManager.kt +88 -22
- package/index.d.ts +5 -4
- package/package.json +11 -2
- package/src/Media3Player.js +21 -18
- package/src/Media3PlayerNativeComponent.ts +53 -0
package/README.md
CHANGED
|
@@ -5,14 +5,16 @@
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Table of Contents
|
|
8
|
-
|
|
9
|
-
- [
|
|
10
|
-
- [
|
|
11
|
-
- [
|
|
12
|
-
- [
|
|
13
|
-
- [
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
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 {
|
|
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={{
|
|
65
|
+
<View style={{flex: 1}}>
|
|
46
66
|
<Media3Player
|
|
47
|
-
style={{
|
|
48
|
-
source={{
|
|
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={
|
|
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
|
|
66
|
-
|
|
67
|
-
| `source` | `
|
|
68
|
-
| `autoplay` | `boolean`
|
|
69
|
-
| `play` | `boolean`
|
|
70
|
-
| `mute` | `boolean`
|
|
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
|
|
78
|
-
|
|
79
|
-
| `onReady`
|
|
80
|
-
| `onEnd`
|
|
81
|
-
| `onError`
|
|
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 {
|
|
92
|
-
import Media3Player, {
|
|
204
|
+
import {ViewStyle} from 'react-native';
|
|
205
|
+
import Media3Player, {Media3PlayerProps} from 'react-native-media3-player';
|
|
93
206
|
|
|
94
207
|
const props: Media3PlayerProps = {
|
|
95
|
-
source: {
|
|
208
|
+
source: {uri: 'https://example.com/video.mp4'},
|
|
96
209
|
autoplay: true,
|
|
97
210
|
play: true,
|
|
98
211
|
mute: false,
|
|
99
|
-
style: {
|
|
212
|
+
style: {width: '100%', height: 250},
|
|
100
213
|
onReady: () => console.log('Ready'),
|
|
101
214
|
onEnd: () => console.log('End'),
|
|
102
|
-
onError:
|
|
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={{
|
|
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={{
|
|
259
|
+
source={{uri: 'https://example.com/video.mp4'}}
|
|
128
260
|
onReady={() => console.log('Ready')}
|
|
129
261
|
onEnd={() => console.log('End')}
|
|
130
|
-
onError={
|
|
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
|
-
|
package/android/build.gradle
CHANGED
|
@@ -1,37 +1,68 @@
|
|
|
1
|
+
// Apply the Android library plugin
|
|
1
2
|
apply plugin: "com.android.library"
|
|
3
|
+
// Apply the Kotlin Android plugin for Kotlin language support
|
|
2
4
|
apply plugin: "org.jetbrains.kotlin.android"
|
|
5
|
+
// Apply the React Native plugin to enable React Native integration
|
|
6
|
+
apply plugin: "com.facebook.react"
|
|
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"
|
|
3
12
|
|
|
4
13
|
android {
|
|
14
|
+
// Set the namespace for the generated R class and manifest
|
|
5
15
|
namespace "com.rnmedia3playerdemo"
|
|
16
|
+
|
|
17
|
+
// Compile SDK version to build against
|
|
6
18
|
compileSdkVersion 35
|
|
7
19
|
|
|
8
20
|
defaultConfig {
|
|
21
|
+
// Minimum SDK version supported by the library
|
|
9
22
|
minSdkVersion 24
|
|
23
|
+
// Target SDK version for compatibility and behaviors
|
|
10
24
|
targetSdkVersion 34
|
|
11
25
|
}
|
|
12
26
|
|
|
13
27
|
compileOptions {
|
|
28
|
+
// Specify Java source and target compatibility
|
|
14
29
|
sourceCompatibility JavaVersion.VERSION_17
|
|
15
30
|
targetCompatibility JavaVersion.VERSION_17
|
|
16
31
|
}
|
|
17
32
|
|
|
18
33
|
kotlinOptions {
|
|
34
|
+
// Set the Kotlin JVM target version
|
|
19
35
|
jvmTarget = "17"
|
|
20
36
|
}
|
|
37
|
+
|
|
38
|
+
sourceSets {
|
|
39
|
+
main {
|
|
40
|
+
// Include codegen outputs (for TurboModules, Fabric, etc.) in the main sources
|
|
41
|
+
java.srcDirs += "${buildDir}/generated/source/codegen/java"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
21
44
|
}
|
|
22
45
|
|
|
23
46
|
dependencies {
|
|
24
|
-
//
|
|
25
|
-
|
|
47
|
+
// Dependency for React Native bridge (provided by the app, NOT bundled in aar)
|
|
48
|
+
compileOnly "com.facebook.react:react-android"
|
|
49
|
+
|
|
50
|
+
// Kotlin standard library dependency
|
|
51
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib"
|
|
52
|
+
|
|
53
|
+
// AndroidX Media3 ExoPlayer, UI components, and Common classes
|
|
54
|
+
implementation "androidx.media3:media3-exoplayer:$media3Version"
|
|
55
|
+
implementation "androidx.media3:media3-ui:$media3Version"
|
|
56
|
+
implementation "androidx.media3:media3-common:$media3Version"
|
|
26
57
|
|
|
27
|
-
//
|
|
28
|
-
implementation "
|
|
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"
|
|
29
62
|
|
|
30
|
-
//
|
|
31
|
-
implementation "androidx.media3:media3-
|
|
32
|
-
implementation "androidx.media3:media3-ui:1.4.1"
|
|
33
|
-
implementation "androidx.media3:media3-common:1.4.1"
|
|
63
|
+
// OkHttp data source for Media3 (DRM/auth/advanced networking)
|
|
64
|
+
implementation "androidx.media3:media3-datasource-okhttp:$media3Version"
|
|
34
65
|
|
|
35
|
-
//
|
|
66
|
+
// Android core utilities and extensions (KTX)
|
|
36
67
|
implementation "androidx.core:core-ktx:1.13.1"
|
|
37
68
|
}
|
|
@@ -2,13 +2,25 @@ package com.rnmedia3playerdemo
|
|
|
2
2
|
|
|
3
3
|
import com.facebook.react.ReactPackage
|
|
4
4
|
import com.facebook.react.bridge.NativeModule
|
|
5
|
-
import com.facebook.react.uimanager.ViewManager
|
|
6
5
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Media3Package integrates the custom Media3PlayerViewManager with React Native.
|
|
10
|
+
*
|
|
11
|
+
* This package does not register any NativeModules. It only provides the
|
|
12
|
+
* Media3PlayerViewManager for use as a native UI component from JavaScript.
|
|
13
|
+
*/
|
|
8
14
|
class Media3Package : ReactPackage {
|
|
9
|
-
|
|
15
|
+
/**
|
|
16
|
+
* No native modules are exported by this package.
|
|
17
|
+
*/
|
|
18
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
|
|
19
|
+
emptyList()
|
|
10
20
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Registers Media3PlayerViewManager as the only custom ViewManager for this package.
|
|
23
|
+
*/
|
|
24
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> =
|
|
25
|
+
listOf(Media3PlayerViewManager())
|
|
14
26
|
}
|
|
@@ -9,26 +9,33 @@ import androidx.media3.common.Player
|
|
|
9
9
|
import androidx.media3.common.PlaybackException
|
|
10
10
|
import androidx.media3.exoplayer.ExoPlayer
|
|
11
11
|
import androidx.media3.ui.PlayerView
|
|
12
|
-
import com.facebook.react.bridge.ReactContext
|
|
13
|
-
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
14
12
|
import com.facebook.react.bridge.Arguments
|
|
13
|
+
import com.facebook.react.bridge.ReactContext
|
|
14
|
+
import com.facebook.react.bridge.WritableMap
|
|
15
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
16
|
+
import com.facebook.react.uimanager.events.Event
|
|
17
|
+
import androidx.media3.common.C
|
|
15
18
|
|
|
16
19
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* Handles ExoPlayer playback and exposes events to React Native:
|
|
20
|
-
* - onReady
|
|
21
|
-
* - onEnd
|
|
22
|
-
* - onError
|
|
20
|
+
* Custom view that wraps ExoPlayer and PlayerView, integrating with React Native.
|
|
23
21
|
*/
|
|
24
22
|
class Media3PlayerView(context: Context) : FrameLayout(context) {
|
|
23
|
+
// ExoPlayer instance to handle media playback
|
|
25
24
|
private var exoPlayer: ExoPlayer? = null
|
|
25
|
+
// PlayerView to display video content
|
|
26
26
|
private var playerView: PlayerView
|
|
27
|
+
// Currently loaded media source URI
|
|
27
28
|
private var sourceUri: String? = null
|
|
29
|
+
// Should video autoplay when ready
|
|
28
30
|
private var autoplay: Boolean = false
|
|
31
|
+
// Play state controlled by the JS prop
|
|
29
32
|
private var play: Boolean = false
|
|
33
|
+
// Mute state controlled by the JS prop
|
|
30
34
|
private var mute: Boolean = false
|
|
31
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Constructor: initialize the PlayerView and attach it to the layout.
|
|
38
|
+
*/
|
|
32
39
|
init {
|
|
33
40
|
playerView = PlayerView(context)
|
|
34
41
|
playerView.layoutParams = LayoutParams(
|
|
@@ -38,16 +45,23 @@ class Media3PlayerView(context: Context) : FrameLayout(context) {
|
|
|
38
45
|
addView(playerView)
|
|
39
46
|
}
|
|
40
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Initializes ExoPlayer and attaches a listener for player events.
|
|
50
|
+
* Ensures only one instance exists.
|
|
51
|
+
*/
|
|
41
52
|
private fun initializePlayer() {
|
|
42
53
|
if (exoPlayer == null) {
|
|
43
54
|
exoPlayer = ExoPlayer.Builder(context).build()
|
|
44
55
|
playerView.player = exoPlayer
|
|
45
56
|
|
|
57
|
+
// Listen for playback state changes and errors
|
|
46
58
|
exoPlayer?.addListener(object : Player.Listener {
|
|
47
59
|
override fun onPlaybackStateChanged(state: Int) {
|
|
48
60
|
when (state) {
|
|
49
61
|
Player.STATE_READY -> {
|
|
50
|
-
|
|
62
|
+
// Notify React Native that player is ready
|
|
63
|
+
sendEvent("topReady")
|
|
64
|
+
// If autoplay is enabled, start playback when ready
|
|
51
65
|
if (autoplay) {
|
|
52
66
|
exoPlayer?.playWhenReady = true
|
|
53
67
|
exoPlayer?.play()
|
|
@@ -55,62 +69,133 @@ class Media3PlayerView(context: Context) : FrameLayout(context) {
|
|
|
55
69
|
}
|
|
56
70
|
}
|
|
57
71
|
Player.STATE_ENDED -> {
|
|
58
|
-
|
|
72
|
+
// Notify React Native that playback has ended
|
|
73
|
+
sendEvent("topEnd")
|
|
59
74
|
}
|
|
60
75
|
}
|
|
61
76
|
}
|
|
62
77
|
|
|
63
78
|
override fun onPlayerError(error: PlaybackException) {
|
|
64
|
-
|
|
79
|
+
// Report playback errors to React Native
|
|
80
|
+
sendEvent("topError", error.message ?: "Unknown error")
|
|
65
81
|
}
|
|
66
82
|
})
|
|
67
83
|
}
|
|
68
84
|
}
|
|
69
85
|
|
|
70
|
-
|
|
86
|
+
/**
|
|
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.
|
|
93
|
+
*/
|
|
94
|
+
fun setSource(
|
|
95
|
+
uriString: String?,
|
|
96
|
+
licenseUrl: String?,
|
|
97
|
+
headers: Map<String, String>?
|
|
98
|
+
) {
|
|
99
|
+
// Return early if no valid URI is provided
|
|
71
100
|
if (uriString.isNullOrEmpty()) return
|
|
72
101
|
sourceUri = uriString
|
|
73
102
|
|
|
103
|
+
// Ensure the ExoPlayer instance is initialized before use
|
|
74
104
|
initializePlayer()
|
|
75
105
|
|
|
76
|
-
val
|
|
77
|
-
|
|
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)
|
|
78
128
|
exoPlayer?.prepare()
|
|
79
129
|
}
|
|
80
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Sets the autoplay flag to control whether playback starts when player is ready.
|
|
133
|
+
* @param value Boolean value from JS prop
|
|
134
|
+
*/
|
|
81
135
|
fun setAutoplay(value: Boolean) {
|
|
82
136
|
autoplay = value
|
|
83
137
|
}
|
|
84
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Set the play state of the player.
|
|
141
|
+
* If true, the player will start/resume playback; otherwise, pause.
|
|
142
|
+
* @param value Boolean value from JS prop
|
|
143
|
+
*/
|
|
85
144
|
fun setPlay(value: Boolean) {
|
|
86
145
|
play = value
|
|
87
|
-
//
|
|
88
|
-
// if
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
// }
|
|
146
|
+
// Optionally: Disabling autoplay if play is used directly
|
|
147
|
+
// Only play or pause if player is ready
|
|
148
|
+
if (play) {
|
|
149
|
+
exoPlayer?.play()
|
|
150
|
+
} else {
|
|
151
|
+
exoPlayer?.pause()
|
|
152
|
+
}
|
|
95
153
|
}
|
|
96
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Sets the mute state of the player.
|
|
157
|
+
* @param value Boolean indicating if the audio should be muted
|
|
158
|
+
*/
|
|
97
159
|
fun setMute(value: Boolean) {
|
|
98
160
|
mute = value
|
|
99
161
|
exoPlayer?.volume = if (mute) 0f else 1f
|
|
100
162
|
}
|
|
101
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Releases the player and cleans up resources.
|
|
166
|
+
* This should be called when the view is destroyed.
|
|
167
|
+
*/
|
|
102
168
|
fun releasePlayer() {
|
|
103
169
|
exoPlayer?.release()
|
|
104
170
|
exoPlayer = null
|
|
105
171
|
}
|
|
106
172
|
|
|
107
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Helper function to send events and optional data payloads to React Native JS side.
|
|
175
|
+
* @param eventName Name of the event (e.g., "topReady", "topError")
|
|
176
|
+
* @param message Optional message string to pass in the event payload
|
|
177
|
+
*/
|
|
108
178
|
private fun sendEvent(eventName: String, message: String? = null) {
|
|
109
179
|
val reactContext = context as? ReactContext ?: return
|
|
110
|
-
val
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
180
|
+
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
181
|
+
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
|
|
182
|
+
val payload = Arguments.createMap()
|
|
183
|
+
message?.let { payload.putString("message", it) }
|
|
184
|
+
eventDispatcher?.dispatchEvent(
|
|
185
|
+
Media3Event(surfaceId, id, eventName, payload)
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Custom event class to bridge native events to React Native UIManager system.
|
|
191
|
+
*/
|
|
192
|
+
private class Media3Event(
|
|
193
|
+
surfaceId: Int,
|
|
194
|
+
viewId: Int,
|
|
195
|
+
private val name: String,
|
|
196
|
+
private val payload: WritableMap
|
|
197
|
+
) : Event<Media3Event>(surfaceId, viewId) {
|
|
198
|
+
override fun getEventName(): String = name
|
|
199
|
+
override fun getEventData(): WritableMap = payload
|
|
115
200
|
}
|
|
116
201
|
}
|
|
@@ -1,61 +1,127 @@
|
|
|
1
1
|
package com.rnmedia3playerdemo
|
|
2
2
|
|
|
3
|
-
import com.facebook.react.bridge.ReactApplicationContext
|
|
4
3
|
import com.facebook.react.bridge.ReadableMap
|
|
4
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
5
5
|
import com.facebook.react.uimanager.SimpleViewManager
|
|
6
6
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
7
|
+
import com.facebook.react.uimanager.ViewManagerDelegate
|
|
7
8
|
import com.facebook.react.uimanager.annotations.ReactProp
|
|
9
|
+
import com.facebook.react.viewmanagers.Media3PlayerViewManagerDelegate
|
|
10
|
+
import com.facebook.react.viewmanagers.Media3PlayerViewManagerInterface
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* Exposes the native view to React Native.
|
|
13
|
-
* Props: source { uri: string }, autoplay, play, mute
|
|
14
|
-
* Events: onReady, onEnd, onError
|
|
13
|
+
* ViewManager for the custom Media3PlayerView.
|
|
14
|
+
* Responsible for creating the view, handling props from JS, and managing events.
|
|
15
15
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
@ReactModule(name = Media3PlayerViewManager.NAME)
|
|
17
|
+
class Media3PlayerViewManager :
|
|
18
|
+
SimpleViewManager<Media3PlayerView>(),
|
|
19
|
+
Media3PlayerViewManagerInterface<Media3PlayerView> {
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
companion object {
|
|
22
|
+
// The name used to reference this view manager from JS
|
|
23
|
+
const val NAME = "Media3PlayerView"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Delegate handles communication and prop mapping automatically when generated by Codegen
|
|
27
|
+
private val delegate = Media3PlayerViewManagerDelegate(this)
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Returns the delegate instance for prop/event mapping.
|
|
31
|
+
*/
|
|
32
|
+
override fun getDelegate(): ViewManagerDelegate<Media3PlayerView> = delegate
|
|
20
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Returns the unique name for this ViewManager, used by React Native.
|
|
36
|
+
*/
|
|
37
|
+
override fun getName(): String = NAME
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Creates and returns a new Media3PlayerView instance for this ViewManager.
|
|
41
|
+
* @param reactContext ThemedReactContext provided by React Native
|
|
42
|
+
*/
|
|
21
43
|
override fun createViewInstance(reactContext: ThemedReactContext): Media3PlayerView {
|
|
22
44
|
return Media3PlayerView(reactContext)
|
|
23
45
|
}
|
|
24
46
|
|
|
47
|
+
/**
|
|
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.
|
|
51
|
+
*/
|
|
25
52
|
@ReactProp(name = "source")
|
|
26
|
-
fun setSource(view: Media3PlayerView, source: ReadableMap?) {
|
|
27
|
-
val uri
|
|
53
|
+
override fun setSource(view: Media3PlayerView, source: ReadableMap?) {
|
|
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
|
+
}
|
|
28
75
|
if (!uri.isNullOrEmpty()) {
|
|
29
|
-
view.setSource(uri)
|
|
76
|
+
view.setSource(uri, licenseUrl, headers)
|
|
30
77
|
}
|
|
31
78
|
}
|
|
32
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Sets the "autoplay" property on the Media3PlayerView.
|
|
82
|
+
* Controls whether playback should start automatically when ready.
|
|
83
|
+
*/
|
|
33
84
|
@ReactProp(name = "autoplay", defaultBoolean = false)
|
|
34
|
-
fun setAutoplay(view: Media3PlayerView,
|
|
35
|
-
view.setAutoplay(
|
|
85
|
+
override fun setAutoplay(view: Media3PlayerView, value: Boolean) {
|
|
86
|
+
view.setAutoplay(value)
|
|
36
87
|
}
|
|
37
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Sets the "play" property on the Media3PlayerView.
|
|
91
|
+
* Controls whether the player should be playing or paused.
|
|
92
|
+
*/
|
|
38
93
|
@ReactProp(name = "play", defaultBoolean = false)
|
|
39
|
-
fun setPlay(view: Media3PlayerView,
|
|
40
|
-
view.setPlay(
|
|
94
|
+
override fun setPlay(view: Media3PlayerView, value: Boolean) {
|
|
95
|
+
view.setPlay(value)
|
|
41
96
|
}
|
|
42
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Sets the "mute" property on the Media3PlayerView.
|
|
100
|
+
* Controls whether the audio should be muted.
|
|
101
|
+
*/
|
|
43
102
|
@ReactProp(name = "mute", defaultBoolean = false)
|
|
44
|
-
fun setMute(view: Media3PlayerView,
|
|
45
|
-
view.setMute(
|
|
103
|
+
override fun setMute(view: Media3PlayerView, value: Boolean) {
|
|
104
|
+
view.setMute(value)
|
|
46
105
|
}
|
|
47
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Called when the React Native view instance is about to be dropped/remounted.
|
|
109
|
+
* Ensures resources are released to avoid leaks.
|
|
110
|
+
*/
|
|
48
111
|
override fun onDropViewInstance(view: Media3PlayerView) {
|
|
49
112
|
super.onDropViewInstance(view)
|
|
50
113
|
view.releasePlayer()
|
|
51
114
|
}
|
|
52
115
|
|
|
53
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Exports native direct event types to React Native for event binding in JS.
|
|
118
|
+
* Maps native event names to the registration name expected by React.
|
|
119
|
+
*/
|
|
54
120
|
override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> {
|
|
55
121
|
return mutableMapOf(
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
122
|
+
"topReady" to mapOf("registrationName" to "onReady"),
|
|
123
|
+
"topEnd" to mapOf("registrationName" to "onEnd"),
|
|
124
|
+
"topError" to mapOf("registrationName" to "onError")
|
|
59
125
|
)
|
|
60
126
|
}
|
|
61
127
|
}
|
package/index.d.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { ViewStyle } from 'react-native';
|
|
2
1
|
import * as React from 'react';
|
|
2
|
+
import {ViewStyle} from 'react-native';
|
|
3
3
|
|
|
4
4
|
export interface Media3PlayerProps {
|
|
5
5
|
style?: ViewStyle | ViewStyle[];
|
|
6
|
-
source: {
|
|
6
|
+
source: {uri: string};
|
|
7
7
|
autoplay?: boolean;
|
|
8
8
|
play?: boolean;
|
|
9
9
|
mute?: boolean;
|
|
10
10
|
onReady?: () => void;
|
|
11
11
|
onEnd?: () => void;
|
|
12
|
-
onError?: (
|
|
12
|
+
onError?: (event: {nativeEvent: {message: string}}) => void;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
declare const Media3Player: React.FC<Media3PlayerProps>;
|
|
16
|
+
export default Media3Player;
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-media3-player",
|
|
3
|
-
"version": "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
|
+
"react-native": "index.js",
|
|
6
7
|
"types": "index.d.ts",
|
|
7
8
|
"keywords": [
|
|
8
9
|
"react-native",
|
|
@@ -31,5 +32,13 @@
|
|
|
31
32
|
"src",
|
|
32
33
|
"index.js",
|
|
33
34
|
"index.d.ts"
|
|
34
|
-
]
|
|
35
|
+
],
|
|
36
|
+
"codegenConfig": {
|
|
37
|
+
"name": "RNMedia3PlayerSpec",
|
|
38
|
+
"type": "components",
|
|
39
|
+
"jsSrcsDir": "src",
|
|
40
|
+
"android": {
|
|
41
|
+
"javaPackageName": "com.rnmedia3playerdemo"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
35
44
|
}
|
package/src/Media3Player.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const NativeMedia3Player = requireNativeComponent('Media3PlayerView');
|
|
2
|
+
import {StyleSheet} from 'react-native';
|
|
3
|
+
// Import the native codegen-wrapped player view component
|
|
4
|
+
import NativeMedia3PlayerView from './Media3PlayerNativeComponent';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
|
-
* Media3Player
|
|
7
|
+
* Media3Player React component
|
|
9
8
|
*
|
|
10
9
|
* Props:
|
|
11
|
-
* -
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
14
|
-
* -
|
|
15
|
-
* -
|
|
16
|
-
* -
|
|
17
|
-
* -
|
|
10
|
+
* - style: (optional) additional style overrides
|
|
11
|
+
* - source: { uri: string } – required, the media URI to load
|
|
12
|
+
* - autoplay: (optional, default false) – whether playback should start automatically when ready
|
|
13
|
+
* - play: (optional, default false) – whether playback should currently be running (true = play, false = pause)
|
|
14
|
+
* - mute: (optional, default false) – whether audio should be muted
|
|
15
|
+
* - onReady: callback when player is ready
|
|
16
|
+
* - onEnd: callback when playback has ended
|
|
17
|
+
* - onError: callback when an error occurs during playback or loading
|
|
18
18
|
*/
|
|
19
19
|
export default function Media3Player({
|
|
20
20
|
style,
|
|
@@ -26,14 +26,16 @@ export default function Media3Player({
|
|
|
26
26
|
onEnd,
|
|
27
27
|
onError,
|
|
28
28
|
}) {
|
|
29
|
+
// Validate that source.uri is provided; warn and render nothing if missing
|
|
29
30
|
if (!source || !source.uri) {
|
|
30
|
-
console.warn('Media3Player: "source
|
|
31
|
+
console.warn('Media3Player: "source.uri" is required.');
|
|
31
32
|
return null;
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
// Render the native player view, passing all relevant props and composing the style
|
|
34
36
|
return (
|
|
35
|
-
<
|
|
36
|
-
style={[styles.default, style]}
|
|
37
|
+
<NativeMedia3PlayerView
|
|
38
|
+
style={[styles.default, style]}
|
|
37
39
|
source={source}
|
|
38
40
|
autoplay={autoplay}
|
|
39
41
|
play={play}
|
|
@@ -45,10 +47,11 @@ export default function Media3Player({
|
|
|
45
47
|
);
|
|
46
48
|
}
|
|
47
49
|
|
|
50
|
+
// Default styling applied to the player view unless overridden via 'style' prop
|
|
48
51
|
const styles = StyleSheet.create({
|
|
49
52
|
default: {
|
|
50
|
-
width: '100%',
|
|
51
|
-
height: 250,
|
|
52
|
-
backgroundColor: 'black',
|
|
53
|
+
width: '100%', // Occupy full width of parent
|
|
54
|
+
height: 250, // Fixed height for the player
|
|
55
|
+
backgroundColor: 'black', // Default background color
|
|
53
56
|
},
|
|
54
57
|
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Import the base ViewProps type from React Native to extend the native component's props
|
|
2
|
+
import type {ViewProps} from 'react-native';
|
|
3
|
+
// Import the DirectEventHandler type used for native-to-JS event callback typings
|
|
4
|
+
import type {DirectEventHandler} from 'react-native/Libraries/Types/CodegenTypes';
|
|
5
|
+
// Import codegenNativeComponent to register the native UI component for use in React Native
|
|
6
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
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
|
+
}>;
|
|
24
|
+
// Event type emitted when the player is ready
|
|
25
|
+
type OnReadyEvent = Readonly<{}>;
|
|
26
|
+
// Event type emitted when playback reaches the end
|
|
27
|
+
type OnEndEvent = Readonly<{}>;
|
|
28
|
+
// Event type emitted when an error occurs during playback
|
|
29
|
+
type OnErrorEvent = Readonly<{
|
|
30
|
+
message: string; // Error message describing what went wrong
|
|
31
|
+
}>;
|
|
32
|
+
|
|
33
|
+
// Props supported by the native Media3PlayerView component
|
|
34
|
+
export interface NativeProps extends ViewProps {
|
|
35
|
+
// Source URI for the media to play
|
|
36
|
+
source?: Source;
|
|
37
|
+
// Whether playback should start automatically when ready
|
|
38
|
+
autoplay?: boolean;
|
|
39
|
+
// Whether playback should currently be running (true = play, false = pause)
|
|
40
|
+
play?: boolean;
|
|
41
|
+
// Whether audio should be muted
|
|
42
|
+
mute?: boolean;
|
|
43
|
+
|
|
44
|
+
// Callback invoked when the player is ready to play
|
|
45
|
+
onReady?: DirectEventHandler<OnReadyEvent>;
|
|
46
|
+
// Callback invoked when playback reaches the end of the media
|
|
47
|
+
onEnd?: DirectEventHandler<OnEndEvent>;
|
|
48
|
+
// Callback invoked when an error occurs in playback or loading
|
|
49
|
+
onError?: DirectEventHandler<OnErrorEvent>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Create and export the code-generated native component using the props interface
|
|
53
|
+
export default codegenNativeComponent<NativeProps>('Media3PlayerView');
|