产品文档 回放技术文档 Android 回放 Core SDK

百家云Android回放核心SDK集成文档

简介

百家云Android回放SDK是提供百家云直播系统(包括网页、移动版和电脑版)重播功能的SDK。支持直播间聊天、视频、ppt等模块的回看,所涉及到的老师、学生、助教等概念和直播系统完全相同。其中视频模块使用的是百家云点播SDK,其他模块与百家云直播SDK相同。提供在线回放以及离线回放功能,支持视频和ppt缓存。

功能描述

SDK支持Android4.0(api level 14)及以上

功能 描述
在线回放 支持百家云后台配置回放房间在线观看
离线回放 支持本地视频文件和信令文件离线观看,详情参考下载模块
片头片尾 点播SDK
视频缓存 支持百家云后台配置回放房间的视频和信令包下载功能

注:普通房间包含一个视频,此视频对应一个信令包文件,信令包包含ppt、表情包等文件,根据视频的播放进度,SDK查找对应时间的信令,从而达到复原直播场景的效果。对于长期房间,一个房间对应多个视频文件,每个视频都有相应的信令包文件,长期房间的回放可看做多个不同的普通房间的集合。

示例工程

demo
github链接:https://github.com/baijia/PlayBackDemo_Android
注:此示例工程UI界面仅供参考。

集成SDK

集成前的准备

1) 推荐使用最新版Android studio集成SDK 点击下载(需科学上网)
2) 在工程根目录下build.gradle添加远程仓库

maven { url 'https://raw.github.com/baijia/maven/master/' }

对于部分国内用户,如果github被墙或者访问比较慢,可以使用我们国内的镜像仓库

maven { url 'http://git.baijiashilian.com/open-android/maven/raw/master/' }

3) 在需要集成的module添加如下依赖

compile 'com.baijia.player:video-playback:1.3.1'
compile 'com.baijia.live:liveplayer-sdk-core-ppt:1.3.5' //如果不需要ppt功能可以不添加此依赖项

推荐使用最新版本
4)在build.gradle中添加ndk过滤

ndk {
    abiFilters 'armeabi-v7a', 'armeabi', 'x86' //x86虚拟机测试用,发版可去掉
}

快速集成

1)在xml文件中添加播放器控件

<com.baijiahulian.player.BJPlayerView
    android:id="@+id/playerView"
    android:layout_width="match_parent"
    android:layout_height="280dp">
</com.baijiahulian.player.BJPlayerView>

2) 初始化播放器
同点播播放器初始化

playerView = (BJPlayerView) findViewById(R.id.playerView);
playerView.setTopPresenter(new BJTopViewPresenter(playerView.getTopView()));
playerView.setCenterPresenter(new BJCenterViewPresenter(playerView.getCenterView()));
playerView.setBottomPresenter(new BJBottomViewPresenter(playerView.getBottomView()));

3) 创建房间
创建录播回放房间 -- 在线回放

//默认获取加密视频地址
PBRoom mRoom = LivePlaybackSDK.newPlayBackRoom(this, classId, classToken);

参数说明如下

/**
 * @param context
 * @param classId 教室 ID
*/
public static PBRoom newPlayBackRoom(Context context, long classId, String token)
/**
  * @param context
  * @param classId      教室id
  * @param token        token
  * @param encryptType  加密参数 1加密0不加密
  * @return
  */
public static PBRoom newPlayBackRoom(Context context, long classId, String token, int encryptType){
        return new PBRoomImpl(context, classId, token, deployType, encryptType);
    }

创建长期课回放房间 -- 在线回放[长期房间,分段]

PBRoom mRoom = LivePlaybackSDK.newPlayBackRoom(this, classId, sessionId, classToken);

参数如下

/**
 * @param context
 * @param classId    教室 ID
 * @param sessionId  分段id
 */
public static PBRoom newPlayBackRoom(Context context, long classId, long sessionId, String token)
/**
  * @param context
  * @param classId    教室id
  * @param sessionId  分段id
  * @param token      token
  * @param encryptType 是否加密,1加密0不加密
  * @return
  */
public static PBRoom newPlayBackRoom(Context context, long classId, long sessionId, String token, int encryptType) {
        return new PBRoomImpl(context, classId, sessionId, token, deployType, encryptType);
    }

创建回放房间 -- 离线回放, 直接传未解压的文件

/**
 * @param context
 * @param classId    教室 ID
 * @param deployType
 * @param videoFile  视频文件
 * @param signalFile 离线信令包文件
 */
 PBRoom mRoom = LivePlaybackSDK.newPlayBackRoom(this, classId, videoFile, signalFile);

参数如下

/**
  * 创建回放房间 -- 离线回放, 直接传未解压的文件
  * @param context
  * @param classId        教室 ID
  * @param videoFilePath  视频文件
  * @param signalFilePath 离线信令包文件
*/
public static PBRoom newPlayBackRoom(Context context, long classId, String videoFilePath, String signalFilePath)

4)进入房间

/**
* @param listener 进入房间监听
*/
mRoom.enterRoom(your LPLaunchListener listener);

接口说明

/**
 * 进度
 *
 * @param step      当前加载了多少步
 * @param totalStep 加载完房间总共需要几步
 */
void onLaunchSteps(int step, int totalStep);
/**
 * 进入房间失败
 *
 * @param error 错误信息,同直播SDK
 */
void onLaunchError(LPError error);
/**
 * 进入房间成功
 */
void onLaunchSuccess(LiveRoom liveRoom);

5) 绑定播放器

mRoom.bindPlayerView(playerView);

6) 回调方法 每个回调方法与点播播放器回调相同

mRoom.setOnPlayerListener(new OnPlayerListener() {
     /**
     * VideoInfo 初始化完成
     * @param exception
     */
    @Override
    public void onVideoInfoInitialized(BJPlayerView playerView, long duration, HttpException exception) {
    }
    @Override
    public void onError(BJPlayerView playerView, int code) {
    }
     /**
     * 实时更新位置
     * @param playerView
     * @param position 当前播放的秒数
     */
    @Override
    public void onUpdatePosition(BJPlayerView playerView, int position) {
    }
     /**
     * 拖动进度条完成的回调
     * @param playerView
     * @param position 拖动到的秒数
     */
    @Override
    public void onSeekComplete(BJPlayerView playerView, int position)  {
    }
    /**
     * 调整倍速回调
     * @param playerView
     * @param speedUp 播放倍数
     */
    @Override
    public void onSpeedUp(BJPlayerView playerView, float speedUp) {
    }
    /**
     * 调整视频清晰度回调
     * @param playerView
     * @param definition 清晰度
     */
    @Override
    public void onVideoDefinition(BJPlayerView playerView, int definition) {
    }
    /**
     * 播放完成回调
     * @param playerView
     * @param nextSection
     */
    @Override
    public void onPlayCompleted(BJPlayerView playerView, VideoItem item, SectionItem nextSection) {
    }
    /**
     * 视频信息加载成功 即将播放
     * @param playerView
     */
    @Override
    public void onVideoPrepared(BJPlayerView playerView) {
    }
     /**
     * 暂停视频
     * @param playerView
     */
    public void onPause(BJPlayerView playerView){
    }

    /**
     * 开始播放视频
     * @param playerView
     */
    public void onPlay(BJPlayerView playerView){
    }
});

7)ppt
如果您不需要ppt功能,可以忽略此小节
ppt是一个Fragment,您可以添加到您想要的布局中去

mPPTFragment = new LPPPTFragment();
mPPTFragment.setLiveRoom(mRoom);

8)退出房间
在合适的位置退出房间,比如Activity的onDestroy方法中

mRoom.quitRoom();

至此,回放SDK快速集成完毕。

回放视频和信令包下载(旧版)

1)初始化下载器

/**
 * @param context        上下文
 * @param partnerId      partnerId
 * @param deploy         部署环境,同点播SDK
 */
PBDownloader pbDownloader = new PBDownloader(this, 1234567L, deploy);
/**
  * @param context     上下文
  * @param partnerId   合作伙伴id
  * @param deploy      部署环境
  * @param encryptType 是否加密,0不加密;1加密
  */
public PBDownloader(Context context, long partnerId, int deploy, int encryptType)

2)设置文件存放目录

pbDownloader.setTargetFolder(Environment.getExternalStorageDirectory().getAbsolutePath() + "/aa_video_downloaded/");

3)获取房间信息

/**
 * 获取房间信息
 *
 * @param roomId    房间id
 * @param sessionId 长期房间id
 * @param token     token
 */
public void getAllDefinitions(long roomId, long sessionId, String token, final OnGetRoomInfoListener listener) 

获取成功后接口返回房间信息数据模型PBRoomData,继承自点播播放器VideoItem类,视频的相关信息都可以查到,常用字段有: definition(清晰度列表),packageSignal(信令包信息,可用于自己实现下载)
4) 添加下载任务

/**
 * @param fileName       文件名称,只用于存储,文件管理中的文件名是taskKey
 * @param roomId         房间id
 * @param sessionId      长期房间id,如果不是长期房间传0的值即可
 * @param token          房间token
 * @param definitionType 清晰度 同点播
 * @param encryptType    是否加密 同点播
 * @param extraInfo      额外信息,客户自己存储的字符串信息,sdk只是转存
 */
public void downloadRoomPackage(final String fileName, final long roomId, final long sessionId, final String token, final int definitionType, final int encryptType, final String extraInfo, final OnAddDownloadTaskListener onAddDownloadTaskListener)

注:回放SDK的下载管理功能与点播播放器下载管理完全相同,可参考点播播放器文档

回放视频和信令包下载(新版)

1)manifest中添加DownloadService

<service
            android:name="com.baijiayun.download.DownloadService"
            android:enabled="true"
            android:exported="false"></service>

2)初始化PlaybackDownloader

//回放下载初始化PlaybackDownloader
playbackDownloader = new PlaybackDownloader(this, 32975272, Environment.getExternalStorageDirectory().getAbsolutePath() + "/bb_video_downloaded/", 1);

PlaybackDownloader的参数定义如下

/**
  * @param context        上下文
  * @param partnerId      partnerId
  * @param targetFolderPath  保存路径
  * @param encryptType  是否加密。1加密;0不加密
  */
public PlaybackDownloader(Context context, long partnerId, String targetFolderPath, int encryptType)

缺省参数的默认加密。

public PlaybackDownloader(Context context, long partnerId, String targetFolderPath) {
        this(context, partnerId, targetFolderPath, 1);
    }

PlaybackDownloader内部初始化了点播的DownloadManager,所以如果同时集成回放和点播,调用了PlaybackDownloader初始化下载模块之后就不用再初始化点播中的DownloadManager。
3)调用PlaybackDownloader.donwloadRoomPackage

// 回放下载room的视频和信令
playbackDownloader.downloadRoomPackage(fileName, Long.parseLong(roomId), Long.parseLong(sessionId), token, definitionList, 0, "hahaha")
                    .subscribe(new Action1<DownloadTask>() {
                        @Override
                        public void call(DownloadTask task) {
                            //开始下载
                            task.start();
                        }
                    }, new Action1<Throwable>() {
                        @Override
                        public void call(Throwable throwable) {
                        }
                    });

downloadRoomPackage参数含义如下:

    /**
     * @param fileName    文件名称
     * @param roomId      房间id
     * @param sessionId   长期房间id,如果不是长期房间传0的值即可
     * @param token       房间token
     * @param definitions 清晰度 同点播
     * @param encryptType 是否加密 同点播
     * @param extraInfo   额外信息,客户自己存储的字符串信息,sdk只是转存
     * @param
     */
public Observable<DownloadTask> downloadRoomPackage(final String fileName, final long roomId, final long sessionId, final String token,
                                                        final List<VideoDefinition> definitions, final int encryptType, final String extraInfo)

4)这里的DownloadTask和点播下载结构一致,设置监听也一致。回放下载同样存在下载链接失效的问题,在onError的回调中判断errorCode==403||errorCode==5103则重新调downloadRoomPackage获取回放视频和信令包。同样,这里必须传入新的token。
5)获取视频名称:DownloadTask.getFileName()。获取信令文件名称:DownloadTask.getSignalFileName()
6)可参考demo中的SimpleVideoDownloadActivity

数据迁移

回放的下载模块是复用的点播,因此这里的数据迁移只需要在一个地方调用即可。具体可以参考点播的数据迁移

常用api

注:回放SDK与直播SDK都采用RxJava
1)视频开关状态

mRoom.getObservableOfVideoStatus().observeOn(AndroidSchedulers.mainThread())
        .subscribe(new LPErrorPrintSubscriber<Boolean>() {
            @Override
            public void call(Boolean aBoolean) {
                tvVideoStatus.setText(aBoolean ? "视频开启" : "视频关闭");
            }
        });

2)当前用户数

mRoom.getObservableOfUserNumberChange()
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Action1<Integer>() {
            @Override
            public void call(Integer integer) {
                mTextView.setText("当前用户数: " + integer);
            }
        });

3)聊天信息

mRoom.getChatVM().getObservableOfNotifyDataChange()
        .onBackpressureBuffer(1000)
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Action1<Void>() {
    @Override
    public void call(Void aVoid) {
        //TODO:聊天消息改变
    }
});
//在recyclerview或者listview中调用此方法
IMessageModel messageModel = mRoom.getChatVM().getMessage(position);

4)获取当前用户

IUserModel currentUser = liveRoom.getCurrentUser();

5)获取老师用户

IUserModel currentUser = liveRoom.getTeacherUser();

错误码

由于回放SDK依赖点播SDK,点播SDK中的错误码在回放SDK中同样适用,另外由于回放SDK还依赖直播SDK,因此直播SDK中的错误码同样适用于回放SDK

常见问题

1)我不想使用SDK提供的下载模块,可不可以自己实现下载模块? 可以的,回放下载管理器提供另外一个构造函数,如下:

/**
 * @param context        上下文
 * @param partnerId      partnerId
 * @param deploy         部署环境
 * @param useSDKDownload 是否使用sdk下载管理 true使用;false不使用,客户自己实现,sdk只提供信息
 */
public PBDownloader(Context context, long partnerId, int deploy, boolean useSDKDownload)

最后一个参数可以传false,此时添加下载任务只会获取到房间信息,具体的下载操作可以用户自由实现。
2)我可不可以实现自己的播放器UI?
可以,因为回放SDK依赖点播SDK,点播SDK定制UI在回放SDK中同样适用。
3) 能否实现后台播放?
可以,在播放的activity的OnPause和OnResume分别注释掉BJPlayerView.onPause()和BJPlayerView.onResume()即可。

 @Override
    protected void onResume() {
        super.onResume();
        if (playerView != null) {
//            playerView.onResume();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (playerView != null) {
//            playerView.onPause();
        }
    }

4)后台播放能否实现音频焦点监听?
可以,具体可以参考demo中的MainActivity

private boolean requestFocus() {
        //申请音频焦点
        int result = mAm.requestAudioFocus(afChangeListener,
                // Use the music stream.
                AudioManager.STREAM_MUSIC,
                // Request permanent focus.
                AudioManager.AUDIOFOCUS_GAIN);
        return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
    }

设置AudioManager.OnAudioFocusChangeListener,失去音频焦点的回调里务必调用 IJKMediaPlayer.isPlayBackground = true这行代码,否则程序重新获得音频焦点也无法继续播放。

AudioManager.OnAudioFocusChangeListener afChangeListener = new AudioManager.OnAudioFocusChangeListener() {
        public void onAudioFocusChange(int focusChange) {
            switch (focusChange) {
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                    //失去焦点
                    playerView.pauseVideo();
                    IJKMediaPlayer.isPlayBackground = true;
                    break;
                case AudioManager.AUDIOFOCUS_GAIN:
                    //获得焦点
                    if (!playerView.isPlaying()) {
                        playerView.playVideo();
                    }
                    break;
                case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
                    //临时短时间的音频焦点
                    if (!playerView.isPlaying()) {
                        playerView.playVideo();
                    }
                    break;
                case AudioManager.AUDIOFOCUS_LOSS:
                    //长时间失去焦点,不应该再期望有焦点返回
                    playerView.pauseVideo();
                    //设置播放器处于后台模式(这一行很重要,否则重新获取焦点之后无法继续播放)
                    IJKMediaPlayer.isPlayBackground = true;
                    //TODO 这里可以选择放弃afChangeListener的监听
                    //mAm.abandonAudioFocus(afChangeListener);
                    break;
            }
        }
    };