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

ApsaraVideo VOD:エージェントのベストプラクティス

最終更新日:Jun 06, 2026

このドキュメントでは、2つのエンドツーエンドのデモを通じて、エージェントがllms.txt インデックスを使用して Alibaba Cloud ApsaraVideo VOD サービスにアクセスするためのベストプラクティスを説明します。

説明

エージェントの入門ガイドをお読みいただき、 llms.txt の基本構造を理解しておいてください。

説明

コーディングエージェントをインストールしていない場合は、「付録:コーディングエージェントの選定とセルフチェック」をご参照ください。

llms.txt を利用したエージェントのワークフロー

このセクションでは、開発者とエージェント間のワークフローと役割分担について概説します。これは、以降の 2 つのデモの基盤となります。各デモの Key Design Decisions テーブルでは、この方法論が特定のシナリオに適用される様子を示しています。

役割分担:開発者とエージェント

次の表に、役割分担の詳細を示します。開発者は要件を記述し、環境を準備し、結果を受け入れます。一方、エージェントはドキュメント検索、ソリューション設計、コード生成を担当します。

フェーズ

開発者

エージェント

要件の起案

自然言語で要件を記述します。

-

ドキュメント検索

-

llms.txt インデックスを読み取り、シナリオを照合して詳細なドキュメントを見つけます。

ドキュメントの理解

-

詳細なドキュメントをフェッチして、リクエスト/レスポンス パラメーター、サンプルコード、重要な注意点を抽出します。

ソリューション設計

ソリューションを確認または調整します。

ドキュメントに基づいて実装ソリューションを提案し、技術的な選択肢を説明します。

コード生成

-

エラーハンドリングを含む実行可能なコードを生成します。

環境設定

AccessKey を設定し、依存関係をインストールし、コンソールをセットアップします。

コマンドと設定ガイダンスを提供します。

実行と検証

コードを実行し、結果を確認します。

出力を説明し、問題をトラブルシューティングします。

エージェントの内部ワークフロー

エージェントは、4 つの連続したフェーズで要件を内部的に処理します。各フェーズの出力が、次のフェーズの入力になります。

フェーズ 1:llms.txt インデックスの読み取り

約 250 行のインデックスファイル全体を 1 回の操作でロードし、次の 3 つのタスクを実行します。

  • クイックスタート セクションを解析して、シナリオドキュメントパスにマッピングします。

  • Common mistakes to avoid セクションを抽出し、以降のコード生成における厳密な制約として使用します。

  • プライマリモジュールとサブモジュールを含むドキュメントのトポロジを決定します。

フェーズ 2:シナリオの照合とドキュメントの特定

  • クイックスタート セクションのシナリオタイトルと要件のキーワードを照合します。

  • シナリオが一致する場合、対応するドキュメントパスを直接使用します。

  • 一致するシナリオがない場合、モジュールインデックスをスキャンして、最も関連性の高いサブドキュメントを 1~3 個特定します。

  • 相対パスの先頭にある ./ を削除してから URL エンコードを行い、ベース URL と連結して完全な URL を構築します。

フェーズ 3:ドキュメントのフェッチと情報の抽出

サブドキュメントから、コード生成の入力として次の要素を抽出します:API 名、リクエスト/レスポンス パラメーター、サンプルコード、QPS 制限、エラーコード、リージョン制限、その他の注意点。

フェーズ 4:コード生成

コードを生成する前に、エージェントは Common mistakes to avoid チェックリストに照らしてセルフチェックを実行します。

  • プライマリアカウントの AccessKey が使用されている場合は、代わりに RAM ユーザーを使用します。

  • ステータス ポーリングが実装されている場合は、イベント通知に切り替えます。

  • URL がエンコードされていない場合は、url_encode を呼び出します。

  • リクエストごとに新しい認証情報が作成される場合は、代わりに Credential オブジェクトを再利用および更新します。

エージェントはまた、最終的なコードに QPS バックオフと非同期処理のロジックを追加します。

URL の構築ルール

インデックスファイルは、サブドキュメントの参照に相対パスを使用します。エージェントは、サブドキュメントをフェッチするために、これらのパスを絶対 URL に変換する必要があります。

ベース URL:https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/vod/llms/
ドキュメント URL = ベース URL + URL エンコーディング (相対パス、先頭の "./" は削除)

例:
  llms.txt 内:[サーバーサイドアップロード](./媒体上传/服务端上传.md)
  完全な URL:ベース URL + %E5%AA%92%E4%BD%93%E4%B8%8A%E4%BC%A0/%E6%9C%8D%E5%8A%A1%E7%AB%AF%E4%B8%8A%E4%BC%A0.md

エンドツーエンドデモ

このモジュールは、サーバー側とクライアント側の 2 つのエンドツーエンドデモを提供します。 GetPlayInfo オペレーションが返す PlayURL によって両側が結び付けられ、完全なクローズドループのワークフローが構築されます。

前提条件

ステップ

場所

アクション

1

Alibaba Cloud コンソール

ApsaraVideo VOD サービスを有効化します。

2

RAM コンソール

RAM ユーザーを作成し、AliyunVODFullAccess ポリシーを付与して、AccessKey を作成します。

3

ApsaraVideo VOD コンソール

トランスコードテンプレートグループを設定します (TemplateGroupId を記録してください)。

4

ApsaraVideo VOD コンソール

HTTP コールバック URL を設定します ([設定管理] > [コールバック設定])。

5

ローカルターミナル

pip install aliyun-python-sdk-core aliyun-python-sdk-vod

6

ローカルターミナル

環境変数 ALIBABA_CLOUD_ACCESS_KEY_ID=xxx および ALIBABA_CLOUD_ACCESS_KEY_SECRET=xxx を設定します。

サーバー側のデモ:バッチアップロード、トランスコード、再生 URL の取得

このデモでは、エージェントが llms.txt インデックスを使用して、 URL のバッチアップロードアップロード時にトランスコーディングテンプレートグループを指定した自動トランスコードと、イベント通知による進捗追跡、および トランスコード完了後の再生 URL の取得 を含む、完全なサーバー側のワークフローを構築する方法を説明します。

エージェントに送信されたプロンプト

llms.txt ドキュメント (https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/vod/llms/llms.txt) を使用して、バッチアップロード、自動トランスコード、再生 URL の取得を含む ApsaraVideo VOD の完全なデモワークフローを Python で実装してください。
具体的な要quirements:
1. ビデオファイルのセット (公開 URL のリストとして提供) を ApsaraVideo VOD にバッチアップロードすること。
2. 指定されたトランスコーディングテンプレートグループを使用して、アップロード完了後に自動的にトランスコードをトリガーすること。
3. トランスコードが完了したら、各画質の再生 URL を取得すること。
4. イベント通知を受信するために、HTTP コールバックサービスが必要です。

エージェントの実行プロセス

フェーズ 1:llms.txt を読み取り、シナリオを照合

エージェントは、次のようにインデックス内のシナリオと制約を照合します:

クイックスタートのキーワード → 一致したドキュメントパス:
  「URL のバッチアップロード」 → media-upload/url-based-upload.md
  「非同期イベント通知の受信」 → event-notification/event-notification_2.md
  「フロントエンド用の再生 URL の取得」 → api-reference/get-playback-url.md
  「Python SDK の初期化」 → api-reference/initialization_2.md
  「アップロード時の自動トランスコード」 → media-processing/workflow.md

一致した一般的な間違い:
  #1 ルートアカウントの AccessKey を使用しない
  #3 ステータスをポーリングしない
  #5 URL はエンコードする必要がある
  #6 QPS バックオフを使用する

フェーズ 2:詳細ドキュメントをオンデマンドでロード

ドキュメント

目的

media-upload/url-based-upload.md

UploadMediaByURL のパラメーターと制限

event-notification/event-notification_2.md

コールバックメカニズムとイベントタイプ

api-reference/get-playback-url.md

GetPlayInfo のパラメーターとレスポンス構造

api-reference/initialization_2.md

Python SDK の初期化メソッド

media-processing/workflow.md

テンプレートグループを指定した、アップロード時の自動トランスコードのメカニズム

フェーズ 3:キー情報の抽出

バッチアップロード: UploadMediaByURL
  - 非同期、China (Shanghai) のみ、リクエストあたり最大 20 URL
  - URL は URL エンコードが必要
  - TemplateGroupId を指定可能 → アップロード後に自動トランスコード

再生 URL: GetPlayInfo
  - 前提条件:ビデオステータス = 正常 (トランスコード完了後)
  - 戻り値:各画質の PlayURL を含む PlayInfoList

イベント通知: HTTP POST コールバック
  - UploadByURLComplete → アップロード完了
  - StreamTranscodeComplete → 単一画質のトランスコード完了 (再生可能な最も早い状態)
  - TranscodeComplete → 全てのトランスコード完了
  - コールバックのタイムアウトは 5 秒、最大 3 回のリトライ

フェーズ 4:コード生成

エージェントは上記の情報に基づいてコアコードを生成します。

エージェントが出力したコアコード

バッチアップロード (主要ロジック): 環境変数から認証情報を読み取り、すべての URL のエンコードを強制します。

# 初期化 - AK を環境変数から読み取り、ハードコードしない (一般的な間違い #1)
client = AcsClient(region_id='cn-shanghai', credential=AccessKeyCredential(
    os.environ['ALIBABA_CLOUD_ACCESS_KEY_ID'],
    os.environ['ALIBABA_CLOUD_ACCESS_KEY_SECRET']
))

# URL のバッチアップロード + トランスコーディングテンプレートグループの指定 (アップロード後の自動トランスコード)
request = UploadMediaByURLRequest()
request.set_UploadURLs(','.join([urllib.parse.quote(url, safe='') for url in urls]))  # URL はエンコードが必要!
request.set_UploadMetadatas(json.dumps([{'SourceURL': urllib.parse.quote(u, safe=''), 'Title': f'Video{i}'} for i, u in enumerate(urls)]))
request.set_TemplateGroupId('your_template_group_id')  # キー:これを指定してアップロード後に自動トランスコード
response = json.loads(client.do_action_with_exception(request))
# JobId のリストを返します。イベント通知を介して進捗を追跡します。

HTTP コールバックサービス (主要ロジック): サーバー側でのリトライを防ぐために、即座に 200 ステータスを返します。

class CallbackHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        body = json.loads(self.rfile.read(int(self.headers['Content-Length'])))
        self.send_response(200)  # 即座に 200 を返す必要があります。5 秒のタイムアウト後にリトライがトリガーされます
        self.end_headers()

        if body['EventType'] == 'UploadByURLComplete' and body['Status'] == 'success':
            print(f"Upload complete: VideoId={body['VideoId']}, waiting for transcoding...")

        elif body['EventType'] == 'TranscodeComplete' and body['Status'] == 'success':
            # すべてのトランスコードが完了 -> 再生 URL を取得
            play_info = get_play_info(body['VideoId'])
            for stream in play_info['PlayInfoList']['PlayInfo']:
                print(f"  [{stream['Definition']}] {stream['PlayURL']}")

再生 URL の取得 (主要ロジック)

request = GetPlayInfoRequest()
request.set_VideoId(video_id)
request.set_Formats('mp4,m3u8')
request.set_AuthTimeout(3600)  # URL は 1 時間有効
response = json.loads(client.do_action_with_exception(request))
# response['PlayInfoList']['PlayInfo'] → 各画質の再生 URL

実行結果

バッチアップロードの送信

$ python vod_batch_upload.py

[1/2] Initializing VOD client... ✓ (region: cn-shanghai)
[2/2] Submitting batch upload job (3 videos)... ✓
  RequestId: 25818875-5F78-4AF6-D7393642CA58****
  JobId: ad90a501b1b9**** ← sample1.mp4
  JobId: bd81b612c2c8**** ← sample2.mp4
  JobId: ce72c723d3d9**** ← sample3.mp4

以降のステップは、イベント通知によって自動的にトリガーされます:
  UploadByURLComplete → Auto Transcode → TranscodeComplete → GetPlayInfo

コールバックサービスがイベントを受信

$ python callback_server.py
Callback service started: http://0.0.0.0:8080 | Waiting for callbacks...

[UploadByURLComplete] VideoId=93ab850b**** | Status=success
  → アップロード完了、自動トランスコードを待機中...

[StreamTranscodeComplete] VideoId=93ab850b**** | Status=success
  → LD 画質のトランスコード完了 (再生準備完了)

[TranscodeComplete] VideoId=93ab850b**** | Status=success
  → すべてのトランスコード完了、再生 URL を取得中:
    [LD] https://vod.example.com/****/sample1-ld.mp4?auth_key=****
    [SD] https://vod.example.com/****/sample1-sd.mp4?auth_key=****
    [HD] https://vod.example.com/****/sample1-hd.mp4?auth_key=****

エージェントのドキュメント検索の可視化

プロンプト:「バッチアップロード + トランスコード + 再生」
  │
  ▼
┌── llms.txt (クイックスタートのマッチング + 一般的な間違いの制約) ──┐
│ ✓ 「URL のバッチアップロード」 → media-upload/url-based-upload.md │
│ ✓ 「イベント通知」 → event-notification/event-notification_2.md │
│ ✓ 「再生 URL」 → api-reference/get-playback-url.md │
│ ✓ 「Python 初期化」 → api-reference/initialization_2.md │
│ ✓ 「自動トランスコード」 → media-processing/workflow.md │
│ ✗ #1 ルート AK を使用しない ✗ #3 ポーリングしない ✗ #5 URL エンコード ✗ #6 QPS バックオフ │
└────────────────────────┬─────────────────────────────────────┘
                         │
   ┌─────────────────────┼─────────────────────┐
   ▼                     ▼                     ▼
url-based-upload.md  event-notification_2.md get-playback-url.md
- UploadMediaByURL       - イベントタイプ一覧          - リクエスト/レスポンスパラメーター
- URL はエンコードが必要 - HTTP コールバック形式       - 画質列挙型
- リージョン制限         - タイムアウトとリトライポリシー - ステータス=正常
- 最大 20 URL          - 5 秒タイムアウト          - QPS=360/s
   │                     │                     │
   ▼                     ▼                     ▼
   └────► initialization_2.md (SDK クライアントコンストラクタ) ◄────┘
                         │
                         ▼
                  workflow.md (TemplateGroupId での自動トランスコード)
                         │
                         ▼
                完全な実行可能コードを生成

主要な設計判断

この表は、このデモにおける主要な設計判断と、そのドキュメント上の根拠を詳述したものです。

判断項目

エージェントの選択

ドキュメント上の根拠

アップロード方法

UploadMediaByURL

ドキュメントでは、公開 URL のリストからバッチアップロードするためにこの API を指定しています。

トランスコードトリガー

アップロード時に TemplateGroupId を指定

ドキュメントには、アップロード時に TemplateGroupId を指定すると自動トランスコードがトリガーされ、個別の API 呼び出しが不要になると記載されています。

ステータス追跡

HTTP イベントコールバック

一般的な間違い #3: ポーリングしない。

再生 URL 取得のタイミング

TranscodeComplete 後

ドキュメントには、 GetPlayInfo はビデオステータスが「正常」であることを要求すると記載されています。

AccessKey 管理

環境変数

一般的な間違い #1: ハードコードしない。

URL の処理

quote(url, safe='')

一般的な間違い #5: エンコードが必要。

リージョン

cn-shanghai

ドキュメントには、URL ベースのアップロードは China (Shanghai) リージョンでのみサポートされていると記載されています。

フォールトトレランス

エクスポネンシャルバックオフによるリトライ

一般的な間違い #6: スロットリングに対するバックオフ。

クライアント側のデモ:Android プレーヤーとライブビューワー

この実行可能な Android デモは、サーバー側のワークフローを基盤としています。最小限のコードで MP4 ファイルを再生する方法と、AUI Kits を使用してインタラクティブライブストリーミングビューワーを統合する方法を示します。

エージェントに送信されたプロンプト

クライアント側の llms インデックス (`client-side-player/`、`client-side-upload/`、`client-side-short-video/` などのディレクトリ内) に基づいて、Android 向けのデモアプリを実装してください。
具体的な要件:
1. サーバー側のデモの GetPlayInfo レスポンスで返される再生 URL を再生するために、必要最小限の依存パッケージを選択します。
2. 同じアプリに、AUI Kits を使用してインタラクティブライブストリーミングビューワー (ストリームプル、弾幕、いいね、ギフトを含む) を起動するための別のエントリーポイントを追加します。
3. ハードコーディングを避けるために、パッケージ名、ライセンス、AppServer ドメインを設定可能にします。

エージェントの実行プロセス

フェーズ 1:クライアント側の llms インデックスを読み取り、シナリオを照合

クイックスタートのマッチング結果:
  「Android で VOD 再生を開始」 → client-side-player/player-atomic-android.md
  「パッケージ / 依存関係の選択方法」 → client-side-player/player-overview.md
  「ライセンスの統合方法」 → client-side-player/player-common-license.md
  「インタラクティブライブストリーミングビューワー」 → client-side-player/player-kits-aui.md
  「エラーコード 4400 / 4013」 → client-side-player/player-common-error-codes.md

一致した一般的な間違い:
  ✗ プレーヤー作成後にライセンスを初期化する → Application.onCreate で行う必要があります
  ✗ Surface の準備が整う前に setSurface を呼び出す → surfaceCreated を待つ必要があります
  ✗ デフォルトでフル機能のパッケージを使用する → シナリオに合わせた最小パッケージを選択する
  ✗ AppServer ドメインをハードコーディングする → BuildConfig / gradle.properties に抽象化する
  ✗ PrivateService.init を複数回呼び出す → Application クラスで一度だけ初期化する

フェーズ 2:詳細ドキュメントをオンデマンドでロード

ドキュメント

目的

client-side-player/player-overview.md

パッケージ選択の決定 (BasicLive / UGC / InteractiveLive / Standard)

client-side-player/player-atomic-android.md

Gradle の依存関係、マニフェストのパーミッション、難読化、および最小限の再生コード

client-side-player/player-common-license.md

ライセンス統合のタイミングとエラー 4400 / 4013 のトラブルシューティング

client-side-player/player-kits-aui.md

AUI Kits のクライアント側モジュールと AppServer の連携モデル

client-side-player/player-common-error-codes.md

再生開始の失敗に関するクイックリファレンス

フェーズ 3:主要な決定事項の抽出

パッケージの選択:
  再生のみ → AliVCSDK_BasicLive
  再生 + ショートビデオ → AliVCSDK_UGC
  共同ホスティング / インタラクティブライブストリーミングを含む → AliVCSDK_InteractiveLive ← このデモではこれを選択
  フル機能 → AliVCSDK_Standard / Premium

ライセンス:
  PrivateService.initService(ctx, licenseFile, licenseKey)
  createAliPlayer を最初に呼び出す前に完了している必要があります

AUI Kits:
  AppServer + クライアント側。AppServer は Identity Token、Live Room Token、IMS Token を発行します。
  クライアント:3 つのモジュール - AUILiveRoomCore、AUILiveRoomViewer、AUIInteraction。

実行結果

クイックプレーヤー統合

プロジェクトの依存関係

プロジェクトレベルの build.gradle ファイルに:

allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url "https://maven.aliyun.com/nexus/content/repositories/releases" }
    }
}

アプリレベルの build.gradle ファイルに:

android {
    compileSdk 34
    defaultConfig {
        applicationId "com.example.aliplayer.sample"
        minSdk 21
        targetSdk 34

        ndk { abiFilters "armeabi-v7a", "arm64-v8a" }

        buildConfigField "String", "APP_SERVER_HOST",
                "\"${project.findProperty('APP_SERVER_HOST') ?: 'https://your-appserver.example.com'}\""
        buildConfigField "String", "APP_SERVER_API_PREFIX", "\"/api/v1/live/\""
        buildConfigField "String", "LICENSE_KEY",
                "\"${project.findProperty('LICENSE_KEY') ?: ''}\""
    }
    buildFeatures { buildConfig true }
}

dependencies {
    implementation 'com.aliyun.aio:AliVCSDK_InteractiveLive:6.0.0'
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.11.0'
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
    implementation 'com.google.code.gson:gson:2.10.1'
}

パーミッションと難読化

AndroidManifest.xml ファイルに:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

proguard-rules.pro ファイルに:

-keep class com.alivc.**{*;}
-keep class com.aliyun.**{*;}
-keep class com.cicada.**{*;}
-dontwarn com.alivc.**
-dontwarn com.aliyun.**
-dontwarn com.cicada.**

ライセンスの初期化

public class SampleApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        // license.crt を app/src/main/assets/ に配置します
        PrivateService.initService(getApplicationContext(),
                "file:///android_asset/license.crt",
                BuildConfig.LICENSE_KEY);
    }
}

AndroidManifest.xml ファイルに登録します:

<application
    android:name=".SampleApp"
    android:label="@string/app_name"
    android:theme="@style/Theme.MaterialComponents.DayNight">
    <activity android:name=".MainActivity" android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity android:name=".SimplePlayerActivity" />
</application>
単一ビデオ再生 UI

activity_player.xml ファイルを生成します:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000">

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="220dp"
        android:layout_gravity="center" />
</FrameLayout>

SimplePlayerActivity.java ファイルを生成します:

public class SimplePlayerActivity extends AppCompatActivity {

    public static final String EXTRA_PLAY_URL = "play_url";

    private AliPlayer aliPlayer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_player);

        SurfaceView surfaceView = findViewById(R.id.surfaceView);
        aliPlayer = AliPlayerFactory.createAliPlayer(getApplicationContext());

        surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override public void surfaceCreated(SurfaceHolder h) {
                aliPlayer.setSurface(h.getSurface());
            }
            @Override public void surfaceChanged(SurfaceHolder h, int f, int w, int hh) {
                aliPlayer.surfaceChanged();
            }
            @Override public void surfaceDestroyed(SurfaceHolder h) {
                aliPlayer.setSurface(null);
            }
        });

        aliPlayer.setOnPreparedListener(() -> aliPlayer.start());
        aliPlayer.setOnErrorListener(err ->
                Log.e("AliPlayer", "code=" + err.getCode() + " msg=" + err.getMsg()));

        UrlSource source = new UrlSource();
        // ソース:サーバー側のデモにある GetPlayInfo レスポンスで返される再生 URL (auth_key 付き)
        String playUrl = getIntent().getStringExtra(EXTRA_PLAY_URL);
        source.setUri(playUrl != null ? playUrl : "https://your.cdn/test.mp4");
        aliPlayer.setDataSource(source);
        aliPlayer.setAutoPlay(true);
        aliPlayer.prepare();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (aliPlayer != null) {
            aliPlayer.stop();
            aliPlayer.release();
        }
    }
}

ログに prepared イベントと最初のフレームイベントが表示され、SurfaceView が画像を正しくレンダリングしている場合は、下層のリンクに問題がないことを意味します。

AUI Kits ライブビューワー

AUI Kits モジュールのインポート

settings.gradle ファイルに:

include ':app',
        ':AUILiveRoomCore',
        ':AUILiveRoomViewer',
        ':AUIInteraction'

アプリレベルの build.gradle ファイルに:

implementation project(':AUILiveRoomCore')
implementation project(':AUILiveRoomViewer')
implementation project(':AUIInteraction')

AppServer 連携モデル

クライアントは IMS Token のような機密性の高い認証情報を直接保持しません。代わりに、認証後に AppServer がそれらを発行します。ライブルームに入室するための完全なワークフローは次のとおりです:

[Android アプリ]                                [AppServer]
   │                                              │
   │── GET /api/v1/live/viewer/token?roomId=xxx ─►│
   │                                              ├── 認証して発行:
   │                                              │     userId / nick / pullUrl / imsToken
   │◄──────── 200 OK + ViewerTokenInfo ───────────┤
   │
   │── LiveRoomViewerActivity.start(params) ──►(AUI Kits 内部)
   │                                              │
   │   (ストリームプル用の AliPlayer + 弾幕用の IMS)                 │
  1. Android アプリは、ログイン状態で Appサーバーに GET /api/v1/live/viewer/token?roomId=xxx リクエストを送信します。

  2. AppServer はログインステータスを検証し、userIdnickpullUrl、および imsToken を発行します。

  3. アプリは ViewerTokenInfo を受信した後、LiveRoomViewerActivity.start(params) を呼び出してライブ ルームに入ります。

  4. 内部的に、AUI Kits はストリームプルに AliPlayer を使用し、弾幕、いいね、ギフトなどの機能のために Interactive Messaging Service (IMS) に接続します。

AppServerClient (OkHttp 実装)

public class AppServerClient {

    public interface Callback {
        void onSuccess(ViewerTokenInfo info);
        void onFail(String msg);
    }

    private static final OkHttpClient client = new OkHttpClient();

    public static void fetchViewerToken(String roomId, Callback cb) {
        HttpUrl url = HttpUrl.parse(
                BuildConfig.APP_SERVER_HOST + BuildConfig.APP_SERVER_API_PREFIX + "viewer/token")
                .newBuilder()
                .addQueryParameter("roomId", roomId)
                .build();

        Request req = new Request.Builder()
                .url(url)
                .header("Authorization", "Bearer " + LoginManager.getToken())
                .build();

        client.newCall(req).enqueue(new okhttp3.Callback() {
            @Override public void onFailure(Call call, IOException e) {
                cb.onFail(e.getMessage());
            }
            @Override public void onResponse(Call call, Response resp) throws IOException {
                if (!resp.isSuccessful() || resp.body() == null) {
                    cb.onFail("HTTP " + resp.code());
                    return;
                }
                ViewerTokenInfo info = new Gson().fromJson(
                        resp.body().string(), ViewerTokenInfo.class);
                cb.onSuccess(info);
            }
        });
    }
}

public class ViewerTokenInfo {
    public String roomId;
    public String userId;
    public String nick;
    public String pullUrl;   // rtmp、flv、m3u8、または artc のいずれか
    public String imsToken;  // Interactive Messaging Service (IMS) トークン
}
2 つのデモのエントリーを組み合わせる

activity_main.xml ファイルを生成します:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:padding="24dp"
    android:gravity="center"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button android:id="@+id/btnSimple"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Simple Playback" />

    <Button android:id="@+id/btnLiveRoom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Interactive Live Viewer" />
</LinearLayout>

MainActivity.java ファイルを生成します:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btnSimple).setOnClickListener(v -> {
            Intent intent = new Intent(this, SimplePlayerActivity.class);
            // 実際のアプリケーションでは、サーバー側のデモにある GetPlayInfo レスポンスで返される再生 URL をここに渡します
            intent.putExtra(SimplePlayerActivity.EXTRA_PLAY_URL,
                    "https://vod.example.com/****/sample1-hd.mp4?auth_key=****");
            startActivity(intent);
        });

        findViewById(R.id.btnLiveRoom).setOnClickListener(v -> enterRoom("demo_room_001"));
    }

    private void enterRoom(String roomId) {
        AppServerClient.fetchViewerToken(roomId, new AppServerClient.Callback() {
            @Override public void onSuccess(ViewerTokenInfo info) {
                LiveRoomEnterParams params = new LiveRoomEnterParams.Builder()
                        .roomId(info.roomId)
                        .userId(info.userId)
                        .userNick(info.nick)
                        .pullUrl(info.pullUrl)
                        .imsToken(info.imsToken)
                        .build();
                LiveRoomViewerActivity.start(MainActivity.this, params);
            }
            @Override public void onFail(String msg) {
                Toast.makeText(MainActivity.this, "Failed to enter room: " + msg, Toast.LENGTH_LONG).show();
            }
        });
    }
}

検証チェックリスト

デモを実行した後、次の UI の動作とログ出力を確認します:

  • [シンプル再生] をクリック: SimplePlayerActivity が起動し、ビデオが SurfaceView 内で黒画面にならずにスムーズに再生されます。

  • [インタラクティブライブ配信ビューワー] をクリックすると、AUI Kits の LiveRoomViewerActivity が起動して AliPlayer にホストの映像が表示され、右下隅にある弾幕、いいね、ギフトパネルが機能します。

  • 主要な logcat ログ: AliPlayer: preparedAliPlayer: first frame、および IMS: connected が同時に表示されることは、ストリームのプルとインタラクションの両方の準備が完了したことを示します。

エージェントのドキュメント検索の可視化

プロンプト:「Android 再生 + インタラクティブライブルームへの入室」
  │
  ▼
┌── クライアント側 llms.txt (クイックスタート + 一般的な間違い) ────────┐
│ ✓ 「Android 再生」 → player-atomic-android.md │
│ ✓ 「パッケージの選択方法」 → player-overview.md │
│ ✓ 「ライセンス」 → player-common-license.md │
│ ✓ 「AUI Kits インタラクティブ」 → player-kits-aui.md │
│ ✓ 「エラーコード 4400 / 4013」 → player-common-error-codes.md │
│ ✗ プレーヤー作成前にライセンスを初期化する必要がある │
│ ✗ Surface の準備が整う前に setSurface を呼び出さない │
│ ✗ デフォルトでフル機能のパッケージを使用しない │
│ ✗ AppServer ドメインをハードコーディングしない │
│ ✗ PrivateService.init は Application で一度だけ呼び出す │
└────────────────────────┬────────────────────────────────────┘
                         │
  ┌─────────────┬────────┴─────────┬─────────────┐
  ▼             ▼                  ▼             ▼
player-      player-atomic-   player-common-  player-kits-aui.md
overview.md  android.md       license.md      - AppServer 連携
- パッケージマトリックス - gradle 依存関係 - PrivateService - 3つのトークンタイプ
- シナリオマップ - Manifest パーミッション - 4400 / 4013 - モジュール内訳
- SDK サイズ - 再生コード - パッケージ / BundleID - 4ステップのクライアント統合
  └─────────────┴────────┬─────────┴─────────────┘
                         │
                         ▼
              player-common-error-codes.md
              (4400 / 4013 / 4034 / 4036 のガイダンス)
                         │
                         ▼
              完全な実行可能な Android デモを生成

主要な設計判断

判断項目

エージェントの選択

ドキュメントにおける根拠

SDK バンドル

AliVCSDK_InteractiveLive

player-overview.md の選択マトリックスでは、インタラクティブライブストリーミングシナリオにこのバンドルを推奨しています。

ライセンスのタイミング

Application.onCreate

player-common-license.md:createAliPlayer の前に呼び出す必要があります。

サーフェスのバインド

surfaceCreated コールバック内の setSurface

player-atomic-android.md の標準テンプレート。

AppServer ドメイン

BuildConfig / gradle.properties

一般的な間違い:ハードコードしない。

サーバー側統合

サーバー側のデモにある GetPlayInfo レスポンスで返される再生 URL を再利用

これにより、サーバー側とクライアント側のデモ間のエンドツーエンドの一貫性が保証されます。

インタラクティブライブビューワー

AUI Kits の 3 つのモジュール (Core / Viewer / Interaction)

player-kits-aui.md:主要なクライアント側統合ガイド。

エラーハンドリング

setOnErrorListener は、イベント報告用の一般的なコールバックです。コード 4400、4013、4034、および 4036 は、クライアント側でのカスタム処理用に予約されています。

player-common-error-codes.md

リリース前チェックリスト (Android)

観点

チェック項目

ライセンス

テスト用および本番用のライセンスが申請され、それぞれの BuildConfigs で設定されています。

パッケージサイズ

abiFilters は armv7 と arm64 に限定され、シナリオに基づいて最小の SDK バンドルが選択されています。

再生開始

4400、4013、4034、4036 のリスナーがモニタリング用に実装されています。

ライブルーム

期限切れの IMS トークンの自動更新が実装されており、AppServer で HTTPS と CORS が有効になっています。

プライバシー

音声録音、ネットワークアクセス、メディア読み取りのパーミッションはすべてプライバシーポリシーで宣言されています。

カナリアリリース

最初に、新しいバージョンをユーザーの 5% にリリースします。全面展開の前に、ファーストフレーム時間と障害率を 48 時間監視します。

Monitoring

Enable Alibaba Cloud's player quality monitoring service (QoS) and single-point tracing.

最終的なディレクトリ構造

AliPlayerSampleApp/
├── app/
│   ├── src/main/
│   │   ├── assets/license.crt
│   │   ├── java/com/example/aliplayer/sample/
│   │   │   ├── SampleApp.java
│   │   │   ├── MainActivity.java
│   │   │   ├── SimplePlayerActivity.java         ← シンプル再生
│   │   │   ├── AppServerClient.java              ← インタラクティブライブストリーミング
│   │   │   ├── ViewerTokenInfo.java
│   │   │   └── LoginManager.java
│   │   └── AndroidManifest.xml
│   └── build.gradle
├── AUILiveRoomCore/                               ← インタラクティブライブストリーミング
├── AUILiveRoomViewer/                             ← インタラクティブライブストリーミング
├── AUIInteraction/                                ← インタラクティブライブストリーミング
├── gradle.properties (APP_SERVER_HOST / LICENSE_KEY)
├── settings.gradle
└── build.gradle

サーバーサイドとクライアントサイドのデモで、完全なクローズドループのワークフローが構成されるようになりました。このワークフローは、エージェントが llms.txt インデックスを使用して生成するもので、動画のアップロード、自動トランスコーディング、再生 URL の取得、基本的な動画再生、AUI Kits を使用したインタラクティブなライブストリーミングビューワーを網羅しています。

付録: エージェントの選定とセルフチェック

この付録は、コーディングエージェントを使用したことがない、またはインストールしていない開発者向けです。

コーディングエージェントを使用するタイミング

ユースケース

必要か

AI に llms.txt を参照させ、必要に応じてサブドキュメントを展開し、実行可能なプロジェクトを生成させたい場合。

はい

このドキュメントを読んでアーキテクチャを理解し、その後はコードをコピーして手動で修正するだけでよい場合。

いいえ。Web ブラウザーで十分です。

AI にローカルリポジトリ内のファイルを直接追加、削除、変更させ、変更内容を検証するためにシェルコマンドを実行させたい場合。

はい

推奨オプション

カテゴリ

名称

タイプ

説明

推奨

Qoder / Cursor / Claude Code

デスクトップ IDE または CLI

llms.txt 形式のインデックスを使用した長文ドキュメント検索をネイティブにサポートしており、中国のクラウドプロバイダーの SDK とも相性が良好です。このドキュメントのデモ検証には Qoder を使用しました。

その他

-

-

URL にアクセスでき、ローカルファイルを書き込み、シェルコマンドを呼び出せる任意のコーディングエージェント。

説明

インストールは、公式プロダクト Web サイトのリンクを使用してください。サードパーティ製イメージの使用は避けてください。

最小限のセルフチェック

次のプロンプトをコーディングエージェントに送信し、機能を検証します:

Please read https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/vod/llms/llms.txt,
and tell me:
1. How many top-level modules are in this index? What are they?
2. Which sub-documents are involved in the "batch upload + automatic transcoding + get audio/video playback URL" workflow? Please list their relative paths.
3. In the "Common mistakes to avoid" section, what are the notes related to "AccessKey"?

想定される回答

  • llms.txt に記載されているトップレベルのモジュールを正しく列挙できること。

  • サーバーサイドデモの オンデマンド読み込み 表 (バッチアップロード、イベント通知、音声/動画再生 URL の取得、Python SDK の初期化、メディア処理ワークフロー) と概ね整合するサブドキュメントのリストを提示できること。

  • Do not use an Alibaba Cloud account AccessKey. Use a RAM user instead. と明確に述べられること。

上記 3 つのチェックをすべて通過したエージェントは、このドキュメントのデモを実行する準備ができています。