搜索本产品文档关键词
Android端SDK集成指引
所有文档
menu

音视频直播 LSS

Android端SDK集成指引

一、概述

低延时传输层SDK(RTL, 相对于低延时全功能SDK)是基于webrtc 实现的具备良好抖动缓冲、弱网抗性等能力的低延时传输组件,为了更好的与通用的基于ffmpeg内核的播放器(如百度云xplayer 、ijkplayer、vlc 、ffplayer 等)进行整合,低延时传输层SDK 也实现了ffmpeg的demuxer 接口(ff_rtl_demuxer), 本文主要介绍ijkplayer集成低延时传输层sdk rtl 的相关要点。

二、SDK产物说明

Plain Text
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 音频解码

Plain Text
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:

Plain Text
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 编译脚本如下:

Plain Text
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 加载
Plain Text
1static {
2    System.loadLibrary("rtl");
3}
  • 注册rtl demuxer 协议

修改ff_ffplayer.c, 声明 低延时拉流协议 ff_rtl_demuxer:

image

在read_thread()线程方法里针对 webrtc:// 前缀 url 指定 AVInputFormat 为 ff_rtl_demuxer:

image

  • 配置修改,适配低延时播放场景

    • 不限制拉流缓存大小, 开启无限读 , 防止弱网下延迟累积; ijkMediaPlayer.setOption(ijkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1); 指定rtl dumuxer 为 realtime:image
    • 关闭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

Plain Text
1// add rtl message
2#define FFP_MSG_RTL    

修改ff_ffplay.c 文件 接收来自RTL 库的消息

Plain Text
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 层

Plain Text
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 消息

Plain Text
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 消息监听

Plain Text
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(); 值得注意的是:

  1. 低延时播放流地址没"webrtc://"前缀,该地址可以在域名申请时获取;
  2. option 参数配置
Plain Text
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    
  1. 监听新增的setOnRtlMessageListener接口获得并处理RTL 事件;
  • RTL 事件处理

ijkplayer 通过setOnRtlMessageListener 监听RTL 事件,用户可针对特定的事件进行重连、降级播放处理。

Plain Text
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 回调的事件定义如下:

Plain Text
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    }
上一篇
SDK集成指南概述
下一篇
iOS端