1
1
import { TurboModule , TurboModuleContext } from '@rnoh/react-native-openharmony/ts' ;
2
- import { TM } from "@rnoh/react-native-openharmony/generated/ts"
3
2
import { BusinessError } from '@ohos.base' ;
4
3
import media from '@ohos.multimedia.media' ;
5
- import common from '@ohos.app.ability.common' ;
6
4
import fs from '@ohos.file.fs' ;
7
5
import logger from './Logger' ;
8
- const TAG = "RCTAudioPlayerTurboModule"
9
- interface PlayConfig {
6
+
7
+ interface PlayConfig {
10
8
volume ?: number ,
11
9
pan ?: number ,
12
10
wakeLock ?: boolean ,
@@ -17,55 +15,86 @@ interface PlayConfig{
17
15
continueToPlayInBackground ?: boolean
18
16
}
19
17
20
- interface PlayInfo {
18
+ interface PlayInfo {
21
19
duration : number ,
22
20
position : number
23
21
}
24
22
23
+ interface Error {
24
+ err : string ,
25
+ message ?: string
26
+ }
27
+
28
+ enum ConfigKey {
29
+ LOOPING = 'looping' ,
30
+ SPEED = 'speed' ,
31
+ VOLUME = 'volume' ,
32
+ INITIALIZED = 'initialized'
33
+ }
34
+
35
+ enum StateChange {
36
+ IDLE = 'idle' ,
37
+ INITIALIZED = 'initialized' ,
38
+ PREPARED = 'prepared' ,
39
+ PLAYING = 'playing' ,
40
+ PAUSED = 'paused' ,
41
+ COMPLETED = 'completed' ,
42
+ STOPPED = 'stopped' ,
43
+ RELEASED = 'released' ,
44
+ }
45
+
25
46
export class RCTAudioPlayerTurboModule extends TurboModule {
26
- private isSeek : boolean = true ;
47
+ private readonly SANDBOX_START = '/data/storage' ;
48
+ private readonly FILE_MANAGER_START = 'file://docs' ;
49
+ private readonly FD_PATH = 'fd://' ;
27
50
private playerMap : Map < number , media . AVPlayer > = new Map ( )
28
51
private playConfigMap : Map < number , PlayConfig > = new Map ( )
29
52
private playInfoMap : Map < number , PlayInfo > = new Map ( )
30
- private playSeekCallbacks : Map < number , ( err : string , result ?: PlayInfo ) => void > = new Map ( )
53
+ private playSeekCallbacks : Map < number , ( err : string | Error , result ?: PlayInfo ) => void > = new Map ( )
54
+
31
55
constructor ( protected ctx : TurboModuleContext ) {
32
56
super ( ctx ) ;
33
57
this . ctx = ctx
34
58
this . onBackground ( )
35
59
}
36
- setPlayer ( playerId : number , player : media . AVPlayer ) {
60
+
61
+ setPlayer ( playerId : number , player : media . AVPlayer ) {
37
62
this . playerMap . set ( playerId , player )
38
63
}
39
- getPlayer ( playerId : number ) : media . AVPlayer {
64
+
65
+ getPlayer ( playerId : number ) : media . AVPlayer {
40
66
const player = this . playerMap . get ( playerId )
41
67
return player
42
68
}
69
+
43
70
applyConfig ( playerId : number ) {
44
71
const player = this . playerMap . get ( playerId )
45
72
const config = this . playConfigMap . get ( playerId )
46
73
if ( ! config || ! player ) {
47
74
return
48
75
}
49
- if ( player . state === 'initialized' ) {
76
+ if ( player . state === ConfigKey . INITIALIZED ) {
50
77
return
51
78
}
52
79
Object . keys ( config ) . forEach ( key => {
53
- if ( key === 'looping' ) {
54
- player . loop = config [ 'looping' ]
55
- } else if ( key === 'speed' ) {
56
- player . setSpeed ( config [ 'speed' ] )
57
- } else if ( key === 'volume' ) {
58
- player . setVolume ( config [ 'volume' ] )
80
+ if ( key === ConfigKey . LOOPING ) {
81
+ player . loop = config [ ConfigKey . LOOPING ]
82
+ } else if ( key === ConfigKey . SPEED ) {
83
+ player . setSpeed ( config [ ConfigKey . SPEED ] )
84
+ } else if ( key === ConfigKey . VOLUME ) {
85
+ player . setVolume ( config [ ConfigKey . VOLUME ] )
59
86
}
60
87
} )
61
88
}
89
+
62
90
setConfig ( playerId : number , config : PlayConfig ) {
63
91
const oldConfig = this . playConfigMap . get ( playerId ) || { }
64
92
Object . keys ( config ) . forEach ( key => {
65
93
oldConfig [ key ] = config [ key ]
66
94
} )
67
95
this . playConfigMap . set ( playerId , oldConfig )
68
96
}
97
+
69
98
set ( playerId : number , config : PlayConfig , next : ( err : string , result : object ) => void ) {
70
99
const player = this . playerMap . get ( playerId )
71
100
if ( ! player ) {
@@ -77,12 +106,14 @@ export class RCTAudioPlayerTurboModule extends TurboModule {
77
106
this . applyConfig ( playerId )
78
107
next ( '' , { } )
79
108
}
109
+
80
110
onBackground ( ) {
81
111
this . ctx . rnInstance . subscribeToLifecycleEvents ( 'BACKGROUND' , ( ) => {
82
112
logger . debug ( `app state is BACKGROUND` )
83
113
this . pauseOnBackground ( )
84
114
} )
85
115
}
116
+
86
117
pauseOnBackground ( ) {
87
118
this . playConfigMap . forEach ( ( config , playerId ) => {
88
119
if ( ! config . continueToPlayInBackground ) {
@@ -93,15 +124,18 @@ export class RCTAudioPlayerTurboModule extends TurboModule {
93
124
}
94
125
} )
95
126
}
127
+
96
128
emit ( name : string , data : object ) {
97
129
this . ctx . rnInstance . emitDeviceEvent ( name , data )
98
130
}
131
+
99
132
toEmit ( playerId : number , name : string , data : object ) {
100
133
this . emit ( `RCTAudioPlayerEvent:${ playerId } ` , {
101
134
event : name ,
102
135
data
103
136
} )
104
137
}
138
+
105
139
setAVPlayerCallback ( avPlayer : media . AVPlayer , playerId : number ) {
106
140
// seek操作结果回调函数
107
141
avPlayer . on ( 'seekDone' , ( seekDoneTime : number ) => {
@@ -120,27 +154,24 @@ export class RCTAudioPlayerTurboModule extends TurboModule {
120
154
avPlayer . on ( 'stateChange' , async ( state : string , reason : media . StateChangeReason ) => {
121
155
logger . debug ( `stateChange:${ state } ` )
122
156
switch ( state ) {
123
- case 'idle' : // 成功调用reset接口后触发该状态机上报
157
+ case StateChange . IDLE : // 成功调用reset接口后触发该状态机上报
124
158
avPlayer . release ( ) ; // 调用release接口销毁实例对象
125
159
break ;
126
- case 'initialized' : // avplayer 设置播放源后触发该状态上报
160
+ case StateChange . INITIALIZED : // avplayer 设置播放源后触发该状态上报
127
161
avPlayer . prepare ( ) ;
128
162
break ;
129
- case 'prepared' : // prepare调用成功后上报该状态机
163
+ case StateChange . PREPARED : // prepare调用成功后上报该状态机
130
164
logger . debug ( `prepared called.to and apply config and play` )
131
165
this . applyConfig ( playerId )
132
- avPlayer . play ( ) ; // 调用播放接口开始播放
133
166
break ;
134
- case 'playing' : // play成功调用后触发该状态机上报
167
+ case StateChange . PLAYING : // play成功调用后触发该状态机上报
135
168
break ;
136
- case 'paused' : // pause成功调用后触发该状态机上报
169
+ case StateChange . PAUSED : // pause成功调用后触发该状态机上报
137
170
logger . debug ( 'paused called.' ) ;
138
- this . toEmit ( playerId , 'pause' , {
139
- message : 'player paused'
140
- } )
141
171
break ;
142
- case 'completed' : // 播放结束后触发该状态机上报
172
+ case StateChange . COMPLETED : // 播放结束后触发该状态机上报
143
173
logger . debug ( 'completed called.' ) ;
174
+ avPlayer . seek ( 0 )
144
175
this . toEmit ( playerId , 'ended' , {
145
176
message : 'play completed'
146
177
} )
@@ -149,11 +180,11 @@ export class RCTAudioPlayerTurboModule extends TurboModule {
149
180
this . destroy ( playerId )
150
181
}
151
182
break ;
152
- case 'stopped' : // stop接口成功调用后触发该状态机上报
183
+ case StateChange . STOPPED : // stop接口成功调用后触发该状态机上报
153
184
logger . debug ( 'Player state stopped called.' ) ;
154
185
avPlayer . reset ( ) ; // 调用reset接口初始化avplayer状态
155
186
break ;
156
- case 'released' :
187
+ case StateChange . RELEASED :
157
188
break ;
158
189
default :
159
190
break ;
@@ -163,8 +194,8 @@ export class RCTAudioPlayerTurboModule extends TurboModule {
163
194
const call = this . playSeekCallbacks . get ( playerId )
164
195
if ( call ) {
165
196
call ( `` , this . getInfo ( playerId ) )
197
+ this . playSeekCallbacks . delete ( playerId )
166
198
}
167
- this . playSeekCallbacks . delete ( playerId )
168
199
this . toEmit ( playerId , 'seeked' , {
169
200
message : 'seek completed'
170
201
} )
@@ -205,49 +236,55 @@ export class RCTAudioPlayerTurboModule extends TurboModule {
205
236
}
206
237
} )
207
238
}
239
+
208
240
async createPlayer ( pathStr : string , playerId : number ) {
209
241
logger . debug ( `createPlayer path:${ pathStr } ` )
210
- try {
242
+ try {
211
243
const avPlayer : media . AVPlayer = await media . createAVPlayer ( )
212
244
this . setAVPlayerCallback ( avPlayer , playerId )
213
245
if ( pathStr . startsWith ( 'http' ) ) {
214
246
avPlayer . url = pathStr
215
247
} else {
216
- let fdPath = 'fd: //'
248
+ let fdPath = this . FD_PATH
217
249
const context = this . ctx . uiAbilityContext
218
250
let path = context . filesDir + '/' + pathStr
219
- if ( pathStr . startsWith ( '/data/storage' ) ) {
251
+ if ( pathStr . startsWith ( this . SANDBOX_START ) || pathStr . startsWith ( this . FILE_MANAGER_START ) ) {
220
252
path = pathStr
221
253
}
222
- logger . debug ( `file path:${ path } } ` )
254
+ logger . debug ( `file path:${ path } ` )
223
255
const file = await fs . open ( path )
224
256
fdPath = fdPath + file . fd
257
+ logger . debug ( `fdPath:${ fdPath } ` )
225
258
avPlayer . url = fdPath
226
259
}
227
260
this . setPlayer ( playerId , avPlayer )
228
261
} catch ( e ) {
229
262
logger . warn ( `createPlayer err:${ JSON . stringify ( e ) } ` )
230
263
}
231
264
}
265
+
232
266
getInfo ( playerId : number ) {
233
267
const info = this . playInfoMap . get ( playerId )
234
268
return info
235
269
}
236
- getCurrentTime ( playerId : number , callback :( err : string , result ?: PlayInfo ) => void ) {
270
+
271
+ getCurrentTime ( playerId : number , callback : ( err : string , result ?: PlayInfo ) => void ) {
237
272
const info = this . playInfoMap . get ( playerId )
238
273
if ( info ) {
239
274
callback ( '' , info )
240
275
} else {
241
276
callback ( 'not found player' )
242
277
}
243
278
}
279
+
244
280
prepare ( playerId : number , path : string , option : PlayConfig , next : ( ) => void ) {
245
281
logger . debug ( `prepare start` )
246
282
this . setConfig ( playerId , option )
247
283
this . createPlayer ( path , playerId ) . then ( ( ) => {
248
284
next ( )
249
285
} )
250
286
}
287
+
251
288
checkPlayer ( playerId : number , next : ( err ?: string ) => void ) {
252
289
const hasPlayer = this . playerMap . has ( playerId )
253
290
if ( hasPlayer ) {
@@ -267,7 +304,7 @@ export class RCTAudioPlayerTurboModule extends TurboModule {
267
304
const player = this . getPlayer ( playerId )
268
305
await player . play ( )
269
306
callback ( '' , this . getInfo ( playerId ) )
270
- } catch ( e ) {
307
+ } catch ( e ) {
271
308
callback ?.( `player call play function err:${ JSON . stringify ( e ) } ` )
272
309
}
273
310
}
@@ -279,6 +316,9 @@ export class RCTAudioPlayerTurboModule extends TurboModule {
279
316
}
280
317
const player = this . getPlayer ( playerId )
281
318
await player . pause ( )
319
+ this . toEmit ( playerId , 'pause' , {
320
+ message : 'player paused'
321
+ } )
282
322
callback ( '' , this . getInfo ( playerId ) )
283
323
}
284
324
@@ -296,14 +336,19 @@ export class RCTAudioPlayerTurboModule extends TurboModule {
296
336
} else {
297
337
const oldCall = this . playSeekCallbacks . get ( playerId )
298
338
if ( oldCall ) {
299
- oldCall ( `seek fail.stopped before seek operation counld finish` )
339
+ let err : Error = {
340
+ err : 'seekfail' ,
341
+ message : 'stopped before seek operation counld finish'
342
+ }
343
+ oldCall ( err )
300
344
this . playSeekCallbacks . delete ( playerId )
301
345
}
302
346
this . playSeekCallbacks . set ( playerId , callback )
303
347
player . seek ( 0 )
304
348
await player . pause ( )
305
349
}
306
350
}
351
+
307
352
destroy ( playerId : number , callback ?: ( ) => void ) {
308
353
logger . debug ( `destroy start` )
309
354
const player = this . getPlayer ( playerId )
@@ -316,18 +361,23 @@ export class RCTAudioPlayerTurboModule extends TurboModule {
316
361
callback ( )
317
362
}
318
363
}
364
+
319
365
resume ( playerId : number , callback : ( ) => void ) {
320
366
this . play ( playerId , callback )
321
367
}
368
+
322
369
async seek ( playerId : number , position : number , callback : ( ) => void ) {
323
- logger . debug ( `seek :${ position } `)
370
+ logger . info ( `seekbar :${ position } `)
324
371
if ( ! this . checkPlayer ( playerId , callback ) ) {
325
372
return
326
373
}
327
374
const player = this . getPlayer ( playerId )
328
375
const oldCall = this . playSeekCallbacks . get ( playerId )
329
376
if ( oldCall ) {
330
- oldCall ( `seek fail` )
377
+ let err : Error = {
378
+ err : 'seekfail'
379
+ }
380
+ oldCall ( err )
331
381
this . playSeekCallbacks . delete ( playerId )
332
382
}
333
383
this . playSeekCallbacks . set ( playerId , callback )
0 commit comments