iOS端SDK集成指引
一、概述
低延时传输层sdk(RTL, 相对于低延时全功能SDK)是基于webrtc 实现的具备良好抖动缓冲、弱网抗性等能力的低延时传输组件,为了更好的与通用的基于ffmpeg内核的播放器(如百度云xplayer 、ijkplayer、vlc 、ffplayer 等)进行整合,低延时传输层SDK 也实现了ffmpeg的demuxer 接口(ff_rtl_demuxer), 本文主要介绍ijkplayer集成低延时传输层sdk rtl 的相关要点。
二、SDK产物说明
1提供基于ffmpeg的ff_rtl_demuxer 文件:rtl_dec.c 及其依赖库 RtlSDK.framework 集成到客户播放器程序
2RtlSDK.framework结构如下所示:
3├── Headers
4│ ├── RtlSDK.h
5│ ├── rtl_api.h
6│ └── rtl_def.h
7├── Info.plist
8├── Modules
9│ └── module.modulemap
10├── RtlSDK
11└── _CodeSignature
12 ├── CodeDirectory
13 ├── CodeRequirements
14 ├── CodeRequirements-1
15 ├── CodeResources
16 └── CodeSignature
17
三、集成步骤
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
1// add rtl message
2#define FFP_MSG_RTL 30000
2、修改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}
3、修改 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}
4、修改IJKFFMoviePlayerControler 接收来自native的rtl 消息
1- (void)postEvent: (IJKFFMoviePlayerMessage *)msg
2
3 // ... ...
4 case FFP_MSG_RTL: {
5 av_log(NULL, AV_LOG_DEBUG, "FFP_MSG_RTL: %d\n", avmsg->arg1);
6 [[NSNotificationCenter defaultCenter]
7 postNotificationName:IJKMPMoviePlayerRtlMsgNotification
8 object:self.shell
9 userInfo:@{IJKMPMoviePlayerRtlMsgNotificationKey: @(avmsg->arg1)}];
10 break;
11 }
12
13 }
- 低延时播放跑通验证
至此,已完成rtl 在ijkplayer的集成,我们可以像播放普通视频一样调用ijkplayer 进行播放,具体调用过程同样是 创建 setDataSource("webrtc://xxx")
1 prepare()/start()/stop();
2
3
值得注意的是:
- 低延时播放流地址没"webrtc://"前缀,该地址可以在域名申请时获取;
- 监听新增的setOnRtlMessageListener接口获得并处理RTL 事件;
- RTL 事件处理
ijkplayer 通过BDCloudMediaPlayerRtlMsgNotification 监听RTL 事件,用户可针对特定的事件进行重连、降级播放处理。
1// rtl 事件处理
2- (void)onRtlMsg:(NSNotification *)notification {
3 if (notification.object != _player) {
4 return;
5 }
6 NSNumber *rtlMsg = notification.userInfo[BDCloudMediaPlayerRtlMsgNotificationKey];
7 NSLog(@"onRtlMsg %ld", (long)rtlMsg.integerValue);
8 [self.actions appendVideoLog:[NSString stringWithFormat:@"onRtlMsg %ld\n", rtlMsg.integerValue]];
9
10 switch (rtlMsg.integerValue) {
11 case RTL_EVENT_FIRST_VFRAME:
12 self.bRtlRetried = NO;
13 break;
14 case RTL_ERROR_ICE_CHANNEL:
15 case RTL_ERROR_STREAMING_INTERRUPT:
16 if (!self.bRtlRetried) {
17 // 重试一次,依然失败则降级
18 [self.actions appendVideoLog:[NSString stringWithFormat:@"RTL play need retry since error %ld", rtlMsg.integerValue]];
19 self.bRtlRetried = YES;
20
21 [self stopPlayback];
22 self.bSwitching = true;
23
24 } else {
25 [self rtlFallback:rtlMsg.integerValue];
26 }
27 break;
28 default:
29 if (rtlMsg.integerValue > RTL_ERROR_BASE) {
30 // 直接降级
31 [self rtlFallback:rtlMsg.integerValue];
32 }
33 break;
34 }
35}
36// 降级播放
37- (void)rtlFallback:(int)fallbackReason {
38 [self.actions appendVideoLog:[NSString stringWithFormat:@"RTL play need fallback since error %d\n", fallbackReason]];
39 if (self.model.fallbackUrlForRtl == nil || self.model.fallbackUrlForRtl.length == 0) {
40 [self stopPlayback];
41 self.tipLable.text = [NSString stringWithFormat:@"低延时直播错误:%d", fallbackReason];
42 self.tipLable.hidden = NO;
43 } else {
44 [self stopPlayback];
45 self.model.url = self.model.fallbackUrlForRtl;
46 self.bSwitching = true;
47 }
48}
RTL 回调的事件定义如下:
1typedef NS_ENUM(NSInteger, BDCloudRtlMsg) {
2 RTL_EVENT_ICE_BASE = 1200,
3 // ICE 连接成功
4 RTL_EVENT_ICE_CONNECTED = 1201,
5 // 连接关闭
6 RTL_EVENT_CONNECTION_CLOSED = 1202,
7 // ICE 连接断开
8 RTL_EVENT_ICE_DISCONNECTED = 1206,
9 // 没有检测到媒体流
10 RTL_EVENT_NO_STREAMING_DETECTED = 1207,
11 // 获得 remote sdp
12 RTL_EVENT_REMOTE_SDP_ACQUIRED = 1210,
13 // RTL首屏事件
14 RTL_EVENT_FIRST_VFRAME = 1211,
15
16 // RTL error event
17 RTL_ERROR_BASE = 12000,
18 // ICE 连接错误, 建议重试一次, 重试失败降级为普通直播
19 RTL_ERROR_ICE_CHANNEL = 12001,
20 // Peer连接创建失败,建议降级为普通直播
21 RTL_ERROR_CONNECTION = 12003,
22 // 远端媒体描述请求失败, 建议降级为普通直播
23 RTL_ERROR_REMOTE_SDP_REQUEST = 12006,
24 RTL_ERROR_REMOTE_SDP_SET = 12007,
25 // 播放状态错误, 建议降级为普通直播
26 RTL_ERROR_INVALID_STATE = 12008,
27 // 媒体流中断 一段时间未收媒体流(默认6S) SDK内部检测到断流错误后会立即停止播放. 重试失败降级为普通直播
28 RTL_ERROR_STREAMING_INTERRUPT = 12009,
29 // URL格式错误,降级为普通直播
30 RTL_ERROR_INVALID_URL = 12010,
31 // 约2s后返回, 用户网络MTU Size 检测失败(< 1300 Byte)无法支持RTC媒体传输, 建议降级为普通播放
32 RTL_ERROR_MTU_SIZE_CHECK_FAILED = 12014,
33 // 非法状态异常, 降级为普通直播
34 RTL_ERROR_ILLEGAL_STATE = 12015
35};