通过阅读本文,您可以了解Android端依赖FFmpeg的其他播放器(本文以ijkplayer tag k0.8.8为例)集成Native RTS SDK实现超低延时直播的方法。

前提条件

您已完成ijkplayer源码的编译。具体操作,请参见ijkplayer中README.md介绍。

操作步骤

  1. 下载并解压ijkplayer源码。下载地址,请参见ijkplayer
  2. 下载并解压Native RTS SDK。下载地址,请参见SDK下载
  3. ijkplayer集成Native RTS SDK作为插件。

    ijkplayer集成Native RTS SDK有以下两种方式:

    集成方式 描述 优点 缺点
    拓展FFmpeg 拓展ijk中FFmpeg的demuxer。 使用更加简单,不需要根据ARTC的URL做逻辑区分。 需要重新编译FFmpeg库。
    拓展ijk ijkplayer中添AVInputFormat。 不需要编译FFmpeg。 ff_ffplay.c中需要添加部分逻辑代码。

    拓展FFmpeg

    1. 修改ijkplayer/init-android.sh中的pull_fork,只保留armv7aarm64, 并执行./init-android.sh运行该脚本。
      pull_fork "armv7a"
      pull_fork "arm64"
      说明 目前Native RTS SDK只提供armv7a和arm64的动态库,因此只需保留armv7aarm64的描述。
    2. 修改ijkplayer/android/contrib/compile-ffmpeg.sh中的FF_ACT_ARCHS_64,只保留armv7aarm64
      FF_ACT_ARCHS_64="armv7a arm64"
    3. 复制Native RTS SDK中的rtsdec.c文件至ijkplayer/android/contrib/ffmpeg-arm64/libavformatijkplayer/android/contrib/ffmpeg-armv7a/libavformat目录下。
    4. 修改Makefile文件并编译rtsdec.c文件。

      修改ijkplayer/android/contrib/ffmpeg-arm64/libavformat/Makefileijkplayer/android/contrib/ffmpeg-armv7a/libavformat/Makefile,并编译rtsdec.c文件。

      914
    5. 修改allformats.c文件。

      修改ijkplayer/android/contrib/ffmpeg-arm64/libavformat/allformats.cijkplayer/android/contrib/ffmpeg-armv7a/libavformat/allformats.c,默认支持ARTC协议。

      016
          extern AVInputFormat ff_rtc_demuxer;
          av_register_input_format(&ff_rtc_demuxer);
    6. 修改FFmpeg编译脚本config/module-lite.sh,使其支持PCM解码(Native RTS SDK输出为PCM数据)。
      # aliyun rts
      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"
    7. 编译。

      添加Android NDK环境变量,并在ijkplayer/android/contrib目录下执行./compile-ffmpeg.sh all运行该脚本。编译完成之后,检查并确保ijkplayer/android/contrib/build目录下有对应的FFmpeg编译输出文件。

    8. 修改ijkplayer/Android/compile-ijk.sh中的ACT_ABI_64,只保留armv7aarm64
      ACT_ABI_64="armv7a arm64"
    9. 加入Native RTS SDK的动态库。

      复制Native RTS SDK的动态库至对应的ijkplayer/android/contrib/build/ffmpeg-arm64/outputijkplayer/android/contrib/build/ffmpeg-armv7a/output目录中。

      复制Native RTS SDK的头文件rts_api.h和rts_messages.h分别至ijkplayer/android/contrib/build/ffmpeg-arm64/output/includeijkplayer/android/contrib/build/ffmpeg-armv7a/output/include目录中。

    10. 引入Native RTS SDK的动态库。
      修改ijkplayer/android/ijkplayer/ijkplayer-armv7a/src/main/jni/ffmpeg/Android.mkijkplayer/android/ijkplayer/ijkplayer-arm64/src/main/jni/ffmpeg/Android.mk文件。018
      include $(CLEAR_VARS)
      LOCAL_MODULE := rtssdk
      LOCAL_SRC_FILES := $(MY_APP_FFMPEG_OUTPUT_PATH)/libRtsSDK.so
      include $(PREBUILT_SHARED_LIBRARY)
    11. 修改ijkplayer/ijkmedia/ijkplayer/Android.mk文件,使ijkplayer依赖Native RTS SDK动态库。019
    12. 在ff_ffplay.c中添加RTS逻辑。

      修改ijkplayer/ijkmedia/ijkplayer/ff_ffplay.c文件,设置ARTC的AVInputFormat函数指针。

      021
          extern AVInputFormat ff_rtc_demuxer;
          extern int artc_reload(AVFormatContext *ctx);
          extern void av_set_rts_demuxer_funcs(const struct rts_glue_funcs *funcs);
          extern void artc_set_rts_param(char* key, char* value);
          extern long long artc_get_state(AVFormatContext *ctx, int key);
      
          int version = 2;
          const struct rts_glue_funcs* rts_funcs = get_rts_funcs(version);
          // set to ffmpeg plugin
          av_set_rts_demuxer_funcs(rts_funcs);
          artc_set_rts_param((char*)"AutoReconnect", (char*)"false");
    13. 编译。

      ijkplayer/android目录下执行./compile-ijk.sh all运行该脚本。

    拓展ijk

    1. 修改并运行FFmpeg编译脚本/config/module-lite.sh

      修改FFmpeg编译脚本/config/module-lite.sh,使其支持PCM解码(Native RTS SDK输出为PCM数据),并执行./module-litesh编译FFmpeg。

      # aliyun rts
      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"
    2. 加入Native RTS SDK的动态库。

      复制Native RTS SDK的动态库至对应的ijkplayer/android/contrib/build/ffmpeg-arm64/outputijkplayer/android/contrib/build/ffmpeg-armv7a/output目录中。

      复制Native RTS SDK的头文件rts_api.h和rts_messages.h分别至ijkplayer/android/contrib/build/ffmpeg-arm64/output/includeijkplayer/android/contrib/build/ffmpeg-armv7a/output/include目录中。

    3. 引入Native RTS SDK的动态库。
      修改ijkplayer/android/ijkplayer/ijkplayer-armv7a/src/main/jni/ffmpeg/Android.mkijkplayer/android/ijkplayer/ijkplayer-arm64/src/main/jni/ffmpeg/Android.mk文件。018
      include $(CLEAR_VARS)
      LOCAL_MODULE := rtssdk
      LOCAL_SRC_FILES := $(MY_APP_FFMPEG_OUTPUT_PATH)/libRtsSDK.so
      include $(PREBUILT_SHARED_LIBRARY)
    4. 修改ijkplayer/ijkmedia/ijkplayer/Android.mk文件,使ijkplayer依赖Native RTS SDK动态库。019
    5. 在ff_ffplay.c中添加RTS逻辑。

      修改ijkplayer/ijkmedia/ijkplayer/ff_ffplay.c文件,设置ARTC的AVInputFormat函数指针。

      053
      if(strncmp(is->filename, "artc://", 7) == 0) {
              extern AVInputFormat ff_rtc_demuxer;
              extern int artc_reload(AVFormatContext *ctx);
              extern void av_set_rts_demuxer_funcs(const struct rts_glue_funcs *funcs);
              extern void artc_set_rts_param(char* key, char* value);
              extern long long artc_get_state(AVFormatContext *ctx, int key);
      
              int version = 2;
              const struct rts_glue_funcs* rts_funcs = get_rts_funcs(version);
              // set to ffmpeg plugin
              av_set_rts_demuxer_funcs(rts_funcs);
              artc_set_rts_param((char*)"AutoReconnect", (char*)"false");
              is->iformat = &ff_rtc_demuxer;
          }
          else {
              if(ffp->iformat_name)
                  is->iformat = av_find_input_format(ffp->iformat_name);
          }
    6. 复制Native RTS SDK中的rtsdec.c文件至ijkplayer/ijkmedia/ijkplayer目录下,并修改该目录中android.mk文件添加rtsdec.c文件的编译描述。
      LOCAL_SRC_FILES += rtsdec.c
  4. 用户工程集成ijkplayer。
    1. 复制Native RTS SDK中的RtsNetSDK.jar文件至工程中。
    2. 在使用RTS播放的Activity内引入Native RTS SDK动态库。
      static {
          System.loadLibrary("RtsSDK");
      }
    3. 导入ijkplayer-arm64、ijkplayer-armv7a、ijkplayer-java模块。
      060
  5. 调用ijkplayer接口实现超低延时直播功能。
    • 创建ijkplayer
      mIjkPlayer = new IjkMediaPlayer();
    • 设置回调
      mIjkPlayer.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() {
          @Override
          public void onPrepared(IMediaPlayer iMediaPlayer) {
              
          }
      });
      
      mIjkPlayer.setOnInfoListener(new IMediaPlayer.OnInfoListener() {
          @Override
          public boolean onInfo(IMediaPlayer iMediaPlayer, int arg1, int arg2) {
              
      });
      
      mIjkPlayer.setOnVideoSizeChangedListener(new IMediaPlayer.OnVideoSizeChangedListener() {
          @Override
          public void onVideoSizeChanged(IMediaPlayer iMediaPlayer, int width, int height, int sarNum, int sarDen) {
             
          }
      });
      
      mIjkPlayer.setOnErrorListener(new IMediaPlayer.OnErrorListener() {
          @Override
          public boolean onError(IMediaPlayer iMediaPlayer, int framework_err, int impl_err) {
              
              return false;
          }
      });
    • 设置显示窗口
      mIjkPlayer.setSurface(surface);
    • 设置播放源
      mIjkPlayer.setDataSource("artc://<播流地址>");
    • 状态控制
      public void prepare() {
          if(mIjkPlayer != null){
              mIjkPlayer.prepareAsync();
          }
      }
      
      @Override
      public void start() {
          if(mIjkPlayer != null){
              mIjkPlayer.start();
          }
      }
      
      public void pause() {
          if(mIjkPlayer != null){
              mIjkPlayer.pause();
          }
      }
      
      public void stop() {
          if(mIjkPlayer != null){
              mIjkPlayer.stop();
          }
      }
      public void release() {
          if(mIjkPlayer != null){
              mIjkPlayer.release();
          }
      }