目录

Common FAQs About Distributed Media Sessions

This document describes the typical problems that occur when audio and video apps access AVSession Kit in distributed media sessions (casting) and the methods of locating and solving the problems. You can further locate the faults based on the media session management error codes and HiLog.

What Can I Do If the Total Duration of the Cast Video Cannot Be Obtained Correctly?

Symptom

After the casting is initiated, the total duration (duration) of the video cannot be correctly obtained.

Possible causes

In the casting scenario, the total duration of a video needs to be obtained through AVCastController. If the AVCastController object is not correctly created or the media has not been played at the remote end (the duration is reported after the remote end parses the media), the total duration cannot be obtained.

Solution

  1. Obtain the total playback duration in any of the following ways:

    • Method 1: Register on('playbackStateChange'). The extras field in AVPlaybackState returned by the callback carries the total media playback duration.

      // Register a listener for playback state changes and obtain the total playback duration from extras.
      avCastController.on('playbackStateChange', 'all', (playbackState: avSession.AVPlaybackState) => {
        if (playbackState.extras?.duration !== undefined) {
          let duration = playbackState.extras.duration as number;
          console.info(`Cast duration: ${duration} ms`);
        }
      });
    • Method 2: Register on('mediaItemChange'). In the returned AVQueueItem, the duration field of description (AVMediaDescription) is the total media playback duration.

      // Register a listener for casting media changes and obtain the total casting duration from AVQueueItem.
      avCastController.on('mediaItemChange', (item: avSession.AVQueueItem) => {
        let duration = item.description?.duration;
        console.info(`Cast duration: ${duration} ms`);
      });
    • Method 3: Call getCurrentItem to obtain the current casting resource information. The duration field in description in AVQueueItem is the total media playback duration.

      // Obtain the current casting resource information and obtain the total projection duration from AVQueueItem.
      let item: avSession.AVQueueItem = await avCastController.getCurrentItem();
      let duration = item.description?.duration;
      console.info(`Cast duration: ${duration} ms`);
  2. If the duration cannot be obtained using the preceding methods, ensure that the AVCastController object has been correctly created and the media file has been played on the remote end. The AVCastController object can be obtained only after the casting connection is successful. Perform operations related to duration obtaining after the casting connection is successful.

Why Are the Casting Control Commands Invalid?

Symptom

After a video on a mobile phone is cast to a TV that supports the standard DLNA protocol, the control commands sent by the mobile phone are invalid. For example, the fast-forward, rewind, and seek commands are not responded, or the callback functions such as on('seekDone') are not triggered.

Possible causes

Solution

  1. Check whether the issue is specific to the app. If the issue occurs only in the current app, check whether the control command callbacks are correctly registered on the app side (for example, by sending commands through sendControlCommand and registering the corresponding listeners). If the issue also occurs when other apps are used to cast to the same device, proceed to the next step.
  2. Check whether the issue is related to the peer device. Try using another peer device for verification, for example, a PC/2-in-1 device running HarmonyOS 5.0.0 or later, a TV running HarmonyOS 3.1 or later, or other devices that support the standard DLNA protocol. If the issue is resolved after the device is replaced, the issue lies with the remote device. If the issue persists, the issue lies with the local device.
  3. Check whether there is a casting error callback. If on('error') or a casting control error callback (such as on('castControlGenericError')) is registered and an error is received, you can use a packet capture tool to check the error code in the packet to determine whether the issue is caused by incorrect command parameters on the local device or unsupported capabilities on the peer device.

Why Are the Cast Lyric Not Displayed?

Symptom

After the lyrics are cast to the remote device, the remote device does not display the lyrics.

Possible causes

Solution

  1. Ensure that the lyric content does not exceed 40,960 bytes.
  2. Try using another peer device for verification. It is recommended that you cast content to a PC/2-in-1 device running HarmonyOS 5.0.0 or later, a TV device running HarmonyOS 3.1 or later, or other devices that support full casting capabilities for comparison.

Why Does Lyrics Stop Scrolling When the Mouse Is Hovered Over When Content Is Cast to a PC/2-in-1 Device?

Symptom

When content is cast to a PC/2-in-1 device, it is played in the PC/2-in-1 player. When the mouse pointer hovers over the lyrics area, the lyrics stop scrolling. After the mouse pointer is moved away, the lyrics resume scrolling from the correct playback position.

Possible causes

This is a design behavior of the PC/2-in-1 player. When the mouse pointer hovers over the lyrics area, the PC/2-in-1 player pauses the automatic scrolling of lyrics, allowing users to view and browse the lyrics. After the mouse pointer is moved away, the lyrics automatically return to the position that matches the current playback progress and resume scrolling.

Solution

This is a normal design specification of the PC/2-in-1 player and does not need to be handled on the app side.

What Should I Do for Inconsistent Volume Control on the Remote Device?

Symptom

After the casting, when the volume is adjusted on the local device, the actual volume change on the remote device is inconsistent with that displayed on the local device, or there is no response to the volume adjustment.

Possible causes

The devices that can be cast have hardware differences, and the maximum volume of different remote devices is inconsistent (for example, 100 for a TV device and 20 or 15 for a PC/2-in-1 device). If the volume is directly delivered based on the local volume value, it will not match the actual volume on the remote device.

Solution

You can implement volume normalization and delivery by referring to the following solution:

  1. Listen to the on('playbackStateChange') callback of AVCastController, obtain the maximum number of volume bars (maxCastVolume) of the current peer device from the extras field of AVPlaybackState, calculate the volume change step, and convert the volume reported by the remote device to the local volume.

    import { avSession } from '@kit.AVSessionKit';
    import { hilog } from '@kit.PerformanceAnalysisKit';
    
    const TAG = 'CastVolume';
    // Reference value of the maximum volume at the local end.
    const MAX_VOLUME_VALUE = 100;
    // Maximum number of volume bars of the remote device (TV device: 100; PC/2-in-1 device: 20 or 15; reported by the remote device).
    private maxCastVolume: number = MAX_VOLUME_VALUE;
    // Step value of the remote volume change each time the system volume button is pressed. (The step value may vary depending on the remote device.)
    private changeVolumeStepSize: number = 1;
    // Current volume maintained by the local end.
    private currentVolume: number = 0;
    
    listenPlaybackStateChange() {
      // Create an avCastController object and listen for playback state changes. Pay attention to the volume field.
      avCastController.on('playbackStateChange', ['volume'], (playbackState: avSession.AVPlaybackState) => {
        if (playbackState) {
          this.volumeChangeHandle(playbackState);
        }
      });
    }
    
    volumeChangeHandle(avState: avSession.AVPlaybackState) {
      // Obtain the maximum number of volume bars of the remote end from extras and convert the step.
      if (avState.extras?.maxCastVolume !== undefined && typeof avState.extras?.maxCastVolume === 'number') {
        this.maxCastVolume = avState.extras?.maxCastVolume as number ?? MAX_VOLUME_VALUE;
        this.changeVolumeStepSize = Math.round(MAX_VOLUME_VALUE / this.maxCastVolume) || 1;
        hilog.info(0x0000, TAG, `volumeChangeHandle, changeVolumeStepSize: ${this.changeVolumeStepSize}, maxCastVolume: ${this.maxCastVolume}`);
      }
      // Convert the volume value reported by the remote end to an integer multiple of the step value of the local end.
      if (avState.volume !== undefined && avState.volume >= 0) {
        let currentVolumeStep = Math.round(avState.volume * this.maxCastVolume / MAX_VOLUME_VALUE);
        this.currentVolume = currentVolumeStep * this.changeVolumeStepSize;
      }
    }
  2. Register inputConsumer.on('keyPressed') to intercept physical volume button events. After the volume is increased or decreased based on the calculated step, the setVolume command is delivered through sendControlCommand. For details about the command type, see AVCastControlCommandType.

    • inputConsumer.on('keyPressed') is supported since API version 16. After the subscription is successful, the default system behavior (such as system-level volume adjustment) of the key will be blocked. You need to call inputConsumer.off('keyPressed') to cancel the subscription when it is no longer needed to restore the system response.
    • Device behavior difference: In versions earlier than API version 23, this API can be called on phones and tablets, and error code 801 is returned on other devices. Since API version 23, this API is also supported on PCs/2-in-1 devices and TVs.
    import { avSession } from '@kit.AVSessionKit';
    import { inputConsumer, KeyCode, KeyEvent } from '@kit.InputKit';
    import { BusinessError } from '@kit.BasicServicesKit';
    import { hilog } from '@kit.PerformanceAnalysisKit';
    
    const TAG = 'CastVolume';
    const MAX_VOLUME_VALUE = 100;
    
    // Encapsulate the casting volume setting API.
    public async setAVCastVolume(volume: number): Promise<void> {
      let avCommand: avSession.AVCastControlCommand = { command: 'setVolume', parameter: volume };
      try {
        await this.avCastController.sendControlCommand(avCommand);
      } catch (error) {
        let err = error as BusinessError;
        hilog.error(0x0000, TAG, `setAVCastVolume failed, code: ${err.code}, message: ${err.message}`);
      }
    }
    
    // Intercept the volume increase event.
    public interceptIncrease() {
      let upOptions: inputConsumer.KeyPressedConfig = { key: KeyCode.KEYCODE_VOLUME_UP, action: 1, isRepeat: true };
      try {
        inputConsumer.on('keyPressed', upOptions, async (event: KeyEvent) => {
          let remoteVolume = this.currentVolume + this.changeVolumeStepSize;
          this.currentVolume = remoteVolume > MAX_VOLUME_VALUE ? MAX_VOLUME_VALUE : remoteVolume;
          await this.setAVCastVolume(this.currentVolume);
        });
      } catch (error) {
        let err = error as BusinessError;
        hilog.error(0x0000, TAG, `register volume up failed, code: ${err.code}, message: ${err.message}`);
      }
    }
    
    // Intercept the volume decrease event.
    public interceptDecrease() {
      let downOptions: inputConsumer.KeyPressedConfig = { key: KeyCode.KEYCODE_VOLUME_DOWN, action: 1, isRepeat: true };
      try {
        inputConsumer.on('keyPressed', downOptions, async (event: KeyEvent) => {
          let remoteVolume = this.currentVolume - this.changeVolumeStepSize;
          this.currentVolume = remoteVolume < 0 ? 0 : remoteVolume;
          await this.setAVCastVolume(this.currentVolume);
        });
      } catch (error) {
        let err = error as BusinessError;
        hilog.error(0x0000, TAG, `register volume down failed, code: ${err.code}, message: ${err.message}`);
      }
    }

What Can I Do When Devices Cannot Be Discovered During Casting?

Symptom

When casting is initiated, the target remote device that can be cast cannot be found.

Possible causes

Solution

  1. Verify that the filter field of AVMetadata is set to a remote protocol such as TYPE_CAST_PLUS_STREAM or TYPE_DLNA as required.
  2. Use setExtras to declare that URL-based casting is supported. The value of the input parameter is SUPPORT_URL_CASTING ('url-cast').