iOS端SDK集成指引
一、概述
低延时传输层sdk(RTL, 相对于低延时全功能SDK)是基于webrtc 实现的具备良好抖动缓冲、弱网抗性等能力的低延时传输组件,为了更好的与通用的基于ffmpeg内核的播放器(如百度云xplayer 、ijkplayer、vlc 、ffplayer 等)进行整合,低延时传输层SDK 也实现了ffmpeg的demuxer 接口(ff_rtl_demuxer), 本文主要介绍ijkplayer集成低延时传输层sdk rtl 的相关要点。
二、SDK产物说明
提供基于ffmpeg的ff_rtl_demuxer 文件:rtl_dec.c 及其依赖库 RtlSDK.framework 集成到客户播放器程序
RtlSDK.framework结构如下所示:
├── Headers
│ ├── RtlSDK.h
│ ├── rtl_api.h
│ └── rtl_def.h
├── Info.plist
├── Modules
│ └── module.modulemap
├── RtlSDK
└── _CodeSignature
├── CodeDirectory
├── CodeRequirements
├── CodeRequirements-1
├── CodeResources
└── CodeSignature
三、集成步骤
ijkplayer 集成rtl 传输层sdk步骤:
- 导入头文件及动态库
将上述头文件(rtl_dec.c)及库文件(RtlSDK.framework)分别复制到工程架构路径:添加依赖
- 注册rtl demuxer 协议
修改ff_ffplayer.c, 声明 低延时拉流协议 ff_rtl_demuxer:
在read_thread()线程方法里针对 webrtc:// 前缀 url 指定 AVInputFormat 为 ff_rtl_demuxer:
- 配置修改,适配低延时播放场景
1、不限制拉流缓存大小, 开启无限读 , 防止弱网下延迟累积;
ijkMediaPlayer.setOption(ijkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1);
指定rtl dumuxer 为 realtime:
2、关闭buffering, 保证弱网下也能进行视频低帧率 音频流畅播放;
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0);
3、最大缓存配置低于500ms, 建议使用200ms;
setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-duration", 200);
四、低延时播放及RTL事件监听
- RTL 消息回调
经过上述步骤,ijkplayer 已具体低延时播放能力,接下来将RTL 事件接入到上层。
1、修改 ijkmedia/ijkplayer/ff_ffmsg.h
// add rtl message
#define FFP_MSG_RTL 30000
2、修改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;
}
3、修改 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;
}
}
}
4、修改IJKFFMoviePlayerControler 接收来自native的rtl 消息
- (void)postEvent: (IJKFFMoviePlayerMessage *)msg
// ... ...
case FFP_MSG_RTL: {
av_log(NULL, AV_LOG_DEBUG, "FFP_MSG_RTL: %d\n", avmsg->arg1);
[[NSNotificationCenter defaultCenter]
postNotificationName:IJKMPMoviePlayerRtlMsgNotification
object:self.shell
userInfo:@{IJKMPMoviePlayerRtlMsgNotificationKey: @(avmsg->arg1)}];
break;
}
}
- 低延时播放跑通验证
至此,已完成rtl 在ijkplayer的集成,我们可以像播放普通视频一样调用ijkplayer 进行播放,具体调用过程同样是 创建 setDataSource("webrtc://xxx")
prepare()/start()/stop();
值得注意的是:
- 低延时播放流地址没"webrtc://"前缀,该地址可以在域名申请时获取;
- 监听新增的setOnRtlMessageListener接口获得并处理RTL 事件;
- RTL 事件处理
ijkplayer 通过BDCloudMediaPlayerRtlMsgNotification 监听RTL 事件,用户可针对特定的事件进行重连、降级播放处理。
// rtl 事件处理
- (void)onRtlMsg:(NSNotification *)notification {
if (notification.object != _player) {
return;
}
NSNumber *rtlMsg = notification.userInfo[BDCloudMediaPlayerRtlMsgNotificationKey];
NSLog(@"onRtlMsg %ld", (long)rtlMsg.integerValue);
[self.actions appendVideoLog:[NSString stringWithFormat:@"onRtlMsg %ld\n", rtlMsg.integerValue]];
switch (rtlMsg.integerValue) {
case RTL_EVENT_FIRST_VFRAME:
self.bRtlRetried = NO;
break;
case RTL_ERROR_ICE_CHANNEL:
case RTL_ERROR_STREAMING_INTERRUPT:
if (!self.bRtlRetried) {
// 重试一次,依然失败则降级
[self.actions appendVideoLog:[NSString stringWithFormat:@"RTL play need retry since error %ld", rtlMsg.integerValue]];
self.bRtlRetried = YES;
[self stopPlayback];
self.bSwitching = true;
} else {
[self rtlFallback:rtlMsg.integerValue];
}
break;
default:
if (rtlMsg.integerValue > RTL_ERROR_BASE) {
// 直接降级
[self rtlFallback:rtlMsg.integerValue];
}
break;
}
}
// 降级播放
- (void)rtlFallback:(int)fallbackReason {
[self.actions appendVideoLog:[NSString stringWithFormat:@"RTL play need fallback since error %d\n", fallbackReason]];
if (self.model.fallbackUrlForRtl == nil || self.model.fallbackUrlForRtl.length == 0) {
[self stopPlayback];
self.tipLable.text = [NSString stringWithFormat:@"低延时直播错误:%d", fallbackReason];
self.tipLable.hidden = NO;
} else {
[self stopPlayback];
self.model.url = self.model.fallbackUrlForRtl;
self.bSwitching = true;
}
}
RTL 回调的事件定义如下:
typedef NS_ENUM(NSInteger, BDCloudRtlMsg) {
RTL_EVENT_ICE_BASE = 1200,
// ICE 连接成功
RTL_EVENT_ICE_CONNECTED = 1201,
// 连接关闭
RTL_EVENT_CONNECTION_CLOSED = 1202,
// ICE 连接断开
RTL_EVENT_ICE_DISCONNECTED = 1206,
// 没有检测到媒体流
RTL_EVENT_NO_STREAMING_DETECTED = 1207,
// 获得 remote sdp
RTL_EVENT_REMOTE_SDP_ACQUIRED = 1210,
// RTL首屏事件
RTL_EVENT_FIRST_VFRAME = 1211,
// RTL error event
RTL_ERROR_BASE = 12000,
// ICE 连接错误, 建议重试一次, 重试失败降级为普通直播
RTL_ERROR_ICE_CHANNEL = 12001,
// Peer连接创建失败,建议降级为普通直播
RTL_ERROR_CONNECTION = 12003,
// 远端媒体描述请求失败, 建议降级为普通直播
RTL_ERROR_REMOTE_SDP_REQUEST = 12006,
RTL_ERROR_REMOTE_SDP_SET = 12007,
// 播放状态错误, 建议降级为普通直播
RTL_ERROR_INVALID_STATE = 12008,
// 媒体流中断 一段时间未收媒体流(默认6S) SDK内部检测到断流错误后会立即停止播放. 重试失败降级为普通直播
RTL_ERROR_STREAMING_INTERRUPT = 12009,
// URL格式错误,降级为普通直播
RTL_ERROR_INVALID_URL = 12010,
// 约2s后返回, 用户网络MTU Size 检测失败(< 1300 Byte)无法支持RTC媒体传输, 建议降级为普通播放
RTL_ERROR_MTU_SIZE_CHECK_FAILED = 12014,
// 非法状态异常, 降级为普通直播
RTL_ERROR_ILLEGAL_STATE = 12015
};