فهرست منبع

```
feat(cast): 优化投播控制器的媒体播放功能

- 在音频媒体描述中添加 mediaSize 字段支持
- 添加投播播放进度和时长的实时更新机制
- 移除切换曲目时不必要的停止操作,直接切换资源避免会话中断
- 新增歌曲时长解析方法,支持多种时间格式转换为毫秒
- 在投播模式下实现精确的歌曲切换和元数据同步
- 修复投播播放上一首和下一首时的状态同步问题
```

chendeben 7 ماه پیش
والد
کامیت
07e32d8ec0
2فایلهای تغییر یافته به همراه61 افزوده شده و 25 حذف شده
  1. 9 10
      entry/src/main/ets/controller/CastController.ets
  2. 52 15
      entry/src/main/ets/view/LocalMusic.ets

+ 9 - 10
entry/src/main/ets/controller/CastController.ets

@@ -168,6 +168,7 @@ export class CastController {
         subtitle: 'audio',
         artist: songItem.artist || '',
         mediaType: 'AUDIO',
+        mediaSize: songItem.videoSize || 0,
         startPosition: startPosition,
         duration: duration || 0,
       };
@@ -209,6 +210,12 @@ export class CastController {
       this.isCastPlaying = true;
       this.isSwitchingTrack = false;
       this.hasHandledEnd = false;
+      this.elapsedTime = startPosition;
+      this.onPositionChanged?.(this.elapsedTime);
+      if (typeof description.duration === 'number' && description.duration > 0) {
+        this.duration = description.duration;
+        this.onDurationChanged?.(this.duration);
+      }
       console.info(TAG, '✅✅✅ 投播启动成功,isCastPlaying=true ✅✅✅');
 
     } catch (err) {
@@ -428,12 +435,8 @@ export class CastController {
     this.musicIndex = musicIndex;
     this.isSwitchingTrack = true;
 
-    // 先停止当前播放
+    // 直接切换资源,避免 stop 导致投播会话被终止
     try {
-      await this.setStop();
-     console.info( TAG, 'playNext: stop 成功');
-
-      // 准备并播放下一首
       await this.setCastResource(0, videoUrl, duration);
      console.info( TAG, 'playNext: prepare 和 start 完成');
     } finally {
@@ -455,12 +458,8 @@ export class CastController {
     this.musicIndex = musicIndex;
     this.isSwitchingTrack = true;
 
-    // 先停止当前播放
+    // 直接切换资源,避免 stop 导致投播会话被终止
     try {
-      await this.setStop();
-     console.info( TAG, 'playPrevious: stop 成功');
-
-      // 准备并播放上一首
       await this.setCastResource(0, videoUrl, duration);
      console.info( TAG, 'playPrevious: prepare 和 start 完成');
     } finally {

+ 52 - 15
entry/src/main/ets/view/LocalMusic.ets

@@ -14749,6 +14749,29 @@ export struct LocalMusic {
     return duration;
   }
 
+  private getSongDurationMs(song?: VideoItem): number {
+    if (!song || !song.duration) {
+      return 0;
+    }
+    const raw = song.duration.trim();
+    if (!raw) {
+      return 0;
+    }
+    const parts = raw.split(':').map((part) => Number(part));
+    if (parts.some((part) => Number.isNaN(part))) {
+      return 0;
+    }
+    let seconds = 0;
+    if (parts.length === 3) {
+      seconds = parts[0] * 3600 + parts[1] * 60 + parts[2];
+    } else if (parts.length === 2) {
+      seconds = parts[0] * 60 + parts[1];
+    } else if (parts.length === 1) {
+      seconds = parts[0];
+    }
+    return seconds > 0 ? seconds * 1000 : 0;
+  }
+
   private updateCastProgress(position: number): void {
     const duration = this.getActiveDuration();
     if (duration <= 0) {
@@ -15056,19 +15079,20 @@ export struct LocalMusic {
       console.info('heanup playNext 1 ', `this.isCastPlaying: ${ this.isCastPlaying}`)
       console.info('heanup playNext 2 ', `this.castControllerWrapper: ${this.castControllerWrapper}`)
 
-      // 从 CastController 获取最新的投播状态
-      if (this.castControllerWrapper) {
-        const controllerIsPlaying = this.castControllerWrapper.getIsCastPlaying();
-        console.info('heanup playNext', `从 CastController 获取的 isCastPlaying=${controllerIsPlaying}`);
-        this.isCastPlaying = controllerIsPlaying;
-      }
-
       // 如果是投播模式,使用 CastController 播放下一首
-      if (this.castControllerWrapper && this.isCastPlaying) {
+      if (this.castControllerWrapper) {
         console.info('heanup playNext', '使用 CastController 播放下一首');
         try {
-          // 传入最新的歌曲列表、索引和 videoUrl
-          await this.castControllerWrapper.playNext(this.songList, this.curIndex, this.videoUrl);
+          const nextDuration = this.getSongDurationMs(this.currentSong);
+          if (nextDuration > 0) {
+            this.duration = nextDuration;
+            this.durationTime = Math.floor(nextDuration / 1000);
+            this.durationStringTime = secondToTime(this.durationTime);
+            this.totalTime = this.stringForTime(nextDuration);
+            this.avSessionController.setAVMetadataMusic(this.currentSong, nextDuration, this.lyricContent);
+          }
+          this.updateCastProgress(0);
+          await this.castControllerWrapper.playNext(this.songList, this.curIndex, this.videoUrl, nextDuration);
           this.isCastPlaying = this.castControllerWrapper.getIsCastPlaying();
           console.info('heanup playNext', 'CastController 播放下一首成功');
           return;
@@ -15357,12 +15381,25 @@ export struct LocalMusic {
     this.name = this.songList[this.curIndex].name
     this.artist = this.songList[this.curIndex].artist
 
-    // 如果是投播模式,需要为投播设备准备上一首
-    if (this.castController && this.isCastPlaying) {
+    // 如果是投播模式,使用 CastController 切换上一首
+    if (this.castControllerWrapper) {
       Logger.info('heanup playPrevious', '投播模式播放上一首,不调用本地stop()');
-      this.initQueueItem();
-      await this.prepare();
-      // 投播模式下不需要调用本地的 startPlayOrResumePlay
+      try {
+        const prevDuration = this.getSongDurationMs(this.currentSong);
+        if (prevDuration > 0) {
+          this.duration = prevDuration;
+          this.durationTime = Math.floor(prevDuration / 1000);
+          this.durationStringTime = secondToTime(this.durationTime);
+          this.totalTime = this.stringForTime(prevDuration);
+          this.avSessionController.setAVMetadataMusic(this.currentSong, prevDuration, this.lyricContent);
+        }
+        this.updateCastProgress(0);
+        await this.castControllerWrapper.playPrevious(this.songList, this.curIndex, this.videoUrl, prevDuration);
+        this.isCastPlaying = this.castControllerWrapper.getIsCastPlaying();
+      } catch (error) {
+        Logger.error('heanup playPrevious', `CastController 播放上一首失败: ${error}`);
+        this.isCastPlaying = false;
+      }
       this.cover = this.songList[this.curIndex].pixelMapPath;
       return;
     }