1.概述

提供云课堂基础SDK功能,包括推流,拉流,排麦组件,聊天以及白板组件。为用户提供快速,简便的方法开展自己的实时互动课堂。

1.1 功能特性

安卓端SDK目前支持了音视频sdk、白板插件、聊天插件以及排麦插件

1.1.1 音视频SDK功能

功能特性 描述 备注
推流 支持推流到服务器
拉流 支持从服务器订阅流
获取流状态 支持获取流的状态(发报数、收报数、丢包数、延时)
前后摄像头切换 支持手机前后摄像头切换
后台播放 支持直播退到后台只播放音频
支持https协议 支持接口https请求

1.1.2 白板插件功能

功能特性 描述 备注
文档翻页 支持接收服务端的文档翻页数据
PPT动画 支持接收服务器的PPT动画数据
画笔功能 支持画笔、清除、撤销、历史数据
授权标注功能 学生被授权,支持画笔功能
设为讲师功能 学生被设为讲师,支持画笔、清除、翻页ppt

1.1.3 聊天插件功能

功能特性 描述 备注
文本、表情发送 支持接收服务端的文本和表情数据
图片发送 支持接收服务器的图片数据
禁言 分为指定用户的禁言,以及全体禁言

1.1.4 排麦插件功能

功能特性 描述 备注
自由连麦 互动者可自由连麦,无需老师确认
自动连麦 互动者进入房间后自动连麦
举手连麦 互动者可举手申请连麦,需老师确认才可连麦

1.2 阅读对象

本文档为技术文档,需要阅读者:

具备基本的Android开发能力

准备接入CC视频的基础SDK相关功能

CC基础版本SDK,为了使用硬件媒体编解码器,建议API级别19以上。

2.开发准备

2.1 开发环境

Android Studio : Android 开发IDE

Android SDK : Android 官方SDK

2.2 混淆配置

ccclassroom-base.jar已经混淆过,如果需要对应用进行混淆,需要在混淆的配置文件增加如下代码,以防止SDK的二次混淆:

-keep public class com.bokecc.sskt.base.**{*;}
-keep public interface com.bokecc.sskt.base.**{*;}
-keep public class com.intel.webrtc.base.**{*;}
-keep public interface com.intel.webrtc.base.**{*;}
-keep public class com.intel.webrtc.conference.**{*;}
-keep public interface com.intel.webrtc.conference.**{*;}
-keep public class org.webrtc.**{*;}
-keep public interface org.webrtc.**{*;}

3.快速集成

注:快速集成主要提供的是推流和拉流的功能(核心功能)。白板、聊天以及排麦组件另有开发文档描述。

首先,需要下载最新版本的SDK,下载地址为:

3.1 导入jar

名称 描述
ccclassroom-base.jar CC音视频核心jar包
ccclassroom-docmodule.jar CC白板插件核心jar包
ccclassroom-chatmodule.jar CC聊天插件核心jar包
ccclassroom-barleymodule.jar CC排麦插件核心jar包

3.2 导入so

名称 描述
libjingle_peerconnection.so CC连麦依赖native库

3.3 配置依赖库

修改 build.gradle,打开您的工程目录下的 build.gradle,确保已经添加了如下依赖:

compile('io.socket:socket.io-client:0.8.3') {
        exclude group: 'org.json', module: 'json'
    }
compile 'com.squareup.okhttp3:okhttp:3.8.1'
compile files('libs/ccclassroom-base.jar')
compile files('libs/ccclassroom-docmodule.jar')
compile files('libs/ccclassroom-chatmodule.jar')
compile files('libs/ccclassroom-barleymodule.jar')

3.4初始化渲染器以及布局控件

预览展示控件和订阅展示控件:

<android.support.constraint.ConstraintLayout
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="ccsskt.bokecc.base.example.MainActivity">

        <LinearLayout
            android:id="@+id/id_local_container"
            android:layout_width="0dp"
            android:layout_height="270dp"
            android:orientation="vertical"
            android:gravity="center"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_weight="1"/>

        <LinearLayout
            android:id="@+id/id_remote_mix_container"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:orientation="vertical"
            android:gravity="center"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/id_local_container"
            app:layout_constraintVertical_weight="1"/>
<android.support.constraint.ConstraintLayout

初始化渲染器:

 private void initRenderer() {
        mLocalRenderer = new CCSurfaceRenderer(this);
        mLocalRenderer.init(mAtlasClient.getEglBase().getEglBaseContext(), null);
        mLocalRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
        mLocalContainer.addView(mLocalRenderer);

        mRemoteMixRenderer = new CCSurfaceRenderer(this);
        mRemoteMixRenderer.init(mAtlasClient.getEglBase().getEglBaseContext(), null);
        mRemoteMixRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
        mRemoteMixContainer.addView(mRemoteMixRenderer);
    }

3.5 创建SDK实例

创建音视频sdk实例以及流服务的监听:

 mAtlasClient = CCAtlasClient.getInstance();
 mAtlasClient.setOnNotifyStreamListener(mClientObserver);

系统代理回调:

  private CCAtlasClient.AtlasClientObserver mClientObserver = new CCAtlasClient.AtlasClientObserver() {
        @Override
        public void onServerDisconnected() {
            android.os.Process.killProcess(android.os.Process.myPid());
        }

        @Override
        public void onStreamAdded(CCStream stream) {
            if (stream.isRemoteIsLocal()) { // 不订阅自己的本地流
                return;
            }
            Log.e(TAG, "onStreamAdded: [ " + stream.getStreamId() + " ]");
            if (stream.getStreamType() == CCStream.REMOTE_MIX) { // 不订阅混合流         
               return;
            }
            mStream = stream;
        }

        @Override
        public void onStreamRemoved(CCStream stream) {
            Log.e(TAG, "onStreamRemoved: [ " + stream.getStreamId() + " ]");
            if (stream.getStreamType() != CCStream.REMOTE_MIX) {
                mStream = null;
                mAtlasClient.unSubscribeStream(stream, null);
            }
        }

        @Override
        public void onStreamError(String streamid, String errorMsg) {

        }
    };

创建本地流:

  private void createLocalStream() {
        try {
            ccAtlasClient.setCameraType(LocalCameraStreamParameters.CameraType.FRONT);
            ccAtlasClient.createLocalStream(ccAtlasClient.getMediaMode());
        } catch (StreamException e) {
            showToast(e.getMessage());
        }
    }

3.6 加入直播间和直播间开始结束的接口

加入直播间的接口:

mAtlasClient.join(sessionid, userAccount, new CCAtlasCallBack<CCBaseBean>() {
            @Override
            public void onSuccess(CCBaseBean ccBaseBean) {
                dismissProgress();
                showToast("join room success");
            }

            @Override
            public void onFailure(int errCode, String errMsg) {
                dismissProgress();
                showToast(errMsg);
            }
        });

开始直播:

@OnClick(R.id.id_start)
    void start() {
        mAtlasClient.startLive(new CCAtlasCallBack<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                showToast("start live success");
            }

            @Override
            public void onFailure(int errCode, String errMsg) {
                showToast("start live failed [ " + errMsg + " ]");
            }
        });
    }

结束直播:

@OnClick(R.id.id_stop)
    void stop() {
        mAtlasClient.stopLive(new CCAtlasCallBack<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                showToast("stop live success");
            }

            @Override
            public void onFailure(int errCode, String errMsg) {
                showToast("stop live failed [ " + errMsg + " ]");
            }
        });

3.7 推流调用接口

下面是推流端代码:

 mAtlasClient.publish(new CCAtlasCallBack<Void>() {
                @Override
                public void onSuccess(Void aVoid) {
                    isPublish = true;
                    dismissProgress();
                    showToast("publish success");
                    mPublishBtn.setSelected(!mPublishBtn.isSelected());
                    mPublishBtn.setText(mPublishBtn.isSelected() ? "停止发布" : "发布");
                }

                @Override
                public void onFailure(int errCode, String errMsg) {
                    dismissProgress();
                    showToast("publish failed [ " + errMsg + " ]");
                }
            });

3.8 取消推流接口调用

下面取消推流接口调用代码:

    mAtlasClient.unpublish(new CCAtlasCallBack<Void>() {
                @Override
                public void onSuccess(Void aVoid) {
                    isPublish = false;
                    dismissProgress();
                    showToast("unpublish success");
                    mPublishBtn.setSelected(!mPublishBtn.isSelected());
                    mPublishBtn.setText(mPublishBtn.isSelected() ? "停止发布" : "发布");
                }

                @Override
                public void onFailure(int errCode, String errMsg) {
                    dismissProgress();
                    showToast("unpublish failed [ " + errMsg + " ]");
                }
            });

3.9 订阅流接口调用

下面代码是订阅流接口:

 mAtlasClient.SubscribeStream(mStream, new CCAtlasCallBack<CCStream>() {
             @Override
            public void onSuccess(CCStream stream) {
               dismissProgress();
               showToast("subscribe success");
               mSubscribeBtn.setSelected(!mSubscribeBtn.isSelected());
                        mSubscribeBtn.setText(mSubscribeBtn.isSelected() ? "取消订阅" : "订阅");
                        try {
                            mStream.attach(mRemoteMixRenderer);
                        } catch (StreamException ignored) {
                        }
                    }

                    @Override
                    public void onFailure(int errCode, String errMsg) {
                        dismissProgress();
                        showToast("subscribe failed [ " + errMsg + " ]");
                    }
                });

3.10 取消订阅流接口

下面是取消订阅流接口代码:

mAtlasClient.unSubscribeStream(mStream, new CCAtlasCallBack<Void>() {
                    @Override
                    public void onSuccess(Void aVoid) {
                        dismissProgress();
                        showToast("unsubscribe success");
                        mSubscribeBtn.setSelected(!mSubscribeBtn.isSelected());
                        mSubscribeBtn.setText(mSubscribeBtn.isSelected() ? "取消订阅" : "订阅");
                        try {
                            mStream.detach(mRemoteMixRenderer);
                        } catch (StreamException ignored) {
                        } finally {
                            mRemoteMixRenderer.cleanFrame();
                        }
                    }

                    @Override
                    public void onFailure(int errCode, String errMsg) {
                        dismissProgress();
                        showToast("unsubscribe failed [ " + errMsg + " ]");
                    }
                });

3.11 切换摄像头

下面是切换摄像头代码:

 @OnClick(R.id.id_switch)
    void switchCamera() {
        mAtlasClient.switchCamera(new CCAtlasCallBack<Boolean>() {
            @Override
            public void onSuccess(Boolean aBoolean) {
                isFront = aBoolean;
                mLocalRenderer.setMirror(isFront);
            }

            @Override
            public void onFailure(int errCode, String errMsg) {

            }
        });
    }

4.功能使用

功能使用时相关的核心类:CCAtlasClient(小班课核心类)。

4.1 预览

预览是将初始化相机的流渲染出来:

预览方法void preview();

渲染的方法mLocalStream.attach(mLocalRenderer);

4.2 开始直播

点击开始直播,成功以后,才会进行推流和拉流操作: 开始直播方法void start()

开始直播接口mAtlasClient.startLive(new CCAtlasCallBack())

回调实例new CCAtlasCallBack

4.3 结束直播

结束当前直播:

结束直播方法void stop()

结束直播接口mAtlasClient.stopLive(new CCAtlasCallBack())

回调实例new CCAtlasCallBack

4.4 推流/取消推流

推本地相机的流到atlas服务器:

推流的方法void publish()

推流接口mAtlasClient.publish(new CCAtlasCallBack())

取消推相机本地流到atlas服务器:

取消推流的方法void publish()

取消推流接口mAtlasClient.unpublish(new CCAtlasCallBack())

4.5 拉流/取消拉流

从atlas服务端拉流:

拉流方法 void subscribe()

拉流接口 mAtlasClient.SubscribeStream(mStream, new CCAtlasCallBack())

取消从atlas服务端拉流:

取消拉流方法 void subscribe()

取消拉流接口 mAtlasClient.unSubscribeStream(mStream, new CCAtlasCallBack())

4.6 添加RTMP推流/取消RTMP推流

添加RTMP流到atlas服务端:

添加RTMP流方法 void pushRtmp()

添加RTMP流接口 mAtlasClient.addExternalOutput(rtmp, new CCAtlasCallBack())

取消RTMP流到atlas服务端:

取消添加RTMP流方法 void pushRtmp()

取消添加RTMP流接口 mAtlasClient.removeExternalOutput(rtmp, new CCAtlasCallBack())

4.7 切换摄像头

切换摄像头,前置摄像头和后置摄像头:

切换摄像头方法 void switchCamera()

切换摄像头接口 mAtlasClient.switchCamera(new CCAtlasCallBack())

4.8 开启本地视频/关闭本地视频

开启本地视频流,也就是相机采集的视频:

开启相机视频方法void disableLocalVideo()

开启相机视频的接口mLocalStream.enableVideo();

关闭本地视频,也就是关闭相机采集的视频:

关闭相机视频方法void disableLocalVideo()

关闭相机视频的接口 mLocalStream.disableVideo();

4.9 开启本地音频/关闭本地音频

开启本地视频流的音频,也就是相机采集的音频流:

开启本地音频方法 void disableLocalAduio()

开启本地音频的接口 mLocalStream.enableAudio();

关闭本地视频流的音频,也就是关闭相机采集的音频流:

关闭本地音频方法 void disableLocalAduio()

关闭本地音频的接口 mLocalStream.disableAudio();

4.10 开启远程视频/关闭远程视频

订阅流视频的开启:

开启远程视频方法void disableRemoteVideo()

开启远程视频的接口mStream.enableVideo();

订阅流视频的关闭:

关闭远程视频方法void disableRemoteVideo()

关闭远程视频的接口 mStream.disableVideo();

4.11 开启远程音频/关闭远程音频

订阅流音频的开启:

开启远程音频方法 void disableRemoteAudio()

开启远程音频的接口 mStream.enableAudio();

订阅流音频的关闭:

关闭远程音频方法 void disableRemoteAudio()

关闭远程音频的接口 mStream.disableAudio();

4.12 拍照

拍照功能:

拍照方法void takePic()

拍照接口mLocalRenderer.getBitmap(new CCSurfaceRenderer.OnShotCallback())

4.13 白板和文档

白板文档功能:

白板文档的方法:

白板文档组件的实体类:1、获取白板文档组件的实例:mDocViewManager = DocViewManager.getInstance(); 2、展示白板文档的控件:引用docview和webview控件

4.14 聊天

聊天功能:

聊天的方法:

聊天组件的实体类:1、获取聊天组件的实例:mChatManager = ChatManager.getInstance(); 2、监听聊天时间的文本、表情以及图片:mChatManager.setOnChatListener(mChatList);

4.15排麦

排麦功能:

排麦方法:

排麦组件的实例类: 1、获取排麦组件的实体类: mBarLeyManager = BarLeyManager.getInstance(); 2、监听排麦的流服务:mBarLeyManager.setOnNotifyStreamListener(mClientObserver);

5.API查询

Doc目录打开index.html文件

6.Q&A

6.1 无法拉流

首先开始预览,开始直播

然后发布(推流),在去订阅流。

6.2 推荐权限声明

在AndroidManifest.xml声明权限:

    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.READ_LOGS"/>
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.FLASHLIGHT"/>
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <uses-permission android:name="android.permission.VIBRATE"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>

    <uses-feature android:name="android.hardware.camera"/>
    <uses-feature android:name="android.hardware.camera.autofocus"/>
    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true">
    </uses-feature>

results matching ""

    No results matching ""