This topic describes how to integrate Native RTS SDK for Android with ApsaraVideo Player SDK to use the Real-Time Streaming (RTS) feature.

SDK integration

You can use the following methods to add the dependencies of ApsaraVideo Player SDK and Native RTS SDK.

  • Method 1: Use Maven
    1. Add the dependencies of ApsaraVideo Player SDK and Native RTS SDK to the app/build.gradle file of your project.
      implementation 'com.aliyun.sdk.android:AlivcArtc:5.4.4.0'
      implementation 'com.aliyun.rts.android:RtsSDK:2.1.0'
      implementation 'com.aliyun.sdk.android:AliyunPlayer:5.4.4.0-full'
      Note
      • After you add the AlivcArtc and RtsSDK dependencies, ApsaraVideo Player SDK automatically loads Native RTS SDK as a plug-in.
      • The SDK versions specified in the preceding Maven dependency code are only for reference. For more information about the latest versions of ApsaraVideo Player SDK, see Release notes for ApsaraVideo Player SDK for Android. For more information about the latest versions of RTS SDKs, see Download SDKs.
      • Make sure that the requirements are met when you integrate Native RTS SDK with ApsaraVideo Player SDK. For more information, see Release notes.
    2. Add the URLs of Maven repositories.

      Add the URLs of Maven repositories to the build.gradle file in the root directory.

      // The URL of the Maven repository for MPChart. If you do not need MPChart, you can delete this line of code.
      maven { url 'https://jitpack.io' }
      // The URL of the Maven repository for ApsaraVideo Player SDK and RTS SDK.
      maven { url 'http://maven.aliyun.com/nexus/content/repositories/releases' }
  • Method 2: Manually add AAR files
    1. Copy the corresponding .aar files to the app/libs directory. AAR files
    2. Add the following code to the app/build.gradle file:
      dependencies{
          implementation fileTree(dir: 'libs', include: ['*.aar'])
      }

Call methods of ApsaraVideo Player SDK

Call methods of ApsaraVideo Player SDK to use the RTS feature. For more information about the features of ApsaraVideo Player SDK, see Advanced features and API reference.

Note
  • The following sample code provides an example. For more information about the complete code, visit the Open-source project page.
  • When you perform stream pulling over RTS based on ApsaraVideo Player, you cannot call the pause method to pause a live stream. You can call the stop method to stop playback of a live stream, and then call the prepare method to play the live stream again.
  • Seeking is not supported.
  • Load the library

    Add System.loadLibrary("RtsSDK"); to the required activity based on your business requirements.

    static {
        System.loadLibrary("RtsSDK");
    }
  • Create a player

    Use the AliPlayerFactory class to create a player. You can create the following two types of players: AliPlayer and AliListPlayer. If you want to play a single stream, use AliPlayer. You can run the following code to create an AliPlayer:

    AliPlayer aliyunVodPlayer;
    .....
    aliyunVodPlayer = AliPlayerFactory.createAliPlayer(getApplicationContext());
  • Set player listeners

    ApsaraVideo Player SDK provides a variety of listeners, such as onPrepared and onCompletion. You can run the following code to set listeners:

    aliyunVodPlayer.setOnErrorListener(new IPlayer.OnErrorListener() {
        @Override
        public void onError(ErrorInfo errorInfo) {
            // Listens to the occurrence of errors.
        }
    });
    aliyunVodPlayer.setOnPreparedListener(new IPlayer.OnPreparedListener() {
        @Override
        public void onPrepared() {
            // Listens to successful preparation.
        }
    });
    aliyunVodPlayer.setOnVideoSizeChangedListener(new IPlayer.OnVideoSizeChangedListener() {
        @Override
        public void onVideoSizeChanged(int width, int height) {
            // Listens to the change of video resolution.
        }
    });
    aliyunVodPlayer.setOnRenderingStartListener(new IPlayer.OnRenderingStartListener() {
        @Override
        public void onRenderingStart() {
            // Listens to the display of the first frame.
        }
    });
    aliyunVodPlayer.setOnInfoListener(new IPlayer.OnInfoListener() {
        @Override
        public void onInfo(int type, long extra) {
            // Listens to other events, such as the start of loop playback, the buffer position, the current playback position, and the start of autoplay.
        }
    });
    aliyunVodPlayer.setOnLoadingStatusListener(new IPlayer.OnLoadingStatusListener() {
        @Override
        public void onLoadingBegin() {
            // Listens to the start of buffering.
        }
        @Override
        public void onLoadingProgress(int percent, float kbps) {
            // Listens to the buffering progress.
        }
        @Override
        public void onLoadingEnd() {
            // Listens to the end of buffering.
        }
    });
    aliyunVodPlayer.setOnSubtitleDisplayListener(new IPlayer.OnSubtitleDisplayListener() {
        @Override
        public void onSubtitleShow(long id, String data) {
            // Listens to the display of subtitles.
        }
        @Override
        public void onSubtitleHide(long id) {
            // Listens to the hiding of subtitles.
        }
    });
    aliyunVodPlayer.setOnTrackChangedListener(new IPlayer.OnTrackChangedListener() {
        @Override
        public void onChangedSuccess(TrackInfo trackInfo) {
            // Listens to the success of switching between audio and video streams or between resolutions.
        }
        @Override
        public void onChangedFail(TrackInfo trackInfo, ErrorInfo errorInfo) {
            // Listens to the failure of switching between audio and video streams or between resolutions.
        }
    });
    aliyunVodPlayer.setOnStateChangedListener(new IPlayer.OnStateChangedListener() {
        @Override
        public void onStateChanged(int newState) {
            // Listens to the change of player status.
        }
    });
    aliyunVodPlayer.setOnSnapShotListener(new IPlayer.OnSnapShotListener() {
        @Override
        public void onSnapShot(Bitmap bm, int with, int height) {
            // Listens to the capture of snapshots.
        }
    });
  • Configure a playback source

    ApsaraVideo Player supports four types of playback sources: VidSts, VidAuth, VidMps, and UrlSource. UrlSource is used for URL-based playback. If you use UrlSource, you must prefix URLs with artc:// to use the RTS feature.

    UrlSource urlSource = new UrlSource();
    urlSource.setUri("artc://<Streaming URL>");
    aliyunVodPlayer.setDataSource(urlSource);
  • Set the user interface (UI) view

    If the playback source contains video images, you must set the UI view to display the video images. SurfaceView and TextureView are supported. Sample code for SurfaceView:

    surfaceView = (SurfaceView) findViewById(R.id.playview);
    surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            aliyunVodPlayer.setDisplay(holder);
        }
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            aliyunVodPlayer.surfaceChanged();
        }
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            aliyunVodPlayer.setDisplay(null);
        }
    });
  • Set playback control

    You must create playback control buttons and associate click events with playback control methods to implement playback control. The basic control functions include playing and stopping. Sample code:

    // Prepare for playback. You can specify the start() method in the callback so that the playback starts upon the completion of preparation.
    aliyunVodPlayer.prepare();
    // Start playback.
    aliyunVodPlayer.start();
    // Stop playback.
    aliyunVodPlayer.stop();
    // Reset the player.
    aliyunVodPlayer.reset();
    // Release the player. The player cannot be used after it is released.
    aliyunVodPlayer.release();
  • Set playback parameters

    You can set playback parameters to improve the RTS effect.

    // Query the configurations.
    PlayerConfig config = mAliyunVodPlayer.getConfig();
    // Set the maximum latency to 1 second. The Alibaba Real-Time Communication (ARTC) protocol controls the latency.
    config.mMaxDelayTime = 1000;
    // Set the buffer period to 10 milliseconds. The ARTC protocol controls the buffer period. 
    config.mHighBufferDuration = 10;
    config.mStartBufferDuration = 10;
    .... // Configure other settings.
    // Load the settings for the player.
    mAliyunVodPlayer.setConfig(config);
  • Enable or disable logging

    // Enable logging.
    Logger.getInstance(context).enableConsoleLog(true);
    Logger.getInstance(context).setLogLevel(Logger.LogLevel.AF_LOG_LEVEL_TRACE);
    // Disable logging.
    Logger.getInstance(context).enableConsoleLog(false);
    Logger.getInstance(context).setLogLevel(Logger.LogLevel.AF_LOG_LEVEL_NONE);
  • Use a degraded protocol for live streaming

    You can change the prefix of a source URL from artc:// to rtmp://, and then update the UrlSource in ApsaraVideo Player. This way, you can use a degraded protocol to play live streams.

    /*
        Degradation rules:
            1. Listen to the player callbacks and parse the RTS message events.
            2. Determine whether to use a degraded protocol for live streaming based on the RTS message events.
    */
    
    String mUrl = "artc://xxx";
    
    // The following code lists the RTS message events that are included in the custom enum class.
    public enum RtsError {
            /**
             * DNS resolution failed.
             */
            E_DNS_FAIL(20001),
            /**
             * Authentication failed.
             */
            E_AUTH_FAIL(20002),
            /**
             * Connection signaling timed out.
             */
            E_CONN_TIMEOUT(20011),
            /**
             * An error occurred during subscription signaling, or subscription signaling timed out. 
             */
            E_SUB_TIMEOUT(20012),
            /**
             * The stream to which you subscribe does not exist.
             */
            E_SUB_NO_STREAM(20013),
            /**
             * The media timed out and no audio and video packets were received.
             */
            E_STREAM_BROKEN(20052),
            /**
             * The stop signaling message was received.
             */
            E_RECV_STOP_SIGNAL(20061);
    
            private int code;
    
            RtsError(int code) {
                this.code = code;
            }
    
            public int getCode() {
                return code;
            }
    
            public void setCode(int code) {
                this.code = code;
            }
    }
    
    // Set the onInfo listener and parse RTS message events.
    mRtsAliPlayer.setOnInfoListener(infoBean -> {
        if (infoBean.getCode() == InfoCode.DirectComponentMSG) {
            String extraMsg = infoBean.getExtraMsg();
            parseDirectComponentMSG(extraMsg);
        }
    });
    // Listen to the status of the player.
    mRtsAliPlayer.setOnStateChangedListener((newState) -> {
        mCurrentPlayerState = newState;
    });
    
    /**
     * Parse RTS events.
     */
    private void parseDirectComponentMSG(String msg) {
        if (msg.contains("code=" + RtsError.E_DNS_FAIL.getCode()) || msg.contains("code=" + RtsError.E_AUTH_FAIL.getCode()) || msg.contains("code=" + RtsError.E_CONN_TIMEOUT.getCode()) || msg.contains("code=" + RtsError.E_SUB_TIMEOUT.getCode()) || msg.contains("code=" + RtsError.E_SUB_NO_STREAM.getCode()) || msg.contains("code=" + RtsError.E_STREAM_BROKEN.getCode()) || msg.contains("code=" + RtsError.E_RECV_STOP_SIGNAL.getCode())) {
            // Degradation occurs when the player is not playing.
            if (mCurrentPlayerState != IPlayer.started) {
                willBeDemoted();
            } else {
                // During playback, the STREAM_BROKEN event is received. Playback is retried only once.
                if (msg.contains("code=" + RtsError.E_STREAM_BROKEN.getCode()) && mEnableRetry) {
                    // Start retry.
                    mRtsAliPlayer.prepare();
                    mEnableRetry = false;
                } else {
                    parseError(msg);
                    // Degradation occurs only if failure events other than the stop signaling are received.
                    if (!msg.contains("code=" + RtsError.E_RECV_STOP_SIGNAL.getCode())) {
                        willBeDemoted();
                    }
                }
            }
        }
    }
    
    /**
     * Configure the degradation policy.
     */
    private void willBeDemoted() {
        mRtsAliPlayer.stop();
        if (mUrl.startsWith("artc://")) {
            setDataSource(mUrl.replace("artc://", "rtmp://"));
            mRtsAliPlayer.prepare();
        }
    }
    
    /**
     * Set a playback source.
     */
    public void setDataSource(String url) {
        this.mUrl = url;
        UrlSource urlSource = new UrlSource();
        urlSource.setUri(mUrl);
        PlayerConfig config = mRtsAliPlayer.getConfig();
        // Configure the player.
        if (mUrl.startsWith("artc://")) {
            config.mMaxDelayTime = 1000;
            config.mHighBufferDuration = 10;
            config.mStartBufferDuration = 10;
        } else {
            config.mMaxDelayTime = 10000;
            config.mHighBufferDuration = 100;
            config.mStartBufferDuration = 100;
        }
        mRtsAliPlayer.setConfig(config);
        mRtsAliPlayer.setDataSource(urlSource);
    }
  • Obtain the TraceId

    When a message that is passed through by the playback component is returned in a player event callback, parse the JSON string that describes the player event. The code that you obtain is the message in RTS SDK. When you receive E_HELP_SUPPORT_ID_SUBSCRIBE, parse the part of the string after "-sub-" to obtain the TraceId.

    private static final int TRACE_ID_CODE = 104;
    
    // 1. Listen to the onInfo callback and parse the RTS event message.
    mRtsAliPlayer.setOnInfoListener(infoBean -> {
        if (infoBean.getCode() == InfoCode.DirectComponentMSG) {
            String extraMsg = infoBean.getExtraMsg();
            parseDirectComponentMSG(extraMsg);
        }
    });
    
    private void parseDirectComponentMSG(String msg) {
        if (msg.contains("code=" + TRACE_ID_CODE)) {
            parseTraceId(msg);
        }
    }
    
    /**
     * Parse the TraceId.
     */
    private void parseTraceId(String msg) {
        String[] split = msg.split("-sub-");
        if (split.length >= 1) {
            mTraceId = "RequestId:" + (split[1].substring(0, split[1].length() - 1));
            mTraceId = mTraceId.replace("\"", "").replace("\\", "");
        }
    }