鸿蒙NEXT开发【自由流转】媒体博客实战

华为视频接入播控中心和投播能力概述

华为视频在进入影片详情页播放时,支持在控制中心查看当前播放的视频信息,并进行快进、快退、拖动进度、播放暂停、下一集、调节音量等操作,方便用户通过控制中心来操作当前播放的视频。

当用户希望通过大屏播放当前华为视频的影片时,可以在华为视频或播控中心内进行投播,将影片投播到同一网络下的华为智慧屏等大屏设备进行播放,且通过播控中心来方便地进行播放暂停、快进快退、下一集等操作。

华为视频投播功能需要使用播控中心的能力完成,所以在接入投屏之前,华为视频需要先接入播控中心。

华为视频接入播控中心

华为视频接入播控中心介绍

  • 媒体会话(AVSession):本地播放时用于更新媒体资源的信息和响应系统播控中心。在投播时,AVSession作为在本地播放和投播之间切换的“枢纽”接口,把二者联系起来。通过AVSession可以设置和查询应用投播能力,并创建投播控制器。
  • 媒体会话控制器(AVSessionController):一般由播控中心提供。如果是应用内的控制器,可用于控制应用的后台播放。

华为视频接入播控中心的交互流程如图所示。

华为视频同步播控中心

  • 播放内容信息上报播控中心

    这部分功能负责实现在应用播放的时候,通知播控中心当前播放的影片信息。

    • 应用冷启动之后,需要调用createAVSession创建会话。应用生命周期结束后会话自动销毁,不需要调用destroy。

      说明

      下文中代码示例,可能包含重复的函数和导包引入,因此后续代码示例不再重复展示。

      1. 导入相关模块
        // MainAbility.ets
        import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
        import { hilog } from '@kit.PerformanceAnalysisKit';
        import { AvSessionManager } from '../avsession/AvSessionManager';
        import router from '@system.router';
        // AvSessionManager.ts
        import avSession from '@ohos.multimedia.avsession';
        import hilog from '@ohos.hilog';
        import type { BusinessError } from '@ohos.base';
        import type common from '@ohos.app.ability.common';
        import WantAgent from '@ohos.app.ability.wantAgent';
      2. 调用createAVSession创建会话相关示例代码如下:
        // MainAbility.ets
        export default class MainAbility extends UIAbility {
        onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
        AvSessionManager.getInstance().init(this.context);
        }
        }
        // AvSessionManager.ts
        const TAG = 'AvSessionManager';
        
        /**
        * 对接播控中心管理器
        */
        export class AvSessionManager {
        
        private static readonly instance: AvSessionManager = new AvSessionManager();
        private session: avSession.AVSession = null;
        
        static getInstance(): AvSessionManager {
        return this.instance;
        }
        
        init(abilityContext: common.Context): void {
        avSession.createAVSession(abilityContext, 'himovie', 'video').then(session => {
        this.session = session;
        // 创建完成之后,激活会话。
        this.session.activate();
        hilog.info(0x06666, TAG, 'createAVSession success');
        }).catch((error: BusinessError) => {
        hilog.error(0x06666, TAG, `createAVSession or activate failed, code: ${error?.code}`);
        });
        }
        }

    • 根据当前播放的Volume信息,拼接填写setAVMetadata。
      // 业务Index.ets
      @Entry
      @Component
      struct Index {
      private avsessionMetaData: avSession.AVMetadata | null = null;
      
      aboutToAppear(): void {
      this.setAVSessionMetaData();
      }
      
      setAVSessionMetaData() {
      this.avsessionMetaData = {
      // 影片的id
      assetId: 'test vod id',
      subtitle: 'vod subtitle',
      artist: 'artist name',
      title: 'vod title',
      mediaImage: 'media image url',
      // 仅支持投屏到Cast+ Stream的设备
      filter: avSession.ProtocolType.TYPE_CAST_PLUS_STREAM,
      // 快进快退时间
      skipIntervals: avSession?.SkipIntervals?.SECONDS_30
      };
      AvSessionManager.getInstance().setMetaData(this.avsessionMetaData);
      }
      build() {
      // ...
      }
      }
      // AvSessionManager.ts
      export class AvSessionManager {
      private static readonly instance: AvSessionManager = new AvSessionManager();
      private session: avSession.AVSession = null;
      
      static getInstance(): AvSessionManager {
      return this.instance;
      }
      
      /**
      * 设置metaData并初始化状态
      *
      * @param metadata 影片元数据
      */
      setMetaData(metadata: avSession.AVMetadata): void {
      if (this.session) {
      
      hilog.info(0x06666, TAG, `setMetaData avMetadata: ${JSON.stringify(metadata)}`);
      
      this.session?.setAVMetadata(metadata)
      .then(() => {
      hilog.info(0x06666, TAG, `setMetaData success.`);
      })
      .catch((error: BusinessError) => {
      hilog.error(0x06666, TAG, `setMetaData failed, code: ${error.code}`);
      });
      }
      }
      }

  • 播放状态上报播控中心参考以下示例代码,向播控中心上报应用当前的播放状态。即应用中进行播放、暂停、进度调整等行为,通知播控中心进行不同的状态显示。
    // AvSessionManager.ts
    export class AvSessionManager {
    
    private static readonly instance: AvSessionManager = new AvSessionManager();
    private session: avSession.AVSession = null;
    /** 播放状态 */
    playState?: avSession.AVPlaybackState = {
    state: avSession.PlaybackState.PLAYBACK_STATE_INITIAL,
    position: {
    elapsedTime: 0,
    updateTime: (new Date()).getTime()
    }
    };
    
    static getInstance(): AvSessionManager {
    return this.instance;
    }
    
    /**
    * 播放
    *
    * @returns
    */
    play(currentTime?: number): void {
    hilog.info(0x0666, TAG, `AVSession play, currentTime:${currentTime}, state: ${this.playState?.state}`);
    this.setPlayOrPauseToAvSession('play', currentTime);
    }
    
    /**
    * 暂停
    *
    * @returns
    */
    pause(currentTime?: number): void {
    hilog.info(0x0666, TAG, `AVSession pause, currentTime: ${currentTime}, state: ${this.playState?.state}`);
    this.setPlayOrPauseToAvSession('pause', currentTime);
    }
    
    
    /**
    * 设置播控中心的状态为播放或暂停
    *
    * @param state 状态
    * @param elapsedTime 当前进度
    */
    private setPlayOrPauseToAvSession(state: 'play' | 'pause', elapsedTime: number): void {
    if (elapsedTime === undefined || elapsedTime < 0) {
    hilog.warn(0x0666, TAG, `param error, elapsedTime: ${elapsedTime}, do not play or pause.`);
    return;
    }
    if (this.playState === undefined || this.playState.state === avSession.PlaybackState.PLAYBACK_STATE_STOP) {
    hilog.warn(0x0666, TAG, `playState error, state is PLAYBACK_STATE_STOP or undefined, do not play or pause.`);
    return;
    }
    
    this.playState.state = state === 'play' ? avSession.PlaybackState.PLAYBACK_STATE_PLAY : avSession.PlaybackState.PLAYBACK_STATE_PAUSE;
    this.playState.position = {
    elapsedTime: elapsedTime,
    updateTime: (new Date()).getTime()
    };
    this.setAVPlaybackState();
    }
    
    /**
    * 向播控中心设置播放状态
    */
    private setAVPlaybackState(): void {
    hilog.info(0x0666, TAG, `setAVPlaybackState state: ${this.playState.state}, updateTime: ${this.playState?.position?.updateTime}, speed: ${this.playState?.speed}`);
    this.session?.setAVPlaybackState(this.playState);
    }
    }

  • 详情页退出的特殊逻辑

    当用户从详情页退出到应用首页时,需要通知AVSession清除播放信息。

    // AvSessionManager.ts
    export class AvSessionManager {
    
    private static readonly instance: AvSessionManager = new AvSessionManager();
    private session: avSession.AVSession = null;
    /** 播放状态 */
    playState?: avSession.AVPlaybackState = {
    state: avSession.PlaybackState.PLAYBACK_STATE_INITIAL,
    position: {
    elapsedTime: 0,
    updateTime: (new Date()).getTime()
    }
    };
    
    /**
    * 向播控中心设置播放状态
    */
    private setAVPlaybackState(): void {
    hilog.info(0x0666, TAG, `setAVPlaybackState state: ${this.playState.state}, updateTime: ${this.playState?.position?.updateTime}, speed: ${this.playState?.speed}`);
    this.session?.setAVPlaybackState(this.playState);
    }
    
    /**
    * 释放播放器
    */
    releasePlayer(): void {
    this.playState.state = avSession.PlaybackState.PLAYBACK_STATE_STOP;
    this.setAVPlaybackState();
    }
    }
    

华为视频响应播控中心

当应用处于正常播放的状态时,播放信息和状态同步到播控中心,用户可以在播控中心控制媒体,如暂停、进度调整等。用户在播控中心操作后,需要应用配合响应各种事件,通过AVSession的各种回调完成播放控制。

应用如果已切换到后台,用户点击播控中心,将由播控中心负责拉起华为视频。应用需要配置拉起参数。

同时,应用需要设置监听回调,包括播放、暂停、下一首、进度调整等。只有设置了回调,播控中心侧的按钮才会亮起来,否则按钮将会置灰。

  • 拉起华为视频
    // AvSessionManager.ts
    export class AvSessionManager {
    private static readonly instance: AvSessionManager = new AvSessionManager();
    private session: avSession.AVSession = null;
    /** 播放状态 */
    playState?: avSession.AVPlaybackState = {
    state: avSession.PlaybackState.PLAYBACK_STATE_INITIAL,
    position: {
    elapsedTime: 0,
    updateTime: (new Date()).getTime()
    }
    };
    
    static getInstance(): AvSessionManager {
    return this.instance;
    }
    
    /**
    * 设置metaData并初始化状态
    *
    * @param metadata 影片元数据
    */
    setMetaData(metadata: avSession.AVMetadata): void {
    if (this.session) {
    hilog.info(0x06666, TAG, `setMetaData avMetadata: ${JSON.stringify(metadata)}`);
    
    this.session?.setAVMetadata(metadata)
    .then(() => {
    hilog.info(0x06666, TAG, `setMetaData success.`);
    this.setLaunchAbility(metadata.assetId);
    })
    .catch((error: BusinessError) => {
    hilog.error(0x06666, TAG, `setMetaData failed, code: ${error.code}`);
    });
    }
    }
    
    /**
    * 设置一个WantAgent用于拉起会话的Ability
    * @param vodId 影片Id
    */
    setLaunchAbility(vodId: string): void {
    const ability: WantAgent.WantAgentInfo = {
    wants: [
    {
    bundleName: 'com.huawei.hmsapp.himovie',
    abilityName: 'MainAbility',
    parameters: {
    type: 'avsession',
    routeParams: {
    vodId,
    }
    }
    }
    ],
    requestCode: 0,
    actionType: WantAgent.OperationType.START_ABILITY,
    actionFlags: [WantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
    }
    this.session.setLaunchAbility(ability).then(() => {
    hilog.info(0x0666, TAG, `SetLaunchAbility successfully`);
    }).catch((err: BusinessError) => {
    hilog.info(0x0666, TAG, `SetLaunchAbility failed, code: ${err.code}`);
    });
    }
    }

  • 设置监听回调
    // AvSessionManager.ts
    export class AvSessionManager {
    private session: avSession.AVSession = null;
    
    /**
    * 监听播控中心回调事件,播放
    *
    * @param action 回调方法
    */
    onPlay(action: () => void): void {
    if (this.session) {
    this.session.on('play', action);
    }
    }
    
    /**
    * 监听播控中心回调事件,暂停
    *
    * @param action 回调方法
    */
    onPause(action: () => void): void {
    if (this.session) {
    this.session.on('pause', action);
    }
    }
    }

华为视频支持投播

华为视频应用内发起投播

用户使用华为视频播放影片时,通过点击右上角投播组件

,选择需要投播的大屏设备,连接成功后即可完成投播的流程。效果如下图所示。

图1 从华为视频内播放到投播成功

实现投播效果需要完成以下步骤。

  • 使用隔空投放组件连接远端设备

    用户在播放影片时,右上角会展示一个

    图标,它提供了投播能力。用户点击该图标后,播控中心将拉起设备选择的模态窗口,设备的搜索发现、用户选择设备后的连接均由播控中心完成,此过程华为视频不感知。完成连接后,播控中心通过播放设备变化的监听事件outputDeviceChange通知华为视频,华为视频再进行下一步处理。

    图2 点击投播组件触发设备选择弹框

    应用使用AVSession.on('outputDeviceChange')设置播放设备变化的监听事件,示例代码如下。

    远端设备能够投播,需要满足以下条件:

    • 设备连接成功,即outputDeviceChange事件监听回调返回connectState为1。
    • OutputDeviceInfo中设备列表的第一个设备,必须为远端设备,即castCategory为CATEGORY_REMOTE。
    • 投播协议类型必须支持Cast+ Stream。
      1. 导入相关模块
        // CastType.ts
        import media from '@ohos.multimedia.media';
        
        // 业务Index.ets
        import avSession from '@ohos.multimedia.avsession';
        import { AvSessionManager } from '../avsession/AvSessionManager';
        import { CastManager } from '../avsession/CastManager';
        import hilog from '@ohos.hilog';
        import type { BusinessError } from '@ohos.base';
        
        // CastManager.ets
        import avSession from '@ohos.multimedia.avsession';
        import hilog from '@ohos.hilog';
        import type { BusinessError } from '@ohos.base';
        import { CastMediaInfo, M3U8Info, CastMediaInfoType, CastErrorType } from './CastType';
        import wantAgent from '@ohos.app.ability.wantAgent';
        import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
        import promptAction from '@ohos.promptAction';

      2. 设置播放设备变化的监听事件示例代码:
        // CastManager.ets
        const TAG = 'CastManager';
        
        /**
        * 投播管理器
        */
        export class CastManager {
        /** 单例 */
        private static readonly INSTANCE: CastManager = new CastManager();
        /** 播控中心avSession */
        private avSession?: avSession.AVSession;
        /** 投播控制器 */
        private avCastController?: avSession.AVCastController;
        
        public afterCreateSession(session: avSession.AVSession) {
        this.avSession = session;
        // 监听设备连接状态的变化
        this.setOutputDeviceChangeListener();
        }
        
        /**
        * 设置输出设备变化监听器
        */
        private setOutputDeviceChangeListener(): void {
        this.avSession?.on('outputDeviceChange', (connectState: avSession.ConnectionState,
        device: avSession.OutputDeviceInfo) => {
        const castCategory = device?.devices?.[0].castCategory;
        // 成功连接远程设备
        if (castCategory === avSession.AVCastCategory.CATEGORY_REMOTE && connectState === avSession.ConnectionState.STATE_CONNECTED) {
        // 获取cast控制器
        this.avSession?.getAVCastController().then(async (controller: avSession.AVCastController) => {
        hilog.info(0x0666, TAG
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值