全部产品
Search
文档中心

:Android

更新时间:Nov 11, 2025

Tema ini menjelaskan cara mengintegrasikan Native RTS SDK dengan pemain berbasis FFmpeg di Android untuk mengimplementasikan RTS, menggunakan tag k0.8.8 dari ijkplayer sebagai contoh.

Prasyarat

Kode sumber ijkplayer telah dikompilasi. Untuk informasi lebih lanjut, lihat file README.md di repositori ijkplayer.

Prosedur

  1. Unduh dan ekstrak kode sumber ijkplayer. Untuk tautan unduhan, lihat ijkplayer.

  2. Unduh dan ekstrak Native RTS SDK. Untuk tautan unduhan, lihat Catatan rilis.

  3. Modifikasi skrip kompilasi ijkplayer.

    • Modifikasi ijkplayer-android/init-android.sh. Dalam pemanggilan `pull_fork`, pertahankan hanya arsitektur armv7a dan arm64.init-android.sh

    • Modifikasi ijkplayer-android/config/module-lite.sh untuk mendukung decoding Pulse-Code Modulation (PCM). Native RTS SDK mengeluarkan data 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"
    • Modifikasi ijkplayer-android/android/contrib/compile-ffmpeg.sh. Untuk `FF_ACT_ARCHS_64`, pertahankan hanya arsitektur armv7a dan arm64.

      FF_ACT_ARCHS_64="armv7a arm64"
    • Modifikasi ijkplayer-android/android/compile-ijk.sh. Untuk `ACT_ABI_64`, pertahankan hanya arsitektur armv7a dan arm64.

      ACT_ABI_64="armv7a arm64"
  4. Integrasikan Native RTS SDK ke dalam ijkplayer sebagai plugin. Anda dapat menggunakan salah satu dari dua metode berikut:

    Metode integrasi

    Deskripsi

    Keuntungan

    Kerugian

    Perluas FFmpeg

    Perluas demuxer FFmpeg di ijkplayer.

    Metode ini sederhana. Anda tidak perlu menambahkan logika terpisah untuk URL Alibaba Real-Time Communication (ARTC).

    Anda harus mengkompilasi ulang pustaka FFmpeg.

    Perluas ijkplayer

    Tambahkan AVInputFormat ke ijkplayer.

    Anda tidak perlu mengkompilasi FFmpeg.

    Anda harus menambahkan kode logika ke file ff_ffplay.c.

    Metode 1: Perluas FFmpeg

    1. Salin file rtsdec.c, ali_net_api.h, rts_api.h, dan rts_messages.h dari Native RTS SDK ke direktori ijkplayer/android/contrib/ffmpeg-arm64/libavformat dan ijkplayer/android/contrib/ffmpeg-armv7a/libavformat.

    2. Modifikasi file Makefile dan kompilasi file rtsdec.c.

      Modifikasi file ijkplayer/android/contrib/ffmpeg-arm64/libavformat/Makefile dan ijkplayer/android/contrib/ffmpeg-armv7a/libavformat/Makefile untuk mengkompilasi file rtsdec.c.

      rtsdec.c

    3. Modifikasi file allformats.c.

      Modifikasi file ijkplayer/android/contrib/ffmpeg-arm64/libavformat/allformats.c dan ijkplayer/android/contrib/ffmpeg-armv7a/libavformat/allformats.c untuk mendukung protokol ARTC secara default.

      allformats.c

          extern AVInputFormat ff_rtc_demuxer;
          av_register_input_format(&ff_rtc_demuxer);
    4. Kompilasi.

      Tambahkan variabel lingkungan Android NDK. Kemudian, navigasikan ke direktori ijkplayer/android/contrib dan jalankan skrip ./compile-ffmpeg.sh all. Setelah kompilasi selesai, verifikasi bahwa file output kompilasi FFmpeg ada di direktori ijkplayer/android/contrib/build.

    5. Tambahkan pustaka dinamis Native RTS SDK.

      Salin pustaka dinamis Native RTS SDK ke direktori ijkplayer/android/contrib/build/ffmpeg-arm64/output dan ijkplayer/android/contrib/build/ffmpeg-armv7a/output.

      Salin file header rts_api.h dan rts_messages.h dari Native RTS SDK ke direktori ijkplayer/android/contrib/build/ffmpeg-arm64/output/include dan ijkplayer/android/contrib/build/ffmpeg-armv7a/output/include, masing-masing.

    6. Impor pustaka dinamis Native RTS SDK.

      Modifikasi file ijkplayer/android/ijkplayer/ijkplayer-armv7a/src/main/jni/ffmpeg/Android.mk dan 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. Modifikasi file ijkplayer/ijkmedia/ijkplayer/Android.mk untuk menambahkan pustaka dinamis Native RTS SDK sebagai dependensi.019

    8. Tambahkan logika RTS ke file ff_ffplay.c.

      Modifikasi file ijkplayer/ijkmedia/ijkplayer/ff_ffplay.c untuk mengatur pointer fungsi AVInputFormat untuk ARTC.

      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");
    9. Kompilasi.

      Navigasikan ke direktori ijkplayer/android dan jalankan skrip ./compile-ijk.sh all.

    Metode 2: Perluas ijkplayer

    1. Tambahkan pustaka dinamis Native RTS SDK.

      Salin pustaka dinamis Native RTS SDK ke direktori ijkplayer/android/contrib/build/ffmpeg-arm64/output dan ijkplayer/android/contrib/build/ffmpeg-armv7a/output.

      Salin file header rts_api.h dan rts_messages.h dari Native RTS SDK ke direktori ijkplayer/android/contrib/build/ffmpeg-arm64/output/include dan ijkplayer/android/contrib/build/ffmpeg-armv7a/output/include, masing-masing.

    2. Impor pustaka dinamis Native RTS SDK.

      Modifikasi file ijkplayer/android/ijkplayer/ijkplayer-armv7a/src/main/jni/ffmpeg/Android.mk dan 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. Modifikasi file ijkplayer/ijkmedia/ijkplayer/Android.mk untuk menambahkan pustaka dinamis Native RTS SDK sebagai dependensi.019

    4. Tambahkan logika RTS ke file ff_ffplay.c.

      Modifikasi file ijkplayer/ijkmedia/ijkplayer/ff_ffplay.c untuk mengatur pointer fungsi AVInputFormat untuk ARTC.

      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);
          }
    5. Salin file rtsdec.c dari Native RTS SDK ke direktori ijkplayer/ijkmedia/ijkplayer. Kemudian, modifikasi file android.mk di direktori yang sama untuk menambahkan deskripsi kompilasi untuk file rtsdec.c.

      LOCAL_SRC_FILES += rtsdec.c
  5. Integrasikan ijkplayer ke dalam proyek Anda.

    1. Salin file RtsNetSDK.jar dari Native RTS SDK ke proyek Anda.

    2. Impor pustaka dinamis Native RTS SDK ke dalam Activity yang digunakan untuk pemutaran RTS.

      static {
          System.loadLibrary("RtsSDK");
      }
    3. Impor modul ijkplayer-arm64, ijkplayer-armv7a, dan ijkplayer-java.

      060

  6. Panggil API ijkplayer untuk mengimplementasikan fitur RTS.

    • Buat instance ijkplayer

      mIjkPlayer = new IjkMediaPlayer();
    • Atur callback

      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;
          }
      });
    • Atur jendela tampilan

      mIjkPlayer.setSurface(surface);
    • Atur sumber pemutaran

      mIjkPlayer.setDataSource("artc://<streaming URL>");
    • Kontrol status

      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();
          }
      }

Dengarkan event RTS

  • Dengarkan callback pesan dari Native RTS SDK di ijkplayer

    Buka proyek ijkplayer/android/ijkplayer.

    1. Modifikasi file ff_ffplay.c. Sisipkan blok kode berikut setelah metode 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:receive artc message
      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:send message to app
          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. Modifikasi file ff_ffplay.c. Sisipkan blok kode RTS berikut ke dalam metode 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 code block begin***
             || !strcmp(s->iformat->name, "artc")
             // ***rts code block end***
          )
              return 1;
      
          if(s->pb && (   !strncmp(s->filename, "rtp:", 4)
                       || !strncmp(s->filename, "udp:", 4)
                      )
          )
              return 1;
          return 0;
       }                        
    3. Modifikasi file ff_ffplay.c. Sisipkan blok kode RTS berikut ke dalam metode 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 code block begin***
        ic->opaque = ffp;
        ic->control_message_cb = onArtcDemuxerMessage;
        // ***rts code block end***
      
        ......
        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 code block begin***
        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 code block end***
        ......
        pkt->flags = 0;
        // ***rts code block begin***
        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 ) { // accelerate
                 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) { // restore speed
                 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 code block end***
        ......
      }
    4. Modifikasi file ff_ffmsg.h untuk menambahkan deklarasi antarmuka pesan RTS.

      #define FFP_MSG_ARTC_DIRECTCOMPONENTMSG     3000
    5. Kirim pesan RTS ke lapisan atas.

      • Modifikasi file ijkplayer_android_def.h untuk menambahkan jenis enumerasi pesan RTS.

        enum media_event_type {
            // Other code is omitted.
            MEDIA_ARTC_MESSAGE      = 3000,     // aliyun rts : msg info key
        };
      • Modifikasi file ijkplayer_jni.c untuk mengirim pesan RTS ke lapisan atas.

        static void message_loop_n(JNIEnv *env, IjkMediaPlayer *mp)
        {
            //... Other code is omitted.
        
            while (1) {
                switch (msg.what) {
                    //aliyun rts: post artc event
                    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;
                }
            }
        }
      • Modifikasi file IjkMediaPlayer.java untuk menerima pesan 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:post event
                        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;
                    }
                }
            }

        Tambahkan callback pesan RTS ke file IMediaPlayer.java.

        //aliyun rts:artc message callback
            interface OnARTCMessageListener{
                void onMessage(String name,int externValue,String extraMsg);
            }

        Tambahkan pengaturan listener ke file AbstractMediaPlayer.java.

        //aliyun rts : set artc message listener
            public final void setOnARTCMessageListener(OnARTCMessageListener listener){
                this.mOnARTCMessageListener = listener;
            }
        
            //aliyun rts : call back
            protected final void notifyOnARTCMessage(String name, int externValue, String extraMsg){
                if(mOnARTCMessageListener != null){
                    mOnARTCMessageListener.onMessage(name, externValue, extraMsg);
                }
            }

        Jalankan skrip ./compile-ijk.sh untuk mengompilasi ulang.

  • Tambahkan metode coba ulang untuk Native RTS SDK di ijkplayer

    1. Modifikasi file ff_ffplay.h untuk menambahkan definisi variabel `rts_reload_flag`.

      int       rts_reload_flag;
    2. Modifikasi file ff_ffplay.c. Sisipkan blok metode berikut setelah metode 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. Modifikasi file ff_ffplay.c. Sisipkan blok kode RTS berikut ke dalam metode 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 code block begin***
         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 code block end***
         ......
      }
    4. Modifikasi file ijkplayer.h untuk menambahkan definisi metode `ijkmp_rts_reload`.

      void            ijkmp_rts_reload(IjkMediaPlayer *mp);
    5. Modifikasi file ijkplayer.c untuk mengimplementasikan metode `ijkmp_rts_reload`.

      void ijkmp_rts_reload(IjkMediaPlayer *mp)
      {
          ffp_rts_reload(mp->ffplayer);
      }
    6. Tambahkan antarmuka `rtsReload` ke lapisan atas.

      • Modifikasi file ijkplayer_jni.c untuk menambahkan antarmuka `reload`.

        //aliyun rts : reload native
        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
            { "_reload",                "()V",      (void *) IjkMediaPlayer_reload },
        }
      • Modifikasi file IMediaPlayer.java untuk menambahkan antarmuka `reload`.

        //aliyun rts: reload
            void reload();
      • Modifikasi file IjkMediaPlayer.java untuk memanggil metode asli `reload`.

        //aliyun rts : native method
            private native void _reload();
        
            @Override
            public void reload() {
                _reload();
            }
  • Gunakan callback pesan RTS untuk mengimplementasikan degradasi aliran

    • Dengarkan callback pesan RTS.

      mIjkPlayer.setOnARTCMessageListener(new IMediaPlayer.OnARTCMessageListener() {
          @Override
          public void onMessage(String name, int externValue, String extraMsg) {}
      });
    • Implementasikan logika degradasi aliran dalam metode callback pesan RTS.

      Degradasi aliran adalah strategi di mana Anda mengubah awalan string URL sumber pemain dari `artc://` menjadi `rtmp://` atau `http://xxxx.flv`, memperbarui URL sumber pemain, dan kemudian memulai ulang pemutaran.

      if (SOURCE_URL.startsWith("artc://")) {
          mIjkPlayer.stop();// Hentikan pemutaran.
          mIjkPlayer.reset();// Reset pemain. Jika Anda menggunakan kembali ijkplayer tanpa meresetnya, aplikasi akan crash.
          mIjkPlayer.setDataSource("http://xxx.flv");
          mIjkPlayer.prepareAsync();
      }

      Selama startup pemutaran atau transmisi langsung, callback peristiwa pemain mungkin menerima pesan dari komponen pemutaran. Parsing string JSON deskripsi peristiwa. Bidang `code` berisi pesan dari RTS SDK. Jika pesannya adalah `E_DNS_FAIL`, `E_AUTH_FAIL`, `E_CONN_TIMEOUT`, `E_SUB_TIMEOUT`, atau `E_SUB_NO_STREAM`, turunkan aliran. Jika pesannya adalah `E_STREAM_BROKEN`, coba ulang pemutaran sekali. Jika pesan diterima lagi, turunkan aliran. Jika pesannya adalah `E_RECV_STOP_SIGNAL`, hentikan pemutaran secara langsung. Degradasi aliran tidak diperlukan.

      // Kelas enumerasi informasi Rts kustom
      public enum RtsError {
       /**
        * Gagal resolusi DNS.
        */
       E_DNS_FAIL(20001),
       /**
        * Otentikasi gagal.
        */
       E_AUTH_FAIL(20002),
       /**
        * Waktu habis sinyal koneksi.
        */
       E_CONN_TIMEOUT(20011),
       /**
        * Sinyal langganan mengembalikan kesalahan atau waktu habis.
        */
       E_SUB_TIMEOUT(20012),
       /**
        * Aliran yang dilanggan tidak ada.
        */
       E_SUB_NO_STREAM(20013),
       /**
        * Timeout media. Tidak ada paket audio atau video yang diterima.
        */
       E_STREAM_BROKEN(20052),
       /**
        * Menerima sinyal berhenti dari 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: Implementasikan logika degradasi.
                  }
              }
          }
      });