xugenyuan

ref |> 音频播放接入系统播控中心

import window from '@ohos.window';
import { Logger } from 'wdKit';
import { WDPlayerController } from 'wdPlayer';
import { BackgroundAudioController, WDPlayerController } from 'wdPlayer';
import { BusinessError } from '@ohos.base';
import { EmitterEventId, EmitterUtils } from 'wdKit/Index'
... ... @@ -42,6 +42,14 @@ export class AudioSuspensionModel {
public setPlayerUrl(url: string, srcTitle: string) {
/*console.log(TAG,'this.url', this.url)
console.log(TAG,'url', url)*/
this.playerController.get().keepOnBackground = true
BackgroundAudioController.sharedController().avplayerController = this.playerController.get()
BackgroundAudioController.sharedController().createSession()
BackgroundAudioController.sharedController().startContinuousTask()
BackgroundAudioController.sharedController().setSessionMetaData("aaa", srcTitle, "")
BackgroundAudioController.sharedController().stopUseFeatures()
BackgroundAudioController.sharedController().listenPlayEvents()
if (this.url === url) {
this.isMinimize = AppStorage.link<boolean>('isMinimize')
console.log(TAG, 'this.isMinimize', this.isMinimize?.get())
... ...
... ... @@ -17,3 +17,5 @@ export { WDAliPlayerController } from "./src/main/ets/controller/WDAliPlayerCont
export { WDListPlayerData, WDAliListPlayerController } from "./src/main/ets/controller/WDAliListPlayerController"
export { AliPlayerRenderView } from "./src/main/ets/pages/AliPlayerRenderView"
export { BackgroundAudioController } from "./src/main/ets/controller/BackgroundAudioController"
\ No newline at end of file
... ...
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 { Logger } from 'wdKit/Index'
import { PlayerConstants } from '../constants/PlayerConstants'
import { WDPlayerController } from './WDPlayerController'
const TAG = "BackgroundAudioController"
export class BackgroundAudioController {
private static bgAudioController: BackgroundAudioController
private constructor() {
}
public static sharedController() {
if (!BackgroundAudioController.bgAudioController) {
BackgroundAudioController.bgAudioController = new BackgroundAudioController()
}
return BackgroundAudioController.bgAudioController
}
public gotContextFunc?: () => Context
public avplayerController?: WDPlayerController
private lastSession?: AVSessionManager.AVSession
// 开始创建并激活媒体会话
// 创建session
async createSession() {
if (!this.gotContextFunc) { return }
this.destorySession()
let type: AVSessionManager.AVSessionType = 'audio';
let session = await AVSessionManager.createAVSession(this.gotContextFunc(),'SESSION_NAME', type);
// 激活接口要在元数据、控制命令注册完成之后再执行
await session.activate();
Logger.debug(TAG, `session create done : sessionId : ${session.sessionId}`);
this.lastSession = session
}
destorySession() {
if (this.lastSession) {
this.lastSession.deactivate();
this.lastSession.destroy();
}
}
//设置播放元数据
setSessionMetaData(assetId: string, title: string, mediaImage: string, artist?: string) {
let metadata: AVSessionManager.AVMetadata = {
assetId: assetId,
title: title,
mediaImage: mediaImage,
artist: artist ?? "",
// duration: "",
};
this.lastSession?.setAVMetadata(metadata).then(() => {
Logger.debug(TAG, `SetAVMetadata successfully`);
}).catch((err: BusinessError) => {
Logger.error(TAG, `Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`);
});
}
//设置播放状态
setSessionPlayStatus(playStatus: number) {
let pauseed = playStatus == PlayerConstants.STATUS_PAUSE
let playbackState: AVSessionManager.AVPlaybackState = {
state:pauseed ? AVSessionManager.PlaybackState.PLAYBACK_STATE_PAUSE : AVSessionManager.PlaybackState.PLAYBACK_STATE_PLAY,
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);
}
});
}
//设置进度,单位秒
setSessionPlayProgress(progressDuration: number, totalDuration: number) {
Logger.debug(TAG, `set progress: ` + progressDuration + " duration: " + totalDuration);
if (totalDuration <= 0) {
return
}
// 设置状态: 播放状态,进度位置,播放倍速,缓存的时间
let playbackState: AVSessionManager.AVPlaybackState = {
state: AVSessionManager.PlaybackState.PLAYBACK_STATE_PLAY, // 播放状态
position: {
elapsedTime: progressDuration * 1000, // 已经播放的位置,以ms为单位
updateTime: new Date().getTime(), // 应用更新当前位置时的时间戳,以ms为单位
},
duration: totalDuration * 1000,
// speed: 1.0, // 可选,默认是1.0,播放的倍速,按照应用内支持的speed进行设置,系统不做校验
// bufferedTime: 14000, // 可选,资源缓存的时间,以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.lastSession?.on('pause', () => {
Logger.debug(TAG, `on pause `);
});
this.lastSession?.on('stop', () => {
Logger.debug(TAG, `on 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 ${JSON.stringify(position)}`);
// 由于应用内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实现
});
}
// 开启后台长时任务
startContinuousTask() {
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 }
backgroundTaskManager.startBackgroundRunning(this.gotContextFunc(),
backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK,
wantAgentObj).then(() => {
Logger.debug(TAG, `Succeeded in operationing startBackgroundRunning.`);
}).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}`);
});
}
}
\ No newline at end of file
... ...
import media from '@ohos.multimedia.media';
import prompt from '@ohos.promptAction';
import { Logger } from '../utils/Logger';
import { PlayerConstants, AVPlayerStatus, Events } from '../constants/PlayerConstants';
import { BusinessError } from '@ohos.base';
import { TrackingPlay } from 'wdTracking/Index';
import { ParamType } from 'wdTracking/Index';
import { DateTimeUtils } from 'wdKit/Index';
import { DateTimeUtils, Logger } from 'wdKit/Index';
import { BackgroundAudioController } from './BackgroundAudioController';
interface obj {
loop: boolean
}
const TAG = "WDPlayerController"
@Observed
export class WDPlayerController {
private initPromise: Promise<void>;
... ... @@ -41,7 +43,7 @@ export class WDPlayerController {
public xComponentController?: XComponentController
public videoWidth: number = 0
public videoHeight: number = 0
public keepOnBackground = false
... ... @@ -61,17 +63,17 @@ export class WDPlayerController {
Logger.error("开始创建")
media.createAVPlayer().then((avPlayer) => {
if (avPlayer) {
Logger.error("创建完成1")
Logger.error(TAG, "创建完成1")
this.avPlayer = avPlayer;
this.bindState();
resolve();
} else {
Logger.error("创建完成0")
Logger.error('[PlayVideoModel] createAvPlayer fail!');
Logger.error(TAG, "创建完成0")
Logger.error(TAG, '[PlayVideoModel] createAvPlayer fail!');
reject();
}
}).catch((error: BusinessError) => {
console.error(`AVPlayer catchCallback, error message:${error.message}`);
Logger.error(TAG, `AVPlayer catchCallback, error message:${error.message}`);
});
;
});
... ... @@ -143,10 +145,10 @@ export class WDPlayerController {
this.avPlayer.release();
this.status = PlayerConstants.STATUS_STOP;
this.watchStatus();
Logger.info('[PlayVideoModel] state released called')
Logger.info(TAG, '[PlayVideoModel] state released called')
break;
default:
Logger.info('[PlayVideoModel] unKnown state: ' + state);
Logger.info(TAG, '[PlayVideoModel] unKnown state: ' + state);
break;
}
});
... ... @@ -155,7 +157,7 @@ export class WDPlayerController {
});
this.avPlayer?.on(Events.ERROR, (error) => {
this.playError(error.message);
console.log('播放错误',JSON.stringify(error))
Logger.error(TAG, '播放错误' + JSON.stringify(error))
TrackingPlay.videoPlayError(error.message, this.pageName, this.pageName, this.pageParam)
})
this.avPlayer?.on('seekDone', (time: number) => {
... ... @@ -186,7 +188,7 @@ export class WDPlayerController {
this.url = url;
//加载时长prepareTime
this.creatStartTime = DateTimeUtils.getTimeStamp()
console.log('开始创建',JSON.stringify(this.creatStartTime))
Logger.debug(TAG, '开始创建' + JSON.stringify(this.creatStartTime))
this.prepareTime = 0
if(pageName){
this.pageName = pageName
... ... @@ -195,7 +197,7 @@ export class WDPlayerController {
this.pageParam = pageParam
}
if (this.avPlayer == null) {
console.log("等待")
Logger.debug(TAG, "等待")
this.initPromise = this.createAVPlayer();
await this.initPromise;
} else {
... ... @@ -209,13 +211,13 @@ export class WDPlayerController {
if (this.avPlayer == null) {
return
}
console.log("开始播放", this.url)
Logger.debug(TAG, "开始播放", this.url)
this.avPlayer.url = this.url;
//加载时长prepareTime
this.creatEndTime = DateTimeUtils.getTimeStamp()
this.prepareTime = 0
this.prepareTime = Math.floor((this.creatEndTime - this.creatStartTime)/1000)
console.log('开始播放2',JSON.stringify(this.prepareTime))
Logger.debug(TAG, '开始播放2',JSON.stringify(this.prepareTime))
}
async release() {
... ... @@ -237,6 +239,7 @@ export class WDPlayerController {
// if (this.avPlayer == null) {
// return
// }
Logger.debug(TAG, "start pause")
this.avPlayer?.pause();
}
... ... @@ -253,7 +256,7 @@ export class WDPlayerController {
async startRenderFrame(cb: Function) {
this.avPlayer?.on('startRenderFrame', () => {
cb && cb();
console.info('startRenderFrame success')
Logger.debug(TAG, 'startRenderFrame success')
})
}
... ... @@ -299,6 +302,7 @@ export class WDPlayerController {
// return
// }
if (this.status === PlayerConstants.STATUS_START) {
Logger.debug(TAG, "start pause 1111")
this.avPlayer?.pause();
} else {
this.avPlayer?.play();
... ... @@ -349,6 +353,11 @@ export class WDPlayerController {
this.currentPlayTime=Math.floor(time / 1000);
let nowSeconds = Math.floor(time / 1000);
let totalSeconds = Math.floor(this.duration / 1000);
if (this.keepOnBackground) {
BackgroundAudioController.sharedController().setSessionPlayProgress(time, this.duration)
}
if (this.onTimeUpdate) {
this.onTimeUpdate(time, this.duration);
}
... ... @@ -397,7 +406,7 @@ export class WDPlayerController {
if (this.onVolumeUpdate) {
this.onVolumeUpdate(this.volume);
}
console.log("volume : " + this.volume)
Logger.debug(TAG, "volume : " + this.volume)
}
onBrightActionUpdate(event: GestureEvent) {
... ... @@ -421,22 +430,27 @@ export class WDPlayerController {
}
watchStatus() {
console.log('watchStatus', this.status)
if (this.keepOnBackground) {
BackgroundAudioController.sharedController().setSessionPlayStatus(this.status)
}
Logger.debug(TAG, 'watchStatus ' + this.status)
if(this.status == PlayerConstants.STATUS_START){
console.log('播放视频')
console.log('播放视频prepareTime',JSON.stringify(this.prepareTime))
console.log('播放视频pageName',JSON.stringify(this.pageName))
console.log('播放视频pageParam',JSON.stringify(this.pageParam))
Logger.debug(TAG, '播放视频')
Logger.debug(TAG, '播放视频prepareTime' + JSON.stringify(this.prepareTime))
Logger.debug(TAG, '播放视频pageName' + JSON.stringify(this.pageName))
Logger.debug(TAG, '播放视频pageParam' + JSON.stringify(this.pageParam))
// 播放埋点
TrackingPlay.videoPositivePlay(Number(this.prepareTime),this.pageName, this.pageName, this.pageParam)
}
if(this.status == PlayerConstants.STATUS_COMPLETION){
let initDuration = Math.floor(Number(this.duration)/1000)
console.log('播放结束')
console.log('播放结束currentPlayTime',JSON.stringify(this.currentPlayTime))
console.log('播放结束initDuration',JSON.stringify(initDuration))
console.log('播放结束pageName',JSON.stringify(this.pageName))
console.log('播放结束pageParam',JSON.stringify(this.pageParam))
Logger.debug(TAG, '播放结束')
Logger.debug(TAG, '播放结束currentPlayTime' + JSON.stringify(this.currentPlayTime))
Logger.debug(TAG, '播放结束initDuration' + JSON.stringify(initDuration))
Logger.debug(TAG, '播放结束pageName' + JSON.stringify(this.pageName))
Logger.debug(TAG, '播放结束pageParam' + JSON.stringify(this.pageParam))
// 播放结束埋点
TrackingPlay.videoPlayEnd(this.currentPlayTime, initDuration, this.currentPlayTime, this.pageName, this.pageName, this.pageParam)
}
... ...
... ... @@ -88,7 +88,8 @@ struct Index {
onPageHide() {
// this.status = PlayerConstants.STATUS_PAUSE;
this.AudioSuspension.playerController.get()?.pause();
console.info('onPageHide');
// this.AudioSuspension.playerController.get()?.pause();
}
build() {
... ...
... ... @@ -22,6 +22,7 @@ import { webview } from '@kit.ArkWeb'
import { NewspaperWidgetCommon } from '../dailynewspaperwidget/common/NewspaperWidgetCommon'
import { LiveRoomManager } from 'wdDetailPlayLive/Index'
import { initGlobalPlayerSettings } from 'wdPlayer/src/main/ets/utils/GlobalSetting'
import { BackgroundAudioController } from 'wdPlayer/Index'
const TAG = "[StartupManager]"
... ... @@ -115,6 +116,8 @@ export class StartupManager {
this.initAuthLogin()
this.initLiveChatRoom()
this.initBackgroundAudioTask()
Logger.debug(TAG, "App 必要初始化完成")
}
... ... @@ -210,6 +213,12 @@ export class StartupManager {
}
}
private initBackgroundAudioTask() {
BackgroundAudioController.sharedController().gotContextFunc = () => {
return StartupManager.sharedInstance().context!
}
}
private initThirdPlatformSDK() {
}
... ...
... ... @@ -42,7 +42,8 @@
}
]
}
]
],
"backgroundModes": ["audioPlayback"]
}
],
"metadata": [
... ... @@ -103,6 +104,16 @@
}
},
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
"reason": "$string:permission_background_audio",
"usedScene": {
"abilities": [
"FormAbility"
],
"when": "always"
}
},
{
"name": "ohos.permission.INTERNET"
},
],
... ...
... ... @@ -29,6 +29,10 @@
"value": "开启之后即可用于收录声音进行拍摄视频、语音搜索、语音评论功能,"
},
{
"name": "permission_background_audio",
"value": "开启之后即可用于后台音频播放"
},
{
"name": "dialog_text_title",
"value": "个人隐私保护指引"
},
... ...