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

ApsaraVideo Live:Android でのオーディオ/ビデオ通信の実装

最終更新日:Dec 16, 2025

このガイドでは、ARTC SDK を Android プロジェクトに統合して、インタラクティブライブストリーミングやビデオ通話などのユースケースに適したリアルタイムのオーディオ/ビデオアプリケーションを構築する方法について説明します。

機能説明

開始する前に、以下の主要な概念を理解してください:

  • ARTC SDK:Alibaba Cloud が提供する SDK で、開発者がリアルタイムのオーディオ/ビデオインタラクションを迅速に実装するのに役立ちます。

  • グローバルリアルタイム伝送ネットワーク (GRTN):リアルタイムメディア向けに設計されたグローバルに分散されたネットワークで、超低遅延、高品質、かつ安全な通信を保証します。

  • チャンネル:ユーザーが互いに通信するために参加する仮想的な部屋です。同じチャンネル内のすべてのユーザーはリアルタイムで対話できます。

  • ホスト:チャンネル内でオーディオ/ビデオストリームを公開し、他のホストが公開したストリームをサブスクライブできるユーザーです。

  • 視聴者:チャンネル内のオーディオ/ビデオストリームをサブスクライブできますが、自身のストリームは公開できないユーザーです。

リアルタイムのオーディオ/ビデオインタラクションを実装するための基本的なプロセス:

  1. setChannelProfile を呼び出してシナリオを設定し、joinChannel を呼び出してチャンネルに参加します:

    • ビデオ通話シナリオ:すべてのユーザーがホストであり、ストリームの公開とサブスクライブの両方が可能です。

    • インタラクティブストリーミングシナリオ:チャンネルに参加する前に setClientRole を使用してロールを設定する必要があります。ストリームを公開するユーザーには、ロールをホストに設定します。ユーザーがストリームのサブスクライブのみを必要とする場合は、ロールを視聴者に設定します。

  2. チャンネルに参加した後、ユーザーはロールに基づいて異なる公開およびサブスクライブの動作をします:

    • すべてのユーザーがそのチャンネル内のオーディオ/ビデオストリームを受信できます。

    • ホストはチャンネル内でオーディオ/ビデオストリームを公開できます。

    • 視聴者がストリームを公開したい場合は、setClientRole メソッドを呼び出してロールをホストに切り替えます。

サンプルプロジェクト

ARTC SDK は、リアルタイムのオーディオ/ビデオアプリ向けのオープンソースのサンプルプロジェクトを提供しています。

環境要件

サンプルプロジェクトを実行する前に、ご利用の開発環境が以下の要件を満たしていることを確認してください:

  • 開発ツール: Android Studio 2020.3.1 以降。

  • テストデバイス: Android 5.0 (SDK API レベル 21) 以降を実行するテストデバイス。

    説明

    一部のエミュレーターには必要な機能がない場合があるため、テストには物理デバイスを使用することを推奨します。

  • ネットワーク:安定したインターネット接続。

  • アプリケーション:ARTC アプリケーションの AppID と AppKey を取得します。詳細については、「ARTC アプリケーションの作成」をご参照ください。

プロジェクトの作成 (任意)

このセクションでは、プロジェクトを作成し、オーディオ/ビデオインタラクションに必要な権限を追加する方法について説明します。すでにプロジェクトがある場合は、このセクションをスキップできます。

  1. Android Studio を開き、[New Project] を選択します。

  2. [Phone and Tablet] を選択し、スターターテンプレートを選択します。この例では、[Empty Views Activity] を使用します。

image.png

  1. プロジェクト名、パッケージ名、保存場所、言語 (この例では Java)、ビルド構成言語 (この例では Groovy DSL) など、プロジェクト情報を設定します。

image.png

  1. [Finish] をクリックし、プロジェクトの同期が完了するのを待ちます。

プロジェクトの設定

ステップ 1:SDK のインポート

Maven による自動統合 (推奨)

  1. プロジェクトのルートディレクトリにある settings.gradle ファイルを開き、次の例に示すように、ARTC SDK の Maven リポジトリを dependencyResolutionManagement/repositories フィールドに追加します:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        // ARTC SDK の Maven リポジトリを追加
        maven { url 'https://maven.aliyun.com/repository/google' }
        maven { url 'https://maven.aliyun.com/repository/public' }
    }
}

注意:Android Gradle プラグインのバージョンが 7.1.0 未満の場合、settings.gradle ファイルに対応するフィールドが見つからないことがあります。詳細については、「Android Gradle プラグイン 7.1」をご参照ください。この場合は、次の代替ソリューションを使用してください:

代替ソリューション

プロジェクトのルートディレクトリにある build.gradle ファイルを開き、次の例に示すように、Maven リポジトリを allprojects/repositories に追加します:

allprojects {
    repositories {
        ...
        // ARTC SDK の Maven リポジトリを追加
        maven { url 'https://maven.aliyun.com/repository/google' }
        maven { url 'https://maven.aliyun.com/repository/public' }
    }
}
  1. app/build.gradle ファイルを開き、ARTC SDK の依存関係を dependencies に追加します。「SDK のダウンロード」で最新バージョンを取得し、${latest_version} を特定のバージョン番号に置き換えることができます。利用可能な最新バージョンは 7.9.1 です。

dependencies {
    // ARTC SDK の依存関係を追加
    // ${latest_version} を特定のバージョン番号に置き換えます。
    implementation 'com.aliyun.aio:AliVCSDK_ARTC:${latest_version}'
    // バージョン 7.4.0 以前の場合は、keep 依存関係を追加します
    // implementation 'com.aliyun.aio.keep:keep:1.0.1'
}   

Android Gradle プラグインのバージョン 8.1 以降を使用している場合、Android Studio は依存関係ライブラリの情報をバージョンカタログに移行することを推奨します。詳細については、「依存関係の移行」をご参照ください。

手動統合

  1. ダウンロード」から、必要なバージョンの ARTC SDK AAR ファイル (AliVCSDK_ARTC-x.y.z.aar) を入手します。利用可能な最新バージョンは 7.9.1 です。

  2. ダウンロードした AAR ファイルを、app/libs などのプロジェクトディレクトリにコピーします。このフォルダが存在しない場合は作成してください。

  3. プロジェクトのルートディレクトリにある settings.gradle ファイルを開き、次の例に示すように、AAR ファイルを含むフォルダを dependencyResolutionManagement/repositories に追加します:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        // ARTC SDK が配置されている相対ディレクトリを追加
        flatDir {
            dir 'app/libs'
        }
    }
}

注意:Android Gradle プラグインのバージョンが 7.1.0 未満の場合、settings.gradle ファイルに対応するフィールドが見つからないことがあります。詳細については、「Android Gradle プラグイン 7.1」をご参照ください。この場合は、次の代替ソリューションを使用してください:

プロジェクトのルートディレクトリにある build.gradle ファイルを開き、allprojects/repositories に次のフィールドを追加します:

allprojects {
    repositories {
        ...
        // ARTC SDK が配置されている相対ディレクトリを追加
        flatDir {
            dir 'app/libs'
        }
    }
}
  1. app/build.gradle ファイルを開き、AAR ファイルの依存関係を dependencies に追加します。サンプルコード:

// x.y.z を特定のバージョン番号に置き換えます。
implementation(name:'AliVCSDK_ARTC', version: 'x.y.z', ext:'aar')
  1. External Libraries の下に、対応する依存関係が生成されます。

    image

ステップ 2:サポートされる CPU アーキテクチャの設定

app/build.gradle ファイルを開き、次の例に示すように、defaultConfig でプロジェクトがサポートする CPU アーキテクチャを指定します。利用可能なアーキテクチャには、armeabi-v7aarm64-v8ax86x86_64 があります。

android {
    defaultConfig {
        // ...その他のデフォルト構成
        // armeabi-v7a および arm64-v8a アーキテクチャをサポート
        ndk {
             abiFilters "armeabi-v7a", "arm64-v8a"
        }
    }
}	

ステップ 3:権限の設定

アプリケーションで必要な権限を設定します:

app/src/main ディレクトリに移動し、AndroidManifest.xml ファイルを開いて、必要な権限を追加します。

<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" />
<!-- 古いデバイスで従来の Bluetooth 権限をリクエストします。 -->
<uses-permission
  android:name="android.permission.BLUETOOTH"
  android:maxSdkVersion="30" />
<uses-permission
  android:name="android.permission.BLUETOOTH_ADMIN"
  android:maxSdkVersion="30" />

<!-- アプリがペアリング済みの Bluetooth デバイスと通信する場合にのみ必要です。 -->
<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" />

注意:Android 6.0 (API レベル 23) 以降では、実行時の権限を動的にリクエストする必要があります。AndroidManifest.xml ファイルで静的に宣言するだけでなく、実行時にも権限をリクエストする必要があります。

実行時の権限には以下が含まれます:

  • Manifest.permission.CAMERA

  • Manifest.permission.WRITE_EXTERNAL_STORAGE

  • Manifest.permission.RECORD_AUDIO

  • Manifest.permission.READ_EXTERNAL_STORAGE

  • Manifest.permission.READ_PHONE_STATE

Android 12 (API レベル 31) 以降では、以下の権限も動的にリクエストする必要があります:

  • Manifest.permission.BLUETOOTH_CONNECT

権限の説明:

権限名

説明

目的

必須

動的リクエスト

CAMERA

カメラの権限。

デバイスのカメラにアクセスしてビデオストリームをキャプチャします。

はい

Android 6 以降

RECORD_AUDIO

マイクの権限。

デバイスのマイクにアクセスしてオーディオストリームをキャプチャします。

はい

Android 6 以降

INTERNET

ネットワークの権限。

ネットワーク経由でオーディオおよびビデオデータを送信します (例:WebRTC プロトコル)。

はい

いいえ

ACCESS_NETWORK_STATE

アプリケーションがネットワークステータスを取得できるようにします。

ネットワーク接続ステータスを監視して、オーディオおよびビデオの伝送品質を最適化します (切断時の再接続など)。

いいえ

いいえ

ACCESS_WIFI_STATE

アプリケーションが WiFi ステータスを取得できるようにします。

現在の WiFi 接続情報を取得して、ネットワークパフォーマンスを最適化します。

いいえ

いいえ

MODIFY_AUDIO_SETTINGS

アプリケーションがオーディオ構成を変更できるようにします。

システムボリュームの調整、オーディオ出力デバイスの切り替えなど。

いいえ

いいえ

BLUETOOTH

Bluetooth 権限 (基本機能)

Bluetooth デバイス (Bluetooth ヘッドセットなど) に接続します。

いいえ

いいえ

BLUETOOTH_CONNECT

Bluetooth 接続権限

ペアリング済みの Bluetooth デバイスと通信します (オーディオストリームの送信など)。

いいえ

Android 12 以降

READ_PHONE_STATE

アプリケーションがデバイスの電話状態に関連する情報にアクセスできるようにします

電話の状態に基づいてオーディオを開始または停止します。

いいえ

Android 6 以降

READ_EXTERNAL_STORAGE

アプリケーションが外部ストレージからファイルを読み取れるようにします。

ローカル音楽の再生など。

いいえ

Android 6 以降

WRITE_EXTERNAL_STORAGE

アプリケーションが外部ストレージに書き込めるようにします。

オーディオ/ビデオファイル、ログなどの保存。

いいえ

Android 6 以降

ステップ 4:コードの難読化の防止 (任意)

app/proguard-rules.pro ファイルで、SDK が提供するインターフェイスが難読化され、関数呼び出しが不適切になるのを防ぐために、SDK のルールを設定します。

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

ステップ 5:ユーザーインターフェースの作成

シナリオに適したユーザーインターフェースを作成します。以下のビデオ通話シナリオのサンプルコードでは、ローカルとリモートのビデオストリームを表示するための 2 つのビューを作成します。開発時のリファレンスとして使用できます。

ユーザーインターフェースのコード例

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

実装

このセクションでは、ARTC SDK を使用して基本的なリアルタイムのオーディオ/ビデオアプリケーションを構築する方法について説明します。完全なコードサンプルをプロジェクトにコピーして、機能をテストできます。以下のステップでは、コア API 呼び出しについて説明します。

次の図は、ビデオ通話を実装するための基本的なワークフローを示しています:

ビデオ通話シナリオのコード例

コード例

/**
 * ビデオ通話シナリオの API 呼び出し例。
 */
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) {
            // 戻るボタンがクリックされたときのアクション
            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 = "User " + userId + " Join " + channel + " Success";
            } else {
                str = "User " + userId + " Join " + channel + " Failed!, 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() {

        // エンジンの作成と初期化
        if(mAliRtcEngine == null) {
            mAliRtcEngine = AliRtcEngine.getInstance(this);
        }
        mAliRtcEngine.setRtcEngineEventListener(mRtcEngineEventListener);
        mAliRtcEngine.setRtcEngineNotify(mRtcEngineNotify);


        // チャンネルプロファイルをインタラクティブモードに設定します。RTC の場合は、常に AliRTCSdkInteractiveLive を使用します。
        mAliRtcEngine.setChannelProfile(AliRtcEngine.AliRTCSdkChannelProfile.AliRTCSdkInteractiveLive);
        // ユーザーロールを設定します。公開とサブスクライブの両方を行うには AliRTCSdkInteractive を使用し、サブスクライブのみを行うには AliRTCSdkLive を使用します。
        mAliRtcEngine.setClientRole(AliRtcEngine.AliRTCSdkClientRole.AliRTCSdkInteractive);
        // オーディオプロファイルを設定します。デフォルトは高品質モード (AliRtcEngineHighQualityMode) と音楽シナリオ (AliRtcSceneMusicMode) です。
        mAliRtcEngine.setAudioProfile(AliRtcEngine.AliRtcAudioProfile.AliRtcEngineHighQualityMode, AliRtcEngine.AliRtcAudioScenario.AliRtcSceneMusicMode);
        mAliRtcEngine.setCapturePipelineScaleMode(AliRtcEngine.AliRtcCapturePipelineScaleMode.AliRtcCapturePipelineScaleModePost);

        // ビデオエンコーディングパラメーターを設定
        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 はデフォルトでオーディオを公開するため、publishLocalAudioStream を呼び出す必要はありません。
        mAliRtcEngine.publishLocalAudioStream(true);
        // ビデオ通話の場合、SDK はデフォルトでビデオを公開するため、publishLocalVideoStream(true) を呼び出す必要はありません。
        // オーディオのみの通話の場合は、publishLocalVideoStream(false) を呼び出してビデオの公開を無効にする必要があります。
        mAliRtcEngine.publishLocalVideoStream(true);

        // リモートのオーディオおよびビデオストリームへのデフォルトのサブスクリプションを設定します。
        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 is empty");
        }
    }


    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:必須の処理です。ユーザーに通知することを推奨します。これは、SDK の内部回復戦略が失敗した後にのみ報告されます。 */
                        ToastHelper.showToast(VideoCallActivity.this, R.string.video_chat_connection_failed, Toast.LENGTH_SHORT);
                    } else {
                        /* TODO:任意です。ここにビジネスロジックを追加します。通常はデータ分析や UI の変更に使用します。 */
                    }
                }
            });
        }
        @Override
        public void OnLocalDeviceException(AliRtcEngine.AliRtcEngineLocalDeviceType deviceType, AliRtcEngine.AliRtcEngineLocalDeviceExceptionType exceptionType, String msg){
            super.OnLocalDeviceException(deviceType, exceptionType, msg);
            /* TODO:必須の処理です。デバイスエラーをユーザーに通知することを推奨します。これは、SDK の内部回復戦略が失敗した後にのみ報告されます。 */
            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:必須の処理です。このコールバックがトリガーされたら、現在のユーザーとチャンネルの新しいトークンを取得し、refreshAuthInfo を呼び出して更新します。 */
        }

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

        // onRemoteUserOffLineNotify コールバックでリモートビデオストリームレンダラーの設定を解除します。
        @Override
        public void onRemoteUserOffLineNotify(String uid, AliRtcEngine.AliRtcUserOfflineReason reason){
            super.onRemoteUserOffLineNotify(uid, reason);
        }

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

        /* アプリは、複数のデバイスが同じ UserID で参加しようとするケースも処理する必要があります。 */
        @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, "Leave 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;
    }
}

完全なサンプルコードの詳細については、「Android 用 ARTC デモプロジェクトの実行」をご参照ください。

1. 権限のリクエスト

ビデオ通話を開始する際に、アプリで必要な権限が付与されているか確認します:

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. 認証トークンの取得

ARTC チャンネルに参加するには、ユーザーの ID を検証するための認証トークンが必要です。トークンの生成方法の詳細については、「トークンベースの認証の実装」をご参照ください。トークンは、単一パラメーターメソッドまたは複数パラメーターメソッドを使用して生成できます。使用するメソッドによって、呼び出す必要がある joinChannel API が決まります。

本番環境の場合:

トークンの生成には AppKey が必要であり、クライアント側でハードコーディングするとセキュリティリスクが生じます。本番環境では、サーバーでトークンを生成し、クライアントに送信することを強く推奨します。

開発とデバッグの場合:

開発中に、ビジネスサーバーにまだトークンを生成するロジックがない場合は、APIExample のトークン生成ロジックを一時的に使用して、一時的なトークンを作成できます。リファレンスコードは次のとおりです:

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

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

    /**
     * channelId、userId、timestamp、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;
    }

    /**
     * channelId、userId、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) {
            // 例外をログに記録し、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) {
                // char にはシングルクォートを使用
                stringBuilder.append('0');
            }
            stringBuilder.append(hex);
        }
        return stringBuilder.toString();
    }

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

3. ARTC SDK クラスのインポート

ARTC SDK から関連するクラスとインターフェイスをインポートします:

// ARTC クラスをインポート
import com.alivc.rtc.AliRtcEngine;
import com.alivc.rtc.AliRtcEngineEventListener;
import com.alivc.rtc.AliRtcEngineNotify;

4. エンジンの作成と初期化

  • RTC エンジンの作成

    getInstance[1/2] メソッドを呼び出して、AliRtcEngine インスタンスを作成します。

    private AliRtcEngine mAliRtcEngine = null;
    if(mAliRtcEngine == null) {
        mAliRtcEngine = AliRtcEngine.getInstance(this);
    }
  • エンジンの初期化

    • setChannelProfile を呼び出して、チャンネルを AliRTCSdkInteractiveLive (インタラクティブモード) に設定します。

      ビジネスニーズに応じて、インタラクティブなエンターテインメントシナリオに適したインタラクティブモード、または 1 対 1 や 1 対多のブロードキャストに適した通信モードを選択できます。適切なモードを選択することで、スムーズなユーザーエクスペリエンスと効率的なネットワークリソースの使用が保証されます。

      モード

      公開

      サブスクライブ

      説明

      インタラクティブモード

      • ロールによって制限されます。ホストロールを持つユーザーのみがストリームを公開できます。

      • 参加者はセッション中にロールを柔軟に切り替えることができます。

      ロールの制限はありません。すべての参加者がストリームをサブスクライブする権限を持っています。

      • インタラクティブモードでは、ホストのチャンネルへの参加や退出、ストリームの公開開始などのイベントがリアルタイムで視聴者に通知されます。逆に、視聴者のアクティビティはホストに通知されないため、ストリーミングが中断されることはありません。

      • このモードでは、ホストがインタラクションを担当し、視聴者はコンテンツを消費するだけです。ビジネスニーズが変更される可能性がある場合は、デフォルトでインタラクティブモードを使用することを検討してください。その柔軟性により、ユーザーロールを調整することで、さまざまなインタラクション要件に適応できます。

      通信モード

      ロールの制限はありません。すべての参加者がストリームを公開する権限を持っています。

      ロールの制限はありません。すべての参加者がストリームをサブスクライブする権限を持っています。

      • 通信モードでは、参加者はお互いの存在を認識します。

      • このモードはユーザーロールを区別しませんが、機能的にはインタラクティブモードのホストロールと同等です。目標は操作を簡素化し、ユーザーがより少ない API 呼び出しで目的の機能を実現できるようにすることです。

    • setClientRole を呼び出して、ユーザーロールを AliRTCSdkInteractive (ホスト) または AliRTCSdkLive (視聴者) に設定します。注意:ホストロールはデフォルトで公開とサブスクライブを行います。視聴者ロールはデフォルトでサブスクライブのみを行い、プレビューと公開は無効になっています。

      説明

      ユーザーがチャンネル内でロールを切り替えると、システムはそれに応じてオーディオとビデオの公開ステータスを調整します:

      • ホストから視聴者への切り替え:システムはローカルのオーディオとビデオストリームの公開を停止します。サブスクライブしているリモートストリームは影響を受けず、ユーザーは他の人を見続けることができます。

      • 視聴者からホストへの切り替え:システムはローカルのオーディオとビデオストリームの公開を開始します。サブスクライブしているリモートストリームは変更されず、ユーザーは他の参加者を見続けることができます。

      // チャンネルモードをインタラクティブモードに設定します。RTC には AliRTCSdkInteractiveLive を使用します
      mAliRtcEngine.setChannelProfile(AliRtcEngine.AliRTCSdkChannelProfile.AliRTCSdkInteractiveLive);
      // ユーザーロールを設定します。ストリームの取り込みとプル両方を行う場合は AliRTCSdkInteractive を、ストリームを取り込まずにプルのみを行う場合は AliRTCSdkLive を使用します
      mAliRtcEngine.setClientRole(AliRtcEngine.AliRTCSdkClientRole.AliRTCSdkInteractive);
  • 共通のコールバックの設定

    SDK は、動作中に問題が発生した場合、まず内部のリトライメカニズムを使用して自動的に回復を試みます。自己解決できないエラーについては、SDK は定義済みのコールバックインターフェイスを介してアプリケーションに通知します。

    SDK が処理できない問題に対する主要なコールバックを以下に示します。アプリケーションはこれらをリッスンし、応答する必要があります:

    例外の原因

    コールバックとパラメーター

    ソリューション

    説明

    認証の失敗

    onJoinChannelResult コールバックの result が AliRtcErrJoinBadToken を返します。

    アプリはトークンが正しいか確認する必要があります。

    ユーザーが API を呼び出す際に認証が失敗した場合、API のコールバックは認証失敗エラーを返します。

    トークンの有効期限が近づいている

    onAuthInfoWillExpire

    新しいトークンを取得し、refreshAuthInfo を呼び出して情報を更新します。

    トークンの有効期限エラーは、API 呼び出し時または実行時に発生する可能性があります。エラーは API コールバックまたは別のエラーコールバックを通じて報告されます。

    トークンの有効期限切れ

    onAuthInfoExpired

    アプリはチャンネルに再参加する必要があります。

    トークンの有効期限エラーは、API 呼び出し時または実行時に発生する可能性があります。エラーは API コールバックまたは別のエラーコールバックを通じて報告されます。

    ネットワーク接続の問題

    onConnectionStatusChange コールバックが AliRtcConnectionStatusFailed を返します。

    アプリはチャンネルに再参加する必要があります。

    SDK は短いネットワーク切断から自動的に回復できます。切断時間がしきい値を超えると、タイムアウトします。アプリはネットワークステータスを確認し、ユーザーに再参加を促す必要があります。

    チャンネルからキックされた

    onBye

    • AliRtcOnByeUserReplaced:別のユーザーが同じ userId で参加したかどうかを確認します。

    • AliRtcOnByeBeKickedOut:ユーザーがチャンネルからキックアウトされたため、再参加する必要があります。

    • AliRtcOnByeChannelTerminated:チャンネルが終了したため、ユーザーは再参加する必要があります。

    RTC サービスでは、管理者が参加者を削除できます。

    ローカルデバイスの例外

    onLocalDeviceException

    アプリの権限とハードウェアが正しく動作しているか確認します。

    SDK が解決できないローカルデバイスの例外が発生した場合、コールバックを介してアプリに通知します。アプリはその後、デバイスのステータスを確認するために介入する必要があります。

    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:必須の処理です。ユーザーに通知することを推奨します。これは、SDK の内部回復戦略が失敗した後にのみ報告されます。 */
                        ToastHelper.showToast(VideoChatActivity.this, R.string.video_chat_connection_failed, Toast.LENGTH_SHORT);
                    } else {
                        /* TODO:任意です。ここにビジネスロジックを追加します。通常はデータ分析や UI の変更に使用します。 */
                    }
                }
            });
        }
        @Override
        public void OnLocalDeviceException(AliRtcEngine.AliRtcEngineLocalDeviceType deviceType, AliRtcEngine.AliRtcEngineLocalDeviceExceptionType exceptionType, String msg){
            super.OnLocalDeviceException(deviceType, exceptionType, msg);
            /* TODO:必須の処理です。デバイスエラーをユーザーに通知することを推奨します。これは、SDK の内部回復戦略が失敗した後にのみ報告されます。 */
            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:必須の処理です。このコールバックがトリガーされたら、現在のユーザーとチャンネルの新しいトークンを取得し、refreshAuthInfo を呼び出して更新します。 */
        }
    
        @Override
        public void onRemoteUserOnLineNotify(String uid, int elapsed){
            super.onRemoteUserOnLineNotify(uid, elapsed);
        }
    
        // onRemoteUserOffLineNotify コールバックでリモートビデオストリームレンダラーの設定を解除します。
        @Override
        public void onRemoteUserOffLineNotify(String uid, AliRtcEngine.AliRtcUserOfflineReason reason){
            super.onRemoteUserOffLineNotify(uid, reason);
        }
    
        // 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);
                            }
                        }
                    }
                }
            });
        }
    
        /* アプリは、複数のデバイスが同じ UserID で参加しようとするケースも処理する必要があります。 */
        @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. オーディオとビデオのプロパティ設定

  • オーディオプロパティの設定

    setAudioProfile を呼び出して、オーディオエンコーディングモードとシナリオを設定します。

    mAliRtcEngine.setAudioProfile(AliRtcEngine.AliRtcAudioProfile.AliRtcEngineHighQualityMode, AliRtcEngine.AliRtcAudioScenario.AliRtcSceneMusicMode);
  • ビデオプロパティの設定

    解像度、ビットレート、フレームレートなど、公開するビデオストリームのプロパティを設定します。

    // ビデオエンコーディングパラメーターを設定します。
    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. 公開とサブスクライブのプロパティ設定

オーディオ/ビデオストリームの公開を設定し、デフォルトですべてのユーザーのストリームをサブスクライブするように設定します:

  • publishLocalAudioStream を呼び出してオーディオストリームを公開します。

  • publishLocalVideoStream を呼び出してビデオストリームを公開します。オーディオのみの通話の場合は、これを false に設定できます。

// SDK はデフォルトでオーディオを公開するため、publishLocalAudioStream を呼び出す必要はありません。
mAliRtcEngine.publishLocalAudioStream(true);
// ビデオ通話の場合、SDK はデフォルトでビデオを公開するため、publishLocalVideoStream(true) を呼び出す必要はありません。
// オーディオのみの通話の場合は、publishLocalVideoStream(false) を呼び出してビデオの公開を無効にする必要があります。
mAliRtcEngine.publishLocalVideoStream(true);

// すべてのリモートオーディオおよびビデオストリームへのデフォルトのサブスクリプションを設定します。
mAliRtcEngine.setDefaultSubscribeAllRemoteAudioStreams(true);
mAliRtcEngine.subscribeAllRemoteAudioStreams(true);
mAliRtcEngine.setDefaultSubscribeAllRemoteVideoStreams(true);
mAliRtcEngine.subscribeAllRemoteVideoStreams(true);
説明

デフォルトでは、SDK はローカルのオーディオ/ビデオストリームを自動的に公開し、チャンネル内の他のすべてのユーザーのオーディオ/ビデオストリームをサブスクライブします。上記メソッドを呼び出して、このデフォルトの動作をオーバーライドできます。

7. ローカルプレビューの有効化

  • setLocalViewConfig を呼び出して、ローカルのレンダリングビューを設定し、ローカルビデオの表示プロパティを構成します。

  • startPreview を呼び出して、ローカルビデオのプレビューを開始します。

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. チャンネルへの参加

joinChannel を呼び出してチャンネルに参加します。トークンが単一パラメーターメソッドで生成された場合は、joinChannel[1/3] 操作を呼び出します。複数パラメーターメソッドで生成された場合は、joinChannel[2/3] メソッドを呼び出します。結果は onJoinChannelResult コールバックで返されます。result が 0 の場合は参加成功を示します。0 以外の結果は、無効なトークンを示している可能性があります。

 mAliRtcEngine.joinChannel(token, null, null, null);
説明
  • チャンネルに参加した後、SDK は参加前に設定されたパラメーターに従ってストリームを公開およびサブスクライブします。

  • SDK は、クライアントが行う必要がある API 呼び出しの数を減らすために、デフォルトで自動的に公開およびサブスクライブします。

9. リモートビューの設定

エンジンを初期化する際に、mAliRtcEngine.setRtcEngineNotify で対応するコールバックを設定します。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 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. チャンネルからの退出とエンジンの破棄

セッションが終了したら、チャンネルから退出してエンジンを破棄します:

  1. stopPreview を呼び出してビデオプレビューを停止します。

  2. leaveChannel を呼び出してチャンネルから退出します。

  3. destroy を呼び出してエンジンを破棄し、そのリソースを解放します。

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

11. 効果のデモンストレーション

image

関連ドキュメント

データ構造

AliRtcEngine クラス