最近更新时间:2023.09.22 16:33:42
首次发布时间:2022.07.18 21:22:43
本章节介绍 Android 端互动直播场景核心功能的实现方式。
以下是主播端核心功能实现的时序图和参考接入代码。
主播通过 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);
// 订阅本地视频数据 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();
// 开始视频采集 mRTCVideo.startVideoCapture(); // 开始音频采集 mRTCVideo.startAudioCapture();
// 开始推流,url 为 RTMP 推流地址 mLiveCore.start(url);
// 视频采集回调, 发送视频数据给推流引擎 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 房间 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);
// 加入房间成功通知 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 服务端合流转推 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 视频采集 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 房间 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);
// 初始化 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);
// 开始视频采集 mRTCVideo.startVideoCapture(); // 开始音频采集 mRTCVideo.startAudioCapture();
// 创建 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 房间 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 房间 mRTCRoom.destroy(); mRTCRoom = null; // 销毁 RTC 引擎 RTCVideo.destroyRTCVideo(); mRTCVideo = null;