You need to enable JavaScript to run this app.
最新活动
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们
导航

Android

最近更新时间2023.09.22 16:33:42

首次发布时间2022.07.18 21:22:43

本章节介绍 Android 端互动直播场景核心功能的实现方式。

前提条件

版本说明

  • 互动直播功能需要使用互动直播版 SDK,请您在安装 SDK 时选择正确的版本。
  • 以下接入内容对应互动直播版本为 v1.37.3。

主播端核心功能实现

以下是主播端核心功能实现的时序图和参考接入代码。

主播开播

主播通过 RTC 引擎和推流引擎开始直播推流。

时序图

示例代码

  • 创建 RTC 视频引擎,设置本地预览视图,设置视频编码参数。
// 初始化 RTCVideo 对象
mRTCVideo = RTCVideo.createRTCVideo(Env.getApplicationContext(), mAppId, mRTCVideoEventHandler, null, null);

// 设置本地视图
VideoCanvas videoCanvas = new VideoCanvas();
videoCanvas.renderView = renderView;
videoCanvas.renderMode = VideoCanvas.RENDER_MODE_HIDDEN;
mRTCVideo.setLocalVideoCanvas(StreamIndex.STREAM_INDEX_MAIN, videoCanvas);

// 设置视频编码参数
VideoEncoderConfig config = new VideoEncoderConfig(
        mConfig.mVideoEncoderWidth, mConfig.mVideoEncoderHeight, mConfig.mVideoEncoderFps, mConfig.mVideoEncoderKBitrate * 1000);
mRTCVideo.setVideoEncoderConfig(config);
  • 订阅 RTC 本地音视频数据。
// 订阅本地视频数据
mRTCVideo.setLocalVideoSink(StreamIndex.STREAM_INDEX_MAIN, mVideoFrameListener, IVideoSink.PixelFormat.I420);

// 订阅本地音频数据
mRTCVideo.enableAudioFrameCallback(AudioFrameCallbackMethod.AUDIO_FRAME_CALLBACK_RECORD,
        new AudioFormat(changeSampleRate(mConfig.mAudioCaptureSampleRate),
                changeChannel(mConfig.mAudioCaptureChannel)));
mRTCVideo.registerAudioFrameObserver(mAudioFrameListener);
  • 创建推流引擎,设置推流视频编码参数。
// 创建推流引擎
mLiveCoreBuilder = new LiveCoreBuilder();
mLiveCoreBuilder.setContext(Env.getApplicationContext());
mLiveCoreBuilder.setVideoProfile(LiveStreamBuilder.VIDEO_ENCODER_PROFILE_HIGH);
mLiveCoreBuilder.setEnableVideoBFrame(false); // 关闭B帧
mLiveCoreEngine = mLiveCoreBuilder.getLiveCoreEngine();
mLiveCore = mLiveCoreEngine.getLiveCore();

// 配置推流参数
mLiveCore.changeVideoResolution(mConfig.mVideoEncoderWidth, mConfig.mVideoEncoderHeight);
mLiveCore.changeVideoFps(mConfig.mVideoEncoderFps);
mLiveCore.changeVideoBitrate(bitrate, bitrate, bitrate);
mLiveCoreBuilder.setVideoEncoder(LiveStreamBuilder.VIDEO_ENCODER_AVC);
mLiveCoreBuilder.setEnableVideoEncodeAccelera(mConfig.mIsVideoHardwareEncoder);
mLiveCoreBuilder.setAudioSampleHZ(mConfig.mAudioEncoderSampleRate);
mLiveCoreBuilder.setAudioChannel(mConfig.mAudioEncoderChannel);
mLiveCoreBuilder.setVideoCaptureDevice(LiveStreamBuilder.VIDEO_CAPTURE_DEVICE_EXTERN);
mLiveCoreBuilder.setAudioCaptureDevice(LiveStreamBuilder.AUDIO_CAPTURE_DEVICE_EXTERN);
mLiveCore.startAudioCapture();
mLiveCore.startVideoCapture();
  • 开启 RTC 音视频采集。
// 开始视频采集
mRTCVideo.startVideoCapture();

// 开始音频采集
mRTCVideo.startAudioCapture();
  • 开启推流引擎推流。
// 开始推流,url 为 RTMP 推流地址
mLiveCore.start(url);
  • RTC 本地音视频回调数据发送给推流引擎。
// 视频采集回调, 发送视频数据给推流引擎
IVideoSink mVideoFrameListener = new IVideoSink() {
    @Override
    public void onFrame(com.ss.bytertc.engine.video.VideoFrame frame) {
        final int width = frame.getWidth();
        final int height = frame.getHeight();
        final int chromaHeight = (height + 1) / 2;
        final int chromaWidth = (width + 1) / 2;
        int bufferSize = width * height + chromaWidth * chromaHeight * 2;
        final ByteBuffer dstBuffer = ByteBuffer.allocateDirect(bufferSize);
        YuvHelper.I420Rotate(frame.getPlaneData(0), frame.getPlaneStride(0),
                             frame.getPlaneData(1), frame.getPlaneStride(1),
                             frame.getPlaneData(2), frame.getPlaneStride(2),
                             dstBuffer,width, height,frame.getRotation().value());

        dstBuffer.position(0);
        mLiveCore.pushVideoFrame(dstBuffer, frame.getHeight(), frame.getWidth(), 0, System.currentTimeMillis() * 1000);
    }
};

// 音频采集回调, 发送音频数据给推流引擎
mAudioFrameListener = new IAudioFrameObserver() {
    @Override
    public void onRecordAudioFrame(IAudioFrame audioFrame) {
        mLiveCore.pushAudioFrame(audioFrame.getDataBuffer(), audioFrame.sample_rate().value(), audioFrame.channel().value(),
                    16, audioFrame.data_size() / 2, TimeUtils.nanoTime() / 1000);
        audioFrame.release();
    }

};

主播美颜(可选)

主播美颜功能都是通过 RTC 引擎进行对接,使用方式请参考 美颜特效(CV)

主播上麦

主播停止推流引擎推流,通过 RTC 引擎加入房间连麦,并开启 RTC 服务端合流转推。

时序图

示例代码

  • 停止推流引擎推流。
// 停止推流引擎推流
mLiveCore.stop();
  • 创建 RTC 房间,设置用户信息,加入 RTC 房间。参考使用 Token 完成鉴权了解如何通过业务服务器获取鉴权 token。
// 创建 RTC 房间
mRTCRoom = mRTCVideo.createRTCRoom(roomId);
mRTCRoom.setRTCRoomEventHandler(mIRtcRoomEventHandler);

// 设置用户信息
mUserId = userId;
mRoomId = roomId;
UserInfo userInfo = new UserInfo(userId, null);
RTCRoomConfig roomConfig = new RTCRoomConfig(ChannelProfile.CHANNEL_PROFILE_COMMUNICATION,
        true, true, true);
        
// 加入房间,token 信息通过业务服务器申请
mRTCRoom.joinRoom(token, userInfo, roomConfig);
  • 收到加入 RTC 房间成功回调,开启 RTC 服务端合流转推。
// 加入房间成功通知
private RtcRoomEventHandlerAdapter mIRtcRoomEventHandler = new RtcRoomEventHandlerAdapter() {
    @Override
    public void onRoomStateChanged(String roomId, String uid, int state, String extraInfo) {
        // 创建 RTC 服务端合流配置
        mLiveTranscoding = LiveTranscoding.getDefualtLiveTranscode();
        mLiveTranscoding.setRoomId(mRoomId);
        mLiveTranscoding.setUserId(mUserId);
        
        // 服务端合流
        mLiveTranscoding.setMixType(STREAM_MIXING_BY_SERVER);
        
        // 设置推流地址, 这里为主播的 RTMP 推流地址
        mLiveTranscoding.setUrl(mPushUrl);
        
        // 设置视频编码参数。该参数需要和推流视频编码参数保持一致
        LiveTranscoding.VideoConfig videoConfig = mLiveTranscoding.getVideo();
        // 分辨率宽
        videoConfig.setWidth(mConfig.mVideoEncoderWidth);
        // 分辨率高
        videoConfig.setHeight(mConfig.mVideoEncoderHeight);
        // fps
        videoConfig.setFps(mConfig.mVideoEncoderFps);
        // 比特率
        videoConfig.setKBitRate(mConfig.mVideoEncoderKBitrate);
        mLiveTranscoding.setVideo(videoConfig);
        
        // 设置音频编码参数。该参数需要和推流音频编码参数保持一致
        LiveTranscoding.AudioConfig audioConfig = mLiveTranscoding.getAudio();
        // 音频采样率
        audioConfig.setSampleRate(mConfig.mAudioEncoderSampleRate);
        // 通道数
        audioConfig.setChannels(mConfig.mAudioEncoderChannel);
        // 比特率 k
        audioConfig.setKBitRate(mConfig.mAudioEncoderKBitrate);
        // 配置音频参数
        mLiveTranscoding.setAudio(audioConfig);
        
        LiveTranscoding.Layout.Builder builder = new LiveTranscoding.Layout.Builder();
        // 主播合流布局
        LiveTranscoding.Region region = new LiveTranscoding.Region();
        region.uid(mUserId); // 主播uid
        region.roomId(mRoomId);
        region.setLocalUser(true);
        region.position(0, 0); //仅供参考
        region.size(0.5, 0.5); //仅供参考
        region.alpha(1.0); //仅供参考
        region.zorder(0); //仅供参考
        region.renderMode(LiveTranscoding.TranscoderRenderMode.RENDER_HIDDEN);
        
        builder.addRegion(region);
        LiveTranscoding.Layout layout = builder.builder();
                
        // 设置合流模版
        mLiveTranscoding.setLayout(layout);
        
        // 设置合流任务 Id
        String taskId = "";
    
        // 开始合流转推
        mRTCVideo.startLiveTranscoding(taskId, mLiveTranscoding, mILiveTranscodingObserver); 
    }
}
  • 收到房间内连麦用户的音视频流发布通知后,调整用户视图以及合流布局。
private RtcRoomEventHandlerAdapter mIRtcRoomEventHandler = new RtcRoomEventHandlerAdapter() {
    @Override
    public void onUserPublishStream(String uid, MediaStreamType type) {
        if (type == RTC_MEDIA_STREAM_TYPE_VIDEO || type == RTC_MEDIA_STREAM_TYPE_BOTH) {
            // 添加连麦用户视图
            TextureView renderView = new TextureView(Env.getApplicationContext());
            VideoCanvas canvas = new VideoCanvas(renderView, RENDER_MODE_HIDDEN, mRoomId, uid, false);
            mRTCVideo.setRemoteVideoCanvas(uid, StreamIndex.STREAM_INDEX_MAIN, canvas);
        }
        
        LiveTranscoding.Layout.Builder builder = new LiveTranscoding.Layout.Builder();
        
        // 主播合流布局
        LiveTranscoding.Region region = new LiveTranscoding.Region();
        region.uid(mUserId); // 主播uid
        region.roomId(mRoomId);
        region.setLocalUser(true);
        region.position(0, 0); //仅供参考
        region.size(0.5, 0.5); //仅供参考
        region.alpha(1.0); //仅供参考
        region.zorder(0); //仅供参考
        region.renderMode(LiveTranscoding.TranscoderRenderMode.RENDER_HIDDEN);
        builder.addRegion(region);
        
        // 连麦用户合流布局
        LiveTranscoding.Region regionRemote = new LiveTranscoding.Region();
        regionRemote.uid(uid); // 连麦uid
        regionRemote.roomId(mRoomId);
        regionRemote.setLocalUser(false);
        regionRemote.position(0.5, 0.5); //仅供参考
        regionRemote.size(0.5, 0.5); //仅供参考
        regionRemote.alpha(1.0); //仅供参考
        regionRemote.zorder(1); //仅供参考
        regionRemote.renderMode(LiveTranscoding.TranscoderRenderMode.RENDER_HIDDEN);
        builder.addRegion(regionRemote);
          
        // 设置合流模版
        LiveTranscoding.Layout layout = builder.builder();
        mLiveTranscoding.setLayout(layout);
        
         // 设置合流任务Id,Id 为上一步设置的合流任务 Id
        String taskId = "";
        
        // 更新合流
        mRTCVideo.updateLiveTranscoding(taskId, mLiveTranscoding);
            
    }

    @Override
    public void onUserUnpublishStream(String uid, MediaStreamType type, StreamRemoveReason reason) {
        if (type == RTC_MEDIA_STREAM_TYPE_VIDEO || type == RTC_MEDIA_STREAM_TYPE_BOTH) {
            // 移除连麦用户视图
            VideoCanvas canvas = new VideoCanvas(null, RENDER_MODE_HIDDEN, mRoomId, uid, false);
            mRTCVideo.setRemoteVideoCanvas(uid, StreamIndex.STREAM_INDEX_MAIN, canvas);
        }
        
        LiveTranscoding.Layout.Builder builder = new LiveTranscoding.Layout.Builder();
        // 只保留主播合流布局
        LiveTranscoding.Region region = new LiveTranscoding.Region();
        region.uid(mUserId); // 主播uid
        region.roomId(mRoomId);
        region.setLocalUser(true);
        region.position(0, 0); //仅供参考
        region.size(0.5, 0.5); //仅供参考
        region.alpha(1.0); //仅供参考
        region.zorder(0); //仅供参考
        region.renderMode(LiveTranscoding.TranscoderRenderMode.RENDER_HIDDEN);
        
        builder.addRegion(region);
        LiveTranscoding.Layout layout = builder.builder();
                
        // 设置合流模版
        mLiveTranscoding.setLayout(layout);
        
        // 设置合流任务Id,Id 为上一步设置的合流任务 Id
        String taskId = "";
        
        // 更新合流
        mRTCVideo.updateLiveTranscoding(taskId, mLiveTranscoding);
    }
};

主播下麦

主播停止 RTC 服务端合流转推,离开 RTC 房间,开启推流引擎推流。

时序图

示例代码

  • 停止 RTC 服务端合流,离开房间,移除连麦用户视图。
// 停止 RTC 服务端合流转推
mRTCVideo.stopLiveTranscoding(taskId);

// 离开 RTC 房间
mRTCRoom.leaveRoom();

// 移除连麦用户视图
VideoCanvas canvas = new VideoCanvas(null, RENDER_MODE_HIDDEN, mRoomId, uid, false);
mRTCVideo.setRemoteVideoCanvas(uid, StreamIndex.STREAM_INDEX_MAIN, canvas);
  • 开启推流引擎推流。
// 开始推流引擎推流
mLiveCore.start(url);

主播停播

主播停止直播,销毁 RTC 引擎和推流引擎。

时序图

示例代码

  • 停止推流引擎推流,销毁推流引擎。
// 停止推流引擎推流
mLiveCore.stop();

// 销毁推流引擎
mLiveCoreEngine.release();
mLiveCoreEngine = null;
mLiveCore = null;
  • 停止 RTC 音视频采集,移除本地预览视图。
// 停止 RTC 视频采集
mRTCVideo.stopVideoCapture();

// 停止 RTC 音频采集
mRTCVideo.stopAudioCapture();

// 移除 RTC 本地预览视图
VideoCanvas videoCanvas = new VideoCanvas();
videoCanvas.renderView = NULL;
videoCanvas.renderMode = VideoCanvas.RENDER_MODE_HIDDEN;
mRTCVideo.setLocalVideoCanvas(StreamIndex.STREAM_INDEX_MAIN, videoCanvas);
  • 销毁 RTC 房间,销毁 RTC 视频引擎。
// 销毁 RTC 房间
mRTCRoom.destroy();
mRTCRoom = null;

// 销毁 RTC 视频引擎
RTCVideo.destroyRTCVideo();
mRTCVideo = null;

观众端核心功能实现

以下是观众端核心功能实现的时序图和参考接入代码。

观众拉流

观众端通过播放器拉流观看直播。

时序图

示例代码

  • 创建和设置播放器。
// 创建播放器
VeLivePlayer mLivePlayer = new VideoLiveManager(Env.getApplicationContext());

// 设置播放器回调
mLivePlayer.setObserver(mLivePlayerObserver);

// 播放器参数设置
VeLivePlayerConfiguration config = new VeLivePlayerConfiguration();
config.enableStatisticsCallback = true;
config.enableLiveDNS = true;
mLivePlayer.setConfig(config);
  • 设置播放器视图,设置直播地址,开启拉流播放。
// 设置播放器视图
mLivePlayer.setSurfaceHolder(holder);

// 设置播放地址
mLivePlayer.setPlayUrl(mPullUrl);

// 开始播放
mLivePlayer.play();

观众上麦

连麦观众停止播放器拉流播放,通过 RTC 引擎进行连麦。

时序图

示例代码

  • 停止播放,移除播放器视图。
// 停止播放
mLivePlayer.stop();

// 移除播放器视图
mLocalVideoContainer.removeView(mPlayerView);
  • 创建 RTC 视频引擎,设置本地预览视图,设置视频编码参数。
// 初始化 ByteRTCVideo 对象
mRTCVideo = RTCVideo.createRTCVideo(Env.getApplicationContext(), mAppId, mRTCVideoEventHandler, null, null);

// 设置本地视图
VideoCanvas videoCanvas = new VideoCanvas();
videoCanvas.renderView = renderView;
videoCanvas.renderMode = VideoCanvas.RENDER_MODE_HIDDEN;
mRTCVideo.setLocalVideoCanvas(StreamIndex.STREAM_INDEX_MAIN, videoCanvas);

// 设置视频编码参数
VideoEncoderConfig config = new VideoEncoderConfig(
        mConfig.mVideoEncoderWidth, mConfig.mVideoEncoderHeight, mConfig.mVideoEncoderFps, mConfig.mVideoEncoderKBitrate * 1000);
mRTCVideo.setVideoEncoderConfig(config);
  • 开启 RTC 音视频采集。
// 开始视频采集
mRTCVideo.startVideoCapture();

// 开始音频采集
mRTCVideo.startAudioCapture();
  • 创建 RTC 房间,设置用户信息,加入 RTC 房间。
// 创建 RTC 房间
mRTCRoom = mRTCVideo.createRTCRoom(roomId);
mRTCRoom.setRTCRoomEventHandler(mIRtcRoomEventHandler);

 // 设置用户信息
UserInfo userInfo = new UserInfo(userId, null);
RTCRoomConfig roomConfig = new RTCRoomConfig(ChannelProfile.CHANNEL_PROFILE_COMMUNICATION,
        true, true, true);
    
// 加入房间,token 信息通过业务服务器申请
mRTCRoom.joinRoom(token, userInfo, roomConfig);
  • 收到房间内连麦用户的音视频流发布通知后,调整用户视图。
private RtcRoomEventHandlerAdapter mIRtcRoomEventHandler = new RtcRoomEventHandlerAdapter() {
    @Override
    public void onUserPublishStream(String uid, MediaStreamType type) {
        if (type == RTC_MEDIA_STREAM_TYPE_VIDEO || type == RTC_MEDIA_STREAM_TYPE_BOTH) {
            // 添加连麦用户视图
            TextureView renderView = new TextureView(Env.getApplicationContext());
            VideoCanvas canvas = new VideoCanvas(renderView, RENDER_MODE_HIDDEN, mRoomId, uid, false);
            mRTCVideo.setRemoteVideoCanvas(uid, StreamIndex.STREAM_INDEX_MAIN, canvas);
        }          
    }

    @Override
    public void onUserUnpublishStream(String uid, MediaStreamType type, StreamRemoveReason reason) {
        if (type == RTC_MEDIA_STREAM_TYPE_VIDEO || type == RTC_MEDIA_STREAM_TYPE_BOTH) {
            // 移除连麦用户视图
            VideoCanvas canvas = new VideoCanvas(null, RENDER_MODE_HIDDEN, mRoomId, uid, false);
            mRTCVideo.setRemoteVideoCanvas(uid, StreamIndex.STREAM_INDEX_MAIN, canvas);
        }
    }
};

观众美颜(可选)

观众连麦美时颜功能都是通过 RTC 引擎进行对接,使用方式请参考 美颜特效(CV)

观众下麦

观众停止 RTC 引擎连麦,恢复拉流观看直播。

时序图

示例代码

  • 离开 RTC 房间,停止 RTC 音视频采集,移除 RTC 本地和远端视图。
// 离开 RTC 房间
mRTCRoom.leaveRoom();

// 停止视频采集
mRTCVideo.stopVideoCapture();

// 停止音频采集
mRTCVideo.stopAudioCapture();

// 移除本地用户视图
VideoCanvas canvas = new VideoCanvas(null, RENDER_MODE_HIDDEN, mRoomId, mUserId, false);
mRTCVideo.setLocalVideoCanvas(mUserId, StreamIndex.STREAM_INDEX_MAIN, canvas);

// 移除连麦用户视图
VideoCanvas canvas = new VideoCanvas(null, RENDER_MODE_HIDDEN, mRoomId, uid, false);
mRTCVideo.setRemoteVideoCanvas(uid, StreamIndex.STREAM_INDEX_MAIN, canvas);
  • 设置播放器视图,启动拉流播放。
// 设置播放器视图
mLivePlayer.setSurfaceHolder(holder);
// 开始播放
mLivePlayer.play();

观众离开

观众停止观看直播,销毁播放器。

时序图

示例代码

  • 停止播放,移除播放器视图,销毁播放器。
// 停止播放
mLivePlayer.stop();

// 移除播放器视图
mLocalVideoContainer.removeView(mPlayerView);

// 销毁播放器
mLivePlayer.destroy();
mLivePlayer = null;
  • 销毁 RTC 房间,销毁 RTC 引擎。
// 销毁 RTC 房间
mRTCRoom.destroy();
mRTCRoom = null;

// 销毁 RTC 引擎
RTCVideo.destroyRTCVideo();
mRTCVideo = null;