HarmonyOS 鸿蒙Next中音频播放锁屏状态栏播控设置问题

HarmonyOS 鸿蒙Next中音频播放锁屏状态栏播控设置问题 看这个文档,好像没有关于音频播放的时候设置锁屏状态播控的问题,帮忙给一下相应的文档。

4 回复

再给个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


在HarmonyOS鸿蒙Next中,音频播放锁屏状态栏播控设置问题涉及系统对音频播放的管理。鸿蒙Next系统在锁屏状态下,默认会显示音频播放控制栏,允许用户通过状态栏进行播放、暂停、切换曲目等操作。该功能通过系统的媒体会话服务(MediaSessionService)实现,开发者可以通过MediaSession API进行自定义设置。

如果需要在锁屏状态下隐藏或自定义播控栏,可以在应用中使用MediaSession的setFlags方法,结合MediaSession.FLAG_HANDLES_MEDIA_BUTTONS和MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS标志进行调整。此外,开发者可以通过MediaMetadata API设置音频元数据,如标题、专辑、封面等信息,这些信息会同步显示在锁屏播控栏中。

如果遇到播控栏未正确显示或功能异常,需检查应用的MediaSession配置是否符合鸿蒙系统的要求,并确保音频播放服务在后台正常运行。系统会根据应用的生命周期状态动态管理播控栏的显示与隐藏。

在HarmonyOS鸿蒙Next中,音频播放时锁屏状态栏的播控设置可以通过以下步骤进行配置:

  1. 确保应用支持后台播放:在应用的config.json文件中,确保audio权限已声明,并且应用支持后台播放。

  2. 使用音频服务:通过AVPlayerAudioService进行音频播放,确保系统识别到应用正在播放音频。

  3. 状态栏显示控制:在播放音频时,系统会自动在锁屏状态栏显示播控组件。如需自定义,可通过Notification API设置通知栏的播放控制按钮。

  4. 处理播放状态:在onPlayonPause等回调中更新播放状态,确保锁屏状态栏的播控信息实时同步。

通过以上步骤,可以确保音频播放时锁屏状态栏的播控功能正常显示和操作。

回到顶部