BackgroundAudioController.ets
12.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
import { Context, WantAgent, wantAgent } from '@kit.AbilityKit'
import { avSession as AVSessionManager } from '@kit.AVSessionKit'
import { backgroundTaskManager } from '@kit.BackgroundTasksKit'
import { BusinessError } from '@kit.BasicServicesKit'
import { DateTimeUtils, EmitterEventId, EmitterUtils, Logger } from 'wdKit/Index'
import { Events, PlayerConstants } from '../constants/PlayerConstants'
import { WDPlayerController } from './WDPlayerController'
import { image } from '@kit.ImageKit'
import { JSON } from '@kit.ArkTS'
const TAG = "BackgroundAudioController"
export class BackgroundAudioController {
private static bgAudioController: BackgroundAudioController
private constructor() {
EmitterUtils.receiveEvent(EmitterEventId.APP_ENTER_BACKGROUD, () => {
this.startContinuousTask()
})
}
public static sharedController() {
if (!BackgroundAudioController.bgAudioController) {
BackgroundAudioController.bgAudioController = new BackgroundAudioController()
}
return BackgroundAudioController.bgAudioController
}
public gotContextFunc?: () => Context
public avplayerController?: WDPlayerController
private lastSession?: AVSessionManager.AVSession
private applyedLongTaskPlay: boolean = false
private lastProgress: number = 0.0
private hasSetupProgress: boolean = false
private playing: boolean = false
private lastItemAssetId?: string
private lastItemTitle?: string
private lastItemMediaImage?: Resource
private lastItemArtist?: string
private lastItemTotalDuration: number = 0
// 开始创建并激活媒体会话
// 创建session
async createSession() {
if (!this.gotContextFunc) { return }
if (this.lastSession == null) {
this.destorySession()
let type: AVSessionManager.AVSessionType = 'audio';
let session = await AVSessionManager.createAVSession(this.gotContextFunc(),'SESSION_NAME', type);
this.lastSession = session
}
// 激活接口要在元数据、控制命令注册完成之后再执行
await this.lastSession?.activate();
Logger.debug(TAG, `session create done : sessionId : ${this.lastSession?.sessionId}`);
this.lastProgress = 0
this.hasSetupProgress = false
}
destorySession() {
if (this.lastSession) {
this.lastSession.deactivate();
this.lastSession.destroy();
}
}
//设置播放元数据
async setSessionMetaData(assetId: string, title: string, mediaImage: Resource, artist: string) {
this.lastItemAssetId = assetId.length > 0 ? assetId : ""
this.lastItemTitle = title || ""
this.lastItemMediaImage = mediaImage
this.lastItemArtist = artist || ""
}
async setSessionMetaDataWithDuration(assetId: string, title: string, mediaImage: Resource, artist: string, duration: number) {
Logger.debug(TAG, `SetAVMetadata assetId: ${assetId}, title: ${title}, mediaImage: ${mediaImage}, artist: ${artist}, duration: ${duration}`);
if (!this.gotContextFunc) { return }
let pixelMapImage:image.PixelMap | null = null
try {
const data:Uint8Array = await this.gotContextFunc().resourceManager.getMediaContent(mediaImage)
pixelMapImage = await image.createPixelMap(data.buffer as ArrayBuffer, {
editable: false,
alphaType: image.AlphaType.UNKNOWN,
srcPixelFormat: image.PixelMapFormat.RGB_888,
size: { width:300, height:300 }
})
} catch (e) {
Logger.error(TAG, `SetAVMetadata ` + JSON.stringify(e));
} finally {
// Logger.debug(TAG, `SetAVMetadata ` + JSON.stringify(pixelMapImage));
let metadata: AVSessionManager.AVMetadata = {
assetId: assetId.length > 0 ? assetId : "fake-asset-id",
title: title.length > 0 ? title : " ",
mediaImage: "https://cdnjdphoto.aikan.pdnews.cn/WapApi/800/launcher_icon.png" ,//pixelMapImage ?? ("file://" + mediaImage.id),
artist: artist.length > 0 ? artist : "人日日报",
duration: duration
};
try {
await this.lastSession?.setAVMetadata(metadata)
Logger.debug(TAG, `SetAVMetadata successfully`);
} catch (err) {
Logger.error(TAG, `Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`);
} finally {
pixelMapImage?.release()
}
}
}
//设置播放状态
setSessionPlayStatus(playStatus: number) {
let needUpdateProgress = false
let playbackStatus = AVSessionManager.PlaybackState.PLAYBACK_STATE_PLAY
switch (playStatus){
case PlayerConstants.STATUS_PAUSE: {
playbackStatus = AVSessionManager.PlaybackState.PLAYBACK_STATE_PAUSE
} break
case PlayerConstants.STATUS_START: {
playbackStatus = AVSessionManager.PlaybackState.PLAYBACK_STATE_PLAY
needUpdateProgress = true
} break
case PlayerConstants.STATUS_STOP: {
playbackStatus = AVSessionManager.PlaybackState.PLAYBACK_STATE_STOP
} break
case PlayerConstants.STATUS_ERROR: {
playbackStatus = AVSessionManager.PlaybackState.PLAYBACK_STATE_ERROR
} break
case PlayerConstants.STATUS_COMPLETION: {
playbackStatus = AVSessionManager.PlaybackState.PLAYBACK_STATE_COMPLETED
} break
default: {
playbackStatus = AVSessionManager.PlaybackState.PLAYBACK_STATE_IDLE
} break
}
let playbackState: AVSessionManager.AVPlaybackState = {
state:playbackStatus,
// isFavorite:false
};
this.lastSession?.setAVPlaybackState(playbackState, (err: BusinessError) => {
if (err) {
Logger.error(TAG, `Failed to set AVPlaybackState. Code: ${err.code}, message: ${err.message}`);
} else {
Logger.debug(TAG, `SetAVPlaybackState 设置播放状态成功 ` + playStatus);
if (needUpdateProgress) {
this.hasSetupProgress = false
}
}
});
}
//设置进度,单位ms
async setSessionPlayProgress(progressDuration: number, totalDuration: number) {
this.setSessionPlayProgressForce(progressDuration, totalDuration, false)
}
async setSessionPlayProgressForce(progressDuration: number, totalDuration: number, force: boolean) {
if (totalDuration <= 0) {
return
}
let newProgress = progressDuration / totalDuration
if (force == false && Math.abs(newProgress - this.lastProgress) < 0.01) {
return
}
// Logger.debug(TAG, `set progress: ` + progressDuration + " duration: " + totalDuration);
this.lastProgress = newProgress
if (this.lastItemAssetId != undefined) {
this.lastItemTotalDuration = totalDuration
await this.setSessionMetaDataWithDuration(
this.lastItemAssetId ?? "",
this.lastItemTitle ?? "",
this.lastItemMediaImage!,
this.lastItemArtist ?? "",
totalDuration
)
this.lastItemAssetId = undefined
}
if (force == false && this.hasSetupProgress) {
return
}
this.hasSetupProgress = true
Logger.debug(TAG, `set progress: ` + DateTimeUtils.secondToTime(progressDuration / 1000)
+ " duration: " + DateTimeUtils.secondToTime(totalDuration / 1000));
// 设置状态: 播放状态,进度位置,播放倍速,缓存的时间
let playbackState: AVSessionManager.AVPlaybackState = {
state: AVSessionManager.PlaybackState.PLAYBACK_STATE_PLAY, // 播放状态
position: {
elapsedTime: progressDuration, // 已经播放的位置,以ms为单位
updateTime: new Date().getTime(), // 应用更新当前位置时的时间戳,以ms为单位
},
// duration: totalDuration,
// speed: 1.0, // 可选,默认是1.0,播放的倍速,按照应用内支持的speed进行设置,系统不做校验
// bufferedTime: totalDuration, // 可选,资源缓存的时间,以ms为单位
};
this.lastSession?.setAVPlaybackState(playbackState, (err) => {
if (err) {
Logger.error(TAG, `Failed to set AVPlaybackState. Code: ${err.code}, message: ${err.message}`);
} else {
Logger.debug(TAG, `SetAVPlaybackState successfully`);
}
});
}
// 置灰或禁用不支持的按钮
stopUseFeatures() {
// 取消指定session下的相关监听
// this.lastSession?.off('playFromAssetId');
// this.lastSession?.off('setSpeed');
// this.lastSession?.off('setLoopMode');
// this.lastSession?.off('toggleFavorite');
// this.lastSession?.off('skipToQueueItem');
// this.lastSession?.off('handleKeyEvent');
// this.lastSession?.off('commonCommand');
// this.lastSession?.off('rewind');
// this.lastSession?.off('fastForward');
}
listenPlayEvents() {
this.lastSession?.on('play', () => {
Logger.debug(TAG, `on play `);
this.avplayerController?.resume()
this.hasSetupProgress = false
});
this.lastSession?.on('pause', () => {
Logger.debug(TAG, `on pause `);
this.avplayerController?.pause()
});
this.lastSession?.on('stop', () => {
Logger.debug(TAG, `on stop `);
this.avplayerController?.stop()
});
// this.lastSession?.on('playNext', () => {
// Logger.debug(TAG, `on playNext `);
// });
// this.lastSession?.on('playPrevious', () => {
// Logger.debug(TAG, `on playPrevious `);
// });
this.lastSession?.on('seek', (position: number) => {
Logger.debug(TAG, `on seek , the time is ${position}`);
if (this.lastItemTotalDuration == 0) {
return
}
this.hasSetupProgress = true
// 由于应用内seek可能会触发较长的缓冲等待,可以先把状态设置为 Buffering
let playbackState: AVSessionManager.AVPlaybackState = {
state: AVSessionManager.PlaybackState.PLAYBACK_STATE_BUFFERING, // 缓冲状态
};
this.lastSession?.setAVPlaybackState(playbackState, (err) => {
if (err) {
Logger.debug(TAG, `Failed to set AVPlaybackState. Code: ${err.code}, message: ${err.message}`);
} else {
Logger.debug(TAG, `SetAVPlaybackState seek buffering`);
}
// 应用响应seek命令,使用应用内播放器完成seek实现
this.avplayerController?.setSeekMicroSecondsTime(position)
this.setSessionPlayProgressForce(position, this.lastItemTotalDuration, true)
setTimeout(() => {
this.hasSetupProgress = false
}, 1000)
});
});
}
// 开启后台长时任务
startContinuousTask() {
if (this.applyedLongTaskPlay) {
return
}
let params: Record<string, string> = {
"title": "开始后台任务",
"content": "内容?",
// "pushLink": pushLink,
// "taskid": message.taskId,
// "messageId": message.messageId,
// "gtTransmitMsgLocalNotify": "1",
}
let wantAgentInfo: wantAgent.WantAgentInfo = {
// 点击通知后,将要执行的动作列表
// 添加需要被拉起应用的bundleName和abilityName
wants: [
{
deviceId: '',
bundleName: "com.peopledailychina.hosactivity",
abilityName: "EntryAbility",
// action: 'com.test.pushaction',
// entities: [],
// parameters:params,
}
],
// 指定点击通知栏消息后的动作是拉起ability
actionType: wantAgent.OperationType.START_ABILITY,
// 使用者自定义的一个私有值
requestCode: 0,
// 点击通知后,动作执行属性
wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG],
};
// 通过wantAgent模块下getWantAgent方法获取WantAgent对象
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {
if (!this.gotContextFunc) { return }
this.lastSession?.setLaunchAbility(wantAgentObj)
backgroundTaskManager.startBackgroundRunning(this.gotContextFunc(),
backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK,
wantAgentObj).then(() => {
Logger.debug(TAG, `Succeeded in operationing startBackgroundRunning.`);
this.applyedLongTaskPlay = true
}).catch((err: BusinessError) => {
Logger.error(TAG, `Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
});
});
}
stopContinuousTask() {
if (!this.gotContextFunc) { return }
backgroundTaskManager.stopBackgroundRunning(this.gotContextFunc()).then(() => {
Logger.debug(TAG, `Succeeded in operationing stopBackgroundRunning.`);
}).catch((err: BusinessError) => {
Logger.error(TAG, `Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
});
this.applyedLongTaskPlay = false
}
}