Development of HarmonyOS learning path - multimedia development (media session management development)

1. Development of media session management

AVSession is a set of media playback control framework, which decouples media services and interfaces, and provides standardized communication interfaces, so that applications can switch between different media freely and efficiently.

Constraints and Restrictions

  • After using the AVSession class, resources need to be released in time.
  • The player class needs to use ohos.media.player.Player, otherwise key events cannot be received normally.

scene introduction

The AVSession framework has four main classes that control the core of the entire framework. The following figure simply illustrates the relationship between the four core media framework control classes.

  • AVBrowser

    The media browser is usually created on the client. After successfully connecting to the media service, it sends playback control instructions to the server through the media controller AVController.

    The main process is to call the connect method to initiate a connection request to AVBrowserService. After the connection is successful, initiate a subscription data request in the callback method AVConnectionCallback.onConnected, and save the requested media playback data in the callback method AVSubscriptionCallback.onAVElementListLoaded.

    Before calling subscribeByParentMediaId(String, AVSubscriptionCallback) of AVBrowser, you need to execute unsubscribeByParentMediaId(String) first to prevent repeated subscriptions.

  • AVController

    The media controller, created in the callback method AVConnectionCallback.onConnected after the client AVBrowser successfully connects to the service, is used to send playback control instructions to the Service, and respond to server media status changes by implementing AVControllerCallback callbacks, such as track information changes and playback status Changes, etc., to complete the UI refresh.

  • AV Browser Service

    Media browser service, usually on the server side, establishes a connection with the media browser through the media session AVSession, and performs media playback by implementing Player. There are two important methods:

    1. onGetRoot, handles the connection request sent from the media browser AVBrowser, and returns a valid AVBrowserRoot object to indicate that the connection is successful;
    2. onLoadAVElementList, process the data subscription request sent from the media browser AVBrowser, and return the media playback data through the AVBrowserResult.sendAVElementList(List<AVElement>) method.

      Before using the result of onLoadAVElementList(String, AVBrowserResult) to return data, you need to execute detachForRetrieveAsync().

  • AVSession

    A media session is usually created in onStart of AVBrowserService, set to AVBrowserService through the setAVToken method, and implements AVSessionCallback callback to receive and process the playback control instructions sent by the media controller AVController, such as play, pause, jump to the previous song, jump Go to next song etc.

    In addition to the above four classes, the AVSession framework also has AVElement.

  • AVElement

    Media element to pass playlist from AVBrowserService to AVBrowser.

Interface Description

The main interface of AVBrowser

interface name

describe

AVBrowser(Context context, ElementName name, AVConnectionCallback callback, PacMap options)

Construct an AVBrowser instance to browse the media data provided by AVBrowserService.

connect()

Connect to the AVBrowserService.

disconnect()

Disconnect from AVBrowserService.

isConnected()

Determine whether it is currently connected to AVBrowserService.

getElementName()

Get the ohos.bundle.ElementName instance of AVBrowserService.

getRootMediaId()

Get the default media id.

getOptions()

Get additional data provided by AVBrowserService.

getAVToken()

Get a token for a media session.

getAVElement(String mediaId, AVElementCallback callback)

Enter the id of the media, query the corresponding ohos.media.common.sessioncore.AVElement information, and the query result will be returned through the callback.

subscribeByParentMediaId(String parentMediaId, AVSubscriptionCallback callback)

Query all media element information contained in the specified media id, and subscribe to its media information update notification.

subscribeByParentMediaId(String parentMediaId, PacMap options, AVSubscriptionCallback callback)

Query the information of the media element in the specified media id based on the service-specific parameters, and subscribe to its media information update notification.

unsubscribeByParentMediaId(String parentMediaId)

Unsubscribe from information update notifications corresponding to the media id.

unsubscribeByParentMediaId(String parentMediaId, AVSubscriptionCallback callback)

Unsubscribe from the information update notification of the media id related to the specified callback.

The main interface of AVBrowserService

interface name

describe

onGetRoot(String callerPackageName, int clientUid, PacMap options)

The callback method is used to return the root information of the media content of the application, and the callback is made after AVBrowser.connect().

onLoadAVElementList(String parentMediaId, AVBrowserResult result)

The callback method is used to return the result information AVBrowserResult of the application's media content, which contains the AVElement list of the child nodes, and is called back after the method subscribeByParentMediaId or notifyAVElementListUpdated of the AVBrowser is executed.

onLoadAVElement(String mediaId, AVBrowserResult result)

The callback method is used to obtain the result information of a specific media item AVElement, and the callback is performed after the execution of the AVBrowser.getAVElement method.

getAVToken()

Get the session token between AVBrowser and AVBrowserService.

setAVToken(AVToken token)

Set the session token between AVBrowser and AVBrowserService.

getBrowserOptions()

Get the service parameter options set by AVBrowser when connecting to AVBrowserService.

getCallerUserInfo()

Get information about the caller who is currently sending the request.

notifyAVElementListUpdated(String parentMediaId)

Notify all connected AVBrowsers that the child nodes of the current parent node have changed.

notifyAVElementListUpdated(String parentId, PacMap options)

Notify all connected AVBrowsers that the child nodes of the current parent node have changed, and service parameters can be set.

The main interface of AVController

interface name

describe

AVSession(Context context, String tag)

Construct an AVSession instance for controlling media playback.

AVSession(Context context, String tag, PacMap sessionInfo)

构造带有附加会话信息的AVSession实例,用于控制媒体播放。

setAVSessionCallback(AVSessionCallback callback)

设置回调函数来控制播放器,控制逻辑由应用实现。如果callback为null则取消控制。

setAVSessionAbility(IntentAgent ia)

给AVSession设置一个IntentAgent,用来启动用户界面。

setAVButtonReceiver(IntentAgent ia)

为媒体按键接收器设置一个IntentAgent,以便应用结束后,可以通过媒体按键重新拉起应用。

enableAVSessionActive(boolean active)

设置是否激活媒体会话。当会话准备接收命令时,将输入参数设置为true。如果会话停止接收命令,则设置为false。

isAVSessionActive()

查询会话是否激活。

sendAVSessionEvent(String event, PacMap options)

向所有订阅此会话的控制器发送事件。

release()

释放资源,应用播放完之后需调用。

getAVToken()

获取应用连接到会话的令牌。此令牌用于创建媒体播放控制器。

getAVController()

获取会话构造时创建的控制器,方便应用使用。

setAVPlaybackState(AVPlaybackState state)

设置当前播放状态。

setAVMetadata(AVMetadata avMetadata)

设置媒体资源元数据ohos.media.common.AVMetadata。

setAVQueue(List<AVQueueElement> queue)

设置播放队列。

setAVQueueTitle(CharSequence queueTitle)

设置播放队列的标题,UI会显示此标题。

setOptions(PacMap options)

设置此会话关联的附加数据。

getCurrentControllerInfo()

获取发送当前请求的媒体控制器信息。

AVElement的主要接口

接口名

描述

AVElement(AVDescription description, int flags)

构造AVElement实例。

getFlags()

获取flags的值。

isScannable()

判断媒体是否可扫描,如:媒体有子节点,则可继续扫描获取子节点内容。

isPlayable()

检查媒体是否可播放。

getAVDescription()

获取媒体的详细信息。

getMediaId()

获取媒体的id。

开发步骤

使用AVSession媒体框架创建一个播放器示例,分为创建客户端和创建服务端。

创建客户端

在客户端AVClientAbility中声明avBrowser和avController,通过avBrowser并向服务端发送连接请求,然后将avController注册到ability。

public class AVClientAbility extends Ability {
    // 媒体浏览器
    private AVBrowser avBrowser;
    // 媒体控制器
    private AVController avController;
    // 服务端回传的媒体列表
    List<AVElement> avElements;
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        // 用于指向媒体浏览器服务的包路径和类名
        ElementName elementName = new ElementName("", "com.samples.audioplayer", "com.samples.audioplayer.AVService");
        // connectionCallback在调用avBrowser.connect方法后进行回调。
        avBrowser = new AVBrowser(context, elementName, connectionCallback, null);
        // avBrowser发送对媒体浏览器服务的连接请求,connect方法需要确保当前处于断开连接状态。
        avBrowser.connect();
        // 将媒体控制器注册到ability以接收按键事件。
        AVController.setControllerForAbility(this, avController);
    }
}

AVConnectionCallback回调接口中的方法为可选实现,通常需要会在onConnected中订阅媒体数据和创建媒体控制器AVController。

// 发起连接(avBrowser.connect)后的回调方法实现
private AVConnectionCallback connectionCallback = new AVConnectionCallback() {
    @Override
    public void onConnected() {
        // 成功连接媒体浏览器服务时回调该方法,否则回调onConnectionFailed()。
        // 重复订阅会报错,所以先解除订阅。
        avBrowser.unsubscribeByParentMediaId(avBrowser.getRootMediaId());
        // 第二个参数AVSubscriptionCallback,用于处理订阅信息的回调。
        avBrowser.subscribeByParentMediaId(avBrowser.getRootMediaId(), avSubscriptionCallback);
        AVToken token = avBrowser.getAVToken();
        avController = new AVController(AVClient.this, token); // AVController第一个参数为当前类的context
        // 参数AVControllerCallback,用于处理服务端播放状态及信息变化时回调。
        avController.setAVControllerCallback(avControllerCallback);
        // ...
    }
    // 其它回调方法(可选)
    // ...
};

通常在订阅成功时,在AVSubscriptionCallback回调接口onAVElementListLoaded中保存服务端回传的媒体列表。

// 发起订阅信息(avBrowser.subscribeByParentMediaId)后的回调方法实现
private AVSubscriptionCallback avSubscriptionCallback = new AVSubscriptionCallback() {
    @Override
    public void onAVElementListLoaded(String parentId, List<AVElement> children) {
        // 订阅成功时回调该方法,parentID为标识,children为服务端回传的媒体列表
        super.onAVElementListLoaded(parentId, children);
        avElements.addAll(children);
        // ...
    }
};

AVControllerCallback回调接口中的方法均为可选方法,主要用于服务端播放状态及信息的变化后对客户端的回调,客户端可在这些方法中实现UI的刷新。

// 服务对客户端的媒体数据或播放状态变更后的回调 
private AVControllerCallback avControllerCallback = new AVControllerCallback() {
    @Override
    public void onAVMetadataChanged(AVMetadata metadata) {
        // 当服务端调用avSession.setAVMetadata(avMetadata)时,此方法会被回调。
        super.onAVMetadataChanged(metadata);
        AVDescription description = metadata.getAVDescription();
        String title = description.getTitle().toString();
        PixelMap pixelMap = description.getIcon();
        // ...
    }
    @Override
    public void onAVPlaybackStateChanged(AVPlaybackState playbackState) {
        // 当服务端调用avSession.setAVPlaybackState(...)时,此方法会被回调。
        super.onAVPlaybackStateChanged(playbackState);
        long position = playbackState.getCurrentPosition();
        // ...
    }
    // 其它回调方法(可选)
    // ...
};

完成以上实现后,则应用可以在UI事件中调用avController的方法向服务端发送播放控制指令。

// 在UI播放与暂停按钮的点击事件中向服务端发送播放或暂停指令
public void toPlayOrPause() {
    switch (avController.getAVPlaybackState().getAVPlaybackState()) {
        case AVPlaybackState.PLAYBACK_STATE_NONE: {
            avController.getPlayControls().prepareToPlay();
            avController.getPlayControls().play();
            break;
        }
        case AVPlaybackState.PLAYBACK_STATE_PLAYING: {
            avController.getPlayControls().pause();
            break;
        }
        case AVPlaybackState.PLAYBACK_STATE_PAUSED: {
            avController.getPlayControls().play();
            break;
        }
        default: {
            // ...
        }
    }
}

其它播放控制根据业务是否需要实现,比如:

avController.getPlayControls().playNext();
avController.getPlayControls().playPrevious();
avController.getPlayControls().playFastForward();
avController.getPlayControls().rewind();
avController.getPlayControls().seekTo(1000);
avController.getPlayControls().stop();
// ...

也可以主动获取媒体信息、播放状态等数据:

AVMetadata avMetadata = avController.getAVMetadata();
AVPlaybackState avPlaybackState = avController.getAVPlaybackState();
// ...

创建服务端

在服务端AVService中声明AVSession和Player。

public class AVService extends AVBrowserService {
    // 根媒体ID
    private static final String AV_ROOT_ID = "av_root_id";
    // 媒体会话
    private AVSession avSession;
    // 媒体播放器
    private Player player;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        avSession = new AVSession(this, "AVService");
        setAVToken(avSession.getAVToken());
        // 设置sessioncallback,用于响应客户端的媒体控制器发起的播放控制指令。
        avSession.setAVSessionCallback(avSessionCallback);
        // 设置播放状态初始状态为AVPlaybackState.PLAYBACK_STATE_NONE。
        AVPlaybackState playbackState = new AVPlaybackState.Builder().setAVPlaybackState(AVPlaybackState.PLAYBACK_STATE_NONE, 0, 1.0f).build();
        avSession.setAVPlaybackState(playbackState);
        // 完成播放器的初始化,如果使用多个Player,也可以在执行播放时初始化。
        player = new Player(this);
    }
    @Override
    public AVBrowserRoot onGetRoot(String clientPackageName, int clientUid, PacMap rootHints) {
        // 响应客户端avBrowser.connect()方法。若同意连接,则返回有效的AVBrowserRoot实例,否则返回null
        return new AVBrowserRoot(AV_ROOT_ID, null);
    }
    @Override
    public void onLoadAVElementList(String parentId, AVBrowserResult result) {
         HiLog.info(TAG, "onLoadChildren");
         // 响应客户端avBrowser.subscribeByParentMediaId(...)方法。
         // 先执行该方法detachForRetrieveAsync() 
         result.detachForRetrieveAsync();
         // externalAudioItems缓存媒体文件,请开发者自行实现。
         result.sendAVElementList(externalAudioItems.getAudioItems());
    }
    @Override
    public void onLoadAVElementList(String s, AVBrowserResult avBrowserResult, PacMap pacMap) {
         // 响应客户端avBrowser.subscribeByParentMediaId(String, PacMap, AVSubscriptionCallback)方法。
    }
    @Override
    public void onLoadAVElement(String s, AVBrowserResult avBrowserResult) {
        // 响应客户端avBrowser.getAVElement(String, AVElementCallback)方法。
    }
}

响应客户端的媒体控制器发起的播放控制指令的回调实现。

private AVSessionCallback avSessionCallback = new AVSessionCallback() {
    @Override
    public void onPlay() {
        super.onPlay();
        // 当客户端调用avController.getPlayControls().play()时,该方法会被回调。
        // 响应播放请求,开始播放。
        if (avSession.getAVController().getAVPlaybackState() == AVPlaybackState.PLAYBACK_STATE_PAUSED) {
            if (player.play()) {
                AVPlaybackState playbackState = new AVPlaybackState.Builder().setAVPlaybackState(
                    AVPlaybackState.PLAYBACK_STATE_PLAYING, player.getCurrentTime(),
                    player.getPlaybackSpeed()).build();
                avSession.setAVPlaybackState(playbackState);
            }
        }
    }
    @Override
    public void onPause() {
        super.onPause();
        // 当客户端调用avController.getPlayControls().pause()时,该方法会被回调。
        // 响应暂停请求,暂停播放。
    }
    @Override
    public void onPlayNext() {
        super.onPlayNext();
        // 当客户端调用avController.getPlayControls().playNext()时,该方法会被回调。
        // 响应播放下一曲请求,通过avSession.setAVMetadata设置下一曲曲目的信息。
        avSession.setAVMetadata(avNextMetadata);
    }
    // 重写以处理按键事件
    @Override
    public boolean onMediaButtonEvent(Intent mediaButtonIntent) {
        KeyEvent ke = mediaButtonIntent.getSequenceableParam(AVSession.PARAM_KEY_EVENT);
        if (ke == null) {
            HiLog.error(TAG, "getSequenceableParam failed");
            return false;
        }
        if (ke.isKeyDown()) {
            // 只处理按键抬起事件
            return true;
        }

        switch (ke.getKeyCode()) {
            case KeyEvent.KEY_MEDIA_PLAY_PAUSE: {
                if (playbackState.getAVPlaybackState() == AVPlaybackState.PLAYBACK_STATE_PAUSED) {
                    onPlay();
                    break;
                }
                if (playbackState.getAVPlaybackState() == AVPlaybackState.PLAYBACK_STATE_PLAYING) {
                    onPause();
                    break;
                }
                break;
            }
            case KeyEvent.KEY_MEDIA_PLAY: {
                onPlay();
                break;
            }
            case KeyEvent.KEY_MEDIA_PAUSE: {
                onPause();
                break;
            }
            case KeyEvent.KEY_MEDIA_STOP: {
                onStop();
                break;
            }
            case KeyEvent.KEY_MEDIA_NEXT: {
                onPlayNext();
                break;
            }
            case KeyEvent.KEY_MEDIA_PREVIOUS: {
                onPlayPrevious();
                break;
            }
            default: {
                break;
            }
        }
        return true;
    }
    // 其它回调方法(可选)
    // ...
}

Guess you like

Origin blog.csdn.net/weixin_47094733/article/details/131331658