全部产品
Search
文档中心

ApsaraVideo Live:Implementasikan komunikasi audio/video di Android

更新时间:Dec 16, 2025

Panduan ini menjelaskan cara mengintegrasikan ARTC SDK ke dalam proyek Android Anda untuk membangun aplikasi audio dan video real-time, yang cocok untuk kasus penggunaan seperti live streaming interaktif dan panggilan video.

Deskripsi fitur

Sebelum memulai, pahami konsep-konsep utama berikut:

  • ARTC SDK: SDK yang disediakan oleh Alibaba Cloud untuk membantu developer mengimplementasikan interaksi audio dan video real-time secara cepat.

  • Global Realtime Transport Network (GRTN): Jaringan terdistribusi global yang dirancang khusus untuk media real-time, menjamin latensi ultra-rendah, kualitas tinggi, dan komunikasi yang aman.

  • Channel: Ruang virtual tempat pengguna bergabung untuk saling berkomunikasi. Semua pengguna dalam channel yang sama dapat berinteraksi secara real-time.

  • Host: Pengguna yang dapat menerbitkan aliran audio dan video dalam suatu channel serta berlangganan aliran yang diterbitkan oleh host lain.

  • Viewer: Pengguna yang dapat berlangganan aliran audio dan video dalam suatu channel tetapi tidak dapat menerbitkan alirannya sendiri.

Proses dasar untuk mengimplementasikan interaksi audio dan video real-time:

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

    • Skenario panggilan video: Semua pengguna adalah host dan dapat menerbitkan serta berlangganan aliran.

    • Skenario streaming interaktif: Peran harus diatur menggunakan setClientRole sebelum bergabung ke channel. Untuk pengguna yang akan menerbitkan aliran, atur perannya menjadi host. Jika pengguna hanya perlu berlangganan aliran, atur perannya menjadi viewer.

  2. Setelah bergabung ke channel, pengguna memiliki perilaku penerbitan dan langganan yang berbeda berdasarkan perannya:

    • Semua pengguna dapat menerima aliran audio dan video dalam channel tersebut.

    • Seorang host dapat menerbitkan aliran audio dan video dalam channel.

    • Jika seorang viewer ingin menerbitkan aliran, panggil metode setClientRole untuk mengubah perannya menjadi host.

Proyek contoh

ARTC SDK menyediakan proyek contoh open-source untuk aplikasi audio dan video real-time.

Persyaratan lingkungan

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

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

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

    Catatan

    Kami merekomendasikan penggunaan perangkat fisik untuk pengujian, karena beberapa emulator mungkin tidak memiliki fungsi yang diperlukan.

  • Network: Koneksi internet yang stabil.

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

Buat proyek (opsional)

Bagian ini menjelaskan cara membuat proyek dan menambahkan izin yang diperlukan untuk interaksi audio dan video. Anda dapat melewati bagian ini jika sudah memiliki proyek.

  1. Buka Android Studio dan pilih New Project.

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

image.png

  1. Konfigurasikan informasi proyek Anda, termasuk nama proyek, nama paket, lokasi penyimpanan, bahasa (Java pada contoh ini), dan bahasa konfigurasi build (Groovy DSL pada contoh ini).

image.png

  1. Klik Finish dan tunggu hingga proyek selesai disinkronkan.

Konfigurasi proyek

Langkah 1: Impor SDK

Integrasi otomatis melalui Maven (direkomendasikan)

  1. Buka file settings.gradle di direktori root proyek Anda dan tambahkan Repositori Maven untuk ARTC SDK ke bidang dependencyResolutionManagement/repositories, seperti pada contoh 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' }
    }
}

Note: Jika Anda menggunakan Android Gradle Plugin versi di bawah 7.1.0, Anda mungkin tidak menemukan bidang yang sesuai di file settings.gradle. Untuk informasi selengkapnya, lihat Android Gradle Plugin 7.1. Dalam kasus ini, gunakan solusi alternatif berikut:

Solusi alternatif

Buka file build.gradle di direktori root proyek Anda dan tambahkan Repositori Maven ke allprojects/repositories, seperti pada contoh berikut:

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 untuk ARTC SDK ke dependencies. Anda dapat memperoleh versi terbaru di Download SDK dan ganti ${latest_version} dengan nomor versi spesifik. Versi terbaru yang tersedia adalah 7.9.1.

dependencies {
    // Tambahkan dependensi untuk ARTC SDK
    // Ganti ${latest_version} dengan nomor versi spesifik.
    implementation 'com.aliyun.aio:AliVCSDK_ARTC:${latest_version}'
    // Untuk versi 7.4.0 dan di bawahnya, tambahkan dependensi keep
    // implementation 'com.aliyun.aio.keep:keep:1.0.1'
}   

Jika Anda menggunakan Android Gradle Plugin versi 8.1 atau lebih baru, Android Studio merekomendasikan migrasi informasi library dependensi ke version catalog. Untuk informasi selengkapnya, lihat Migrasi dependensi.

Integrasi manual

  1. Unduh file AAR ARTC SDK untuk versi yang Anda butuhkan (AliVCSDK_ARTC-x.y.z.aar). Versi terbaru yang tersedia adalah 7.9.1.

  2. Salin file AAR yang telah diunduh ke direktori proyek Anda, misalnya app/libs. Buat folder ini jika belum tersedia.

  3. Buka file settings.gradle di direktori root proyek Anda dan tambahkan folder yang berisi file AAR ke dependencyResolutionManagement/repositories, seperti pada contoh berikut:

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

Note: Jika Anda menggunakan Android Gradle Plugin versi di bawah 7.1.0, Anda mungkin tidak menemukan bidang yang sesuai di file settings.gradle. Untuk informasi selengkapnya, lihat Android Gradle Plugin 7.1. Dalam kasus ini, gunakan solusi alternatif berikut:

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

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

// Ganti x.y.z dengan nomor versi spesifik.
implementation(name:'AliVCSDK_ARTC', version: 'x.y.z', ext:'aar')
  1. Dependensi yang sesuai akan dibuat di bawah External Libraries.

    image

Langkah 2: Konfigurasi arsitektur CPU yang didukung

Buka file app/build.gradle dan tentukan arsitektur CPU yang didukung oleh proyek Anda di defaultConfig, seperti pada contoh berikut. Arsitektur yang tersedia meliputi armeabi-v7a, arm64-v8a, x86, dan x86_64.

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

Langkah 3: Konfigurasi izin

Konfigurasikan izin yang diperlukan oleh 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 lama pada perangkat lama. -->
<uses-permission
  android:name="android.permission.BLUETOOTH"
  android:maxSdkVersion="30" />
<uses-permission
  android: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" />

Note: Untuk Android 6.0 (API level 23) dan versi yang lebih baru, izin runtime perlu diminta secara dinamis. Selain mendeklarasikannya secara statis di file AndroidManifest.xml, Anda juga perlu meminta izin tersebut saat runtime.

Izin runtime meliputi:

  • Manifest.permission.CAMERA

  • Manifest.permission.WRITE_EXTERNAL_STORAGE

  • Manifest.permission.RECORD_AUDIO

  • Manifest.permission.READ_EXTERNAL_STORAGE

  • Manifest.permission.READ_PHONE_STATE

Untuk Android 12 (API level 31) dan versi yang lebih baru, izin berikut juga perlu diminta secara dinamis:

  • Manifest.permission.BLUETOOTH_CONNECT

Deskripsi izin:

Nama izin

Deskripsi

Tujuan

Wajib

Permintaan dinamis

CAMERA

Izin kamera.

Akses kamera perangkat untuk menangkap aliran video.

Ya

Android 6 dan versi lebih baru

RECORD_AUDIO

Izin mikrofon.

Akses mikrofon perangkat untuk menangkap aliran audio.

Ya

Android 6 dan versi lebih baru

INTERNET

Izin jaringan.

Mengirimkan data audio dan video melalui jaringan (misalnya, protokol WebRTC).

Ya

Tidak

ACCESS_NETWORK_STATE

Mengizinkan aplikasi untuk mendapatkan status jaringan.

Memantau status koneksi jaringan untuk mengoptimalkan kualitas transmisi audio dan video, seperti menyambung ulang saat terputus.

Tidak

Tidak

ACCESS_WIFI_STATE

Mengizinkan aplikasi untuk mendapatkan status WiFi.

Mendapatkan informasi koneksi WiFi saat ini untuk mengoptimalkan kinerja jaringan.

Tidak

Tidak

MODIFY_AUDIO_SETTINGS

Mengizinkan aplikasi untuk mengubah konfigurasi audio.

Menyesuaikan volume sistem, mengganti perangkat output audio, dll.

Tidak

Tidak

BLUETOOTH

Izin Bluetooth (fungsi dasar)

Menghubungkan ke perangkat Bluetooth (seperti headset Bluetooth).

Tidak

Tidak

BLUETOOTH_CONNECT

Izin koneksi Bluetooth

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

Tidak

Android 12 dan versi lebih baru

READ_PHONE_STATE

Mengizinkan aplikasi untuk mengakses informasi terkait status telepon perangkat

Memulai atau menghentikan audio berdasarkan status telepon.

Tidak

Android 6 dan versi lebih baru

READ_EXTERNAL_STORAGE

Mengizinkan aplikasi untuk membaca file dari penyimpanan eksternal.

Memutar musik lokal, dll.

Tidak

Android 6 dan versi lebih baru

WRITE_EXTERNAL_STORAGE

Mengizinkan aplikasi untuk menulis ke penyimpanan eksternal.

Menyimpan file audio dan video, log, dll.

Tidak

Android 6 dan versi lebih baru

Langkah 4: Cegah obfuscation kode (opsional)

Dalam file app/proguard-rules.pro, konfigurasikan aturan untuk SDK agar antarmuka yang disediakan oleh SDK tidak di-obfuscate, yang dapat menyebabkan pemanggilan fungsi tidak berjalan semestinya.

-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.** {
*;
}

Langkah 5: Buat antarmuka pengguna

Buat antarmuka pengguna yang sesuai dengan skenario Anda. Contoh kode berikut untuk skenario panggilan video membuat dua tampilan untuk menampilkan aliran video lokal dan remote. Anda dapat menggunakannya sebagai referensi selama pengembangan.

Contoh kode antarmuka pengguna

<?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>

Implementasi

Bagian ini menjelaskan cara menggunakan ARTC SDK untuk membangun aplikasi audio dan video real-time dasar. Anda dapat menyalin contoh kode lengkap ke dalam proyek Anda untuk menguji fungsionalitasnya. Langkah-langkah di bawah ini menjelaskan pemanggilan API inti.

Diagram berikut menunjukkan alur kerja dasar untuk mengimplementasikan panggilan video:

image

Contoh kode untuk skenario panggilan video

Contoh kode

/**
 * 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) {
            // Aksi yang diambil saat tombol kembali diklik
            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, selalu gunakan AliRTCSdkInteractiveLive.
        mAliRtcEngine.setChannelProfile(AliRtcEngine.AliRTCSdkChannelProfile.AliRTCSdkInteractiveLive);
        // Atur peran pengguna. Untuk menerbitkan dan berlangganan, gunakan AliRTCSdkInteractive. Untuk hanya berlangganan, gunakan AliRTCSdkLive.
        mAliRtcEngine.setClientRole(AliRtcEngine.AliRTCSdkClientRole.AliRTCSdkInteractive);
        // Atur profil audio. Default 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 menerbitkan audio secara default, sehingga Anda tidak perlu memanggil publishLocalAudioStream.
        mAliRtcEngine.publishLocalAudioStream(true);
        // Untuk panggilan video, Anda tidak perlu memanggil publishLocalVideoStream(true) karena SDK menerbitkan video secara default.
        // Untuk panggilan hanya audio, Anda perlu memanggil publishLocalVideoStream(false) untuk menonaktifkan penerbitan video.
        mAliRtcEngine.publishLocalVideoStream(true);

        // Atur langganan default ke aliran audio dan video remote.
        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: Harus ditangani. Kami merekomendasikan memberi tahu pengguna. Ini dilaporkan hanya setelah strategi pemulihan internal SDK gagal. */
                        ToastHelper.showToast(VideoCallActivity.this, R.string.video_chat_connection_failed, Toast.LENGTH_SHORT);
                    } else {
                        /* TODO: Opsional. Tambahkan logika bisnis di sini, biasanya untuk analitik data atau perubahan UI. */
                    }
                }
            });
        }
        @Override
        public void OnLocalDeviceException(AliRtcEngine.AliRtcEngineLocalDeviceType deviceType, AliRtcEngine.AliRtcEngineLocalDeviceExceptionType exceptionType, String msg){
            super.OnLocalDeviceException(deviceType, exceptionType, msg);
            /* TODO: Harus ditangani. Kami merekomendasikan memberi tahu pengguna tentang kesalahan perangkat. Ini dilaporkan hanya setelah strategi pemulihan internal SDK gagal. */
            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: Harus ditangani. Saat callback ini dipicu, ambil token baru untuk pengguna dan channel saat ini, lalu panggil refreshAuthInfo untuk memperbaruinya. */
        }

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

        // Hapus renderer aliran video remote di callback onRemoteUserOffLineNotify.
        @Override
        public void onRemoteUserOffLineNotify(String uid, AliRtcEngine.AliRtcUserOfflineReason reason){
            super.onRemoteUserOffLineNotify(uid, reason);
        }

        // Atur renderer aliran video remote di callback onRemoteTrackAvailableNotify.
        @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 juga 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, lihat Jalankan proyek demo ARTC untuk Android.

1. Minta izin

Saat memulai panggilan video, periksa apakah izin yang diperlukan telah diberikan di aplikasi:

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 memverifikasi identitas pengguna. Untuk detail cara pembuatan token, lihat Implementasikan autentikasi berbasis token. Token dapat dihasilkan menggunakan metode parameter tunggal atau metode multi-parameter. Metode yang Anda gunakan menentukan API joinChannel mana yang perlu Anda panggil.

Untuk lingkungan produksi:

Karena pembuatan token memerlukan AppKey Anda, hardcoding-nya di sisi klien menimbulkan risiko keamanan. Di lingkungan produksi, kami sangat merekomendasikan menghasilkan token di server Anda dan mengirimkannya ke klien.

Untuk pengembangan dan debugging:

Saat pengembangan, jika server bisnis Anda belum memiliki logika untuk menghasilkan token, Anda dapat sementara menggunakan logika pembuatan token dari APIExample untuk membuat token sementara. Kode referensinya sebagai berikut:

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

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

    /**
     * Hasilkan token parameter tunggal untuk bergabung ke pertemuan berdasarkan channelId, userId, timestamp, 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 parameter tunggal untuk bergabung ke pertemuan 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 terkait dari 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 engine

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

      Bergantung pada kebutuhan bisnis Anda, Anda dapat memilih mode interaktif, yang cocok untuk skenario hiburan interaktif, atau mode komunikasi, yang cocok untuk siaran satu-ke-satu atau satu-ke-banyak. Memilih mode yang tepat memastikan pengalaman pengguna yang lancar dan penggunaan sumber daya jaringan yang efisien.

      Mode

      Penerbitan

      Berlangganan

      Deskripsi

      Mode interaktif

      • Dibatasi oleh peran. Hanya pengguna dengan peran host yang dapat menerbitkan aliran.

      • Partisipan dapat secara fleksibel mengganti peran sepanjang sesi.

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

      • Dalam mode interaktif, event seperti host bergabung atau meninggalkan channel, atau mulai menerbitkan aliran, diberitahukan kepada viewer secara real-time. Sebaliknya, aktivitas viewer tidak diberitahukan kepada host, memastikan streaming tidak terganggu.

      • Dalam mode ini, host bertanggung jawab atas interaksi, sedangkan viewer hanya mengonsumsi konten. Jika kebutuhan bisnis Anda mungkin berubah, pertimbangkan untuk menggunakan mode interaktif secara default. Fleksibilitasnya memungkinkan Anda beradaptasi dengan berbagai kebutuhan interaksi dengan menyesuaikan peran pengguna.

      Mode komunikasi

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

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

      • Dalam mode komunikasi, partisipan saling mengetahui keberadaan satu sama lain.

      • Meskipun mode ini tidak membedakan peran pengguna, secara fungsional setara dengan peran host dalam mode interaktif. Tujuannya adalah 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 menerbitkan dan berlangganan secara default. Peran viewer hanya berlangganan secara default, dengan pratinjau dan penerbitan dinonaktifkan.

      Catatan

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

      • Beralih dari host ke viewer: Sistem menghentikan penerbitan aliran audio dan video lokal. Aliran remote yang telah dilanggan tidak terpengaruh, dan pengguna dapat terus menonton orang lain.

      • Beralih dari viewer ke host: Sistem mulai menerbitkan aliran audio dan video lokal. Aliran remote yang telah dilanggan tetap tidak berubah, dan pengguna dapat terus menonton partisipan lain.

      // Atur mode channel ke mode interaktif, gunakan AliRTCSdkInteractiveLive untuk RTC
      mAliRtcEngine.setChannelProfile(AliRtcEngine.AliRTCSdkChannelProfile.AliRTCSdkInteractiveLive);
      // Atur peran pengguna, gunakan AliRTCSdkInteractive untuk menerbitkan dan menarik aliran, gunakan AliRTCSdkLive jika hanya menarik aliran tanpa menerbitkan
      mAliRtcEngine.setClientRole(AliRtcEngine.AliRTCSdkClientRole.AliRTCSdkInteractive);
  • Atur callback umum

    Jika SDK mengalami masalah selama operasi, SDK akan terlebih dahulu mencoba memulihkan secara otomatis menggunakan mekanisme retry internalnya. Untuk kesalahan yang tidak dapat diselesaikan sendiri, SDK akan memberi tahu aplikasi Anda melalui antarmuka callback yang telah ditentukan.

    Berikut adalah callback penting untuk masalah yang tidak dapat ditangani SDK, yang harus didengarkan dan ditanggapi oleh aplikasi Anda:

    Penyebab pengecualian

    Callback dan parameter

    Solusi

    Deskripsi

    Otentikasi gagal

    result di callback onJoinChannelResult mengembalikan AliRtcErrJoinBadToken.

    Aplikasi harus memeriksa apakah token benar.

    Saat pengguna memanggil API, jika otentikasi gagal, callback API akan mengembalikan kesalahan kegagalan otentikasi.

    Token akan kedaluwarsa

    onAuthInfoWillExpire

    Ambil token baru dan panggil refreshAuthInfo untuk memperbarui informasi.

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

    Token kedaluwarsa

    onAuthInfoExpired

    Aplikasi harus bergabung kembali ke channel.

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

    Masalah koneksi jaringan

    Callback onConnectionStatusChange mengembalikan AliRtcConnectionStatusFailed.

    Aplikasi harus bergabung kembali ke channel.

    SDK dapat memulihkan secara otomatis dari gangguan koneksi jaringan singkat. Jika waktu putus melebihi ambang batas, akan terjadi timeout. Aplikasi harus memeriksa status jaringan dan memandu pengguna untuk bergabung kembali.

    Dikeluarkan dari channel

    onBye

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

    • AliRtcOnByeBeKickedOut: Pengguna dikeluarkan dari channel dan perlu bergabung kembali.

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

    Layanan RTC memungkinkan administrator menghapus peserta.

    Pengecualian perangkat lokal

    onLocalDeviceException

    Periksa izin aplikasi dan apakah perangkat keras berfungsi dengan benar.

    Saat terjadi pengecualian perangkat lokal yang tidak dapat diselesaikan SDK, SDK akan memberi tahu aplikasi melalui callback. Aplikasi kemudian harus melakukan intervensi untuk 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: Harus ditangani. Kami merekomendasikan memberi tahu pengguna. Ini dilaporkan hanya setelah strategi pemulihan internal SDK gagal. */
                        ToastHelper.showToast(VideoChatActivity.this, R.string.video_chat_connection_failed, Toast.LENGTH_SHORT);
                    } else {
                        /* TODO: Opsional. Tambahkan logika bisnis di sini, biasanya untuk analitik data atau perubahan UI. */
                    }
                }
            });
        }
        @Override
        public void OnLocalDeviceException(AliRtcEngine.AliRtcEngineLocalDeviceType deviceType, AliRtcEngine.AliRtcEngineLocalDeviceExceptionType exceptionType, String msg){
            super.OnLocalDeviceException(deviceType, exceptionType, msg);
            /* TODO: Harus ditangani. Kami merekomendasikan memberi tahu pengguna tentang kesalahan perangkat. Ini dilaporkan hanya setelah strategi pemulihan internal SDK gagal. */
            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: Harus ditangani. Saat callback ini dipicu, ambil token baru untuk pengguna dan channel saat ini, lalu panggil refreshAuthInfo untuk memperbaruinya. */
        }
    
        @Override
        public void onRemoteUserOnLineNotify(String uid, int elapsed){
            super.onRemoteUserOnLineNotify(uid, elapsed);
        }
    
        // Hapus renderer aliran video remote di callback onRemoteUserOffLineNotify.
        @Override
        public void onRemoteUserOffLineNotify(String uid, AliRtcEngine.AliRtcUserOfflineReason reason){
            super.onRemoteUserOffLineNotify(uid, reason);
        }
    
        // Atur renderer aliran video remote di callback onRemoteTrackAvailableNotify.
        @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 juga 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.

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

    Atur properti untuk aliran video yang diterbitkan, 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 langganan

Konfigurasikan penerbitan aliran audio/video dan atur langganan default ke semua aliran pengguna:

  • Panggil publishLocalAudioStream untuk menerbitkan aliran audio.

  • Panggil publishLocalVideoStream untuk menerbitkan aliran video. Untuk panggilan hanya audio, Anda dapat mengatur ini ke false.

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

// Atur langganan default ke semua aliran audio dan video remote.
mAliRtcEngine.setDefaultSubscribeAllRemoteAudioStreams(true);
mAliRtcEngine.subscribeAllRemoteAudioStreams(true);
mAliRtcEngine.setDefaultSubscribeAllRemoteVideoStreams(true);
mAliRtcEngine.subscribeAllRemoteVideoStreams(true);
Catatan

Secara default, SDK secara otomatis menerbitkan aliran audio dan video lokal serta berlangganan aliran audio dan video semua pengguna lain dalam channel. Anda dapat memanggil metode di atas untuk mengganti perilaku default ini.

7. Aktifkan pratinjau lokal

  • Panggil setLocalViewConfig untuk mengatur tampilan render lokal dan mengonfigurasi properti tampilan video lokal.

  • Panggil 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 ke channel

Panggil joinChannel untuk bergabung ke channel. Jika token dihasilkan menggunakan metode parameter tunggal, panggil operasi joinChannel[1/3]. Jika dihasilkan menggunakan metode multi-parameter, panggil metode joinChannel[2/3]. Hasilnya dikembalikan dalam callback onJoinChannelResult. Nilai result 0 menunjukkan keberhasilan bergabung. Nilai non-nol mungkin menunjukkan token tidak valid.

 mAliRtcEngine.joinChannel(token, null, null, null);
Catatan
  • Setelah bergabung ke channel, SDK akan menerbitkan dan berlangganan aliran sesuai dengan parameter yang diatur sebelum bergabung.

  • SDK secara otomatis menerbitkan dan berlangganan secara default untuk mengurangi jumlah pemanggilan API yang perlu dilakukan klien.

9. Atur tampilan remote

Saat menginisialisasi engine, atur callback yang sesuai dengan mAliRtcEngine.setRtcEngineNotify. Di callback onRemoteTrackAvailableNotify, Anda dapat mengatur tampilan remote untuk pengguna remote. Contoh kode:

@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 hapus engine

Saat sesi berakhir, tinggalkan channel dan hapus engine:

  1. Panggil stopPreview untuk menghentikan pratinjau video.

  2. Panggil leaveChannel untuk meninggalkan channel.

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

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

11. Demonstrasi efek

image

Referensi

Struktur data

Kelas AliRtcEngine