すべてのプロダクト
Search
ドキュメントセンター

:Android

最終更新日:Nov 10, 2025

このトピックでは、Android 上で Native RTS SDK を FFmpeg ベースのプレーヤーと統合して RTS を実装する方法について説明します。このトピックでは、ijkplayer の k0.8.8 タグを例として使用します。

前提条件

ijkplayer のソースコードがコンパイル済みであること。詳細については、「ijkplayer」リポジトリの README.md ファイルをご参照ください。

手順

  1. ijkplayer のソースコードをダウンロードして解凍します。ダウンロードリンクについては、「ijkplayer」をご参照ください。

  2. Native RTS SDK をダウンロードして解凍します。ダウンロードリンクについては、「リリースノート」をご参照ください。

  3. ijkplayer のコンパイルスクリプトを変更します。

    • ijkplayer-android/init-android.sh を変更します。`pull_fork` 呼び出しで、armv7a と arm64 アーキテクチャのみを保持します。init-android.sh

    • ijkplayer-android/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"
    • ijkplayer-android/android/contrib/compile-ffmpeg.sh を変更します。`FF_ACT_ARCHS_64` については、armv7a と arm64 アーキテクチャのみを保持します。

      FF_ACT_ARCHS_64="armv7a arm64"
    • ijkplayer-android/android/compile-ijk.sh を変更します。`ACT_ABI_64` については、armv7a と arm64 アーキテクチャのみを保持します。

      ACT_ABI_64="armv7a arm64"
  4. Native RTS SDK をプラグインとして ijkplayer に統合します。次の 2 つの方法のいずれかを使用できます。

    統合方法

    説明

    利点

    欠点

    FFmpeg の拡張

    ijkplayer の FFmpeg のデマルチプレクサを拡張します。

    この方法は簡単です。Alibaba Real-Time Communication (ARTC) URL 用に個別のロジックを追加する必要はありません。

    FFmpeg ライブラリを再コンパイルする必要があります。

    ijkplayer の拡張

    AVInputFormat を ijkplayer に追加します。

    FFmpeg をコンパイルする必要はありません。

    ff_ffplay.c ファイルにロジックコードを追加する必要があります。

    方法 1: FFmpeg の拡張

    1. Native RTS SDK から rtsdec.c、ali_net_api.h、rts_api.h、および rts_messages.h ファイルを ijkplayer/android/contrib/ffmpeg-arm64/libavformat および ijkplayer/android/contrib/ffmpeg-armv7a/libavformat ディレクトリにコピーします。

    2. Makefile ファイルを変更し、rtsdec.c ファイルをコンパイルします。

      ijkplayer/android/contrib/ffmpeg-arm64/libavformat/Makefile および ijkplayer/android/contrib/ffmpeg-armv7a/libavformat/Makefile ファイルを変更して、rtsdec.c ファイルをコンパイルします。

      rtsdec.c

    3. allformats.c ファイルを変更します。

      ijkplayer/android/contrib/ffmpeg-arm64/libavformat/allformats.c および ijkplayer/android/contrib/ffmpeg-armv7a/libavformat/allformats.c ファイルを変更して、デフォルトで ARTC プロトコルをサポートするようにします。

      allformats.c

          extern AVInputFormat ff_rtc_demuxer;
          av_register_input_format(&ff_rtc_demuxer);
    4. コンパイル。

      Android NDK 環境変数を追加します。次に、ijkplayer/android/contrib ディレクトリに移動し、./compile-ffmpeg.sh all スクリプトを実行します。コンパイルが完了したら、FFmpeg コンパイル出力ファイルが ijkplayer/android/contrib/build ディレクトリに存在することを確認します。

    5. Native RTS SDK の動的ライブラリを追加します。

      Native RTS SDK の動的ライブラリを ijkplayer/android/contrib/build/ffmpeg-arm64/output および ijkplayer/android/contrib/build/ffmpeg-armv7a/output ディレクトリにコピーします。

      Native RTS SDK から rts_api.h および rts_messages.h ヘッダーファイルを、それぞれ ijkplayer/android/contrib/build/ffmpeg-arm64/output/include および ijkplayer/android/contrib/build/ffmpeg-armv7a/output/include ディレクトリにコピーします。

    6. Native RTS SDK の動的ライブラリをインポートします。

      ijkplayer/android/ijkplayer/ijkplayer-armv7a/src/main/jni/ffmpeg/Android.mk および ijkplayer/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)
    7. ijkplayer/ijkmedia/ijkplayer/Android.mk ファイルを変更して、Native RTS SDK 動的ライブラリを依存関係として追加します。019

    8. RTS ロジックを ff_ffplay.c ファイルに追加します。

      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);
          // ffmpeg プラグインに設定
          av_set_rts_demuxer_funcs(rts_funcs);
          artc_set_rts_param((char*)"AutoReconnect", (char*)"false");
    9. コンパイル。

      ijkplayer/android ディレクトリに移動し、./compile-ijk.sh all スクリプトを実行します。

    方法 2: ijkplayer の拡張

    1. Native RTS SDK の動的ライブラリを追加します。

      Native RTS SDK の動的ライブラリを ijkplayer/android/contrib/build/ffmpeg-arm64/output および ijkplayer/android/contrib/build/ffmpeg-armv7a/output ディレクトリにコピーします。

      Native RTS SDK から rts_api.h および rts_messages.h ヘッダーファイルを、それぞれ ijkplayer/android/contrib/build/ffmpeg-arm64/output/include および ijkplayer/android/contrib/build/ffmpeg-armv7a/output/include ディレクトリにコピーします。

    2. Native RTS SDK の動的ライブラリをインポートします。

      ijkplayer/android/ijkplayer/ijkplayer-armv7a/src/main/jni/ffmpeg/Android.mk および ijkplayer/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)
    3. ijkplayer/ijkmedia/ijkplayer/Android.mk ファイルを変更して、Native RTS SDK 動的ライブラリを依存関係として追加します。019

    4. RTS ロジックを ff_ffplay.c ファイルに追加します。

      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);
              // ffmpeg プラグインに設定
              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);
          }
    5. Native RTS SDK から rtsdec.c ファイルを ijkplayer/ijkmedia/ijkplayer ディレクトリにコピーします。次に、同じディレクトリにある android.mk ファイルを変更して、rtsdec.c ファイルのコンパイル記述を追加します。

      LOCAL_SRC_FILES += rtsdec.c
  5. ijkplayer をプロジェクトに統合します。

    1. Native RTS SDK から RtsNetSDK.jar ファイルをプロジェクトにコピーします。

    2. Native RTS SDK の動的ライブラリを、RTS 再生に使用される Activity にインポートします。

      static {
          System.loadLibrary("RtsSDK");
      }
    3. ijkplayer-arm64、ijkplayer-armv7a、および ijkplayer-java モジュールをインポートします。

      060

  6. ijkplayer API を呼び出して RTS 機能を実装します。

    • 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://<ストリーミング URL>");
    • ステータスの制御

      public void prepare() {
          if(mIjkPlayer != null){
              mIjkPlayer.prepareAsync();
          }
      }
      
      @Override
      public void start() {
          if(mIjkPlayer != null){
              mIjkPlayer.start();
          }
      }
      
      public void stop() {
          if(mIjkPlayer != null){
              mIjkPlayer.stop();
          }
      }
      public void release() {
          if(mIjkPlayer != null){
              mIjkPlayer.release();
          }
      }

RTS イベントのリッスン

  • ijkplayer での Native RTS SDK からのメッセージコールバックのリッスン

    ijkplayer/android/ijkplayer プロジェクトを開きます。

    1. ff_ffplay.c ファイルを変更します。次のコードブロックを static int audio_open(FFPlayer *opaque, int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate, struct AudioParams *audio_hw_params){} メソッドの後に挿入します。

      extern int artcDemuxerMessage(struct AVFormatContext *s, int type, void *data, size_t data_size);
      //aliyun rts: artc メッセージを受信
      int onArtcDemuxerMessage(struct AVFormatContext *s, int type, void *data, size_t data_size)
      {
          return artcDemuxerMessage(s, type, data, data_size);
      }
      
      int artcDemuxerMessage(struct AVFormatContext *s, int type, void *data, size_t data_size)
      {
          //aliyun rts: アプリにメッセージを送信
          FFPlayer *ffp = (FFPlayer *)s->opaque;
          const char *data_msg = (const char *)data;
          ffp_notify_msg4(ffp,FFP_MSG_ARTC_DIRECTCOMPONENTMSG,type,0,data_msg,data_size);
          return 0;
      }
    2. ff_ffplay.c ファイルを変更します。次の RTS コードブロックを static int is_realtime(AVFormatContext *s){} メソッドに挿入します。

      static int is_realtime(AVFormatContext *s)
      {
          if(   !strcmp(s->iformat->name, "rtp")
             || !strcmp(s->iformat->name, "rtsp")
             || !strcmp(s->iformat->name, "sdp")
             // ***rts コードブロック開始***
             || !strcmp(s->iformat->name, "artc")
             // ***rts コードブロック終了***
          )
              return 1;
      
          if(s->pb && (   !strncmp(s->filename, "rtp:", 4)
                       || !strncmp(s->filename, "udp:", 4)
                      )
          )
              return 1;
          return 0;
       }                        
    3. ff_ffplay.c ファイルを変更します。次の RTS コードブロックを static int read_thread(void *arg){} メソッドに挿入します。

      static int read_thread(void *arg)
      {
        ......
        ic = avformat_alloc_context();
        if (!ic) {
          av_log(NULL, AV_LOG_FATAL, "Could not allocate context.\n");
          ret = AVERROR(ENOMEM);
          goto fail;
        }
      
        // ***rts コードブロック開始***
        ic->opaque = ffp;
        ic->control_message_cb = onArtcDemuxerMessage;
        // ***rts コードブロック終了***
      
        ......
        if (ffp->skip_calc_frame_rate) {
           av_dict_set_int(&ic->metadata, "skip-calc-frame-rate", ffp->skip_calc_frame_rate, 0);
           av_dict_set_int(&ffp->format_opts, "skip-calc-frame-rate", ffp->skip_calc_frame_rate, 0);
        }
      
        // ***rts コードブロック開始***
        if(strncmp(is->filename, "artc://", 7) == 0) {
           extern AVInputFormat ff_rtc_demuxer;
           is->iformat = &ff_rtc_demuxer;
        } else {
           if(ffp->iformat_name)
             is->iformat = av_find_input_format(ffp->iformat_name);
         }
        // ***rts コードブロック終了***
        ......
        pkt->flags = 0;
        // ***rts コードブロック開始***
        if(strncmp(is->filename, "artc://", 7) == 0) {
            bool videoExist = is->video_stream >= 0;
          bool audioExist = is->audio_stream >= 0;
          // av_log(NULL, AV_LOG_INFO, "videoDuration %lld audioDuration %lld rate %f videoframeQue %d audioFrameque %d\n",
          // is->videoq.duration, is->audioq.duration, ffp->pf_playback_rate,
          // frame_queue_nb_remaining(&is->pictq), frame_queue_nb_remaining(&is->sampq));
          if(!videoExist) {
             if(is->audioq.duration > 300 ) { // 加速
                 if(ffp->pf_playback_rate <= 1.0) {
                     ffp->pf_playback_rate = 1.3;
                     ffp->pf_playback_rate_changed = 1;
                     av_log(NULL, AV_LOG_INFO, "aliyun rts set rate to %f\n", ffp->pf_playback_rate);
                 }
             }
             else if(is->audioq.duration < 200) { // 速度を復元
                 if(ffp->pf_playback_rate > 1.0) {
                     ffp->pf_playback_rate = 1.0;
                     ffp->pf_playback_rate_changed = 1;
                     av_log(NULL, AV_LOG_INFO, "aliyun rts restore rate 1.0\n");
                  }
             }
          }
          else if((!videoExist || (videoExist && is->videoq.duration > 300)) && (!audioExist || (audioExist && is->audioq.duration > 300))) {
             if(ffp->pf_playback_rate <= 1) {
                 ffp->pf_playback_rate = 1.3;
                 ffp->pf_playback_rate_changed = 1;
                 av_log(NULL, AV_LOG_INFO, "aliyun rts set rate 1.1\n");
             }
          } else if((videoExist && is->videoq.duration <= 100) ||  (audioExist && is->audioq.duration <= 100)){
             if(ffp->pf_playback_rate > 1) {
                 ffp->pf_playback_rate = 1;
                 ffp->pf_playback_rate_changed = 1;
                 av_log(NULL, AV_LOG_INFO, "aliyun rts set rate 1\n");
              }
          }
        }
        // ***rts コードブロック終了***
        ......
      }
    4. ff_ffmsg.h ファイルを変更して、RTS メッセージのインターフェイス宣言を追加します。

      #define FFP_MSG_ARTC_DIRECTCOMPONENTMSG     3000
    5. RTS メッセージを上位レイヤーに送信します。

      • ijkplayer_android_def.h ファイルを変更して、RTS メッセージの列挙型を追加します。

        enum media_event_type {
            // 他のコードは省略
            MEDIA_ARTC_MESSAGE      = 3000,     // aliyun rts : msg info key
        };
      • ijkplayer_jni.c ファイルを変更して、RTS メッセージを上位レイヤーに送信します。

        static void message_loop_n(JNIEnv *env, IjkMediaPlayer *mp)
        {
            //... 他のコードは省略
        
            while (1) {
                switch (msg.what) {
                    //aliyun rts: artc イベントを投稿
                    case FFP_MSG_ARTC_DIRECTCOMPONENTMSG:
                        if (msg.obj) {
                            const char * result = (const char *)msg.obj;
                            ALOGE("aliyun rts : FFP_MSG_ARTC_DIRECTCOMPONENTMSG = %d , %s\n", msg.arg1,result);
                            jstring data_msg = (*env)->NewStringUTF(env, result);
                            post_event2(env, weak_thiz, MEDIA_ARTC_MESSAGE, msg.arg1, 0, data_msg);
                            J4A_DeleteLocalRef__p(env, &data_msg);
                        }
                        else {
                            post_event2(env, weak_thiz, MEDIA_ARTC_MESSAGE, 0, 0, NULL);
                        }
        
                        break;
                }
            }
        }
      • IjkMediaPlayer.java ファイルを変更して、RTS メッセージを受信します。

        private static class EventHandler extends Handler {
        
                @Override
                public void handleMessage(Message msg) {
                    IjkMediaPlayer player = mWeakPlayer.get();
                    if (player == null || player.mNativeMediaPlayer == 0) {
                        DebugLog.w(TAG,
                                "IjkMediaPlayer went away with unhandled events");
                        return;
                    }
        
                    switch (msg.what) {
                        //aliyun artc: イベントを投稿
                        case MEDIA_ARTC_MESSAGE:
                            if(msg.arg1 == FFP_ARTC_CONNECT_LOST){
                                player.notifyOnARTCMessage("NetWorkDisconnect",0,"");
                            }else if(msg.arg1 == FFP_ARTC_RECOVERED){
                                player.notifyOnARTCMessage("NetWorkRetrySuccess",0,"");
                            }else{
                                if(msg.obj == null){
                                    player.notifyOnARTCMessage("DirectComponentMSG",0, "");
                                }else{
                                    String result = (String) msg.obj;
                                    result = result.replaceAll("\"","");
                                    player.mAliyunRTSMsgBuilder = new StringBuilder("{\"content\":");
                                    player.mAliyunRTSMsgBuilder.append("\"").append(result).append("\"}");
                                    player.notifyOnARTCMessage("DirectComponentMSG",0, (String) result);
                                }
                            }
                            break;
                    }
                }
            }

        RTS メッセージコールバックを IMediaPlayer.java ファイルに追加します。

        //aliyun rts: artc メッセージコールバック
            interface OnARTCMessageListener{
                void onMessage(String name,int externValue,String extraMsg);
            }

        リスナー設定を AbstractMediaPlayer.java ファイルに追加します。

        //aliyun rts : artc メッセージリスナーを設定
            public final void setOnARTCMessageListener(OnARTCMessageListener listener){
                this.mOnARTCMessageListener = listener;
            }
        
            //aliyun rts : コールバック
            protected final void notifyOnARTCMessage(String name,int externValue,String extraMsg){
                if(mOnARTCMessageListener != null){
                    mOnARTCMessageListener.onMessage(name,externValue,extraMsg);
                }
            }

        ./compile-ijk.sh スクリプトを実行して再コンパイルします。

  • ijkplayer に Native RTS SDK のリトライメソッドを追加する

    1. ff_ffplay.h ファイルを変更して、`rts_reload_flag` 変数の定義を追加します。

      int       rts_reload_flag;
    2. ff_ffplay.c ファイルを変更します。次のメソッドブロックを static int audio_open(FFPlayer *opaque, int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate, struct AudioParams *audio_hw_params){} メソッドの後に挿入します。

      extern int artc_reload(AVFormatContext *ctx);
      
      void ffp_rts_reload(FFPlayer *ffp){
         if(rts_reload_flag == 0)
         {
            rts_reload_flag = 1;
         }
      }
    3. ff_ffplay.c ファイルを変更します。次の RTS コードブロックを static int read_thread(void *arg){} メソッドに挿入します。

      static int read_thread(void *arg)
      {
           ......
      #ifdef FFP_MERGE
         if (is->paused != is->last_paused) {
            is->last_paused = is->paused;
            if (is->paused)
               is->read_pause_return = av_read_pause(ic);
             else
                av_read_play(ic);
         }
      #endif
         // ***rts コードブロック開始***
         if(rts_reload_flag){
              rts_reload_flag = 0;
              av_log(ffp, AV_LOG_ERROR, "param  == ffp_rts_reload\n");
              VideoState *is = ffp->is;
              AVFormatContext *ic = is->ic;
              artc_reload(ic);
          }
         // ***rts コードブロック終了***
         ......
      }
    4. ijkplayer.h ファイルを変更して、`ijkmp_rts_reload` メソッドの定義を追加します。

      void            ijkmp_rts_reload(IjkMediaPlayer *mp);
    5. ijkplayer.c ファイルを変更して、`ijkmp_rts_reload` メソッドを実装します。

      void ijkmp_rts_reload(IjkMediaPlayer *mp)
      {
          ffp_rts_reload(mp->ffplayer);
      }
    6. 上位レイヤーに `rtsReload` インターフェイスを追加します。

      • ijkplayer_jni.c ファイルを変更して、`reload` インターフェイスを追加します。

        //aliyun rts : ネイティブリロード
        static void
        IjkMediaPlayer_reload(JNIEnv *env,jobject thiz)
        {
            IjkMediaPlayer *mp = jni_get_media_player(env,thiz);
            ijkmp_rts_reload(mp);
        LABEL_RETURN:
            ijkmp_dec_ref_p(&mp);
        }
        
        
        static JNINativeMethod g_methods[] = {
            //aliyun rts: リロード
            { "_reload",                "()V",      (void *) IjkMediaPlayer_reload },
        }
      • IMediaPlayer.java ファイルを変更して、`reload` インターフェイスを追加します。

        //aliyun rts: リロード
            void reload();
      • IjkMediaPlayer.java ファイルを変更して、ネイティブの `reload` メソッドを呼び出します。

        //aliyun rts : ネイティブメソッド
            private native void _reload();
        
            @Override
            public void reload() {
                _reload();
            }
  • RTS メッセージコールバックを使用してストリームのデグレードを実装する

    • RTS メッセージコールバックをリッスンします。

      mIjkPlayer.setOnARTCMessageListener(new IMediaPlayer.OnARTCMessageListener() {
          @Override
          public void onMessage(String name, int externValue, String extraMsg) {}
      });
    • RTS メッセージコールバックメソッドでストリームのデグレードロジックを実装します。

      ストリームのデグレードは、プレーヤーのソース URL 文字列のプレフィックスを `artc://` から `rtmp://` または `http://xxxx.flv` に変更し、プレーヤーの URL ソースを更新してから再生を再開する戦略です。

      if (SOURCE_URL.startsWith("artc://")) {
          mIjkPlayer.stop();// 再生を停止します。
          mIjkPlayer.reset();// プレーヤーをリセットします。リセットせずに ijkplayer を再利用すると、アプリケーションがクラッシュします。
          mIjkPlayer.setDataSource("http://xxx.flv");
          mIjkPlayer.prepareAsync();
      }

      再生の開始時またはライブストリーミング中に、プレーヤーのイベントコールバックが再生コンポーネントからパススルーメッセージを受信することがあります。イベント記述の JSON 文字列を解析します。`code` フィールドには、RTS SDK からのメッセージが含まれています。メッセージが `E_DNS_FAIL`、`E_AUTH_FAIL`、`E_CONN_TIMEOUT`、`E_SUB_TIMEOUT`、または `E_SUB_NO_STREAM` の場合は、ストリームをデグレードします。メッセージが `E_STREAM_BROKEN` の場合は、再生を一度リトライします。メッセージが再度受信された場合は、ストリームをデグレードします。メッセージが `E_RECV_STOP_SIGNAL` の場合は、再生を直接停止します。ストリームのデグレードは必要ありません。

      // カスタム Rts 情報列挙クラス
      public enum RtsError {
       /**
        * DNS 解決に失敗しました。
        */
       E_DNS_FAIL(20001),
       /**
        * 認証に失敗しました。
        */
       E_AUTH_FAIL(20002),
       /**
        * 接続シグナリングがタイムアウトしました。
        */
       E_CONN_TIMEOUT(20011),
       /**
        * サブスクリプションシグナリングがエラーを返すか、タイムアウトしました。
        */
       E_SUB_TIMEOUT(20012),
       /**
        * サブスクライブしたストリームが存在しません。
        */
       E_SUB_NO_STREAM(20013),
       /**
        * メディアタイムアウト。音声または動画パケットが受信されませんでした。
        */
       E_STREAM_BROKEN(20052),
       /**
        * CDN から停止信号を受信しました。
        */
       E_RECV_STOP_SIGNAL(20061);
      
       private int code;
      
       RtsError(int code) {
        this.code = code;
       }
      
       public int getCode() {
        return code;
       }
      
       public void setCode(int code) {
        this.code = code;
       }
      }
      
      
      mIjkPlayer.setOnARTCMessageListener(new IMediaPlayer.OnARTCMessageListener() {
          @Override
          public void onMessage(String name, int externValue, String msg) {
              if("DirectComponentMSG".equals(name)){
                  if (msg.contains("code=" + RtsError.E_DNS_FAIL.getCode()) || msg.contains("code=" + RtsError.E_AUTH_FAIL.getCode())
                      || msg.contains("code=" + RtsError.E_CONN_TIMEOUT.getCode()) || msg.contains("code=" + RtsError.E_SUB_TIMEOUT.getCode())
                      || msg.contains("code=" + RtsError.E_SUB_NO_STREAM.getCode()) || msg.contains("code=" + RtsError.E_STREAM_BROKEN.getCode())
                      || msg.contains("code=" + RtsError.E_RECV_STOP_SIGNAL.getCode())) {
                      //TODO: デグレードロジックを実装します。
                  }
              }
          }
      });