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