xugenyuan

ref |> 处理播控中心进度条问题

@@ -45,11 +45,10 @@ export class AudioSuspensionModel { @@ -45,11 +45,10 @@ export class AudioSuspensionModel {
45 this.playerController.get().keepOnBackground = true 45 this.playerController.get().keepOnBackground = true
46 BackgroundAudioController.sharedController().avplayerController = this.playerController.get() 46 BackgroundAudioController.sharedController().avplayerController = this.playerController.get()
47 await BackgroundAudioController.sharedController().createSession() 47 await BackgroundAudioController.sharedController().createSession()
48 - BackgroundAudioController.sharedController().startContinuousTask()  
49 - let id = $r('app.media.newspaper_default').id  
50 - BackgroundAudioController.sharedController().setSessionMetaData(srcContentId ?? "", srcTitle, 'file://', srcSource ?? "")  
51 - BackgroundAudioController.sharedController().stopUseFeatures() 48 + // BackgroundAudioController.sharedController().startContinuousTask()
52 BackgroundAudioController.sharedController().listenPlayEvents() 49 BackgroundAudioController.sharedController().listenPlayEvents()
  50 + await BackgroundAudioController.sharedController().setSessionMetaData(srcContentId ?? "", srcTitle, $r("app.media.system_audio_icon_bk_center"), srcSource ?? "")
  51 + BackgroundAudioController.sharedController().stopUseFeatures()
53 52
54 if (this.url === url) { 53 if (this.url === url) {
55 this.isMinimize = AppStorage.link<boolean>('isMinimize') 54 this.isMinimize = AppStorage.link<boolean>('isMinimize')
@@ -2,10 +2,11 @@ import { Context, WantAgent, wantAgent } from '@kit.AbilityKit' @@ -2,10 +2,11 @@ import { Context, WantAgent, wantAgent } from '@kit.AbilityKit'
2 import { avSession as AVSessionManager } from '@kit.AVSessionKit' 2 import { avSession as AVSessionManager } from '@kit.AVSessionKit'
3 import { backgroundTaskManager } from '@kit.BackgroundTasksKit' 3 import { backgroundTaskManager } from '@kit.BackgroundTasksKit'
4 import { BusinessError } from '@kit.BasicServicesKit' 4 import { BusinessError } from '@kit.BasicServicesKit'
5 -import { Logger } from 'wdKit/Index'  
6 -import { PlayerConstants } from '../constants/PlayerConstants' 5 +import { DateTimeUtils, EmitterEventId, EmitterUtils, Logger } from 'wdKit/Index'
  6 +import { Events, PlayerConstants } from '../constants/PlayerConstants'
7 import { WDPlayerController } from './WDPlayerController' 7 import { WDPlayerController } from './WDPlayerController'
8 import { image } from '@kit.ImageKit' 8 import { image } from '@kit.ImageKit'
  9 +import { JSON } from '@kit.ArkTS'
9 10
10 const TAG = "BackgroundAudioController" 11 const TAG = "BackgroundAudioController"
11 12
@@ -13,6 +14,9 @@ export class BackgroundAudioController { @@ -13,6 +14,9 @@ export class BackgroundAudioController {
13 14
14 private static bgAudioController: BackgroundAudioController 15 private static bgAudioController: BackgroundAudioController
15 private constructor() { 16 private constructor() {
  17 + EmitterUtils.receiveEvent(EmitterEventId.APP_ENTER_BACKGROUD, () => {
  18 + this.startContinuousTask()
  19 + })
16 } 20 }
17 public static sharedController() { 21 public static sharedController() {
18 if (!BackgroundAudioController.bgAudioController) { 22 if (!BackgroundAudioController.bgAudioController) {
@@ -24,9 +28,17 @@ export class BackgroundAudioController { @@ -24,9 +28,17 @@ export class BackgroundAudioController {
24 public gotContextFunc?: () => Context 28 public gotContextFunc?: () => Context
25 public avplayerController?: WDPlayerController 29 public avplayerController?: WDPlayerController
26 private lastSession?: AVSessionManager.AVSession 30 private lastSession?: AVSessionManager.AVSession
  31 +
27 private applyedLongTaskPlay: boolean = false 32 private applyedLongTaskPlay: boolean = false
  33 +
28 private lastProgress: number = 0.0 34 private lastProgress: number = 0.0
29 private hasSetupProgress: boolean = false 35 private hasSetupProgress: boolean = false
  36 + private playing: boolean = false
  37 + private lastItemAssetId?: string
  38 + private lastItemTitle?: string
  39 + private lastItemMediaImage?: Resource
  40 + private lastItemArtist?: string
  41 + private lastItemTotalDuration: number = 0
30 42
31 // 开始创建并激活媒体会话 43 // 开始创建并激活媒体会话
32 // 创建session 44 // 创建session
@@ -56,23 +68,53 @@ export class BackgroundAudioController { @@ -56,23 +68,53 @@ export class BackgroundAudioController {
56 } 68 }
57 69
58 //设置播放元数据 70 //设置播放元数据
59 - setSessionMetaData(assetId: string, title: string, mediaImage: image.PixelMap | string, artist: string) {  
60 - Logger.debug(TAG, `SetAVMetadata assetId: ${assetId}}, title: ${title}, mediaImage: ${mediaImage}, artist: ${artist}`); 71 + async setSessionMetaData(assetId: string, title: string, mediaImage: Resource, artist: string) {
  72 + this.lastItemAssetId = assetId
  73 + this.lastItemTitle = title
  74 + this.lastItemMediaImage = mediaImage
  75 + this.lastItemArtist = artist
  76 + }
  77 +
  78 + async setSessionMetaDataWithDuration(assetId: string, title: string, mediaImage: Resource, artist: string, duration: number) {
  79 + Logger.debug(TAG, `SetAVMetadata assetId: ${assetId}}, title: ${title}, mediaImage: ${mediaImage}, artist: ${artist}, duration: ${duration}`);
  80 +
  81 + if (!this.gotContextFunc) { return }
  82 + let pixelMapImage:image.PixelMap | null = null
  83 + try {
  84 + const data:Uint8Array = await this.gotContextFunc().resourceManager.getMediaContent(mediaImage)
  85 + pixelMapImage = await image.createPixelMap(data.buffer as ArrayBuffer, {
  86 + editable: false,
  87 + alphaType: image.AlphaType.UNKNOWN,
  88 + srcPixelFormat: image.PixelMapFormat.RGB_888,
  89 + size: { width:300, height:300 }
  90 + })
  91 + } catch (e) {
  92 + Logger.error(TAG, `SetAVMetadata ` + JSON.stringify(e));
  93 + } finally {
  94 + // Logger.debug(TAG, `SetAVMetadata ` + JSON.stringify(pixelMapImage));
61 let metadata: AVSessionManager.AVMetadata = { 95 let metadata: AVSessionManager.AVMetadata = {
62 assetId: assetId.length > 0 ? assetId : "fake-asset-id", 96 assetId: assetId.length > 0 ? assetId : "fake-asset-id",
63 title: title.length > 0 ? title : " ", 97 title: title.length > 0 ? title : " ",
64 - mediaImage: mediaImage, 98 + mediaImage: pixelMapImage ?? ("file://" + mediaImage.id),
65 artist: artist.length > 0 ? artist : "人日日报", 99 artist: artist.length > 0 ? artist : "人日日报",
  100 + duration: duration
66 }; 101 };
67 - this.lastSession?.setAVMetadata(metadata).then(() => { 102 +
  103 + try {
  104 + await this.lastSession?.setAVMetadata(metadata)
68 Logger.debug(TAG, `SetAVMetadata successfully`); 105 Logger.debug(TAG, `SetAVMetadata successfully`);
69 - }).catch((err: BusinessError) => { 106 + } catch (err) {
70 Logger.error(TAG, `Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`); 107 Logger.error(TAG, `Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`);
71 - }); 108 + } finally {
  109 + pixelMapImage?.release()
  110 + }
  111 + }
  112 +
72 } 113 }
73 114
74 //设置播放状态 115 //设置播放状态
75 setSessionPlayStatus(playStatus: number) { 116 setSessionPlayStatus(playStatus: number) {
  117 + let needUpdateProgress = false
76 let playbackStatus = AVSessionManager.PlaybackState.PLAYBACK_STATE_PLAY 118 let playbackStatus = AVSessionManager.PlaybackState.PLAYBACK_STATE_PLAY
77 switch (playStatus){ 119 switch (playStatus){
78 case PlayerConstants.STATUS_PAUSE: { 120 case PlayerConstants.STATUS_PAUSE: {
@@ -80,6 +122,7 @@ export class BackgroundAudioController { @@ -80,6 +122,7 @@ export class BackgroundAudioController {
80 } break 122 } break
81 case PlayerConstants.STATUS_START: { 123 case PlayerConstants.STATUS_START: {
82 playbackStatus = AVSessionManager.PlaybackState.PLAYBACK_STATE_PLAY 124 playbackStatus = AVSessionManager.PlaybackState.PLAYBACK_STATE_PLAY
  125 + needUpdateProgress = true
83 } break 126 } break
84 case PlayerConstants.STATUS_STOP: { 127 case PlayerConstants.STATUS_STOP: {
85 playbackStatus = AVSessionManager.PlaybackState.PLAYBACK_STATE_STOP 128 playbackStatus = AVSessionManager.PlaybackState.PLAYBACK_STATE_STOP
@@ -103,13 +146,15 @@ export class BackgroundAudioController { @@ -103,13 +146,15 @@ export class BackgroundAudioController {
103 Logger.error(TAG, `Failed to set AVPlaybackState. Code: ${err.code}, message: ${err.message}`); 146 Logger.error(TAG, `Failed to set AVPlaybackState. Code: ${err.code}, message: ${err.message}`);
104 } else { 147 } else {
105 Logger.debug(TAG, `SetAVPlaybackState 设置播放状态成功 ` + playStatus); 148 Logger.debug(TAG, `SetAVPlaybackState 设置播放状态成功 ` + playStatus);
  149 + if (needUpdateProgress) {
  150 + this.hasSetupProgress = false
  151 + }
106 } 152 }
107 }); 153 });
108 } 154 }
109 155
110 - //设置进度,单位秒  
111 - setSessionPlayProgress(progressDuration: number, totalDuration: number) {  
112 - // Logger.debug(TAG, `set progress: ` + progressDuration + " duration: " + totalDuration); 156 + //设置进度,单位ms
  157 + async setSessionPlayProgress(progressDuration: number, totalDuration: number) {
113 if (totalDuration <= 0) { 158 if (totalDuration <= 0) {
114 return 159 return
115 } 160 }
@@ -117,25 +162,38 @@ export class BackgroundAudioController { @@ -117,25 +162,38 @@ export class BackgroundAudioController {
117 if (Math.abs(newProgress - this.lastProgress) < 0.01) { 162 if (Math.abs(newProgress - this.lastProgress) < 0.01) {
118 return 163 return
119 } 164 }
  165 + // Logger.debug(TAG, `set progress: ` + progressDuration + " duration: " + totalDuration);
120 this.lastProgress = newProgress 166 this.lastProgress = newProgress
121 167
  168 + if (this.lastItemAssetId) {
  169 + this.lastItemTotalDuration = totalDuration
  170 + await this.setSessionMetaDataWithDuration(
  171 + this.lastItemAssetId ?? "",
  172 + this.lastItemTitle ?? "",
  173 + this.lastItemMediaImage!,
  174 + this.lastItemArtist ?? "",
  175 + totalDuration
  176 + )
  177 + this.lastItemAssetId = undefined
  178 + }
  179 +
122 if (this.hasSetupProgress) { 180 if (this.hasSetupProgress) {
123 return 181 return
124 } 182 }
125 this.hasSetupProgress = true 183 this.hasSetupProgress = true
126 -  
127 - Logger.debug(TAG, `set progress: ` + progressDuration + " duration: " + totalDuration); 184 + Logger.debug(TAG, `set progress: ` + DateTimeUtils.secondToTime(progressDuration / 1000)
  185 + + " duration: " + DateTimeUtils.secondToTime(totalDuration / 1000));
128 186
129 // 设置状态: 播放状态,进度位置,播放倍速,缓存的时间 187 // 设置状态: 播放状态,进度位置,播放倍速,缓存的时间
130 let playbackState: AVSessionManager.AVPlaybackState = { 188 let playbackState: AVSessionManager.AVPlaybackState = {
131 state: AVSessionManager.PlaybackState.PLAYBACK_STATE_PLAY, // 播放状态 189 state: AVSessionManager.PlaybackState.PLAYBACK_STATE_PLAY, // 播放状态
132 position: { 190 position: {
133 - elapsedTime: progressDuration * 1000, // 已经播放的位置,以ms为单位 191 + elapsedTime: progressDuration, // 已经播放的位置,以ms为单位
134 updateTime: new Date().getTime(), // 应用更新当前位置时的时间戳,以ms为单位 192 updateTime: new Date().getTime(), // 应用更新当前位置时的时间戳,以ms为单位
135 }, 193 },
136 - duration: totalDuration * 1000,  
137 - speed: 1.0, // 可选,默认是1.0,播放的倍速,按照应用内支持的speed进行设置,系统不做校验  
138 - bufferedTime: totalDuration * 1000, // 可选,资源缓存的时间,以ms为单位 194 + // duration: totalDuration,
  195 + // speed: 1.0, // 可选,默认是1.0,播放的倍速,按照应用内支持的speed进行设置,系统不做校验
  196 + // bufferedTime: totalDuration, // 可选,资源缓存的时间,以ms为单位
139 }; 197 };
140 this.lastSession?.setAVPlaybackState(playbackState, (err) => { 198 this.lastSession?.setAVPlaybackState(playbackState, (err) => {
141 if (err) { 199 if (err) {
@@ -181,8 +239,12 @@ export class BackgroundAudioController { @@ -181,8 +239,12 @@ export class BackgroundAudioController {
181 // Logger.debug(TAG, `on playPrevious `); 239 // Logger.debug(TAG, `on playPrevious `);
182 // }); 240 // });
183 this.lastSession?.on('seek', (position: number) => { 241 this.lastSession?.on('seek', (position: number) => {
184 - Logger.debug(TAG, `on seek , the time is ${JSON.stringify(position)}`); 242 + Logger.debug(TAG, `on seek , the time is ${position}`);
  243 + if (this.lastItemTotalDuration == 0) {
  244 + return
  245 + }
185 246
  247 + this.hasSetupProgress = true
186 // 由于应用内seek可能会触发较长的缓冲等待,可以先把状态设置为 Buffering 248 // 由于应用内seek可能会触发较长的缓冲等待,可以先把状态设置为 Buffering
187 let playbackState: AVSessionManager.AVPlaybackState = { 249 let playbackState: AVSessionManager.AVPlaybackState = {
188 state: AVSessionManager.PlaybackState.PLAYBACK_STATE_BUFFERING, // 缓冲状态 250 state: AVSessionManager.PlaybackState.PLAYBACK_STATE_BUFFERING, // 缓冲状态
@@ -193,11 +255,13 @@ export class BackgroundAudioController { @@ -193,11 +255,13 @@ export class BackgroundAudioController {
193 } else { 255 } else {
194 Logger.debug(TAG, `SetAVPlaybackState seek buffering`); 256 Logger.debug(TAG, `SetAVPlaybackState seek buffering`);
195 } 257 }
196 - });  
197 258
198 // 应用响应seek命令,使用应用内播放器完成seek实现 259 // 应用响应seek命令,使用应用内播放器完成seek实现
199 - this.avplayerController?.setSeekTime(position * 0.001, SliderChangeMode.End) 260 + this.avplayerController?.setSeekMicroSecondsTime(position)
  261 + setTimeout(() => {
200 this.hasSetupProgress = false 262 this.hasSetupProgress = false
  263 + }, 1000)
  264 + });
201 }); 265 });
202 } 266 }
203 267
@@ -239,6 +303,7 @@ export class BackgroundAudioController { @@ -239,6 +303,7 @@ export class BackgroundAudioController {
239 // 通过wantAgent模块下getWantAgent方法获取WantAgent对象 303 // 通过wantAgent模块下getWantAgent方法获取WantAgent对象
240 wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => { 304 wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {
241 if (!this.gotContextFunc) { return } 305 if (!this.gotContextFunc) { return }
  306 + this.lastSession?.setLaunchAbility(wantAgentObj)
242 backgroundTaskManager.startBackgroundRunning(this.gotContextFunc(), 307 backgroundTaskManager.startBackgroundRunning(this.gotContextFunc(),
243 backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK, 308 backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK,
244 wantAgentObj).then(() => { 309 wantAgentObj).then(() => {
@@ -336,6 +336,13 @@ export class WDPlayerController { @@ -336,6 +336,13 @@ export class WDPlayerController {
336 this.avPlayer?.seek(this.seekTime); 336 this.avPlayer?.seek(this.seekTime);
337 } 337 }
338 } 338 }
  339 + async setSeekMicroSecondsTime(value: number) {
  340 + // 防止seek报错
  341 + if (this.avPlayer && PlayerConstants.OPERATE_STATE.indexOf(this.avPlayer?.state) === -1) {
  342 + return;
  343 + }
  344 + this.avPlayer?.seek(value);
  345 + }
339 346
340 setBright() { 347 setBright() {
341 // globalThis.windowClass.setWindowBrightness(this.playerThis.bright) 348 // globalThis.windowClass.setWindowBrightness(this.playerThis.bright)