All Products
Search
Document Center

ApsaraVideo Live:Panggilan audio dan video di Android

Last Updated:Aug 20, 2026

Integrasikan ARTC SDK ke dalam proyek Android Anda untuk membangun aplikasi audio dan video real-time guna streaming interaktif dan panggilan video.

Konsep utama

Sebelum memulai, tinjau konsep-konsep utama berikut:

  • ARTC SDK: SDK Alibaba Cloud untuk interaksi audio dan video real-time.

  • GRTN: Global Realtime Transport Network milik Alibaba Cloud yang menyediakan layanan komunikasi audio dan video dengan latensi ultra-rendah, kualitas tinggi, aman, dan andal.

  • channel: Ruangan virtual untuk interaksi audio dan video real-time.

  • host: Peran yang memungkinkan pengguna untuk memublikasikan aliran audio dan video di dalam channel serta berlangganan aliran yang dipublikasikan oleh host lainnya.

  • viewer: Peran yang memungkinkan pengguna untuk berlangganan aliran audio dan video di dalam channel tetapi tidak dapat memublikasikan aliran.

  1. Panggil setChannelProfile untuk mengatur skenario channel, lalu panggil joinChannel untuk bergabung ke channel:

    • Dalam skenario panggilan video, semua pengguna memiliki peran host dan dapat memublikasikan serta berlangganan aliran.

    • Dalam skenario streaming interaktif, Anda harus memanggil setClientRole untuk mengatur peran pengguna. Atur peran menjadi host bagi pengguna yang akan memublikasikan aliran. Jika pengguna hanya perlu berlangganan aliran, atur perannya menjadi viewer.

  2. Setelah bergabung ke channel, peran pengguna menentukan apakah mereka dapat memublikasikan atau berlangganan aliran:

    • Semua pengguna di dalam channel dapat berlangganan aliran audio dan videonya.

    • Host dapat memublikasikan aliran audio dan video di dalam channel.

    • Jika viewer perlu memublikasikan aliran, mereka harus memanggil metode setClientRole untuk mengganti perannya menjadi host.

Proyek contoh

Alibaba Cloud menyediakan proyek contoh ARTC SDK sumber terbuka untuk interaksi audio dan video real-time. Anda dapat mengunduh proyek tersebut atau melihat kode sumber contoh.

Prasyarat

Sebelum menjalankan proyek contoh, pastikan lingkungan pengembangan Anda memenuhi persyaratan berikut:

  • Alat pengembangan: Android Studio 2020.3.1 atau versi yang lebih baru.

  • Perangkat uji: Perangkat fisik yang menjalankan Android 5.0 (API level 21) atau versi yang lebih baru.

    Catatan

    Gunakan perangkat fisik untuk pengujian. Emulator mungkin tidak memiliki fungsi yang diperlukan.

  • Lingkungan jaringan: Koneksi jaringan yang stabil.

  • Persiapan aplikasi: Dapatkan AppID dan AppKey untuk aplikasi Anda. Untuk detailnya, lihat Buat aplikasi.

Buat proyek

Bagian ini menjelaskan cara membuat proyek dan menambahkan izin yang diperlukan untuk audio dan video real-time. Lewati bagian ini jika Anda sudah memiliki proyek.

  1. Buka Android Studio dan pilih New Project.

  2. Pilih Phone and Tablet, lalu pilih templat awal. Contoh ini menggunakan Empty Views Activity.

  3. Atur nama proyek, nama paket, lokasi penyimpanan, bahasa pengembangan (Java pada contoh ini), dan bahasa konfigurasi build (Groovy DSL pada contoh ini).

  4. Klik Finish dan tunggu hingga proyek selesai disinkronkan.

Konfigurasi proyek

Langkah 1: Impor SDK

Integrasi otomatis Maven (disarankan)

  1. Buka file settings.gradle di direktori root proyek Anda dan tambahkan repositori Maven yang dibutuhkan oleh ARTC SDK ke blok dependencyResolutionManagement/repositories sebagai berikut:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        // Tambahkan repositori Maven untuk ARTC SDK
        maven { url 'https://maven.aliyun.com/repository/google' }
        maven { url 'https://maven.aliyun.com/repository/public' }
    }
}

Catatan: Jika Anda menggunakan versi Android Gradle Plugin sebelum 7.1.0, blok ini mungkin tidak tersedia di file settings.gradle. Untuk informasi lebih lanjut, lihat Android Gradle Plugin 7.1. Dalam kasus ini, gunakan alternatif berikut:

Alternatif untuk versi Android Gradle Plugin sebelum 7.1.0

Buka file build.gradle di direktori root proyek Anda dan tambahkan URL repositori Maven ke blok allprojects/repositories:

allprojects {
    repositories {
        ...
        // Tambahkan repositori Maven untuk ARTC SDK
        maven { url 'https://maven.aliyun.com/repository/google' }
        maven { url 'https://maven.aliyun.com/repository/public' }
    }
}
  1. Buka file app/build.gradle dan tambahkan dependensi ARTC SDK ke blok dependencies. Informasi versi dapat ditemukan di Unduh dan integrasi SDK. Ganti ${latest_version} dengan nomor versi tertentu. Versi terbaru adalah 7.11.0.

dependencies {
    // Tambahkan dependensi untuk SDK audio dan video real-time// Ganti ${latest_version} dengan nomor versi tertentu
    implementation 'com.aliyun.aio:AliVCSDK_ARTC:${latest_version}'// Untuk versi 7.4.0 dan sebelumnya, Anda harus menambahkan dependensi keep// implementation 'com.aliyun.aio.keep:keep:1.0.1'
}

Jika Anda menggunakan Android Gradle Plugin 8.1 atau versi yang lebih baru, Android Studio merekomendasikan agar Anda Memigrasi dependensi proyek ke katalog versi.

Integrasi manual

  1. Unduh file AAR ARTC SDK versi yang diperlukan dari Unduh dan integrasi SDK. Versi terbaru adalah 7.11.0, dengan nama file mirip AliVCSDK_ARTC-x.y.z.aar.

  2. Salin file AAR yang telah diunduh ke direktori proyek Anda, misalnya app/libs. Jika folder ini belum ada, buatlah.

  3. Buka file settings.gradle di direktori root proyek Anda dan tambahkan direktori yang berisi file AAR ke dependencyResolutionManagement/repositories:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        // Tambahkan jalur relatif ke direktori tempat SDK ARTC berada
        flatDir {
            dir 'app/libs'
        }
    }
}

Catatan: Jika Anda menggunakan versi Android Gradle Plugin sebelum 7.1.0, blok ini mungkin tidak tersedia di file settings.gradle. Untuk informasi lebih lanjut, lihat Android Gradle Plugin 7.1. Dalam kasus ini, gunakan alternatif berikut:

Buka file build.gradle di direktori root proyek Anda dan tambahkan kode berikut ke blok allprojects/repositories:

allprojects {
    repositories {
        ...
        // Tambahkan jalur relatif ke direktori tempat SDK ARTC berada
        flatDir {
            dir 'app/libs'
        }
    }
}
  1. Buka file app/build.gradle dan tambahkan dependensi file AAR ke blok dependencies:

// Ganti x.y.z dengan nomor versi yang sesuai
implementation(name:'AliVCSDK_ARTC', version: 'x.y.z', ext:'aar')
  1. Setelah proses build selesai, dependensi akan muncul di bagian External Libraries.

    image

Langkah 2: Tentukan arsitektur CPU yang didukung

Buka file app/build.gradle dan tentukan arsitektur CPU yang didukung di blok defaultConfig. Arsitektur yang tersedia mencakup armeabi-v7a, arm64-v8a, x86, dan x86_64. Pilih arsitektur yang Anda butuhkan.

android {
    defaultConfig {
        // ...konfigurasi default lainnya// Dukungan untuk arsitektur armeabi-v7a dan arm64-v8a
        ndk {
             abiFilters "armeabi-v7a", "arm64-v8a"
        }
    }
}	

Langkah 3: Atur izin

Atur izin yang dibutuhkan aplikasi Anda.

Buka direktori app/src/main, buka file AndroidManifest.xml, dan tambahkan izin yang diperlukan.

<uses-feature android:name="android.hardware.camera" android:required="false" /><uses-permission android:name="android.permission.CAMERA" /><uses-permission android:name="android.permission.RECORD_AUDIO" /><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!-- Minta izin Bluetooth lawas pada perangkat lama. --><uses-permissionandroid:name="android.permission.BLUETOOTH"android:maxSdkVersion="30" /><uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"android:maxSdkVersion="30" /><!-- Hanya diperlukan jika aplikasi Anda berkomunikasi dengan perangkat Bluetooth yang sudah dipasangkan. --><uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /><uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /><uses-permission android:name="android.permission.READ_PHONE_STATE" /><uses-permission android:name="android.permission.WRITE_SETTINGS"tools:ignore="ProtectedPermissions" /><uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Izin Bluetooth runtime

File AndroidManifest.xml internal ARTC SDK mendeklarasikan beberapa izin. Perhatikan bahwa Anda bertanggung jawab untuk meminta izin dinamis yang diperlukan saat runtime. Izin BLUETOOTH_CONNECT, yang diperkenalkan di Android 12, merupakan salah satu izin tersebut. Setelah Anda mengimpor ARTC SDK, izin yang dideklarasikannya akan digabungkan ke dalam manifes aplikasi Anda. Kami menyarankan agar Anda secara eksplisit mendeklarasikan semua izin yang diperlukan dalam manifes Anda sendiri demi kejelasan dan untuk mematuhi prinsip hak istimewa minimal.

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /><uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

Berdasarkan skenario bisnis Anda, jika Anda tidak memerlukan salah satu izin berikut, Anda dapat mendeklarasikan penghapusannya di file AndroidManifest.xml  proyek utama Anda:

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" tools:node="remove"/>

Skenario izin Bluetooth:

Skenario 1: Bluetooth diperlukan

targetSdk aplikasi Anda kurang dari 31

Ketika targetSdk proyek Anda kurang dari 31, fungsi Bluetooth dikendalikan oleh izin lawas BLUETOOTH. Deklarasikan hal berikut di manifes aplikasi Anda:

<!-- Deklarasikan izin Bluetooth --><uses-permission android:name="android.permission.BLUETOOTH" /><!-- Timpa dan hapus izin untuk API 31+ --><uses-permission android:name="android.permission.BLUETOOTH_CONNECT" tools:node="remove" />

Catatan 

  • SDK menyertakan deklarasi untuk izin BLUETOOTH_CONNECT. Izin ini diperkenalkan di Android 12 (API level 31) dan harus diminta saat runtime dengan memanggil requestPermissions.

  • Pada beberapa perangkat, jika aplikasi menyertakan deklarasi BLUETOOTH_CONNECT, sistem mengharuskan permintaan runtime untuk izin ini guna menghindari SecurityException. Jika Anda mengalami masalah ini, gunakan salah satu pendekatan berikut:

    • Opsi 1: Hapus deklarasi izin dengan menggunakan tools:node="remove".

    • Opsi 2: Minta izin tersebut saat runtime.

targetSdk aplikasi Anda 31 atau lebih tinggi

Ketika targetSdk proyek Anda 31 atau lebih tinggi, Anda harus menangani izin Bluetooth untuk mendukung versi Android lama maupun baru. Deklarasikan izin tersebut di file AndroidManifest.xml aplikasi utama Anda:

<!-- Deklarasikan izin Bluetooth dan atur android:maxSdkVersion ke 30 untuk kompatibilitas dengan perangkat lama --><uses-permission android:name="android.permission.BLUETOOTH"/><!-- Deklarasikan izin Bluetooth untuk API 31+ --><uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/><!-- Izin Bluetooth lainnya -->

Selain itu, karena BLUETOOTH_CONNECT merupakan izin dinamis, Anda harus memintanya saat runtime:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    String[] permissions = {
        android.Manifest.permission.BLUETOOTH_CONNECT
    };
    ActivityCompat.requestPermissions(activity, permissions, REQUEST_BLUETOOTH);
}
Skenario 2: Bluetooth tidak diperlukan

Untuk menghindari crash atau prompt izin terkait Bluetooth, kami menyarankan agar Anda menghapus izin Bluetooth yang tidak diperlukan.

Di file AndroidManifest.xml proyek utama, gunakan tools:node="remove" untuk menimpa dan menghapus izin tersebut:

<!-- Timpa dan hapus deklarasi izin Bluetooth dari ARTC SDK --><uses-permission android:name="android.permission.BLUETOOTH" tools:node="remove" /><uses-permission android:name="android.permission.BLUETOOTH_CONNECT" tools:node="remove" />

Izin runtime lainnya

Pada Android 6.0 (API level 23) dan versi yang lebih baru, Anda harus meminta izin berisiko tinggi saat runtime. Selain mendeklarasikannya di file AndroidManifest.xml, Anda juga harus memintanya di kode Anda saat runtime.

Izin berikut memerlukan permintaan runtime:

  • Manifest.permission.CAMERA

  • Manifest.permission.WRITE_EXTERNAL_STORAGE

  • Manifest.permission.RECORD_AUDIO

  • Manifest.permission.READ_EXTERNAL_STORAGE

  • Manifest.permission.READ_PHONE_STATE

Pada Android 12 (API level 31) atau versi yang lebih baru, Anda juga harus meminta izin berikut saat runtime:

  • Manifest.permission.BLUETOOTH_CONNECT

Tabel berikut menjelaskan izin utama dan tujuannya:

Izin

Deskripsi

Tujuan

Wajib

Izin runtime

CAMERA

Memberikan akses ke kamera.

Untuk menangkap video guna komunikasi real-time.

Ya

Android >= 6.0

RECORD_AUDIO

Memberikan akses ke mikrofon.

Untuk menangkap audio guna komunikasi real-time.

Ya

Android >= 6.0

INTERNET

Memberikan akses ke internet.

Untuk mengirimkan data audio dan video melalui jaringan.

Ya

Tidak

ACCESS_NETWORK_STATE

Memungkinkan aplikasi mendapatkan status jaringan.

Untuk memantau konektivitas jaringan dan mengoptimalkan kualitas streaming, misalnya dengan mengelola koneksi ulang.

Opsional

Tidak

ACCESS_WIFI_STATE

Memungkinkan aplikasi mendapatkan status Wi-Fi.

Mendapatkan informasi tentang koneksi Wi-Fi saat ini untuk mengoptimalkan kinerja jaringan.

Opsional

Tidak

MODIFY_AUDIO_SETTINGS

Memungkinkan aplikasi memodifikasi pengaturan audio.

Untuk menyesuaikan volume sistem atau beralih antar perangkat output audio (misalnya, speakerphone, headset).

Opsional

Tidak

BLUETOOTH

Izin Bluetooth (fungsi dasar)

Menghubungkan ke perangkat Bluetooth, seperti headset Bluetooth.

Opsional

Tidak

BLUETOOTH_CONNECT

Izin koneksi Bluetooth

Berkomunikasi dengan perangkat Bluetooth yang telah dipasangkan, misalnya untuk mengirimkan aliran audio.

Opsional

Android >= 12

READ_PHONE_STATE

Memungkinkan aplikasi mengakses informasi terkait status telepon perangkat.

Untuk mengelola aliran audio selama panggilan telepon, misalnya dengan menjeda sesi saat panggilan dimulai.

Opsional

Android >= 6.0

READ_EXTERNAL_STORAGE

Memungkinkan aplikasi membaca file dari penyimpanan eksternal.

Untuk mengaktifkan fitur seperti memutar file audio lokal sebagai efek suara atau musik latar.

Opsional

Android >= 6.0

WRITE_EXTERNAL_STORAGE

Memungkinkan aplikasi menulis ke penyimpanan eksternal.

Untuk menyimpan file seperti log aplikasi atau media yang direkam.

Opsional

Android >= 6.0

Langkah 4: Cegah obfuscation kode (opsional)

Di file app/proguard-rules.pro, tambahkan aturan ProGuard berikut untuk mencegah antarmuka publik SDK di-obfuscate.

-keep class com.aliyun.allinone.** {*;}-keep class com.aliyun.rts.network.AliHttpTool {*;}-keep class com.aliyun.common.AlivcBase {*;}-keep class com.huawei.multimedia.alivc.** {*;}-keep class com.alivc.rtc.** {*;}-keep class com.alivc.component.** {*;}-keep class org.webrtc.** {*;}

FAQ

ARTC SDK dan izin tambahan

Setelah Anda mengimpor ARTC SDK, izin dari manifesnya akan secara otomatis digabungkan ke dalam manifes aplikasi Anda. Daftar tepatnya dapat berbeda tergantung versi SDK. Untuk daftar paling akurat, periksa file AndroidManifest.xml di dalam file .aar SDK.

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /><uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

Izin ini terutama digunakan untuk mendukung fitur seperti deteksi status jaringan, akses informasi Wi-Fi, koneksi Bluetooth, dan routing audio.

Menangani konflik izin

ARTC SDK dirancang untuk beradaptasi dengan berbagai versi Android. Misalnya, SDK memperhitungkan izin runtime BLUETOOTH_CONNECT baru di Android 12 (API level 31) dan versi yang lebih baru. Meskipun desain ini umumnya mencegah error kompilasi, Anda mungkin tetap mengalami masalah seperti:

  • Perilaku runtime yang tidak terduga, seperti kegagalan penggabungan Manifest.

  • Risiko peninjauan Google Play: Jika aplikasi Anda mendeklarasikan izin untuk fitur yang tidak digunakan, aplikasi tersebut mungkin ditolak karena melanggar prinsip hak istimewa minimal.

Untuk mengurangi potensi masalah ini, pertimbangkan strategi berikut:

  1. Evaluasi kebutuhan izin
    Pertama, tentukan apakah aplikasi Anda memerlukan semua izin yang dideklarasikan oleh SDK. Jika aplikasi Anda tidak menggunakan fitur seperti Bluetooth, hapus deklarasi izin terkait untuk mengikuti prinsip hak istimewa minimal. Misalnya, jika targetSdk aplikasi Anda kurang dari 31, aplikasi tersebut tidak memerlukan izin BLUETOOTH_CONNECT.







  2. Gunakan direktif penggabungan Manifest untuk menyelesaikan konflik
    Jika izin yang dideklarasikan oleh SDK bertentangan dengan deklarasi di modul lain (misalnya, atribut maxSdkVersion yang berbeda), gunakan direktif namespace tools di AndroidManifest.xml utama aplikasi Anda untuk menyelesaikan konflik:







    <manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><!-- Contoh: Hapus izin yang dideklarasikan oleh SDK yang tidak diperlukan aplikasi Anda --><uses-permissionandroid:name="android.permission.BLUETOOTH_CONNECT"tools:node="remove" /><!-- Contoh: Timpa atribut izin --><uses-permissionandroid:name="android.permission.BLUETOOTH"android:maxSdkVersion="30"tools:replace="android:maxSdkVersion" /></manifest>

Langkah 5: Buat UI

Buat antarmuka pengguna untuk skenario interaktif real-time Anda. Kode contoh berikut membuat dua tampilan untuk panggilan video guna menampilkan video lokal dan jarak jauh.

Contoh kode

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/video_chat_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".VideoCall.VideoCallActivity"
    >
    <LinearLayout
        android:id="@+id/ll_channel_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toTopOf="@id/ll_video_layout"
        android:orientation="vertical">

        <LinearLayout
            android:id="@+id/ll_channel_desc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginTop="12dp"
            android:layout_marginLeft="8dp"
            android:layout_marginRight="12dp"
        >
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/video_chat_channel_desc"
                />

        </LinearLayout>
        <LinearLayout
            android:id="@+id/ll_channel_id"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginTop="12dp"
            android:layout_marginLeft="8dp"
            android:layout_marginRight="12dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            android:visibility="visible">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="0"
                android:text="ChannelID:"
                android:layout_marginTop="5dp"
                />
            <EditText
                android:id="@+id/channel_id_input"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text=""
                android:padding="5dp"
                android:textSize="15sp"
                android:layout_marginLeft="10dp"
                android:layout_marginTop="5dp"
                android:layout_marginRight="10dp"
                android:background="@drawable/edittext_border"
                />
        </LinearLayout>
        <LinearLayout
            android:id="@+id/ll_bottom_bar"
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:layout_marginTop="20dp"
            android:orientation="horizontal"
            android:gravity="center_vertical"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/ll_channel_desc"
            app:layout_constraintBottom_toBottomOf="parent">
            <TextView
                android:id="@+id/join_room_btn"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="@string/video_chat_join_room"
                android:layout_marginStart="20dp"
                android:layout_marginEnd="20dp"
                android:gravity="center"
                android:padding="10dp"
                android:background="@color/layout_base_blue"
                />

        </LinearLayout>
    </LinearLayout>
    <LinearLayout
        android:id="@+id/ll_video_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintTop_toBottomOf="@id/ll_channel_layout"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        >

        <LinearLayout
            android:id="@+id/video_layout_1"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="0.5"
            android:orientation="horizontal">

            <FrameLayout
                android:id="@+id/fl_local"
                android:layout_width="108dp"
                android:layout_weight="0.5"
                android:layout_height="192dp"
                />
            <FrameLayout
                android:id="@+id/fl_remote"
                android:layout_marginLeft="5dp"
                android:layout_width="108dp"
                android:layout_weight="0.5"
                android:layout_height="192dp"
                />

        </LinearLayout>

        <LinearLayout
            android:id="@+id/video_layout_2"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="0.5"
            android:layout_marginTop="10dp"
            android:orientation="horizontal">

            <FrameLayout
                android:id="@+id/fl_remote2"
                android:layout_width="108dp"
                android:layout_weight="0.5"
                android:layout_height="192dp"
                />
            <FrameLayout
                android:id="@+id/fl_remote3"
                android:layout_marginLeft="5dp"
                android:layout_width="108dp"
                android:layout_weight="0.5"
                android:layout_height="192dp"
                />

        </LinearLayout>
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

Prosedur

Bagian ini menjelaskan cara menggunakan ARTC SDK untuk membangun aplikasi audio dan video real-time dasar. Salin contoh kode untuk pengujian cepat, lalu ikuti langkah-langkah untuk memahami pemanggilan API inti.

Diagram berikut menunjukkan alur kerja dasar untuk panggilan audio dan video real-time:

Berikut adalah contoh kode lengkap yang menunjukkan proses dasar untuk mengimplementasikan panggilan video:

Contoh kode alur kerja dasar

/**
 * Contoh pemanggilan API untuk skenario panggilan video.
 */
public class VideoCallActivity extends AppCompatActivity {

    private Handler handler;
    private EditText mChannelEditText;
    private TextView mJoinChannelTextView;
    private boolean hasJoined = false;
    private FrameLayout fl_local, fl_remote, fl_remote_2, fl_remote_3;

    private AliRtcEngine mAliRtcEngine = null;
    private AliRtcEngine.AliRtcVideoCanvas mLocalVideoCanvas = null;
    private Map<String, ViewGroup> remoteViews = new ConcurrentHashMap<String, ViewGroup>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handler = new Handler(Looper.getMainLooper());
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_video_chat);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.video_chat_main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
        setTitle(getString(R.string.video_chat));
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        fl_local = findViewById(R.id.fl_local);
        fl_remote = findViewById(R.id.fl_remote);
        fl_remote_2 = findViewById(R.id.fl_remote2);
        fl_remote_3 = findViewById(R.id.fl_remote3);

        mChannelEditText = findViewById(R.id.channel_id_input);
        mChannelEditText.setText(GlobalConfig.getInstance().gerRandomChannelId());
        mJoinChannelTextView = findViewById(R.id.join_room_btn);
        mJoinChannelTextView.setOnClickListener(v -> {
            if(hasJoined) {
                destroyRtcEngine();
                mJoinChannelTextView.setText(R.string.video_chat_join_room);
            } else {
                startRTCCall();
            }
        });
    }

    public static void startActionActivity(Activity activity) {
        Intent intent = new Intent(activity, VideoCallActivity.class);
        activity.startActivity(intent);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == android.R.id.home) {
            // Tangani klik tombol kembali.
            destroyRtcEngine();
            finish();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private FrameLayout getAvailableView() {
        if (fl_remote.getChildCount() == 0) {
            return fl_remote;
        } else if (fl_remote_2.getChildCount() == 0) {
            return fl_remote_2;
        } else if (fl_remote_3.getChildCount() == 0) {
            return fl_remote_3;
        } else {
            return null;
        }
    }

    private void handleJoinResult(int result, String channel, String userId) {
        handler.post(() -> {
            String  str = null;
            if(result == 0) {
                str = "Pengguna " + userId + " Bergabung ke " + channel + " Berhasil";
            } else {
                str = "Pengguna " + userId + " Bergabung ke " + channel + " Gagal, error:" + result;
            }
            ToastHelper.showToast(this, str, Toast.LENGTH_SHORT);
            ((TextView)findViewById(R.id.join_room_btn)).setText(R.string.leave_channel);
        });
    }

    private void startRTCCall() {
        if(hasJoined) {
            return;
        }
        initAndSetupRtcEngine();
        startPreview();
        joinChannel();
    }

    private void initAndSetupRtcEngine() {

        // Buat dan inisialisasi engine.
        if(mAliRtcEngine == null) {
            mAliRtcEngine = AliRtcEngine.getInstance(this);
        }
        mAliRtcEngine.setRtcEngineEventListener(mRtcEngineEventListener);
        mAliRtcEngine.setRtcEngineNotify(mRtcEngineNotify);

        // Atur profil channel ke mode interaktif. Untuk RTC, gunakan AliRTCSdkInteractiveLive.
        mAliRtcEngine.setChannelProfile(AliRtcEngine.AliRTCSdkChannelProfile.AliRTCSdkInteractiveLive);
        // Atur peran pengguna. Gunakan AliRTCSdkInteractive untuk memublikasikan dan berlangganan, atau AliRTCSdkLive hanya untuk berlangganan.
        mAliRtcEngine.setClientRole(AliRtcEngine.AliRTCSdkClientRole.AliRTCSdkInteractive);
        // Atur profil audio. Default-nya adalah mode kualitas tinggi (AliRtcEngineHighQualityMode) dan skenario musik (AliRtcSceneMusicMode).
        mAliRtcEngine.setAudioProfile(AliRtcEngine.AliRtcAudioProfile.AliRtcEngineHighQualityMode, AliRtcEngine.AliRtcAudioScenario.AliRtcSceneMusicMode);
        mAliRtcEngine.setCapturePipelineScaleMode(AliRtcEngine.AliRtcCapturePipelineScaleMode.AliRtcCapturePipelineScaleModePost);

        // Atur parameter pengkodean video.
        AliRtcEngine.AliRtcVideoEncoderConfiguration aliRtcVideoEncoderConfiguration = new AliRtcEngine.AliRtcVideoEncoderConfiguration();
        aliRtcVideoEncoderConfiguration.dimensions = new AliRtcEngine.AliRtcVideoDimensions(
                720, 1280);
        aliRtcVideoEncoderConfiguration.frameRate = 20;
        aliRtcVideoEncoderConfiguration.bitrate = 1200;
        aliRtcVideoEncoderConfiguration.keyFrameInterval = 2000;
        aliRtcVideoEncoderConfiguration.orientationMode = AliRtcVideoEncoderOrientationModeAdaptive;
        mAliRtcEngine.setVideoEncoderConfiguration(aliRtcVideoEncoderConfiguration);

        // SDK memublikasikan audio secara default, sehingga Anda tidak perlu memanggil publishLocalAudioStream.
        mAliRtcEngine.publishLocalAudioStream(true);
        // Untuk panggilan video, Anda tidak perlu memanggil publishLocalVideoStream(true) karena SDK memublikasikan video secara default.
        // Untuk panggilan hanya audio, Anda harus memanggil publishLocalVideoStream(false) untuk menonaktifkan publikasi video.
        mAliRtcEngine.publishLocalVideoStream(true);

        // Atur default untuk berlangganan aliran audio dan video jarak jauh.
        mAliRtcEngine.setDefaultSubscribeAllRemoteAudioStreams(true);
        mAliRtcEngine.subscribeAllRemoteAudioStreams(true);
        mAliRtcEngine.setDefaultSubscribeAllRemoteVideoStreams(true);
        mAliRtcEngine.subscribeAllRemoteVideoStreams(true);

    }

    private void startPreview(){
        if (mAliRtcEngine != null) {

            if (fl_local.getChildCount() > 0) {
                fl_local.removeAllViews();
            }

            findViewById(R.id.ll_video_layout).setVisibility(VISIBLE);
            ViewGroup.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            if(mLocalVideoCanvas == null) {
                mLocalVideoCanvas = new AliRtcEngine.AliRtcVideoCanvas();
                SurfaceView localSurfaceView = mAliRtcEngine.createRenderSurfaceView(VideoCallActivity.this);
                localSurfaceView.setZOrderOnTop(true);
                localSurfaceView.setZOrderMediaOverlay(true);
                fl_local.addView(localSurfaceView, layoutParams);
                mLocalVideoCanvas.view = localSurfaceView;
                mAliRtcEngine.setLocalViewConfig(mLocalVideoCanvas, AliRtcVideoTrackCamera);
                mAliRtcEngine.startPreview();
            }
        }
    }

    private void joinChannel() {
        String channelId = mChannelEditText.getText().toString();
        if(!TextUtils.isEmpty(channelId)) {
            String userId = GlobalConfig.getInstance().getUserId();
            String appId = ARTCTokenHelper.AppId;
            String appKey = ARTCTokenHelper.AppKey;
            long timestamp = ARTCTokenHelper.getTimesTamp();
            String token = ARTCTokenHelper.generateSingleParameterToken(appId, appKey, channelId, userId, timestamp);
            mAliRtcEngine.joinChannel(token, null, null, null);
            hasJoined = true;
        } else {
            Log.e("VideoCallActivity", "channelId kosong");
        }
    }

    private AliRtcEngineEventListener mRtcEngineEventListener = new AliRtcEngineEventListener() {
        @Override
        public void onJoinChannelResult(int result, String channel, String userId, int elapsed) {
            super.onJoinChannelResult(result, channel, userId, elapsed);
            handleJoinResult(result, channel, userId);
        }

        @Override
        public void onLeaveChannelResult(int result, AliRtcEngine.AliRtcStats stats){
            super.onLeaveChannelResult(result, stats);
        }

        @Override
        public void onConnectionStatusChange(AliRtcEngine.AliRtcConnectionStatus status, AliRtcEngine.AliRtcConnectionStatusChangeReason reason){
            super.onConnectionStatusChange(status, reason);

            handler.post(new Runnable() {
                @Override
                public void run() {
                    if(status == AliRtcEngine.AliRtcConnectionStatus.AliRtcConnectionStatusFailed) {
                        /* TODO: Callback ini harus ditangani. Ini dipicu hanya setelah strategi pemulihan internal SDK gagal. Kami menyarankan memberi tahu pengguna. */
                        ToastHelper.showToast(VideoCallActivity.this, R.string.video_chat_connection_failed, Toast.LENGTH_SHORT);
                    } else {
                        /* TODO: Opsional. Tambahkan logika bisnis di sini, seperti untuk analitik data atau pembaruan UI. */
                    }
                }
            });
        }
        @Override
        public void onLocalDeviceException(AliRtcEngine.AliRtcEngineLocalDeviceType deviceType, AliRtcEngine.AliRtcEngineLocalDeviceExceptionType exceptionType, String msg){
            super.onLocalDeviceException(deviceType, exceptionType, msg);
            /* TODO: Callback ini harus ditangani. Ini dipicu hanya setelah strategi pemulihan internal SDK gagal. Kami menyarankan memberi tahu pengguna tentang error perangkat. */
            handler.post(new Runnable() {
                @Override
                public void run() {
                    String str = "OnLocalDeviceException deviceType: " + deviceType + " exceptionType: " + exceptionType + " msg: " + msg;
                    ToastHelper.showToast(VideoCallActivity.this, str, Toast.LENGTH_SHORT);
                }
            });
        }

    };

    private AliRtcEngineNotify mRtcEngineNotify = new AliRtcEngineNotify() {
        @Override
        public void onAuthInfoWillExpire() {
            super.onAuthInfoWillExpire();
            /* TODO: Ini harus ditangani. Token akan segera kedaluwarsa. Aplikasi Anda harus mendapatkan token otentikasi baru untuk channel dan pengguna saat ini, lalu memanggil refreshAuthInfo. */
        }

        @Override
        public void onRemoteUserOnLineNotify(String uid, int elapsed){
            super.onRemoteUserOnLineNotify(uid, elapsed);
        }

        // Di callback onRemoteUserOffLineNotify, lepaskan renderer untuk aliran video jarak jauh.
        @Override
        public void onRemoteUserOffLineNotify(String uid, AliRtcEngine.AliRtcUserOfflineReason reason){
            super.onRemoteUserOffLineNotify(uid, reason);
        }

        // Di callback onRemoteTrackAvailableNotify, siapkan renderer untuk aliran video jarak jauh.
        @Override
        public void onRemoteTrackAvailableNotify(String uid, AliRtcEngine.AliRtcAudioTrack audioTrack, AliRtcEngine.AliRtcVideoTrack videoTrack){
            handler.post(new Runnable() {
                @Override
                public void run() {
                    if(videoTrack == AliRtcVideoTrackCamera) {
                        SurfaceView surfaceView = mAliRtcEngine.createRenderSurfaceView(VideoCallActivity.this);
                        surfaceView.setZOrderMediaOverlay(true);
                        FrameLayout view = getAvailableView();
                        if (view == null) {
                            return;
                        }
                        remoteViews.put(uid, view);
                        view.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
                        AliRtcEngine.AliRtcVideoCanvas remoteVideoCanvas = new AliRtcEngine.AliRtcVideoCanvas();
                        remoteVideoCanvas.view = surfaceView;
                        mAliRtcEngine.setRemoteViewConfig(remoteVideoCanvas, uid, AliRtcVideoTrackCamera);
                    } else if(videoTrack == AliRtcVideoTrackNo) {
                        if(remoteViews.containsKey(uid)) {
                            ViewGroup view = remoteViews.get(uid);
                            if(view != null) {
                                view.removeAllViews();
                                remoteViews.remove(uid);
                                mAliRtcEngine.setRemoteViewConfig(null, uid, AliRtcVideoTrackCamera);
                            }
                        }
                    }
                }
            });
        }

        /* Aplikasi Anda harus menangani kasus di mana beberapa perangkat mencoba bergabung dengan userId yang sama. */
        @Override
        public void onBye(int code){
            handler.post(new Runnable() {
                @Override
                public void run() {
                    String msg = "onBye code:" + code;
                    ToastHelper.showToast(VideoCallActivity.this, msg, Toast.LENGTH_SHORT);
                }
            });
        }

    };

    private void destroyRtcEngine() {
        if( mAliRtcEngine != null) {
            mAliRtcEngine.stopPreview();
            mAliRtcEngine.setLocalViewConfig(null, AliRtcVideoTrackCamera);
            mAliRtcEngine.leaveChannel();
            mAliRtcEngine.destroy();
            mAliRtcEngine = null;

            handler.post(() -> {
                ToastHelper.showToast(this, "Tinggalkan Channel", Toast.LENGTH_SHORT);
            });
        }
        hasJoined = false;
        for (ViewGroup value : remoteViews.values()) {
            value.removeAllViews();
        }
        remoteViews.clear();
        findViewById(R.id.ll_video_layout).setVisibility(View.GONE);
        fl_local.removeAllViews();
        mLocalVideoCanvas = null;
    }
}

Untuk detail kode contoh lengkap dan cara menjalankannya, lihat Jalankan demo ARTC untuk Android.

1. Minta izin

Sebelum memulai panggilan video, verifikasi bahwa aplikasi Anda memiliki izin yang diperlukan:

private static final int REQUEST_PERMISSION_CODE = 101;

private static final String[] PERMISSION_MANIFEST = {
    Manifest.permission.RECORD_AUDIO,
    Manifest.permission.READ_PHONE_STATE,
    Manifest.permission.WRITE_EXTERNAL_STORAGE,
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.CAMERA
};

private static final String[] PERMISSION_MANIFEST33 = {
    Manifest.permission.RECORD_AUDIO,
    Manifest.permission.READ_PHONE_STATE,
    Manifest.permission.CAMERA
};

private static String[] getPermissions() {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
        return PERMISSION_MANIFEST;
    }
    return PERMISSION_MANIFEST33;
}

public boolean checkOrRequestPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (ContextCompat.checkSelfPermission(this, "android.permission.CAMERA") != PackageManager.PERMISSION_GRANTED
                || ContextCompat.checkSelfPermission(this, "android.permission.RECORD_AUDIO") != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(getPermissions(), REQUEST_PERMISSION_CODE);
            return false;
        }
    }
    return true;
}

2. Dapatkan token otentikasi

Bergabung ke channel ARTC memerlukan token otentikasi. Untuk detail cara menghasilkan token, lihat Otentikasi berbasis token. Metode pembuatan token (parameter tunggal atau multiparameter) menentukan API joinChannel mana yang harus dipanggil.

Lingkungan produksi:

Menghasilkan token memerlukan AppKey Anda. Menyematkan AppKey secara langsung di sisi klien merupakan risiko keamanan, sehingga kami sangat menyarankan menghasilkan token di App Server Anda dan mengirimkannya ke klien.

Pengembangan dan debugging:

Saat pengembangan, jika App Server Anda belum dapat menghasilkan token, gunakan contoh berikut untuk membuat token sementara:

public final class ARTCTokenHelper {
    /**
     * RTC AppId
     */
    public static String AppId = "";

    /**
     * RTC AppKey
     */
    public static String AppKey = "";

    /**
     * Hasilkan token rapat parameter tunggal berdasarkan channelId, userId, dan nonce
     */
    public static String generateSingleParameterToken(String appId, String appKey, String channelId, String userId, long timestamp,  String nonce) {

        StringBuilder stringBuilder = new StringBuilder()
                .append(appId)
                .append(appKey)
                .append(channelId)
                .append(userId)
                .append(timestamp);
        String token =  getSHA256(stringBuilder.toString());
        try{
            JSONObject tokenJson = new JSONObject();
            tokenJson.put("appid", AppId);
            tokenJson.put("channelid", channelId);
            tokenJson.put("userid", userId);
            tokenJson.put("nonce", nonce);
            tokenJson.put("timestamp", timestamp);
            tokenJson.put("token", token);
            String base64Token = Base64.encodeToString(tokenJson.toString().getBytes(StandardCharsets.UTF_8), Base64.NO_WRAP);
            return base64Token;
        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Hasilkan token rapat parameter tunggal berdasarkan channelId, userId, dan timestamp
     */
    public static String generateSingleParameterToken(String appId, String appKey, String channelId, String userId, long timestamp) {
        return generateSingleParameterToken(appId, appKey, channelId, userId, timestamp, "");
    }

    public static String getSHA256(String str) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
            byte[] hash = messageDigest.digest(str.getBytes(StandardCharsets.UTF_8));
            return byte2Hex(hash);
        } catch (NoSuchAlgorithmException e) {
            // Pertimbangkan untuk mencatat exception dan/atau melemparkannya kembali sebagai RuntimeException
            e.printStackTrace();
        }
        return "";
    }

    private static String byte2Hex(byte[] bytes) {
        StringBuilder stringBuilder = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                // Gunakan tanda kutip tunggal untuk char
                stringBuilder.append('0');
            }
            stringBuilder.append(hex);
        }
        return stringBuilder.toString();
    }

    public static long getTimesTamp() {
        return System.currentTimeMillis() / 1000 + 60 * 60 * 24;
    }
}

3. Impor kelas ARTC SDK

Impor kelas dan antarmuka ARTC SDK:

// Impor kelas ARTC
import com.alivc.rtc.AliRtcEngine;
import com.alivc.rtc.AliRtcEngineEventListener;
import com.alivc.rtc.AliRtcEngineNotify;

4. Buat dan inisialisasi engine

  • Buat engine RTC

    Panggil metode getInstance[1/2] untuk membuat instans AliRTCEngine.

    private AliRtcEngine mAliRtcEngine = null;
    if(mAliRtcEngine == null) {
        mAliRtcEngine = AliRtcEngine.getInstance(this);
    }
  • Inisialisasi mesin

    • Panggil setChannelProfile untuk mengatur profil channel ke AliRTCSdkInteractiveLive (mode interaktif).

      Pilih antara mode interaktif untuk hiburan dan mode komunikasi untuk panggilan. Memilih mode yang tepat memastikan pengalaman pengguna yang lancar dan penggunaan jaringan yang efisien.

      Mode

      Penerbitan

      Berlangganan

      Deskripsi

      Mode interaktif

      1. Berlaku batasan berbasis peran. Hanya pengguna dengan peran host yang dapat memublikasikan aliran.

      2. Partisipan dapat mengganti peran kapan saja selama sesi.

      Tidak ada batasan peran. Semua partisipan memiliki izin untuk berlangganan aliran.

      1. Dalam mode interaktif, event seperti host bergabung atau keluar dikirim ke viewer. Aktivitas viewer tidak dikirim ke host, sehingga memastikan pengalaman host tidak terganggu.

      2. Jika Anda memperkirakan bahwa viewer mungkin perlu berinteraksi di masa depan, kami menyarankan menggunakan mode interaktif. Fleksibilitasnya memungkinkan Anda menyesuaikan peran pengguna untuk memenuhi kebutuhan interaksi yang berbeda.

      Mode komunikasi

      Tidak ada batasan peran. Semua partisipan memiliki izin untuk memublikasikan aliran.

      Tidak ada batasan peran. Semua partisipan memiliki izin untuk berlangganan aliran.

      1. Dalam mode komunikasi, partisipan saling mengetahui keberadaan satu sama lain dalam sesi.

      2. Meskipun mode ini tidak membedakan peran pengguna, fungsinya setara dengan peran host dalam mode interaktif. Hal ini menyederhanakan operasi, memungkinkan pengguna mencapai fungsionalitas yang diinginkan dengan lebih sedikit pemanggilan API.

    • Panggil setClientRole untuk mengatur peran pengguna menjadi AliRTCSdkInteractive (host) atau AliRTCSdkLive (viewer). Catatan: Peran host secara default memublikasikan dan berlangganan. Peran viewer secara default hanya berlangganan, dengan pratinjau dan penerbitan dinonaktifkan.

      Catatan

      Saat pengguna mengganti peran di dalam channel, sistem menyesuaikan status penerbitan aliran audio dan video sebagai berikut:

      • Beralih dari host ke viewer: Sistem menghentikan penerbitan aliran audio dan video lokal. Aliran jarak jauh yang berlangganan tidak terpengaruh, dan pengguna dapat terus menonton partisipan lain.

      • Beralih dari viewer ke host: Sistem mulai memublikasikan aliran audio dan video lokal. Aliran jarak jauh yang berlangganan tetap tidak berubah, dan pengguna dapat terus menonton partisipan lain.

      // Atur profil channel ke mode interaktif. Di RTC, selalu gunakan AliRTCSdkInteractiveLive.
      mAliRtcEngine.setChannelProfile(AliRtcEngine.AliRTCSdkChannelProfile.AliRTCSdkInteractiveLive);
      // Atur peran pengguna. Gunakan AliRTCSdkInteractive untuk memublikasikan dan berlangganan aliran, atau AliRTCSdkLive hanya untuk berlangganan.
      mAliRtcEngine.setClientRole(AliRtcEngine.AliRTCSdkClientRole.AliRTCSdkInteractive);
  • Atur callback umum

    Saat SDK mengalami masalah, SDK terlebih dahulu mencoba pemulihan otomatis. Untuk error yang tidak dapat diselesaikan, SDK memberi tahu aplikasi Anda melalui callback.

    Berikut adalah callback utama untuk masalah yang tidak dapat dipulihkan yang harus ditangani aplikasi Anda:

    Penyebab exception

    Callback dan parameter

    Solusi

    Deskripsi

    Otentikasi gagal

    Parameter result dari callback onJoinChannelResult adalah AliRtcErrJoinBadToken.

    Aplikasi Anda harus memeriksa apakah token benar.

    Jika otentikasi gagal saat pengguna memanggil API, callback mengembalikan error kegagalan otentikasi.

    Token akan segera kedaluwarsa

    onAuthInfoWillExpire

    Dapatkan token baru dan panggil refreshAuthInfo untuk memperbarui informasi otentikasi.

    Error kedaluwarsa token dapat terjadi baik saat API dipanggil maupun selama runtime. Error dilaporkan melalui callback API atau callback error terpisah.

    Token kedaluwarsa

    onAuthInfoExpired

    Aplikasi Anda harus bergabung kembali ke channel.

    Error kedaluwarsa token dapat terjadi baik saat API dipanggil maupun selama runtime. Error dilaporkan melalui callback API atau callback error terpisah.

    Masalah koneksi jaringan

    Callback onConnectionStatusChange mengembalikan AliRtcConnectionStatusFailed.

    Aplikasi Anda harus bergabung kembali ke channel.

    SDK secara otomatis memulihkan diri dari gangguan koneksi jaringan singkat. Jika gangguan melebihi ambang batas yang telah ditentukan, terjadi timeout dan koneksi terputus. Aplikasi Anda harus memeriksa status jaringan dan meminta pengguna untuk bergabung kembali ke channel.

    Dikeluarkan dari channel

    onBye

    • AliRtcOnByeUserReplaced: Periksa apakah pengguna lain telah bergabung dengan userId yang sama.

    • AliRtcOnByeBeKickedOut: Pengguna dikeluarkan dari channel oleh App Server dan harus bergabung kembali.

    • AliRtcOnByeChannelTerminated: Channel dihentikan, dan pengguna harus bergabung kembali.

    Layanan RTC memungkinkan administrator menghapus partisipan.

    Exception perangkat lokal

    onLocalDeviceException

    Aplikasi Anda harus memeriksa izin dan apakah perangkat keras berfungsi dengan benar.

    Layanan RTC mendukung pendeteksian perangkat dan diagnostik. Saat terjadi exception perangkat lokal yang tidak dapat diselesaikan SDK, SDK memberi tahu aplikasi Anda melalui callback. Aplikasi Anda kemudian harus memeriksa status perangkat.

    private AliRtcEngineEventListener mRtcEngineEventListener = new AliRtcEngineEventListener() {
        @Override
        public void onJoinChannelResult(int result, String channel, String userId, int elapsed) {
            super.onJoinChannelResult(result, channel, userId, elapsed);
            handleJoinResult(result, channel, userId);
        }
    
        @Override
        public void onLeaveChannelResult(int result, AliRtcEngine.AliRtcStats stats){
            super.onLeaveChannelResult(result, stats);
        }
    
        @Override
        public void onConnectionStatusChange(AliRtcEngine.AliRtcConnectionStatus status, AliRtcEngine.AliRtcConnectionStatusChangeReason reason){
            super.onConnectionStatusChange(status, reason);
    
            handler.post(new Runnable() {
                @Override
                public void run() {
                    if(status == AliRtcEngine.AliRtcConnectionStatus.AliRtcConnectionStatusFailed) {
                        /* TODO: Callback ini harus ditangani. Ini dipicu hanya setelah strategi pemulihan internal SDK gagal. Kami menyarankan memberi tahu pengguna. */
                        ToastHelper.showToast(VideoChatActivity.this, R.string.video_chat_connection_failed, Toast.LENGTH_SHORT);
                    } else {
                        /* TODO: Opsional. Tambahkan logika bisnis di sini, seperti untuk analitik data atau pembaruan UI. */
                    }
                }
            });
        }
        @Override
        public void onLocalDeviceException(AliRtcEngine.AliRtcEngineLocalDeviceType deviceType, AliRtcEngine.AliRtcEngineLocalDeviceExceptionType exceptionType, String msg){
            super.onLocalDeviceException(deviceType, exceptionType, msg);
            /* TODO: Callback ini harus ditangani. Ini dipicu hanya setelah strategi pemulihan internal SDK gagal. Kami menyarankan memberi tahu pengguna tentang error perangkat. */
            handler.post(new Runnable() {
                @Override
                public void run() {
                    String str = "OnLocalDeviceException deviceType: " + deviceType + " exceptionType: " + exceptionType + " msg: " + msg;
                    ToastHelper.showToast(VideoChatActivity.this, str, Toast.LENGTH_SHORT);
                }
            });
        }
    
    };
    
    private AliRtcEngineNotify mRtcEngineNotify = new AliRtcEngineNotify() {
        @Override
        public void onAuthInfoWillExpire() {
            super.onAuthInfoWillExpire();
            /* TODO: Ini harus ditangani. Token akan segera kedaluwarsa. Aplikasi Anda harus mendapatkan token otentikasi baru untuk channel dan pengguna saat ini, lalu memanggil refreshAuthInfo. */
        }
    
        @Override
        public void onRemoteUserOnLineNotify(String uid, int elapsed){
            super.onRemoteUserOnLineNotify(uid, elapsed);
        }
    
        // Di callback onRemoteUserOffLineNotify, lepaskan renderer untuk aliran video jarak jauh.
        @Override
        public void onRemoteUserOffLineNotify(String uid, AliRtcEngine.AliRtcUserOfflineReason reason){
            super.onRemoteUserOffLineNotify(uid, reason);
        }
    
        // Di callback onRemoteTrackAvailableNotify, siapkan renderer untuk aliran video jarak jauh.
        @Override
        public void onRemoteTrackAvailableNotify(String uid, AliRtcEngine.AliRtcAudioTrack audioTrack, AliRtcEngine.AliRtcVideoTrack videoTrack){
            handler.post(new Runnable() {
                @Override
                public void run() {
                    if(videoTrack == AliRtcVideoTrackCamera) {
                        SurfaceView surfaceView = mAliRtcEngine.createRenderSurfaceView(VideoChatActivity.this);
                        surfaceView.setZOrderMediaOverlay(true);
                        FrameLayout view = getAvailableView();
                        if (view == null) {
                            return;
                        }
                        remoteViews.put(uid, view);
                        view.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
                        AliRtcEngine.AliRtcVideoCanvas remoteVideoCanvas = new AliRtcEngine.AliRtcVideoCanvas();
                        remoteVideoCanvas.view = surfaceView;
                        mAliRtcEngine.setRemoteViewConfig(remoteVideoCanvas, uid, AliRtcVideoTrackCamera);
                    } else if(videoTrack == AliRtcVideoTrackNo) {
                        if(remoteViews.containsKey(uid)) {
                            ViewGroup view = remoteViews.get(uid);
                            if(view != null) {
                                view.removeAllViews();
                                remoteViews.remove(uid);
                                mAliRtcEngine.setRemoteViewConfig(null, uid, AliRtcVideoTrackCamera);
                            }
                        }
                    }
                }
            });
        }
    
        /* Aplikasi Anda harus menangani kasus di mana beberapa perangkat mencoba bergabung dengan userId yang sama. */
        @Override
        public void onBye(int code){
            handler.post(new Runnable() {
                @Override
                public void run() {
                    String msg = "onBye code:" + code;
                    ToastHelper.showToast(VideoChatActivity.this, msg, Toast.LENGTH_SHORT);
                }
            });
        }
    };
    
    mAliRtcEngine.setRtcEngineEventListener(mRtcEngineEventListener);
    mAliRtcEngine.setRtcEngineNotify(mRtcEngineNotify);

5. Atur properti audio dan video

  • Atur properti audio

    Panggil setAudioProfile untuk mengatur mode pengkodean audio dan skenario audio.

    mAliRtcEngine.setAudioProfile(AliRtcEngine.AliRtcAudioProfile.AliRtcEngineHighQualityMode, AliRtcEngine.AliRtcAudioScenario.AliRtcSceneMusicMode);
  • Atur properti video

    Atur properti untuk aliran video yang dipublikasikan, seperti resolusi, bitrate, dan laju frame.

    // Atur parameter pengkodean video.
    AliRtcEngine.AliRtcVideoEncoderConfiguration aliRtcVideoEncoderConfiguration = new AliRtcEngine.AliRtcVideoEncoderConfiguration();
    aliRtcVideoEncoderConfiguration.dimensions = new AliRtcEngine.AliRtcVideoDimensions(
                    720, 1280);
    aliRtcVideoEncoderConfiguration.frameRate = 20;
    aliRtcVideoEncoderConfiguration.bitrate = 1200;
    aliRtcVideoEncoderConfiguration.keyFrameInterval = 2000;
    aliRtcVideoEncoderConfiguration.orientationMode = AliRtcVideoEncoderOrientationModeAdaptive;
    mAliRtcEngine.setVideoEncoderConfiguration(aliRtcVideoEncoderConfiguration);

6. Atur properti penerbitan dan berlangganan

Konfigurasikan penerbitan aliran dan perilaku berlangganan default:

  • Panggil publishLocalAudioStream untuk memublikasikan aliran audio.

  • Panggil publishLocalVideoStream untuk memublikasikan aliran video. Untuk panggilan hanya audio, atur nilai ini menjadi false.

// SDK memublikasikan audio secara default, sehingga Anda tidak perlu memanggil publishLocalAudioStream.
mAliRtcEngine.publishLocalAudioStream(true);
// Untuk panggilan video, Anda tidak perlu memanggil publishLocalVideoStream(true) karena SDK memublikasikan video secara default.
// Untuk panggilan hanya audio, Anda harus memanggil publishLocalVideoStream(false) untuk menonaktifkan publikasi video.
mAliRtcEngine.publishLocalVideoStream(true);

// Atur default untuk berlangganan aliran audio dan video jarak jauh.
mAliRtcEngine.setDefaultSubscribeAllRemoteAudioStreams(true);
mAliRtcEngine.subscribeAllRemoteAudioStreams(true);
mAliRtcEngine.setDefaultSubscribeAllRemoteVideoStreams(true);
mAliRtcEngine.subscribeAllRemoteVideoStreams(true);
Catatan

Secara default, SDK secara otomatis memublikasikan aliran lokal dan berlangganan aliran jarak jauh. Anda dapat memanggil metode di atas untuk menonaktifkan perilaku otomatis ini.

7. Mulai pratinjau lokal

  • Panggil setLocalViewConfig untuk mengonfigurasi tampilan pratinjau lokal. Ini memerlukan objek AliRtcVideoCanvas.

  • Panggil metode startPreview untuk memulai pratinjau video lokal.

mLocalVideoCanvas = new AliRtcEngine.AliRtcVideoCanvas();
SurfaceView localSurfaceView = mAliRtcEngine.createRenderSurfaceView(VideoChatActivity.this);
localSurfaceView.setZOrderOnTop(true);
localSurfaceView.setZOrderMediaOverlay(true);
FrameLayout fl_local = findViewById(R.id.fl_local);
fl_local.addView(localSurfaceView, layoutParams);
mLocalVideoCanvas.view = localSurfaceView;
mAliRtcEngine.setLocalViewConfig(mLocalVideoCanvas, AliRtcVideoTrackCamera);
mAliRtcEngine.startPreview();

8. Bergabung dengan saluran

Panggil joinChannel untuk bergabung ke channel. Jika token dihasilkan berdasarkan aturan parameter tunggal, panggil antarmuka joinChannel[1/3] parameter tunggal SDK. Jika token dihasilkan berdasarkan aturan multiparameter, panggil antarmuka joinChannel[2/3] multiparameter SDK. Setelah Anda bergabung ke channel, callback onJoinChannelResult memberikan hasilnya. Jika result bernilai 0, Anda telah berhasil bergabung ke channel. Jika tidak, periksa apakah token yang diberikan tidak valid.

 mAliRtcEngine.joinChannel(token, null, null, null);
Catatan
  • Setelah Anda bergabung ke channel, SDK memublikasikan dan berlangganan aliran berdasarkan parameter yang diatur sebelum bergabung.

  • SDK secara default memublikasikan dan berlangganan aliran secara otomatis untuk mengurangi jumlah pemanggilan API yang diperlukan.

9. Atur tampilan jarak jauh

Saat inisialisasi engine, atur callback mAliRtcEngine.setRtcEngineNotify. Di callback onRemoteTrackAvailableNotify, konfigurasikan tampilan jarak jauh untuk setiap pengguna jarak jauh:

@Override
public void onRemoteTrackAvailableNotify(String uid, AliRtcEngine.AliRtcAudioTrack audioTrack, AliRtcEngine.AliRtcVideoTrack videoTrack){
    handler.post(new Runnable() {
        @Override
        public void run() {
            if(videoTrack == AliRtcVideoTrackCamera) {
                SurfaceView surfaceView = mAliRtcEngine.createRenderSurfaceView(VideoChatActivity.this);
                surfaceView.setZOrderMediaOverlay(true);
                FrameLayout fl_remote = findViewById(R.id.fl_remote);
                if (fl_remote == null) {
                    return;
                }
                fl_remote.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
                AliRtcEngine.AliRtcVideoCanvas remoteVideoCanvas = new AliRtcEngine.AliRtcVideoCanvas();
                remoteVideoCanvas.view = surfaceView;
                mAliRtcEngine.setRemoteViewConfig(remoteVideoCanvas, uid, AliRtcVideoTrackCamera);
            } else if(videoTrack == AliRtcVideoTrackNo) {
                FrameLayout fl_remote = findViewById(R.id.fl_remote);
                fl_remote.removeAllViews();
                mAliRtcEngine.setRemoteViewConfig(null, uid, AliRtcVideoTrackCamera);
            }
        }
    });
}

10. Tinggalkan channel dan hancurkan engine

Saat sesi audio dan video berakhir, tinggalkan channel dan hancurkan engine:

  1. Panggil stopPreview untuk menghentikan pratinjau video.

  2. Panggil leaveChannel untuk meninggalkan channel.

  3. Panggil destroy untuk menghancurkan engine dan melepaskan sumber dayanya.

private void destroyRtcEngine() {
    mAliRtcEngine.stopPreview();
    mAliRtcEngine.setLocalViewConfig(null, AliRtcVideoTrackCamera);
    mAliRtcEngine.leaveChannel();
    mAliRtcEngine.destroy();
    mAliRtcEngine = null;
}

11. Demonstrasi

image

Dokumen terkait

Struktur data

Antarmuka AliRtcEngine