HarmonyOS 鸿蒙Next中音频播放锁屏状态栏播控设置问题
HarmonyOS 鸿蒙Next中音频播放锁屏状态栏播控设置问题 看这个文档,好像没有关于音频播放的时候设置锁屏状态播控的问题,帮忙给一下相应的文档。
再给个Demo:
import media from '[@ohos](/user/ohos).multimedia.media';
import { BusinessError } from '[@ohos](/user/ohos).base';
import pipWindow from '[@ohos](/user/ohos).PiPWindow';
import { display, window } from '[@kit](/user/kit).ArkUI';
import { GlobalContext } from '../util/GlobalContext';
import { avSession } from '[@kit](/user/kit).AVSessionKit';
import { audio } from '[@kit](/user/kit).AudioKit';
import { image } from '[@kit](/user/kit).ImageKit';
import { abilityAccessCtrl, PermissionRequestResult, Permissions, WantAgent, wantAgent } from '[@kit](/user/kit).AbilityKit';
import { backgroundTaskManager } from '[@kit](/user/kit).BackgroundTasksKit';
[@Entry](/user/Entry)
[@Component](/user/Component)
struct AVPlayerDemo {
[@Watch](/user/Watch)('setWindowLayOut') [@State](/user/State) isFullScreen: boolean = false;
[@State](/user/State) isLandscape: boolean = false;
[@State](/user/State) isVideo: boolean = true;
[@State](/user/State) isOpacity: boolean = false;
[@State](/user/State) isPlay: boolean = false;
[@State](/user/State) currentTime: number = 0;
[@State](/user/State) durationTime: number = 0;
[@State](/user/State) durationStringTime: string = '00:00';
[@State](/user/State) currentStringTime: string = '00:00';
[@State](/user/State) flag: boolean = false;
[@State](/user/State) videoFiles: media.AVFileDescriptor[] = [];
[@State](/user/State) audioFiles: media.AVFileDescriptor[] = [];
[@State](/user/State) sourceFiles: media.AVFileDescriptor[] = [];
[@State](/user/State) currentIndex: number = 0;
[@State](/user/State) speed: number = media.PlaybackSpeed.SPEED_FORWARD_1_00_X;
private avPlayer: media.AVPlayer | undefined = undefined;
private xComponentController = new XComponentController();
private surfaceID: string = '';
private OPERATE_STATE: Array<string> = ['prepared', 'playing', 'paused', 'completed'];
private pipController: pipWindow.PiPController | undefined = undefined;
private windowClass: window.Window = GlobalContext.getContext().getObject('windowClass') as window.Window;
private currentAVSession: avSession.AVSession | undefined = undefined;
private curSessionId: string = '';
private avsessionController: avSession.AVSessionController | undefined = undefined;
private curPixelMap: image.PixelMap | undefined = undefined;
private playbackState: avSession.AVPlaybackState = {
state: avSession.PlaybackState.PLAYBACK_STATE_PLAY,
position: {
elapsedTime: 0,
updateTime: 0,
},
speed: this.speed,
duration: 0,
loopMode: avSession.LoopMode.LOOP_MODE_SEQUENCE,
};
setWindowLayOut() {
this.windowClass.setWindowLayoutFullScreen(this.isFullScreen);
}
aboutToAppear(): void {
this.initFiles();
this.createAVPlayer();
this.createPipWindow();
this.createAVSession();
this.saveRawFileToPixelMap('first.png')
this.reset(true)
try {
this.windowClass.on('windowSizeChange', (data) => {
console.info('AVPlayerDemo Succeeded in enabling the listener for window size changes. Data: ' + JSON.stringify(data));
let orientation = display.getDefaultDisplaySync().orientation;
if (orientation == display.Orientation.LANDSCAPE || orientation == display.Orientation.LANDSCAPE_INVERTED) {
this.isLandscape = true;
}
if (orientation == display.Orientation.PORTRAIT || orientation == display.Orientation.PORTRAIT_INVERTED) {
this.isLandscape = false;
}
});
} catch (exception) {
console.error('AVPlayerDemo Failed to enable the listener for window size changes. Cause: ' + JSON.stringify(exception));
}
}
aboutToDisappear(): void {
if (this.avPlayer) {
this.avPlayer.off('timeUpdate');
this.avPlayer.off('seekDone');
this.avPlayer.off('error');
this.avPlayer.off('stateChange');
this.avPlayer.release();
}
if (this.currentAVSession) {
this.currentAVSession.off('play');
this.currentAVSession.off('pause');
this.currentAVSession.off('stop');
this.currentAVSession.off('playNext');
this.currentAVSession.off('playPrevious');
this.currentAVSession.off('fastForward');
this.currentAVSession.off('rewind');
this.currentAVSession.off('playFromAssetId');
this.currentAVSession.off('seek');
this.currentAVSession.off('setSpeed');
this.currentAVSession.deactivate();
this.currentAVSession.destroy();
}
try {
this.windowClass.off('windowSizeChange');
} catch (exception) {
console.error('AVPlayerDemo Failed to disable the listener for window size changes. Cause: ' + JSON.stringify(exception));
}
this.stopContinuousTask();
}
build() {
Column() {
if (!this.isFullScreen) {
Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceAround }) {
Column() {
Text('视频播放')
.fontColor(this.isVideo ? Color.Blue : Color.Black)
.fontSize(16)
.fontWeight(this.isVideo ? 500 : 400)
.lineHeight(22)
.margin({ top: 17, bottom: 7 })
Divider()
.strokeWidth(2)
.color('#007DFF')
.opacity(this.isVideo ? 1 : 0)
}
.onClick(() => {
this.isVideo = true
this.reset(true);
})
Column() {
Text('音频播放')
.fontColor(!this.isVideo ? Color.Blue : Color.Black)
.fontSize(16)
.fontWeight(!this.isVideo ? 500 : 400)
.lineHeight(22)
.margin({ top: 17, bottom: 7 })
Divider()
.strokeWidth(2)
.color('#007DFF')
.opacity(!this.isVideo ? 1 : 0)
}
.onClick(() => {
this.isVideo = false
this.reset(true);
})
}
.margin({ bottom: '8vp' })
}
Flex({
direction: FlexDirection.Column,
justifyContent: this.isFullScreen ? FlexAlign.Center : FlexAlign.Start
}) {
if (this.isVideo) {
this.VideoPlayer()
} else {
this.AudioPlayer()
}
if (!this.isFullScreen) {
this.Buttons();
}
}
.width('100%')
.height('100%')
.backgroundColor(this.isFullScreen ? Color.Black : Color.White)
}
.width('100%')
.height('100%')
}
[@Builder](/user/Builder)
Buttons() {
Column() {
Scroll() {
GridRow({
columns: 2,
gutter: { x: 5, y: 10 },
direction: GridRowDirection.Row
}) {
GridCol({ span: 1, offset: 0, order: 0 }) {
Button('播放').width(140).onClick(() => {
this.play();
});
}
GridCol({ span: 1, offset: 0, order: 0 }) {
Button('暂停').width(140).onClick(() => {
this.isPlay = false;
if (this.avPlayer) {
this.avPlayer.pause();
}
});
}
GridCol({ span: 1, offset: 0, order: 0 }) {
Button('停止').width(140).onClick(() => {
this.stopPlay();
});
}
GridCol({ span: 1, offset: 0, order: 0 }) {
Button('跳转5秒位置').width(140).onClick(() => {
this.setSeek(5, 0);
});
}
GridCol({ span: 1, offset: 0, order: 0 }) {
Button('进入全屏').width(140).onClick(() => {
this.isFullScreen = true;
});
}
GridCol({ span: 1, offset: 0, order: 0 }) {
Button('0.75倍速').width(140).onClick(() => {
this.setSpeed(media.PlaybackSpeed.SPEED_FORWARD_0_75_X);
});
}
GridCol({ span: 1, offset: 0, order: 0 }) {
Button('1倍速').width(140).onClick(() => {
this.setSpeed(media.PlaybackSpeed.SPEED_FORWARD_1_00_X);
});
}
GridCol({ span: 1, offset: 0, order: 0 }) {
Button('1.25倍速').width(140).onClick(() => {
this.setSpeed(media.PlaybackSpeed.SPEED_FORWARD_1_25_X);
});
}
GridCol({ span: 1, offset: 0, order: 0 }) {
Button('1.75倍速').width(140).onClick(() => {
this.setSpeed(media.PlaybackSpeed.SPEED_FORWARD_1_75_X);
});
}
GridCol({ span: 1, offset: 0, order: 0 }) {
Button('2倍速').width(140).onClick(() => {
this.setSpeed(media.PlaybackSpeed.SPEED_FORWARD_2_00_X);
});
}
GridCol({ span: 1, offset: 0, order: 0 }) {
Button('快进5秒').width(140).onClick(() => {
this.setSeek(5);
});
}
GridCol({ span: 1, offset: 0, order: 0 }) {
Button('快退5秒').width(140).onClick(() => {
this.setSeek(-5);
});
}
GridCol({ span: 1, offset: 0, order: 0 }) {
Button('上一个').width(140).onClick(() => {
this.goToNextOrPre(-1);
});
}
GridCol({ span: 1, offset: 0, order: 0 }) {
Button('下一个').width(140).onClick(() => {
this.goToNextOrPre(1);
});
}
if (this.isVideo) {
GridCol({ span: 1, offset: 0, order: 0 }) {
Button('开启画中画').width(140).onClick(() => {
this.startPipWindow();
});
}
GridCol({ span: 1, offset: 0, order: 0 }) {
Button('关闭画中画').width(140).onClick(() => {
this.stopPipWindow();
});
}
}
}
.margin({ bottom: 20, top: 20 })
.borderRadius(20)
}
.scrollBar(BarState.Off)
}
}
[@Builder](/user/Builder)
VideoPlayer() {
Stack({
alignContent: this.isFullScreen ? (this.isLandscape ? Alignment.Bottom : Alignment.Center) : Alignment.Bottom
}) {
Stack() {
if (!this.isPlay) {
Image($r('app.media.ic_public_play'))
.width(50)
.height(50)
.zIndex(2)
.onClick(() => {
this.play();
});
}
Column() {
XComponent({
id: '',
type: XComponentType.SURFACE,
libraryname: '',
controller: this.xComponentController
})
.onLoad(() => {
this.xComponentController.setXComponentSurfaceSize({
surfaceWidth: 1920,
surfaceHeight: 1080
});
this.surfaceID = this.xComponentController.getXComponentSurfaceId();
})
.width('100%')
.height('100%');
}
.zIndex(1)
.onClick(() => {
this.playOrPause();
})
}
.width('100%')
.height(this.isFullScreen ? (this.isLandscape ? '100%' : 260) : '100%')
this.PlayControl()
}
.height(this.isFullScreen ? '100%' : 260)
.backgroundColor(Color.Black)
.width('100%')
}
[@Builder](/user/Builder)
AudioPlayer() {
Stack({ alignContent: this.isFullScreen ? Alignment.Center : Alignment.Bottom }) {
Image($r('app.media.ic_camera_story_playing')).objectFit(ImageFit.Contain)
this.PlayControl()
}
.height(260)
.backgroundImage($r('app.media.background'), ImageRepeat.NoRepeat)
.width('100%')
}
[@Builder](/user/Builder)
PlayControl() {
Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
Image($r('app.media.ic_previous'))
.width('20vp')
.height('20vp')
.onClick(() => {
this.goToNextOrPre(-1)
});
Image(this.isPlay ? $r('app.media.ic_pause') : $r('app.media.ic_play'))
.width('20vp')
.height('20vp')
.onClick(() => {
this.iconOnclick();
});
Image($r('app.media.ic_next'))
.width('20vp')
.height('20vp')
.onClick(() => {
this.goToNextOrPre(1)
});
Text(this.currentStringTime)
.fontSize('14vp')
.fontColor(Color.White)
.margin({ left: '2vp' })
Slider({
value: this.currentTime,
step: 1,
min: 0,
max: this.durationTime,
style: SliderStyle.OutSet
})
.blockColor(Color.White)
.width('50%')
.trackColor(Color.Gray)
.selectedColor(Color.White)
.showSteps(false)
.showTips(false)
.trackThickness(this.isOpacity ? 2 : 4)
.onChange((value: number, mode: SliderChangeMode) => {
this.sliderOnchange(value, mode);
});
Text(this.durationStringTime)
.fontSize('14vp')
.fontColor(Color.White)
.margin({ left: '2vp', right: '2vp' })
Image($r('app.media.ic_public_reduce'))
.width('20vp')
.height('20vp')
.onClick(() => {
this.isFullScreen = !this.isFullScreen
});
}
.zIndex(2)
.padding({ right: '2vp' })
.opacity(this.isOpacity ? 0.7 : 1)
.width('100%')
.offset({ x: 0, y: this.isFullScreen ? (this.isLandscape ? 0 : 110) : 0 })
.backgroundBlurStyle(BlurStyle.Thin, { colorMode: ThemeColorMode.DARK })
}
createPipWindow() {
let config: pipWindow.PiPConfiguration = {
context: getContext(this),
componentController: this.xComponentController,
templateType: pipWindow.PiPTemplateType.VIDEO_PLAY,
contentWidth: 800,
contentHeight: 600,
};
pipWindow.create(config).then((data: pipWindow.PiPController) => {
console.info(`AVPlayerDemo Succeeded in creating pip controller. Data:${data}`);
this.pipController = data;
this.setPipWindowCallback(data)
}).catch((err: BusinessError) => {
console.error(`AVPlayerDemo Failed to create pip controller. Cause:${err.code}, message:${err.message}`);
});
}
// 注册PipWindow回调函数
setPipWindowCallback(pipController: pipWindow.PiPController) {
// 注册PipWindow生命周期状态监听
pipController.on('stateChange', (state: pipWindow.PiPState, reason: string) => {
let curState: string = '';
switch (state) {
case pipWindow.PiPState.ABOUT_TO_START:
curState = 'ABOUT_TO_START';
break;
case pipWindow.PiPState.STARTED:
curState = 'STARTED';
break;
case pipWindow.PiPState.ABOUT_TO_STOP:
curState = 'ABOUT_TO_STOP';
break;
case pipWindow.PiPState.STOPPED:
pipController.setAutoStartEnabled(false)
curState = 'STOPPED';
break;
case pipWindow.PiPState.ABOUT_TO_RESTORE:
curState = 'ABOUT_TO_RESTORE';
break;
case pipWindow.PiPState.ERROR:
curState = 'ERROR';
break;
default:
break;
}
console.info('AVPlayerDemo pipController stateChange:' + curState + ' reason:' + reason);
});
// 注册PipWindow控制事件监听
pipController.on('controlPanelActionEvent', (event: pipWindow.PiPActionEventType) => {
switch (event) {
case 'playbackStateChanged':
// 开始或停止视频
this.playOrPause();
break;
case 'nextVideo':
// 切换到下一个视频
this.goToNextOrPre(1);
break;
case 'previousVideo':
// 切换到上一个视频
this.goToNextOrPre(-1);
break;
default:
break;
}
console.info('AVPlayerDemo pipController registerActionEventCallback, event:' + event);
});
}
startPipWindow() {
if (this.pipController) {
// 设置是否需要在返回桌面时自动启动画中画
this.pipController.setAutoStartEnabled(true)
this.pipController.startPiP().then(() => {
console.info(`AVPlayerDemo Succeeded in starting pip.`);
}).catch((err: BusinessError) => {
console.error(`AVPlayerDemo Failed to start pip. Cause:${err.code}, message:${err.message}`);
});
}
}
stopPipWindow() {
if (this.pipController) {
this.pipController.stopPiP().then(() => {
console.info(`AVPlayerDemo Succeeded in stop pip.`);
}).catch((err: BusinessError) => {
console.error(`AVPlayerDemo Failed to stop pip. Cause:${err.code}, message:${err.message}`);
});
}
}
// 注册avplayer回调函数
setAVPlayerCallback(avPlayer: media.AVPlayer) {
avPlayer.on('timeUpdate', (time: number) => {
console.info(`AVPlayerDemo AVPlayer timeUpdate. time is ${time}`);
this.currentTime = Math.floor(time * this.durationTime / avPlayer.duration);
console.info(`AVPlayerDemo this.currentTime. time is ${this.currentTime}`);
this.currentStringTime = this.secondToTime(Math.floor(time / 1000));
if (this.currentAVSession) {
this.playbackState.position = {
elapsedTime: time,
updateTime: (new Date()).getTime(),
};
this.currentAVSession.setAVPlaybackState(this.playbackState);
}
})
// seek操作结果回调函数
avPlayer.on('seekDone', (seekDoneTime: number) => {
console.info(`AVPlayerDemo AVPlayer seekDone succeeded, seek time is ${seekDoneTime}`);
if (this.currentAVSession) {
this.playbackState.position = {
elapsedTime: avPlayer.currentTime,
updateTime: (new Date()).getTime(),
};
this.currentAVSession.setAVPlaybackState(this.playbackState);
}
})
// 监听setSpeed生效的事件
avPlayer.on('speedDone', (speed: number) => {
console.info(`AVPlayerDemo AVPlayer speedDone succeeded, speed is ${speed}`);
if (this.currentAVSession) {
this.playbackState.speed = speed;
this.currentAVSession.setAVPlaybackState(this.playbackState);
}
})
// error回调监听函数,当avPlayer在操作过程中出现错误时调用 reset接口触发重置流程
avPlayer.on('error', (err: BusinessError) => {
console.error(`AVPlayerDemo Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);
avPlayer.reset(); // 调用reset重置资源,触发idle状态
})
// 状态机变化回调函数
avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
switch (state) {
case 'idle': // 成功调用reset接口后触发该状态机上报
console.info('AVPlayerDemo AVPlayer state idle called.');
if (avPlayer && this.sourceFiles.length > this.currentIndex) {
// 网络视频使用avPlayer.url赋值
avPlayer.fdSrc = this.sourceFiles[this.currentIndex];
}
break;
case 'initialized': // avplayer 设置播放源后触发该状态上报
console.info('AVPlayerDemo AVPlayer state initialized called.');
this.reset()
if (this.isVideo) {
avPlayer.surfaceId = this.surfaceID;
}
avPlayer.prepare();
break;
case 'prepared': // prepare调用成功后上报该状态机
console.info('AVPlayerDemo AVPlayer state prepared called.');
this.flag = true;
this.durationTime = Math.floor(avPlayer.duration / 1000);
this.durationStringTime = this.secondToTime(this.durationTime);
await this.startAVSession();
break;
case 'completed': // prepare调用成功后上报该状态机
console.info('AVPlayerDemo AVPlayer state completed called.');
this.isPlay = false;
if (this.currentAVSession) {
// 设置状态: 播放状态,进度位置,播放倍速,缓存的时间,资源的时长
this.playbackState.state = avSession.PlaybackState.PLAYBACK_STATE_COMPLETED;
this.currentAVSession.setAVPlaybackState(this.playbackState);
}
break;
case 'playing': // play成功调用后触发该状态机上报
console.info('AVPlayerDemo AVPlayer state playing called.');
if (this.currentAVSession) {
// 设置状态: 播放状态,进度位置,播放倍速,缓存的时间,资源的时长
this.playbackState.state = avSession.PlaybackState.PLAYBACK_STATE_PLAY;
this.currentAVSession.setAVPlaybackState(this.playbackState);
}
break;
case 'paused': // pause成功调用后触发该状态机上报
console.info('AVPlayerDemo AVPlayer state paused called.');
if (this.currentAVSession) {
// 设置状态: 播放状态,进度位置,播放倍速,缓存的时间,资源的时长
this.playbackState.state = avSession.PlaybackState.PLAYBACK_STATE_PAUSE;
this.playbackState.position = {
elapsedTime: avPlayer.currentTime,
updateTime: (new Date()).getTime(),
};
this.currentAVSession.setAVPlaybackState(this.playbackState);
}
break;
case 'stopped': // stop接口成功调用后触发该状态机上报
console.info('AVPlayerDemo AVPlayer state stopped called.');
if (this.currentAVSession) {
// 设置状态: 播放状态,进度位置,播放倍速,缓存的时间,资源的时长
this.playbackState.state = avSession.PlaybackState.PLAYBACK_STATE_STOP;
this.currentAVSession.setAVPlaybackState(this.playbackState);
}
break;
case 'released':
console.info('AVPlayerDemo AVPlayer state released called.');
break;
default:
console.info('AVPlayerDemo AVPlayer state unknown called.');
break;
}
})
}
startContinuousTask() {
let wantAgentInfo: wantAgent.WantAgentInfo = {
wants: [
{
bundleName: "com.huawei.flexlaout.myapplication",
abilityName: "EntryAbility"
}
],
actionType: wantAgent.OperationType.START_ABILITY,
requestCode: 0,
actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
};
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {
backgroundTaskManager.startBackgroundRunning(
getContext(this),
backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK, wantAgentObj).then(() => {
console.info(AVPlayerDemo Succeeded in operationing startBackgroundRunning.);
}).catch((err: BusinessError) => {
console.error(AVPlayerDemo Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message});
});
});
}
stopContinuousTask() {
backgroundTaskManager.stopBackgroundRunning(getContext(this)).then(() => {
console.info(AVPlayerDemo Succeeded in operationing stopBackgroundRunning.);
}).catch((err: BusinessError) => {
console.error(AVPlayerDemo Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message});
});
}
reset(sourceFlag?: boolean) {
this.isPlay = false;
this.currentTime = 0;
this.durationTime = 0;
this.durationStringTime = '00:00';
this.currentStringTime = '00:00';
this.flag = false;
if (sourceFlag) {
this.currentIndex = 0;
this.isFullScreen = false;
if (this.isVideo) {
this.sourceFiles = this.videoFiles;
} else {
this.sourceFiles = this.audioFiles;
}
if (this.avPlayer) {
this.avPlayer.reset()
}
}
if (this.pipController) {
this.pipController.stopPiP();
}
}
setSpeed(playSpeed: number) {
if (!this.avPlayer || this.OPERATE_STATE.indexOf(this.avPlayer.state) === -1) {
console.error('AVPlayerDemo setSpeed failed. no avPlayer or state is not prepared/playing/paused/completed')
return;
}
this.avPlayer.setSpeed(playSpeed);
}
async play() {
if (!this.avPlayer || this.OPERATE_STATE.indexOf(this.avPlayer.state) === -1 || this.OPERATE_STATE.indexOf(this.avPlayer.state) === 1) {
console.error('AVPlayerDemo play failed. no avPlayer or state is not prepared/paused/completed')
return;
}
this.isPlay = true;
if (this.avPlayer.state === 'completed') {
this.currentTime = 0
this.currentStringTime = '00:00'
this.avPlayer.seek(1, media.SeekMode.SEEK_PREV_SYNC);
}
this.avPlayer.play();
}
async startAVSession() {
if (!this.currentAVSession) {
console.error('AVPlayerDemo currentAVSession is undefined.')
return;
}
let metadata: avSession.AVMetadata = this.generateAVMetadata();
await this.currentAVSession.setAVMetadata(metadata).then(() => {
console.info(AVPlayerDemo SetAVMetadata successfully);
}).catch((err: BusinessError) => {
console.error(AVPlayerDemo SetAVMetadata BusinessError: code: ${err.code}, message: ${err.message});
});
this.playbackState.state = avSession.PlaybackState.PLAYBACK_STATE_PREPARE;
this.playbackState.duration = this.avPlayer?.duration;
this.currentAVSession.setAVPlaybackState(this.playbackState);
// 通过按钮申请长时任务
this.startContinuousTask();
this.currentAVSession.getController().then((avcontroller: avSession.AVSessionController) => {
this.avsessionController = avcontroller;
console.info(AVPlayerDemo GetController : SUCCESS : sessionid : ${avcontroller.sessionId});
}).catch((err: BusinessError) => {
console.error(AVPlayerDemo GetController BusinessError: code: ${err.code}, message: ${err.message});
});
await this.currentAVSession.activate().then(() => {
console.info(AVPlayerDemo Activate : SUCCESS );
}).catch((err: BusinessError) => {
console.error(AVPlayerDemo Activate BusinessError: code: ${err.code}, message: ${err.message});
});
}
private generateAVMetadata() {
let previousIndex = this.addStepIndex(-1);
let nextIndex = this.addStepIndex(1);
let metadata: avSession.AVMetadata = {
assetId: ${this.sourceFiles[this.currentIndex].fd},
title: ${this.sourceFiles[this.currentIndex].fd},
artist: 艺术家${this.currentIndex},
author: 专辑作者${this.currentIndex},
album: 专辑名称${this.currentIndex},
writer: 词作者${this.currentIndex},
composer: 作曲者${this.currentIndex},
duration: this.avPlayer?.duration,
mediaImage: this.curPixelMap,
publishDate: new Date(),
subtitle: 子标题${this.currentIndex},
description: 媒体描述${this.currentIndex},
previousAssetId: ${this.sourceFiles[previousIndex].fd},
nextAssetId: ${this.sourceFiles[nextIndex].fd},
};
return metadata;
}
async createAVSession() {
let type: avSession.AVSessionType = this.isVideo ? 'video' : 'audio';
await avSession.createAVSession(getContext(this), 'AVPlayerDemo', type).then((data: avSession.AVSession) => {
console.info(AVPlayerDemo CreateAVSession : SUCCESS : sessionId = ${data.sessionId});
this.currentAVSession = data;
this.curSessionId = data.sessionId;
this.setAVSessionCallback(data);
}).catch((err: BusinessError) => {
console.error(AVPlayerDemo CreateAVSession BusinessError: code: ${err.code}, message: ${err.message});
});
}
setAVSessionCallback(curAvSession: avSession.AVSession) {
// 播放
curAvSession.on('play', () => {
console.info(AVPlayerDemo AVSession on play entry);
this.play();
});
// 暂停
curAvSession.on('pause', () => {
console.info(AVPlayerDemo AVSession on pause entry);
this.isPlay = false;
if (this.avPlayer) {
this.avPlayer.pause();
}
});
// 停止
curAvSession.on('stop', () => {
console.info(AVPlayerDemo AVSession on stop entry);
this.stopPlay();
});
// 下一个
curAvSession.on('playNext', () => {
console.info(AVPlayerDemo AVSession on playNext entry);
this.goToNextOrPre(1);
});
// 上一个
curAvSession.on('playPrevious', () => {
console.info(AVPlayerDemo AVSession on playPrevious entry);
this.goToNextOrPre(-1);
});
// 快进
curAvSession.on('fastForward', () => {
console.info(AVPlayerDemo AVSession on fastForward entry);
this.setSeek(15);
});
// 快退
curAvSession.on('rewind', () => {
console.info(AVPlayerDemo AVSession on rewind entry);
this.setSeek(-15);
});
// 媒体id播放监听事件
curAvSession.on('playFromAssetId', (assetId: number) => {
console.info(AVPlayerDemo AVSession on playFromAssetId entry assetId : ${assetId});
});
// 跳转节点监听事件
curAvSession.on('seek', (time: number) => {
console.info(AVPlayerDemo AVSession on seek entry time : ${time});
this.setSeek(time);
});
// 播放速率的监听事件
curAvSession.on('setSpeed', (speed: number) => {
console.info(AVPlayerDemo AVSession on setSpeed entry speed : ${speed});
this.setSpeed(speed);
});
}
async saveRawFileToPixelMap(rawFilePath: string) {
let value: Uint8Array = await getContext(this).resourceManager.getRawFileContent(rawFilePath);
let imageBuffer: ArrayBuffer = value.buffer as ArrayBuffer;
let imageSource: image.ImageSource = image.createImageSource(imageBuffer);
await imageSource.createPixelMap({ desiredSize: { width: 900, height: 900 } }).then((pixelMap: image.PixelMap) => {
console.info('AVPlayerDemo Succeeded in creating pixelMap object through image decoding parameters.');
this.curPixelMap = pixelMap;
}).catch((error: BusinessError) => {
console.error('AVPlayerDemo Failed to create pixelMap object through image decoding parameters.');
})
}
private addStepIndex(num: number): number {
let result = this.currentIndex + num;
if (result < 0) {
result = this.sourceFiles.length - 1;
} else if (result > this.sourceFiles.length - 1) {
result = 0;
}
return result;
}
iconOnclick() {
if (this.isPlay === true) {
this.avPlayer && this.avPlayer.pause();
this.isPlay = false;
this.isOpacity = false;
return;
}
if (this.flag === true) {
this.avPlayer && this.avPlayer.play();
this.isPlay = true;
this.isOpacity = true;
} else {
let intervalFlag = setInterval(() => {
if (this.flag === true) {
this.avPlayer && this.avPlayer.play();
this.isPlay = true;
this.isOpacity = true;
clearInterval(intervalFlag);
}
}, 100);
}
}
sliderOnchange(value: number, mode: SliderChangeMode) {
console.info(AVPlayerDemo sliderOnchange. value is ${value});
if (!this.avPlayer) {
return;
}
this.currentTime = value
if (mode === SliderChangeMode.Begin || mode === SliderChangeMode.Moving) {
this.isOpacity = false;
}
if (mode === SliderChangeMode.End && this.avPlayer) {
let seekTime: number = value * this.avPlayer.duration / this.durationTime;
this.currentStringTime = this.secondToTime(Math.floor(seekTime / 1000));
console.info(AVPlayerDemo sliderOnchange. time is ${seekTime}, currentTime is ${this.currentTime});
this.avPlayer.seek(seekTime, media.SeekMode.SEEK_PREV_SYNC);
this.isOpacity = true;
}
}
/**
*
* Seconds converted to HH:mm:ss.
* [@param](/user/param) seconds Maximum video duration (seconds).
* [@return](/user/return) Time after conversion.
*/
secondToTime(seconds: number): string {
let hourUnit = 60 * 60;
let hour: number = Math.floor(seconds / hourUnit);
let minute: number = Math.floor((seconds - hour * hourUnit) / 60);
let second: number = seconds - hour * hourUnit - minute * 60;
let hourStr: string = hour < 10 ? `0${hour.toString()}` : `${hour.toString()}`
let minuteStr: string = minute < 10 ? `0${minute.toString()}` : `${minute.toString()}`
let secondStr: string = second < 10 ? `0${second.toString()}` : `${second.toString()}`
if (hour > 0) {
return `${hourStr}:${minuteStr}:${secondStr}`;
}
if (minute > 0) {
return `${minuteStr}:${secondStr}`;
} else {
return `00:${secondStr}`;
}
}
playOrPause() {
if (this.avPlayer) {
if (this.isPlay) {
this.isPlay = false;
this.avPlayer.pause();
} else {
this.play();
}
}
}
createAVPlayer() {
media.createAVPlayer().then(video => {
if (video != null) {
this.avPlayer = video;
this.setAVPlayerCallback(this.avPlayer);
if (this.avPlayer && this.sourceFiles.length > this.currentIndex) {
this.avPlayer.fdSrc = this.sourceFiles[this.currentIndex];
}
console.info('AVPlayerDemo createAVPlayer success');
} else {
console.error('AVPlayerDemo createAVPlayer fail');
}
}).catch(error => {
console.error(AVPlayerDemo AVPlayer catchCallback, error message:${error.message});
});
}
initFiles() {
let fileList: string[] = getContext(this).resourceManager.getRawFileListSync('video');
fileList.forEach(fileStr => {
let fileDescriptor = getContext().resourceManager.getRawFdSync(video/${fileStr});
let avFileDescriptor: media.AVFileDescriptor = {
fd: fileDescriptor.fd,
offset: fileDescriptor.offset,
length: fileDescriptor.length
};
this.videoFiles.push(avFileDescriptor)
})
let fileList2: string[] = getContext(this).resourceManager.getRawFileListSync('audio');
fileList2.forEach(fileStr => {
let fileDescriptor = getContext().resourceManager.getRawFdSync(audio/${fileStr});
let avFileDescriptor: media.AVFileDescriptor = {
fd: fileDescriptor.fd,
offset更多关于HarmonyOS 鸿蒙Next中音频播放锁屏状态栏播控设置问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
需要使用AVSession(媒体会话)和申请长时任务,避免播放过程中音频模块被系统强制中断。
AVSession文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/avsession-overview-V5
长时任务文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/continuous-task-V5
AVSession开发步骤:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/using-avsession-developer-V5#开发步骤
应用接入AVSession场景介绍:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/avsession-access-scene-V5
AVSession Kit(音视频播控服务)官网demo:https://developer.huawei.com/consumer/cn/sdk/avsession-kit/?ha_source=sousuo&ha_sourceId=89000251
在HarmonyOS鸿蒙Next中,音频播放锁屏状态栏播控设置问题涉及系统对音频播放的管理。鸿蒙Next系统在锁屏状态下,默认会显示音频播放控制栏,允许用户通过状态栏进行播放、暂停、切换曲目等操作。该功能通过系统的媒体会话服务(MediaSessionService)实现,开发者可以通过MediaSession API进行自定义设置。
如果需要在锁屏状态下隐藏或自定义播控栏,可以在应用中使用MediaSession的setFlags方法,结合MediaSession.FLAG_HANDLES_MEDIA_BUTTONS和MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS标志进行调整。此外,开发者可以通过MediaMetadata API设置音频元数据,如标题、专辑、封面等信息,这些信息会同步显示在锁屏播控栏中。
如果遇到播控栏未正确显示或功能异常,需检查应用的MediaSession配置是否符合鸿蒙系统的要求,并确保音频播放服务在后台正常运行。系统会根据应用的生命周期状态动态管理播控栏的显示与隐藏。
在HarmonyOS鸿蒙Next中,音频播放时锁屏状态栏的播控设置可以通过以下步骤进行配置:
-
确保应用支持后台播放:在应用的
config.json文件中,确保audio权限已声明,并且应用支持后台播放。 -
使用音频服务:通过
AVPlayer或AudioService进行音频播放,确保系统识别到应用正在播放音频。 -
状态栏显示控制:在播放音频时,系统会自动在锁屏状态栏显示播控组件。如需自定义,可通过
NotificationAPI设置通知栏的播放控制按钮。 -
处理播放状态:在
onPlay、onPause等回调中更新播放状态,确保锁屏状态栏的播控信息实时同步。
通过以上步骤,可以确保音频播放时锁屏状态栏的播控功能正常显示和操作。

