using-avcastpicker.md 8.8 KB
Newer Older
Z
zengyawen 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
# 使用音视频投播

通过本开发指导,完成一次音频跨设备投播。

## 接口说明

对于每个异步调用的API均提供了callback函数和Promise函数,以下示例均采用callback函数。详细的API接口说明,请参考[AVCastPicker组件参考](../reference/arkui-ts/ohos-avcastpicker.md)[AVCastController API参考文档](../reference/apis/js-apis-avsession.md#avcastcontroller10)

| 接口 |  说明 |
| -------- | -------- |
| getAVCastController(callback: AsyncCallback\<AVCastController\>): void | 获取远端投播时的控制接口。|
| on(type: 'outputDeviceChange', callback: (state: ConnectionState, device: OutputDeviceInfo) => void): void  | 注册设备变化的回调,同时包含了设备的连接状态。|
| sendControlCommand(command: AVCastControlCommand, callback: AsyncCallback\<void\>): void  | 投播会话的控制接口,用于进行投播中的各种播控指令 |
| prepare(item: AVQueueItem, callback: AsyncCallback\<void>): void | 准备播放,进行资源加载和缓冲,不会触发真正的播放 |
| start(item: AVQueueItem, callback: AsyncCallback\<void>): void  | 开始播放媒体资源 |
| on(type: 'playbackStateChange', filter: Array\<keyof AVPlaybackState\> \| 'all', callback: (state: AVPlaybackState) => void)  | 注册播放状态变化的回调 |
| on(type: 'mediaItemChange', callback: AsyncCallback\<AVQueueItem\>): void | 注册当前播放内容更新的回调,返回当前播放的内容的信息。|

>**说明:**
>
> AVCastController由系统获取并返回,在设备连接成功后获取,在设备断开后不能继续使用,否则会抛出异常。

## 开发步骤

1. 创建播放器,并创建AVSession。

C
cheng 已提交
27
  通过AVSessionManager创建并激活媒体会话。
Z
zengyawen 已提交
28

C
cheng 已提交
29 30
  ```ts
  import AVSessionManager from '@ohos.multimedia.avsession'; //导入AVSession模块
Z
zengyawen 已提交
31

C
cheng 已提交
32 33 34 35 36 37 38
  // 创建session
  async createSession() {
    let session: AVSessionManager.AVSession = await AVSessionManager.createAVSession(this.context, 'SESSION_NAME', 'audio');
    session.activate();
    console.info(`session create done : sessionId : ${session.sessionId}`);
  }
  ```
Z
zengyawen 已提交
39 40 41

2. 在需要投播的播放界面创建投播组件AVCastPicker。

C
cheng 已提交
42 43 44 45 46 47 48 49 50 51 52 53 54
  ```ts
  // 创建组件,并设置大小
  build() {
    Row() {
      Column() {
        AVCastPicker()
          .width('40vp')
          .height('40vp')
          .border({ width: 1, color: Color.Red })
      }
    }
  }
  ```
Z
zengyawen 已提交
55 56 57

3. 设置AVSession的信息,注册AVSession的回调。用于感知投播连接。

C
cheng 已提交
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
  ```ts
  async setListenerFromController() {
    let session: AVSessionManager.AVSession = ALLREADY_CREATE_A_SESSION;

    // 监听设备连接状态的变化
    session.on("outputDeviceChange", (state, device) => {
      if (state == AVCastState.STATE_CONNECTED) { // 设备连接成功
        global.avcastcontroller = avsession.getAVCastController();
        console.info('get a cast controller successfully');
        console.info(`device connected ` + ${JSON.stringify(device)});

        // 设置播放参数,开始播放
        var playItem = {
          itemId: 0,
          description: {
            mediaId: '12345',
            mediaName: 'song1',
            mediaType: 'AUDIO',
            mediaUri: 'http://resource1_address',
            mediaSize: 12345,
            startPosition: 0,
            duration: 1000,
            artist: 'mysong',
            albumTitle: 'song1_title',
            albumCoverUri: "http://resource1_album_address",
            lyricUri: "http://resource1_lyric_address",
            iconUri: "http://resource1_icon_address",
            appName: 'MyMusic'
          }
        };
        // 准备播放,这个不会触发真正的播放,会进行加载和缓冲
        global.avcastcontroller.prepare(playItem, () => {
          console.info('prepare done');
        });
        // 启动播放
        global.avcastcontroller.start(playItem, () => {
          console.info('play done');
        });

        // 列表里的第一首歌曲投播的时候,本端的Player要停止
        this.localplayer.stop();

        // 本端投播后的应用界面由应用进行专辑图、歌手等信息的刷新

        // 远端状态的回调,这个回调需要应用进行界面刷新,而不需要执行控制操作
        // 回调包括播放状态,播放位置,播放速度,播放模式等
        global.avcontroller.on('playbackStateChange', filter: 'all',  (state) => {
          console.info('playbackStateChange ' + state);
        });
        // 用于响应远端进行上一首、下一首
        global.avcontroller.on('playNext', () => {
          // 应用根据列表顺序,更新新的资源进行播放
          var nextPlayItem = {
            itemId: 2,
            description: {
              mediaId: '12345',
              mediaName: 'song2',
              mediaType: 'AUDIO',
              mediaUri: 'http://resource2_address',
              mediaSize: 12345,
              startPosition: 0,
              duration: 2000,
              artist: 'mysong',
              albumTitle: 'song2_title',
              albumCoverUri: "http://resource2_album_address",
              lyricUri: "http://resource2_lyric_address",
              iconUri: "http://resource2_icon_address",
              appName: 'MyMusic'
            }
          };
          // 准备播放
          global.avcastcontroller.prepare(nextPlayItem, () => {
            console.info('prepare done');
          });
          // 启动播放
          global.avcastcontroller.start(nextPlayItem, () => {
            console.info('play done');
          });
        });
      }
    }

    // 可以通过getOutputDevice接口主动查询连接的设备
    session.getOutputDevice((device) => {
      console.info(`current routing device` + ${JSON.stringify(device)});
    });
  }
  ```
Z
zengyawen 已提交
146 147 148

4. 通过AVSession获取投播控制器AVCastController,用于控制应用内的播放。

C
cheng 已提交
149 150 151 152 153 154
  ```ts
  async sendCommandToSessionByController() {
    // 记录从avsession获取的远端控制器
    // 下发播放命令
    let avCommand: AVSessionManager.AVCastControlCommand = {command:'play'};
    global.avcontroller.sendControlCommand(avCommand);
Z
zengyawen 已提交
155

C
cheng 已提交
156 157 158
    // 下发暂停命令
    let avCommand: AVSessionManager.AVCastControlCommand = {command:'pause'};
    global.avcontroller.sendControlCommand(avCommand);
Z
zengyawen 已提交
159

C
cheng 已提交
160 161 162
    // 播放、暂停等的操作,可以通过状态的回调接口获取到,然后进行本端界面的刷新
  }
  ```
Z
zengyawen 已提交
163 164 165

5. 申请投播长时任务,避免应用在投播进入后台时被系统冻结,导致无法持续投播。

C
cheng 已提交
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
  ```ts
  import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
  import wantAgent from '@ohos.app.ability.wantAgent';

  function startContinuousTask() {
      let wantAgentInfo = {
          // 点击通知后,将要执行的动作列表
          wants: [
            {
              bundleName: "com.example.myapplication",
              abilityName: "EntryAbility",
            }
          ],
          // 点击通知后,动作类型
          operationType: wantAgent.OperationType.START_ABILITY,
          // 使用者自定义的一个私有值
          requestCode: 0,
          // 点击通知后,动作执行属性
          wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
      };

      // 通过wantAgent模块的getWantAgent方法获取WantAgent对象
      try {
          wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
              try {
                  backgroundTaskManager.startBackgroundRunning(mContext,
                      backgroundTaskManager.BackgroundMode.MULTI_DEVICE_CONNECTION, wantAgentObj).then(() => {
                      console.info("Operation succeeded");
                  }).catch((error) => {
                      console.error(`Operation failed. code is ${error.code} message is ${error.message}`);
                  });
              } catch (error) {
                  console.error(`Operation failed. code is ${error.code} message is ${error.message}`);
              }
          });
      } catch (error) {
          console.error(`Operation getWantAgent failed. code is ${error.code} message is ${error.message}`);
      }
  }
  ```
Z
zengyawen 已提交
206 207 208 209 210 211 212 213 214 215 216 217 218

6. 音频焦点的处理。请参考[多音频并发处理](audio-playback-concurrency.md)

   在应用进入投播后,当前应用需要去注册焦点处理,以免被其他应用的焦点申请而影响。

   **当前投播支持全局切换,后续如果规格变更,再同步调整方案说明。**

7. 结束投播。

   当远端设备断开的时候,应用会收到事件,系统会自动断开连接。

   应用也可以使用断开投播的接口,主动进行投播连接的断开。

C
cheng 已提交
219 220 221 222 223 224
  ```ts
  async release() {
    // 一般来说,应用退出时,而不希望继续投播,可以主动结束
    await global.avsession.stopCasting();
  }
  ```