|
|
@@ -10,6 +10,8 @@ import { DataPersistenceService, PlaylistSyncService, PlaylistSyncListener, IDat
|
|
|
import { StateSyncService, IStateSyncService, StateChangeCallback, WidgetControlCallback } from './StateSyncService';
|
|
|
import { ErrorRecoveryStrategy, IErrorRecoveryStrategy, ErrorContext, ErrorRecoveryAction } from './ErrorRecoveryStrategy';
|
|
|
import { EnhancedFormUpdateService, UpdateStats } from '../widget/EnhancedFormUpdateService';
|
|
|
+import { AvSessionController } from '../../controller/AvSessionController';
|
|
|
+import { avSession } from '@kit.AVSessionKit';
|
|
|
import json from '@ohos.util.json';
|
|
|
|
|
|
/**
|
|
|
@@ -75,11 +77,13 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
private stateSync: IStateSyncService;
|
|
|
private errorRecovery: IErrorRecoveryStrategy;
|
|
|
private widgetUpdateService: EnhancedFormUpdateService;
|
|
|
+ private avSessionController: AvSessionController | null = null; // 新增:AVSession控制器
|
|
|
private context: common.UIAbilityContext | null = null;
|
|
|
private progressTimer: number = -1;
|
|
|
private isInitialized: boolean = false;
|
|
|
private isDataRestored: boolean = false; // 新增:数据恢复完成标识
|
|
|
private currentRetryCount: number = 0;
|
|
|
+ private lastAvSessionUpdate: number = 0; // 新增:上次AVSession更新时间
|
|
|
|
|
|
private constructor() {
|
|
|
this.playerManager = PlayerManager.getInstance();
|
|
|
@@ -108,23 +112,12 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
- console.log("Heanup2 UnifiedPlayerService: 开始初始化服务");
|
|
|
+ console.log("Heanup2 UnifiedPlayerService: 开始快速初始化服务");
|
|
|
this.context = context;
|
|
|
|
|
|
- // 初始化各个服务
|
|
|
- await this.playerManager.initialize(context);
|
|
|
-
|
|
|
- // 设置播放器状态回调
|
|
|
+ // 快速初始化核心组件
|
|
|
this.playerManager.setStateCallback(this);
|
|
|
- LogUtils.getInstance().LOGI('UnifiedPlayerService: PlayerManager state callback set');
|
|
|
-
|
|
|
- await this.dataPersistence.initialize(context);
|
|
|
- await this.playlistSync.initialize(context);
|
|
|
- await this.stateSync.initialize(context);
|
|
|
-
|
|
|
- // 初始化卡片更新服务
|
|
|
this.widgetUpdateService.setAppContext(context);
|
|
|
- LogUtils.getInstance().LOGI('UnifiedPlayerService: Widget update service initialized');
|
|
|
|
|
|
// 设置同步监听器
|
|
|
this.playlistSync.addSyncListener(this);
|
|
|
@@ -145,11 +138,20 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
|
|
|
onStateChanged(state: PlayerState): void {
|
|
|
this.stateSync.broadcastState(state);
|
|
|
+ // 状态变化时更新AVSession
|
|
|
+ this.unifiedService.updateSessionPlayState(state.isPlaying);
|
|
|
// 状态变化时更新卡片
|
|
|
this.unifiedService.updateWidgetsForStateChange(state);
|
|
|
}
|
|
|
onSongChanged(song: VideoItem): void {
|
|
|
this.stateSync.broadcastSongChange(song);
|
|
|
+ // 歌曲变化时更新AVSession元数据
|
|
|
+ this.unifiedService.updateAvSessionMetadata(song);
|
|
|
+ // 立即强制更新AVSession播放状态
|
|
|
+ setTimeout(() => {
|
|
|
+ const currentState = this.unifiedService.getCurrentState();
|
|
|
+ this.unifiedService.updateSessionPlayState(currentState.isPlaying);
|
|
|
+ }, 500);
|
|
|
// 歌曲变化时更新卡片
|
|
|
this.unifiedService.updateWidgetsForSongChange(song);
|
|
|
}
|
|
|
@@ -165,19 +167,54 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
const stateListener = new StateListenerImpl(this.stateSync, this.widgetUpdateService, this);
|
|
|
this.stateModel.addStateListener(stateListener);
|
|
|
|
|
|
- // 恢复保存的状态
|
|
|
- await this.restorePersistedState();
|
|
|
-
|
|
|
this.setupProgressTimer();
|
|
|
this.isInitialized = true;
|
|
|
|
|
|
- LogUtils.getInstance().LOGI('UnifiedPlayerService: Initialized successfully');
|
|
|
+ // 异步初始化耗时组件,避免阻塞
|
|
|
+ this.initializeHeavyComponentsAsync(context);
|
|
|
+
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: Core initialization completed');
|
|
|
} catch (error) {
|
|
|
LogUtils.getInstance().LOGI(`UnifiedPlayerService initialization error: ${error}`);
|
|
|
- throw new Error;
|
|
|
+ throw new Error(`初始化失败: ${error}`);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 异步初始化耗时组件
|
|
|
+ */
|
|
|
+ private initializeHeavyComponentsAsync(context: common.UIAbilityContext): void {
|
|
|
+ setTimeout(async () => {
|
|
|
+ try {
|
|
|
+ console.log("Heanup2 UnifiedPlayerService: 开始异步初始化耗时组件");
|
|
|
+
|
|
|
+ // 并行初始化各个服务以提高效率
|
|
|
+ const initPromises = [
|
|
|
+ this.playerManager.initialize(context),
|
|
|
+ this.dataPersistence.initialize(context),
|
|
|
+ this.playlistSync.initialize(context),
|
|
|
+ this.stateSync.initialize(context)
|
|
|
+ ];
|
|
|
+
|
|
|
+ await Promise.all(initPromises);
|
|
|
+
|
|
|
+ // 初始化AVSession控制器
|
|
|
+ this.initializeAvSession();
|
|
|
+
|
|
|
+ // 恢复保存的状态
|
|
|
+ await this.restorePersistedState();
|
|
|
+
|
|
|
+ console.log("Heanup2 UnifiedPlayerService: 异步组件初始化完成");
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: Heavy components initialized');
|
|
|
+ } catch (error) {
|
|
|
+ console.log(`Heanup2 UnifiedPlayerService: 异步组件初始化失败: ${error}`);
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService heavy components init error: ${error}`);
|
|
|
+ // 即使失败也设置数据恢复完成,避免无限等待
|
|
|
+ this.isDataRestored = true;
|
|
|
+ }
|
|
|
+ }, 50); // 短延迟,让主初始化先完成
|
|
|
+ }
|
|
|
+
|
|
|
// 播放控制方法
|
|
|
async startPlayOrResumePlay(): Promise<void> {
|
|
|
try {
|
|
|
@@ -228,20 +265,81 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // 如果是暂停状态,直接恢复播放
|
|
|
- if (this.stateModel.getState().isPaused && !this.stateModel.getState().isLoading) {
|
|
|
- await this.playerManager.startPlayOrResumePlay();
|
|
|
- this.stateModel.updatePlayingState(true);
|
|
|
- this.startProgressTimer();
|
|
|
-
|
|
|
- // 保存播放状态变化
|
|
|
- this.saveCurrentState();
|
|
|
-
|
|
|
- // 更新卡片显示播放状态
|
|
|
- await this.updateWidgetsForPlayStateChange(true);
|
|
|
-
|
|
|
+ // 检查播放器状态,优先处理暂停恢复情况
|
|
|
+ const ijkPlayer = this.playerManager.getIjkPlayer();
|
|
|
+ const isPausedByManager = this.playerManager.isPausedState();
|
|
|
+ const isCurrentlyPlaying = ijkPlayer ? ijkPlayer.isPlaying() : false;
|
|
|
+ const currentPlayerState = this.stateModel.getState();
|
|
|
+
|
|
|
+ console.log(`Heanup2 UnifiedPlayerService: 播放器状态检查 - isPausedByManager: ${isPausedByManager}, isCurrentlyPlaying: ${isCurrentlyPlaying}, stateIsPlaying: ${currentPlayerState.isPlaying}, isPaused: ${currentPlayerState.isPaused}`);
|
|
|
+
|
|
|
+ // 更严格的暂停状态检查:
|
|
|
+ // 1. PlayerManager明确标记为暂停状态,AND
|
|
|
+ // 2. 播放器当前不在播放状态,AND
|
|
|
+ // 3. 播放器有有效的数据源(时长 > 0),AND
|
|
|
+ // 4. 状态模型显示为暂停状态或非播放状态
|
|
|
+ const shouldResumeFromPause = isPausedByManager &&
|
|
|
+ !isCurrentlyPlaying &&
|
|
|
+ ijkPlayer &&
|
|
|
+ ijkPlayer.getDuration() > 0 &&
|
|
|
+ (currentPlayerState.isPaused || !currentPlayerState.isPlaying);
|
|
|
+
|
|
|
+ // 额外检查:如果播放器正在播放,绝对不应该进入暂停恢复逻辑
|
|
|
+ if (isCurrentlyPlaying) {
|
|
|
+ console.log("Heanup2 UnifiedPlayerService: 播放器正在播放,跳过暂停恢复检查");
|
|
|
+ // 直接返回,不做任何操作,避免重置播放器
|
|
|
+ this.stateModel.updateLoadingState(false);
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: Player already playing, no action needed');
|
|
|
return;
|
|
|
}
|
|
|
+
|
|
|
+ if (shouldResumeFromPause && ijkPlayer) {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: Resuming from paused state');
|
|
|
+ console.log("Heanup2 UnifiedPlayerService: 检测到暂停状态,准备恢复播放");
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 检查播放器是否已经有数据源
|
|
|
+ const currentPosition = ijkPlayer.getCurrentPosition();
|
|
|
+ const duration = ijkPlayer.getDuration();
|
|
|
+ console.log(`Heanup2 UnifiedPlayerService: 当前播放位置 ${currentPosition}ms, 总时长 ${duration}ms`);
|
|
|
+
|
|
|
+ // 只要播放器有有效的时长信息,就说明已经准备好,可以直接恢复
|
|
|
+ if (duration > 0) {
|
|
|
+ console.log("Heanup2 UnifiedPlayerService: 播放器已准备,直接恢复播放");
|
|
|
+
|
|
|
+ // 更新加载状态为false(恢复播放不需要加载)
|
|
|
+ this.stateModel.updateLoadingState(false);
|
|
|
+
|
|
|
+ // 直接恢复播放,不需要重新准备
|
|
|
+ await this.playerManager.startPlayOrResumePlay();
|
|
|
+
|
|
|
+ // 手动更新播放状态
|
|
|
+ this.stateModel.updatePlayingState(true);
|
|
|
+
|
|
|
+ // 启动进度定时器
|
|
|
+ this.startProgressTimer();
|
|
|
+
|
|
|
+ // 立即更新一次进度,确保UI显示正确的当前位置
|
|
|
+ setTimeout(() => {
|
|
|
+ this.updateProgress();
|
|
|
+ }, 100);
|
|
|
+
|
|
|
+ // 保存播放状态变化
|
|
|
+ this.saveCurrentState();
|
|
|
+
|
|
|
+ // 更新AVSession播放状态
|
|
|
+ setTimeout(() => {
|
|
|
+ this.updateSessionPlayState(true);
|
|
|
+ }, 200);
|
|
|
+
|
|
|
+ // 更新卡片显示播放状态
|
|
|
+ await this.updateWidgetsForPlayStateChange(true);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Resume from pause failed: ${error}, will re-prepare player`);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
// 检查文件是否存在(对于本地文件)
|
|
|
if (!currentSong.filePath.startsWith('http')) {
|
|
|
@@ -265,23 +363,22 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
// 准备播放器
|
|
|
const preparedPlayer = this.playerManager.getIjkPlayer();
|
|
|
if (preparedPlayer) {
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Preparing async for ${currentSong.name}`);
|
|
|
preparedPlayer.prepareAsync();
|
|
|
// 注意:实际播放和状态更新会在 onPrepared 回调中开始
|
|
|
}
|
|
|
|
|
|
+ // 更新AVSession元数据
|
|
|
+ await this.updateAvSessionMetadata(currentSong);
|
|
|
+
|
|
|
// 恢复播放位置(如果有记忆播放功能)
|
|
|
this.restorePlaybackPosition(currentSong);
|
|
|
|
|
|
// 重置重试计数(播放成功)
|
|
|
this.currentRetryCount = 0;
|
|
|
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Started playing ${currentSong.name}`);
|
|
|
} catch (error) {
|
|
|
this.stateModel.updateLoadingState(false);
|
|
|
this.stateModel.updatePlayingState(false);
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService startPlayOrResumePlay error: ${error}`);
|
|
|
-
|
|
|
// 使用错误恢复机制处理错误
|
|
|
await this.handlePlaybackError(error as Error, PlayerErrorType.PLAYBACK_ERROR);
|
|
|
}
|
|
|
@@ -295,9 +392,7 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
if (!song) {
|
|
|
throw new Error('No song provided for direct play');
|
|
|
}
|
|
|
-
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Starting direct play for ${song.name}`);
|
|
|
-
|
|
|
+
|
|
|
this.stateModel.updateLoadingState(true);
|
|
|
|
|
|
const playerInstance = this.playerManager.getIjkPlayer();
|
|
|
@@ -319,37 +414,44 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
// 准备播放器
|
|
|
const preparedPlayer = this.playerManager.getIjkPlayer();
|
|
|
if (preparedPlayer) {
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Preparing async for ${song.name}`);
|
|
|
preparedPlayer.prepareAsync();
|
|
|
// 注意:实际播放和状态更新会在 onPrepared 回调中开始
|
|
|
}
|
|
|
-
|
|
|
- // 新歌曲不需要恢复播放位置,从头开始播放
|
|
|
- // this.restorePlaybackPosition(song); // 注释掉这行
|
|
|
-
|
|
|
// 重置重试计数
|
|
|
this.currentRetryCount = 0;
|
|
|
-
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Started direct playing ${song.name}`);
|
|
|
}
|
|
|
|
|
|
async pause(): Promise<void> {
|
|
|
try {
|
|
|
+
|
|
|
+ const ijkPlayer = this.playerManager.getIjkPlayer();
|
|
|
+
|
|
|
+ // 保存当前播放位置
|
|
|
this.savePlaybackPosition();
|
|
|
+
|
|
|
+ // 暂停播放器 - 确保先暂停播放器
|
|
|
this.playerManager.pausePlayback();
|
|
|
+
|
|
|
+ // 立即更新状态模型为暂停状态,确保状态同步
|
|
|
this.stateModel.updatePlayingState(false);
|
|
|
+
|
|
|
+ // 停止进度定时器
|
|
|
this.stopProgressTimer();
|
|
|
|
|
|
+ // 等待一小段时间确保播放器状态稳定
|
|
|
+ await new Promise<void>(resolve => setTimeout(resolve, 50));
|
|
|
+
|
|
|
// 保存播放状态变化
|
|
|
this.saveCurrentState();
|
|
|
|
|
|
+ // 立即更新AVSession播放状态
|
|
|
+ this.updateSessionPlayState(false);
|
|
|
+
|
|
|
// 更新卡片显示暂停状态
|
|
|
await this.updateWidgetsForPlayStateChange(false);
|
|
|
-
|
|
|
- LogUtils.getInstance().LOGI('UnifiedPlayerService: Playback paused');
|
|
|
+
|
|
|
} catch (error) {
|
|
|
LogUtils.getInstance().LOGI(`UnifiedPlayerService pause error: ${error}`);
|
|
|
- throw new Error;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -361,7 +463,6 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
this.stateModel.updateProgress(0, 0);
|
|
|
this.stopProgressTimer();
|
|
|
|
|
|
- LogUtils.getInstance().LOGI('UnifiedPlayerService: Playback stopped');
|
|
|
} catch (error) {
|
|
|
LogUtils.getInstance().LOGI(`UnifiedPlayerService stop error: ${error}`);
|
|
|
throw new Error;
|
|
|
@@ -380,11 +481,9 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
throw new Error('WMA format does not support seeking');
|
|
|
}
|
|
|
|
|
|
- this.playerManager.seekToPosition(position);
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Seeked to position ${position}`);
|
|
|
+ await this.playerManager.seekToPosition(position);
|
|
|
} catch (error) {
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService seekTo error: ${error}`);
|
|
|
- throw new Error;
|
|
|
+ throw new Error(`Failed to seek to position: ${error}`);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -394,18 +493,10 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
const playMode = this.stateModel.getState().playMode;
|
|
|
|
|
|
if (!this.playlistModel.hasNext(playMode)) {
|
|
|
- LogUtils.getInstance().LOGI('UnifiedPlayerService: No next song available');
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
- // 先获取当前歌曲信息用于日志
|
|
|
- const currentSong = this.playlistModel.getCurrentSong();
|
|
|
- const currentIndex = this.playlistModel.getCurrentIndex();
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Moving from index ${currentIndex} (${currentSong?.name || 'unknown'})`);
|
|
|
-
|
|
|
const moved = this.playlistModel.moveToNext(playMode);
|
|
|
if (!moved) {
|
|
|
- LogUtils.getInstance().LOGI('UnifiedPlayerService: Failed to move to next song');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -415,7 +506,6 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
this.stateModel.updateCurrentIndex(newIndex);
|
|
|
|
|
|
if (newSong) {
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Moved to index ${newIndex} (${newSong.name})`);
|
|
|
this.stateModel.updateCurrentSong(newSong);
|
|
|
}
|
|
|
|
|
|
@@ -433,9 +523,7 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
await this.updateWidgetsForSongChange(newSong, true);
|
|
|
}
|
|
|
|
|
|
- LogUtils.getInstance().LOGI('UnifiedPlayerService: Moved to next song');
|
|
|
} catch (error) {
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService playNext error: ${error}`);
|
|
|
await this.handlePlaybackError(error as Error, PlayerErrorType.PLAYBACK_ERROR);
|
|
|
}
|
|
|
}
|
|
|
@@ -445,18 +533,15 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
const playMode = this.stateModel.getState().playMode;
|
|
|
|
|
|
if (!this.playlistModel.hasPrevious(playMode)) {
|
|
|
- LogUtils.getInstance().LOGI('UnifiedPlayerService: No previous song available');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 先获取当前歌曲信息用于日志
|
|
|
const currentSong = this.playlistModel.getCurrentSong();
|
|
|
const currentIndex = this.playlistModel.getCurrentIndex();
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Moving from index ${currentIndex} (${currentSong?.name || 'unknown'})`);
|
|
|
|
|
|
const moved = this.playlistModel.moveToPrevious(playMode);
|
|
|
if (!moved) {
|
|
|
- LogUtils.getInstance().LOGI('UnifiedPlayerService: Failed to move to previous song');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -466,7 +551,6 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
this.stateModel.updateCurrentIndex(newIndex);
|
|
|
|
|
|
if (newSong) {
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Moved to index ${newIndex} (${newSong.name})`);
|
|
|
this.stateModel.updateCurrentSong(newSong);
|
|
|
}
|
|
|
|
|
|
@@ -484,9 +568,7 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
await this.updateWidgetsForSongChange(newSong, true);
|
|
|
}
|
|
|
|
|
|
- LogUtils.getInstance().LOGI('UnifiedPlayerService: Moved to previous song');
|
|
|
} catch (error) {
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService playPrevious error: ${error}`);
|
|
|
await this.handlePlaybackError(error as Error, PlayerErrorType.PLAYBACK_ERROR);
|
|
|
}
|
|
|
}
|
|
|
@@ -512,9 +594,7 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
this.stateModel.updateCurrentSong(currentSong);
|
|
|
}
|
|
|
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Playing song at index ${index}`);
|
|
|
} catch (error) {
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService playSongAtIndex error: ${error}`);
|
|
|
throw new Error;
|
|
|
}
|
|
|
}
|
|
|
@@ -664,6 +744,12 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
// 保存当前状态
|
|
|
this.saveCurrentState();
|
|
|
|
|
|
+ // 清理AVSession
|
|
|
+ if (this.avSessionController) {
|
|
|
+ this.avSessionController.unregisterSessionListener();
|
|
|
+ this.avSessionController = null;
|
|
|
+ }
|
|
|
+
|
|
|
// 清理同步监听器
|
|
|
this.playlistSync.removeSyncListener(this);
|
|
|
this.playlistSync.release();
|
|
|
@@ -681,6 +767,130 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // ==================== AVSession 控制方法 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化AVSession
|
|
|
+ * 按照官方文档的要求:创建 -> 注册控制命令 -> 设置元数据 -> 激活
|
|
|
+ */
|
|
|
+ private initializeAvSession(): void {
|
|
|
+ try {
|
|
|
+ // 1. 创建AVSession控制器(音频模式)
|
|
|
+ this.avSessionController = AvSessionController.getInstance(false);
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession controller created');
|
|
|
+
|
|
|
+ // 2. 延迟设置监听器,等待AVSession创建完成
|
|
|
+ setTimeout(() => {
|
|
|
+ this.setAvSessionListener();
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession listeners configured');
|
|
|
+ }, 500);
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to initialize AVSession: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置AVSession监听器
|
|
|
+ * 注意:控制命令已在AvSessionController中注册,这里设置实际的处理逻辑
|
|
|
+ */
|
|
|
+ private setAvSessionListener(): void {
|
|
|
+ if (!this.avSessionController) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const avSession = this.avSessionController.getAvSession();
|
|
|
+ if (!avSession) {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession not available for listener setup');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 重新注册监听器,覆盖AvSessionController中的空实现
|
|
|
+ // 播放事件监听
|
|
|
+ avSession.on('play', () => {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession play command received');
|
|
|
+ this.startPlayOrResumePlay().catch((error: Error) => {
|
|
|
+ LogUtils.getInstance().LOGI(`AVSession play command failed: ${error}`);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 暂停事件监听
|
|
|
+ avSession.on('pause', () => {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession pause command received');
|
|
|
+ this.pause().catch((error: Error) => {
|
|
|
+ LogUtils.getInstance().LOGI(`AVSession pause command failed: ${error}`);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 停止事件监听
|
|
|
+ avSession.on('stop', () => {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession stop command received');
|
|
|
+ this.stop().catch((error: Error) => {
|
|
|
+ LogUtils.getInstance().LOGI(`AVSession stop command failed: ${error}`);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 下一首事件监听
|
|
|
+ avSession.on('playNext', () => {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession playNext command received');
|
|
|
+ this.playNext().catch((error: Error) => {
|
|
|
+ LogUtils.getInstance().LOGI(`AVSession playNext command failed: ${error}`);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 上一首事件监听
|
|
|
+ avSession.on('playPrevious', () => {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession playPrevious command received');
|
|
|
+ this.playPrevious().catch((error: Error) => {
|
|
|
+ LogUtils.getInstance().LOGI(`AVSession playPrevious command failed: ${error}`);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 拖拽进度事件监听
|
|
|
+ avSession.on('seek', (time: number) => {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession seek command received: ${time}ms`);
|
|
|
+ this.seekTo(time.toString()).catch((error: Error) => {
|
|
|
+ LogUtils.getInstance().LOGI(`AVSession seek command failed: ${error}`);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 循环模式设置监听
|
|
|
+ avSession.on('setLoopMode', (mode: avSession.LoopMode) => {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession setLoopMode command received: ${mode}`);
|
|
|
+ // 转换AVSession循环模式到应用内部播放模式
|
|
|
+ const playMode = this.convertLoopModeToPlayMode(mode);
|
|
|
+ this.setPlayMode(playMode);
|
|
|
+ });
|
|
|
+
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession listeners set up successfully');
|
|
|
+ } catch (error) {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to set AVSession listeners: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 转换AVSession循环模式为播放模式
|
|
|
+ */
|
|
|
+ private convertLoopModeToPlayMode(loopMode: avSession.LoopMode): number {
|
|
|
+ switch (loopMode) {
|
|
|
+ case avSession.LoopMode.LOOP_MODE_SINGLE:
|
|
|
+ return PlayMode.SINGLE_REPEAT;
|
|
|
+ case avSession.LoopMode.LOOP_MODE_LIST:
|
|
|
+ return PlayMode.NORMAL;
|
|
|
+ case avSession.LoopMode.LOOP_MODE_SHUFFLE:
|
|
|
+ return PlayMode.RANDOM;
|
|
|
+ case avSession.LoopMode.LOOP_MODE_SEQUENCE:
|
|
|
+ default:
|
|
|
+ return PlayMode.SEQUENCE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
// ==================== 错误处理和恢复方法 ====================
|
|
|
|
|
|
/**
|
|
|
@@ -864,7 +1074,9 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
throw new Error('Player not initialized');
|
|
|
}
|
|
|
|
|
|
- // 重置播放器
|
|
|
+ console.log("Heanup2 UnifiedPlayerService: 开始重新设置播放器,这会重置所有状态");
|
|
|
+
|
|
|
+ // 重置播放器 - 这会清除所有状态包括播放位置
|
|
|
ijkPlayer.reset();
|
|
|
|
|
|
// 重新设置音频模式 - 重置后需要重新设置
|
|
|
@@ -901,6 +1113,7 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
ijkPlayer.setMessageListener();
|
|
|
|
|
|
LogUtils.getInstance().LOGI(`UnifiedPlayerService: Player setup for song ${song.name}`);
|
|
|
+ console.log("Heanup2 UnifiedPlayerService: 播放器重新设置完成,准备开始新的播放");
|
|
|
}
|
|
|
private setupProgressTimer(): void {
|
|
|
this.progressTimer = setInterval(() => {
|
|
|
@@ -909,29 +1122,51 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
}
|
|
|
|
|
|
private startProgressTimer(): void {
|
|
|
- if (this.progressTimer === -1) {
|
|
|
- this.setupProgressTimer();
|
|
|
- }
|
|
|
+ // 先停止现有的定时器,避免重复启动
|
|
|
+ this.stopProgressTimer();
|
|
|
+
|
|
|
+ // 启动新的定时器
|
|
|
+ this.setupProgressTimer();
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: Progress timer started');
|
|
|
}
|
|
|
|
|
|
private stopProgressTimer(): void {
|
|
|
if (this.progressTimer !== -1) {
|
|
|
clearInterval(this.progressTimer);
|
|
|
this.progressTimer = -1;
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: Progress timer stopped');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private updateProgress(): void {
|
|
|
try {
|
|
|
const ijkPlayer = this.playerManager.getIjkPlayer();
|
|
|
- if (!ijkPlayer || !this.stateModel.getState().isPlaying) {
|
|
|
+ const currentState = this.stateModel.getState();
|
|
|
+
|
|
|
+ // 只有在播放器存在且正在播放时才更新进度
|
|
|
+ if (!ijkPlayer || !currentState.isPlaying) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const currentPosition = ijkPlayer.getCurrentPosition();
|
|
|
const duration = ijkPlayer.getDuration();
|
|
|
|
|
|
- this.stateModel.updateProgress(currentPosition, duration);
|
|
|
+ // 确保获取到有效的位置信息
|
|
|
+ if (currentPosition >= 0 && duration > 0) {
|
|
|
+ this.stateModel.updateProgress(currentPosition, duration);
|
|
|
+
|
|
|
+ // 定期更新AVSession播放状态,确保系统媒体控制界面显示正确的进度
|
|
|
+ // 前30秒每3秒更新一次,之后每10秒更新一次
|
|
|
+ const now = Date.now();
|
|
|
+ const timeSinceStart = now - (this.lastAvSessionUpdate || now);
|
|
|
+ const updateInterval = timeSinceStart < 30000 ? 3000 : 10000; // 前30秒3秒一次,之后10秒一次
|
|
|
+
|
|
|
+ if (!this.lastAvSessionUpdate || now - this.lastAvSessionUpdate > updateInterval) {
|
|
|
+ this.lastAvSessionUpdate = now;
|
|
|
+ this.updateSessionPlayState(true);
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession progress updated - Position: ${currentPosition}ms, Duration: ${duration}ms`);
|
|
|
+ }
|
|
|
+ }
|
|
|
} catch (error) {
|
|
|
LogUtils.getInstance().LOGI(`UnifiedPlayerService updateProgress error: ${error}`);
|
|
|
}
|
|
|
@@ -975,15 +1210,14 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private restorePlaybackPosition(song: VideoItem): void {
|
|
|
+ private async restorePlaybackPosition(song: VideoItem): Promise<void> {
|
|
|
try {
|
|
|
// 使用DataPersistenceService恢复播放进度
|
|
|
- this.dataPersistence.loadPlaybackProgress(song.filePath).then(progressData => {
|
|
|
- if (progressData && progressData.position > 0 && !progressData.completed) {
|
|
|
- this.playerManager.seekToPosition(progressData.position.toString());
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService: Restored playback position ${progressData.position}ms for ${song.name}`);
|
|
|
- }
|
|
|
- })
|
|
|
+ const progressData = await this.dataPersistence.loadPlaybackProgress(song.filePath);
|
|
|
+ if (progressData && progressData.position > 0 && !progressData.completed) {
|
|
|
+ await this.playerManager.seekToPosition(progressData.position.toString());
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Restored playback position ${progressData.position}ms for ${song.name}`);
|
|
|
+ }
|
|
|
} catch (error) {
|
|
|
LogUtils.getInstance().LOGI(`UnifiedPlayerService restorePlaybackPosition error: ${error}`);
|
|
|
}
|
|
|
@@ -995,12 +1229,17 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
*/
|
|
|
private async restorePersistedState(): Promise<void> {
|
|
|
try {
|
|
|
- console.log("Heanup2 UnifiedPlayerService: 开始恢复持久化状态");
|
|
|
+ console.log("Heanup2 UnifiedPlayerService: 开始快速恢复持久化状态");
|
|
|
|
|
|
- // 恢复播放列表
|
|
|
- const playlistData = await this.dataPersistence.loadPlaylist();
|
|
|
- console.log(`Heanup2 UnifiedPlayerService: 加载到的播放列表数据: ${playlistData ? 'exists' : 'null'}`);
|
|
|
+ // 并行加载播放列表和播放状态,提高效率
|
|
|
+ const loadResults = await Promise.all([
|
|
|
+ this.dataPersistence.loadPlaylist().catch(() => null),
|
|
|
+ this.dataPersistence.loadPlayerState().catch(() => null)
|
|
|
+ ]);
|
|
|
+ const playlistData = loadResults[0];
|
|
|
+ const stateData = loadResults[1];
|
|
|
|
|
|
+ // 恢复播放列表
|
|
|
if (playlistData && playlistData.songs.length > 0) {
|
|
|
console.log(`Heanup2 UnifiedPlayerService: 恢复播放列表,歌曲数量=${playlistData.songs.length}, 当前索引=${playlistData.currentIndex}`);
|
|
|
this.playlistModel.replaceSongs(playlistData.songs, playlistData.currentIndex);
|
|
|
@@ -1020,25 +1259,16 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
}
|
|
|
|
|
|
// 恢复播放状态
|
|
|
- const stateData = await this.dataPersistence.loadPlayerState();
|
|
|
if (stateData) {
|
|
|
console.log(`Heanup2 UnifiedPlayerService: 加载到的播放状态 - playing: ${stateData.isPlaying}, paused: ${stateData.isPaused}, index: ${stateData.currentIndex}`);
|
|
|
|
|
|
+ // 批量更新状态,减少回调次数
|
|
|
this.stateModel.updateVolume(stateData.volume);
|
|
|
this.stateModel.updateSpeed(stateData.speed);
|
|
|
this.stateModel.updatePlayMode(stateData.playMode);
|
|
|
|
|
|
- // 恢复播放状态(isPlaying 和 isPaused)
|
|
|
- if (stateData.isPlaying) {
|
|
|
- console.log("Heanup2 UnifiedPlayerService: 恢复播放状态为播放中");
|
|
|
- this.stateModel.updatePlayingState(true);
|
|
|
- } else if (stateData.isPaused) {
|
|
|
- console.log("Heanup2 UnifiedPlayerService: 恢复播放状态为暂停");
|
|
|
- this.stateModel.updatePlayingState(false);
|
|
|
- } else {
|
|
|
- console.log("Heanup2 UnifiedPlayerService: 恢复播放状态为停止");
|
|
|
- this.stateModel.updatePlayingState(false);
|
|
|
- }
|
|
|
+ // 恢复播放状态
|
|
|
+ this.stateModel.updatePlayingState(stateData.isPlaying || false);
|
|
|
|
|
|
// 恢复当前播放索引
|
|
|
if (stateData.currentIndex >= 0) {
|
|
|
@@ -1051,7 +1281,7 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
console.log("Heanup2 UnifiedPlayerService: 没有找到保存的播放状态");
|
|
|
}
|
|
|
|
|
|
- console.log("Heanup2 UnifiedPlayerService: 持久化状态恢复完成");
|
|
|
+ console.log("Heanup2 UnifiedPlayerService: 持久化状态快速恢复完成");
|
|
|
|
|
|
// 设置数据恢复完成标识
|
|
|
this.isDataRestored = true;
|
|
|
@@ -1347,20 +1577,45 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
onPrepared(): void {
|
|
|
LogUtils.getInstance().LOGI('UnifiedPlayerService: Player prepared');
|
|
|
this.stateModel.updateLoadingState(false);
|
|
|
+
|
|
|
+ // 播放器准备完成时立即更新AVSession状态
|
|
|
+ setTimeout(() => {
|
|
|
+ const currentState = this.stateModel.getState();
|
|
|
+ this.updateSessionPlayState(currentState.isPlaying);
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession state updated after player prepared');
|
|
|
+ }, 100);
|
|
|
+
|
|
|
// 播放器准备完成时更新卡片(移除加载状态)
|
|
|
this.updateWidgetsForStateChange(this.stateModel.getState());
|
|
|
}
|
|
|
|
|
|
onPlaybackStarted(): void {
|
|
|
- LogUtils.getInstance().LOGI('UnifiedPlayerService: Playback started');
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: Playback started (new song)');
|
|
|
+
|
|
|
+ // 这个回调只应该在新歌曲开始播放时被调用(通过onPrepared触发)
|
|
|
+ // 不应该在暂停恢复时被调用
|
|
|
+
|
|
|
+ // 立即更新播放状态
|
|
|
this.stateModel.updatePlayingState(true);
|
|
|
+
|
|
|
+ // 启动进度定时器
|
|
|
this.startProgressTimer();
|
|
|
|
|
|
// 保存播放状态变化
|
|
|
this.saveCurrentState();
|
|
|
|
|
|
+ // 延迟更新AVSession,确保播放器状态稳定
|
|
|
+ setTimeout(() => {
|
|
|
+ this.updateSessionPlayState(true);
|
|
|
+ // 设置当前播放模式
|
|
|
+ this.setCurrentPlayMode();
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession state updated for new song playback');
|
|
|
+ }, 200);
|
|
|
+
|
|
|
// 播放开始时更新卡片显示播放状态
|
|
|
this.updateWidgetsForPlayStateChange(true);
|
|
|
+
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: New song playback started successfully');
|
|
|
}
|
|
|
|
|
|
onPlaybackCompleted(): void {
|
|
|
@@ -1371,6 +1626,9 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
// 保存播放状态变化
|
|
|
this.saveCurrentState();
|
|
|
|
|
|
+ // 立即更新AVSession播放状态为完成
|
|
|
+ this.updateSessionPlayState(false);
|
|
|
+
|
|
|
// 更新卡片显示播放完成状态
|
|
|
this.updateWidgetsForPlayStateChange(false);
|
|
|
|
|
|
@@ -1431,6 +1689,15 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
const totalCount = this.playlistModel.getTotalCount();
|
|
|
const playMode = currentState.playMode;
|
|
|
|
|
|
+ // 调试封面路径信息
|
|
|
+ if (currentSong) {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: createWidgetData - Song: ${currentSong.name}`);
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: createWidgetData - pixelMapPath: ${currentSong.pixelMapPath || 'empty'}`);
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: createWidgetData - filePath: ${currentSong.filePath || 'empty'}`);
|
|
|
+ } else {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: createWidgetData - No current song`);
|
|
|
+ }
|
|
|
+
|
|
|
return {
|
|
|
playState: {
|
|
|
isPlaying: currentState.isPlaying,
|
|
|
@@ -1583,23 +1850,245 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
*/
|
|
|
private parseDuration(durationStr: string): number {
|
|
|
if (!durationStr || durationStr === '0') {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: parseDuration - empty or zero duration: '${durationStr}'`);
|
|
|
return 0;
|
|
|
}
|
|
|
-
|
|
|
- // 如果已经是数字,直接返回
|
|
|
- const numValue = Number(durationStr);
|
|
|
- if (!isNaN(numValue)) {
|
|
|
- return numValue;
|
|
|
+
|
|
|
+ try {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: parseDuration - parsing: '${durationStr}'`);
|
|
|
+
|
|
|
+ // 如果是纯数字,假设是秒数
|
|
|
+ const numValue = parseFloat(durationStr);
|
|
|
+ if (!isNaN(numValue)) {
|
|
|
+ const result = Math.floor(numValue * 1000); // 转换为毫秒
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: parseDuration - parsed as number: ${numValue}s -> ${result}ms`);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果包含冒号,解析为 MM:SS 或 HH:MM:SS 格式
|
|
|
+ if (durationStr.includes(':')) {
|
|
|
+ const parts = durationStr.split(':').map(part => parseInt(part, 10));
|
|
|
+ let totalSeconds = 0;
|
|
|
+
|
|
|
+ if (parts.length === 2) {
|
|
|
+ // MM:SS 格式
|
|
|
+ totalSeconds = parts[0] * 60 + parts[1];
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: parseDuration - parsed MM:SS format: ${durationStr} -> ${totalSeconds}s`);
|
|
|
+ } else if (parts.length === 3) {
|
|
|
+ // HH:MM:SS 格式
|
|
|
+ totalSeconds = parts[0] * 3600 + parts[1] * 60 + parts[2];
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: parseDuration - parsed HH:MM:SS format: ${durationStr} -> ${totalSeconds}s`);
|
|
|
+ }
|
|
|
+
|
|
|
+ const result = totalSeconds * 1000; // 转换为毫秒
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: parseDuration - final result: ${result}ms`);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: parseDuration - unknown format: '${durationStr}', returning 0`);
|
|
|
+ return 0;
|
|
|
+ } catch (error) {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService parseDuration error for '${durationStr}': ${error}`);
|
|
|
+ return 0;
|
|
|
}
|
|
|
-
|
|
|
- // 解析 MM:SS 格式
|
|
|
- const parts = durationStr.split(':');
|
|
|
- if (parts.length === 2) {
|
|
|
- const minutes = parseInt(parts[0]) || 0;
|
|
|
- const seconds = parseInt(parts[1]) || 0;
|
|
|
- return (minutes * 60 + seconds) * 1000;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== PlayerStateCallback 实现 ====================
|
|
|
+
|
|
|
+
|
|
|
+ // ==================== AVSession 相关方法 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 转换播放模式为AVSession循环模式
|
|
|
+ */
|
|
|
+ private convertPlayModeToLoopMode(playMode: number): avSession.LoopMode {
|
|
|
+ switch (playMode) {
|
|
|
+ case PlayMode.SINGLE_REPEAT:
|
|
|
+ return avSession.LoopMode.LOOP_MODE_SINGLE;
|
|
|
+ case PlayMode.NORMAL:
|
|
|
+ return avSession.LoopMode.LOOP_MODE_LIST;
|
|
|
+ case PlayMode.RANDOM:
|
|
|
+ return avSession.LoopMode.LOOP_MODE_SHUFFLE;
|
|
|
+ case PlayMode.SEQUENCE:
|
|
|
+ default:
|
|
|
+ return avSession.LoopMode.LOOP_MODE_SEQUENCE;
|
|
|
}
|
|
|
-
|
|
|
- return 0;
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新AVSession播放状态
|
|
|
+ */
|
|
|
+ private updateSessionPlayState(isPlaying: boolean): void {
|
|
|
+ try {
|
|
|
+ if (!this.avSessionController) {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession controller not initialized');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const currentSong = this.playlistModel.getCurrentSong();
|
|
|
+ const currentPosition = this.getCurrentPosition();
|
|
|
+
|
|
|
+ // 优先从播放器获取实际duration
|
|
|
+ let duration = 0;
|
|
|
+ try {
|
|
|
+ const ijkPlayer = this.playerManager.getIjkPlayer();
|
|
|
+ if (ijkPlayer) {
|
|
|
+ duration = ijkPlayer.getDuration();
|
|
|
+ }
|
|
|
+ if (duration <= 0 && currentSong?.duration) {
|
|
|
+ duration = this.parseDuration(currentSong.duration);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ duration = currentSong?.duration ? this.parseDuration(currentSong.duration) : 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ const currentState = this.stateModel.getState();
|
|
|
+
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession update - Position: ${currentPosition}ms, Duration: ${duration}ms, Song: ${currentSong?.name}`);
|
|
|
+
|
|
|
+ // 按照官方文档要求设置完整的播放状态
|
|
|
+ const playbackState: avSession.AVPlaybackState = {
|
|
|
+ // 播放状态
|
|
|
+ state: isPlaying ? avSession.PlaybackState.PLAYBACK_STATE_PLAY : avSession.PlaybackState.PLAYBACK_STATE_PAUSE,
|
|
|
+
|
|
|
+ // 播放速度
|
|
|
+ speed: currentState.speed || 1.0,
|
|
|
+
|
|
|
+ // 循环模式
|
|
|
+ loopMode: this.convertPlayModeToLoopMode(currentState.playMode),
|
|
|
+
|
|
|
+ // 收藏状态
|
|
|
+ isFavorite: false // 可以根据实际收藏状态设置
|
|
|
+ };
|
|
|
+
|
|
|
+ // 只有当duration > 0时才设置位置信息,避免进度条显示问题
|
|
|
+ if (duration > 0) {
|
|
|
+ playbackState.position = {
|
|
|
+ elapsedTime: Math.max(0, currentPosition), // 已播放时间(毫秒)
|
|
|
+ updateTime: Date.now() // 更新时间戳
|
|
|
+ };
|
|
|
+ playbackState.bufferedTime = Math.max(currentPosition, 0);
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Setting position info - Position: ${currentPosition}ms, Duration: ${duration}ms`);
|
|
|
+ } else {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Skipping position info due to invalid duration: ${duration}ms`);
|
|
|
+ }
|
|
|
+
|
|
|
+ this.avSessionController.setAvSessionPlayState(playbackState);
|
|
|
+
|
|
|
+ // 记录上次更新时间
|
|
|
+ this.lastAvSessionUpdate = Date.now();
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to update AVSession play state: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置当前播放模式到AVSession
|
|
|
+ */
|
|
|
+ private setCurrentPlayMode(): void {
|
|
|
+ try {
|
|
|
+ if (!this.avSessionController) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const currentSong = this.playlistModel.getCurrentSong();
|
|
|
+ if (!currentSong) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 修复duration转换问题 - 优先从播放器获取实际duration
|
|
|
+ let duration = 0;
|
|
|
+ try {
|
|
|
+ const ijkPlayer = this.playerManager.getIjkPlayer();
|
|
|
+ if (ijkPlayer) {
|
|
|
+ const playerDuration = ijkPlayer.getDuration();
|
|
|
+ if (playerDuration > 0) {
|
|
|
+ duration = playerDuration;
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Using player duration: ${duration}ms`);
|
|
|
+ } else {
|
|
|
+ // 备用方案:解析VideoItem中的duration
|
|
|
+ duration = currentSong.duration ? this.parseDuration(currentSong.duration) : 0;
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Using parsed duration: ${duration}ms from '${currentSong.duration}'`);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ duration = currentSong.duration ? this.parseDuration(currentSong.duration) : 0;
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: No player available, using parsed duration: ${duration}ms`);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ duration = currentSong.duration ? this.parseDuration(currentSong.duration) : 0;
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Error getting player duration, using parsed: ${duration}ms, error: ${error}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取歌词内容(如果有的话)
|
|
|
+ const lyricContent = ''; // 这里可以根据需要获取歌词内容
|
|
|
+
|
|
|
+ this.avSessionController.setAVMetadataMusic(currentSong, duration, lyricContent);
|
|
|
+
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession metadata updated for ${currentSong.name} with duration ${duration}ms`);
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to set current play mode: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新AVSession元数据
|
|
|
+ */
|
|
|
+ private async updateAvSessionMetadata(song: VideoItem): Promise<void> {
|
|
|
+ try {
|
|
|
+ if (!this.avSessionController) {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession controller not initialized');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 调试:打印VideoItem的duration字段
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: VideoItem duration field - value: '${song.duration}', type: ${typeof song.duration}`);
|
|
|
+
|
|
|
+ // 优先从播放器获取实际duration
|
|
|
+ let duration = 0;
|
|
|
+ try {
|
|
|
+ const ijkPlayer = this.playerManager.getIjkPlayer();
|
|
|
+ if (ijkPlayer) {
|
|
|
+ const playerDuration = ijkPlayer.getDuration();
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Player getDuration() returned: ${playerDuration}ms`);
|
|
|
+ if (playerDuration > 0) {
|
|
|
+ duration = playerDuration;
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Using player duration: ${duration}ms`);
|
|
|
+ } else {
|
|
|
+ // 备用方案:解析VideoItem中的duration
|
|
|
+ duration = song.duration ? this.parseDuration(song.duration) : 0;
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Player duration invalid, using parsed duration: ${duration}ms from '${song.duration}'`);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ duration = song.duration ? this.parseDuration(song.duration) : 0;
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: No player available, using parsed duration: ${duration}ms from '${song.duration}'`);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ duration = song.duration ? this.parseDuration(song.duration) : 0;
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Error getting player duration, using parsed: ${duration}ms from '${song.duration}', error: ${error}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 最终验证duration
|
|
|
+ if (isNaN(duration) || duration <= 0) {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Final duration is invalid (${duration}), setting to 0`);
|
|
|
+ duration = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取歌词内容(如果有的话)
|
|
|
+ const lyricContent = song.lyricContent || '';
|
|
|
+
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: About to call setAVMetadataMusic with duration: ${duration}ms (type: ${typeof duration})`);
|
|
|
+ await this.avSessionController.setAVMetadataMusic(song, duration, lyricContent);
|
|
|
+
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession metadata updated for ${song.name} with duration ${duration}ms`);
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to update AVSession metadata: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 辅助方法 ====================
|
|
|
+
|
|
|
+
|
|
|
}
|