Android端SDK集成指引
一、概述
低延时传输层SDK(RTL, 相对于低延时全功能SDK)是基于webrtc 实现的具备良好抖动缓冲、弱网抗性等能力的低延时传输组件,为了更好的与通用的基于ffmpeg内核的播放器(如百度云xplayer 、ijkplayer、vlc 、ffplayer 等)进行整合,低延时传输层SDK 也实现了ffmpeg的demuxer 接口(ff_rtl_demuxer), 本文主要介绍ijkplayer集成低延时传输层sdk rtl 的相关要点。
二、SDK产物说明
1├── include // rtl 头文件
2│ ├── rtl_api.h
3│ └── rtl_def.h
4├── libs // rtl 各架构低延时传输库,可集成相应的架构到自己工程
5│ ├── arm64-v8a
6│ │ └── librtl.so
7│ ├── armeabi-v7a
8│ └── librtl.so
9└── source // ff_rtl_demuxer 参考实现, 可直接集成到播放器 avforamt(如ijkplayer, ijkplayer/ijkavformat) 下
10 └── rtl_dec.c
三、集成步骤
ijkplayer 集成rtl 传输层sdk步骤:
- 导入头文件及动态库
将上述头文件(rtl_api.h、rtl_def.h)及库文件($arch/librtl.so)分别复制到工程架构路径: ijkplayer/android/contrib/build/ffmpeg-$arch/output/include 及ijkplayer/android/contrib/build/ffmpeg-$arch/output 目录
- 修改编译脚本
RTL 回调音频格式为PCM, 修改,新增PCM 音频解码
1export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=pcm_s16be_planar"
2export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=pcm_s16le"
3export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=pcm_s16le_planar"
修改ijkmedia/ijksdl/Android.mk 编译脚本,引入librtl.so:
1include $(CLEAR_VARS)
2LOCAL_MODULE := ijkffmpeg
3LOCAL_SRC_FILES := $(MY_APP_FFMPEG_OUTPUT_PATH)/libijkffmpeg.so
4-include $(PREBUILT_SHARED_LIBRARY)
5\ No newline at end of line
6+include $(PREBUILT_SHARED_LIBRARY)
7+
8+include $(CLEAR_VARS)
9+LOCAL_MODULE := rtl
10+LOCAL_SRC_FILES := $(MY_APP_FFMPEG_OUTPUT_PATH)/librtl.so
11+include $(PREBUILT_SHARED_LIBRARY)
- 添加rtl_dec.c(ff_rtl_demuxer)及动态库
将rtl_dec.c文件拷贝到工程源码目录:src/main/jni/ijkmedia/ijkplayer/ijkavformat,修改对应Android.mk 编译脚本如下:
1 @@ -75,7 +75,9 @@ LOCAL_SRC_FILES += ijkavformat/ijktree.c
2 LOCAL_SRC_FILES += ijkavformat/ijkfifo.c
3 LOCAL_SRC_FILES += ijkavformat/ijkstl.c
4
5 +LOCAL_SRC_FILES += ijkavformat/rtl_dec.c
6
7-LOCAL_SHARED_LIBRARIES := ijkffmpeg ijksdl
8+LOCAL_SHARED_LIBRARIES := ijkffmpeg ijksdl rtl
9 LOCAL_STATIC_LIBRARIES := android-ndk-profiler ijksoundtouch
- rtl so 加载
1static {
2 System.loadLibrary("rtl");
3}
- 注册rtl demuxer 协议
修改ff_ffplayer.c, 声明 低延时拉流协议 ff_rtl_demuxer:
在read_thread()线程方法里针对 webrtc:// 前缀 url 指定 AVInputFormat 为 ff_rtl_demuxer:
-
配置修改,适配低延时播放场景
- 不限制拉流缓存大小, 开启无限读 , 防止弱网下延迟累积;
ijkMediaPlayer.setOption(ijkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1);
指定rtl dumuxer 为 realtime:
- 关闭buffering, 保证弱网下也能进行视频低帧率 音频流畅播放; ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0);
- 最大缓存配置低于500ms, 建议使用200ms; setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-duration", 200);
- 不限制拉流缓存大小, 开启无限读 , 防止弱网下延迟累积;
ijkMediaPlayer.setOption(ijkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1);
指定rtl dumuxer 为 realtime:
四、低延时播放及RTL事件监听
- RTL 消息回调 经过上述步骤,ijkplayer 已具体低延时播放能力,接下来将RTL 事件接入到上层。
修改 ijkmedia/ijkplayer/ff_ffmsg.h
1// add rtl message
2#define FFP_MSG_RTL
修改ff_ffplay.c 文件 接收来自RTL 库的消息
1static int av_format_message_cb(struct AVFormatContext *ic, int type,
2 void *data, size_t data_size) {
3 FFPlayer *ffp = ic->opaque;
4 // rtl modify start
5 if (!ffp) {
6 return 0;
7 }
8
9 // merge rtl info and error event post
10 if ((type > RTL_EVENT_BASE && type < RTL_EVENT_BASE + 100)
11 || (type > RTL_ERROR_BASE && type < RTL_ERROR_BASE + 100)) {
12 ffp_notify_msg4(ffp, FFP_MSG_RTL, type, 0, data, data_size);
13 } else if (ffp->inject_opaque) {
14 return inject_callback(ffp->inject_opaque, type, data, data_size);
15 }
16 // rtl modify end
17 return 0;
18}
修改 ijkmedia/ijkplayer/android/ijkplayer_jni.c 新半RTL 消息post 到JAVA 层
1static void message_loop_n(JNIEnv *env, BDCloudMediaPlayer *mp) {
2 // ... ...
3 while (1) {
4 switch (msg.what) {
5 // ...
6 case FFP_MSG_RTL:
7 MPTRACE ("FFP_MSG_RTL:\n");
8 post_event(env, weak_thiz, MEDIA_RTL_MSG, msg.arg1, 0);
9 break;
10 }
11 }
12}
修改ijkMediaPlayer.java 接收来自native 的rtl 消息
1private static class EventHandler extends Handler {
2 // ... ...
3 @Override
4 public void handleMessage(Message msg) {
5 switch (msg.what) {
6 case MEDIA_RTL_MSG:
7 player.notifyOnRtlMessage(msg.arg1);
8 break;
9 }
10 }
11 }
修改AbstractMediaPlayer.java 新增RTL 消息监听
1 private OnRtlMessageListener mOnRtlMessageListener;
2
3 @Override
4 public void setOnRtlMessageListener(OnRtlMessageListener listener) {
5 mOnRtlMessageListener = listener;
6 }
7
8 protected final void notifyOnRtlMessage(int what) {
9 if (mOnRtlMessageListener != null) {
10 mOnRtlMessageListener.onRtlMessage(this, what);
11 }
12 }
- 低延时播放跑通验证
至此,已完成rtl 在ijkplayer的集成,我们可以像播放普通视频一样调用ijkplayer 进行播放,具体调用过程同样是 创建ijkplayer-> setOnXxxListener()->setSurface()-> setDataSource("webrtc://xxx")->prepare()/start()/stop(); 值得注意的是:
- 低延时播放流地址没"webrtc://"前缀,该地址可以在域名申请时获取;
- option 参数配置
1 // 关闭buffering缓冲逻辑
2 this.setOption(OPT_CATEGORY_PLAYER, "packet-buffering", 0);
3 // 设置信令模式
4 setOption(OPT_CATEGORY_FORMAT, "rtl_signaling_mode", signalingMode);
5 // 设置媒体服务地址,非必需, 不设置则调用系统接口进行域名解析
6 setOption(OPT_CATEGORY_FORMAT, "rtl_mediaserver_host", mediaServerHost);
7
- 监听新增的setOnRtlMessageListener接口获得并处理RTL 事件;
- RTL 事件处理
ijkplayer 通过setOnRtlMessageListener 监听RTL 事件,用户可针对特定的事件进行重连、降级播放处理。
1// 设置rtl事件监听
2mVV.setOnRtlMessageListener(this);
3
4// rtl 事件处理
5@Override
6public void onRtlMessage(IMediaPlayer mp, int what) {
7 Log.i(TAG, "on RTL MSG: " + what);
8 switch (what) {
9 case IMediaPlayer.RTL_EVENT_FIRST_VFRAME:
10 bRtlRetried = false;
11 break;
12 case IMediaPlayer.RTL_ERROR_ICE_CHANNEL:
13 case IMediaPlayer.RTL_ERROR_STREAMING_INTERRUPT:
14 if (!bRtlRetried) {
15 // 重试一次,依然失败则降级
16 updateTextInfo("RTL play need retry since error :" + what);
17 bRtlRetried = true;
18 mVV.rtlReload(info.getUrl());
19 } else {
20 rtlFallback(what);
21 }
22 break;
23 default:
24 if (what > IMediaPlayer.RTL_ERROR_BASE) {
25 // 直接降级
26 rtlFallback(what);
27 }
28 break;
29 }
30}
31
32// 降级播放
33private void rtlFallback(int fallbackReason) {
34 updateTextInfo("RTL play need fallback since error :" + fallbackReason);
35 if (!TextUtils.isEmpty(info.getFallbackUrlForRtl())) {
36 mVV.rtlReload(info.getFallbackUrlForRtl());
37 } else {
38 showError("低延时直播错误:" + fallbackReason);
39 mVV.stopPlayback();
40 }
41}
RTL 回调的事件定义如下:
1 public interface IMediaPlayer {
2 ... ...
3 // RTL 事件消息
4 int RTL_EVENT_ICE_BASE = 1200;
5 // ICE 连接成功
6 int RTL_EVENT_ICE_CONNECTED = 1201;
7 // 连接关闭
8 int RTL_EVENT_CONNECTION_CLOSED = 1202;
9 // ICE 连接断开
10 int RTL_EVENT_ICE_DISCONNECTED = 1206;
11 // 没有检测到媒体流
12 int RTL_EVENT_NO_STREAMING_DETECTED = 1207;
13 // 获得 remote sdp
14 int RTL_EVENT_REMOTE_SDP_ACQUIRED = 1210;
15 // RTL首屏事件
16 int RTL_EVENT_FIRST_VFRAME = 1211;
17
18 // RTL 错误消息
19 int RTL_ERROR_BASE = 12000;
20 // ICE 连接错误, 建议重试一次, 重试失败降级为普通直播
21 int RTL_ERROR_ICE_CHANNEL = 12001;
22 // Peer连接创建失败,建议降级为普通直播
23 int RTL_ERROR_CONNECTION = 12003;
24 // 远端媒体描述请求失败, 建议降级为普通直播
25 int RTL_ERROR_REMOTE_SDP_REQUEST = 12006;
26 int RTL_ERROR_REMOTE_SDP_SET = 12007;
27 // 播放状态错误, 建议降级为普通直播
28 int RTL_ERROR_INVALID_STATE = 12008;
29 // 媒体流中断 一段时间未收媒体流(默认6S) SDK内部检测到断流错误后会立即停止播放. 重试失败降级为普通直播
30 int RTL_ERROR_STREAMING_INTERRUPT = 12009;
31 // URL格式错误,降级为普通直播
32 int RTL_ERROR_INVALID_URL = 12010;
33 // 约2s后返回, 用户网络MTU Size 检测失败(< 1300 Byte)无法支持RTC媒体传输, 建议降级为普通播放
34 int RTL_ERROR_MTU_SIZE_CHECK_FAILED = 12014;
35 // 非法状态异常, 降级为普通直播
36 int RTL_ERROR_ILLEGAL_STATE = 12015;
37 }