全部產品
Search
文件中心

ApsaraVideo VOD:面向智能體的最佳實務

更新時間:Aug 21, 2026

本教程示範 Coding Agent 如何基於 llms.txt 索引自動檢索ApsaraVideo for VOD文檔,產生端到端可運行 Demo。內容涵蓋服務端批量 URL 上傳與自動轉碼、Android 用戶端單視頻起播,以及基於 AUI Kits 的互動直播觀眾端整合。

服務端 Demo

服務端 Demo 包含三個核心模組:批量 URL 上傳並指定轉碼模板組、HTTP 回調服務接收事件通知、轉碼完成後擷取播放地址。

批量 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'視頻{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"上傳完成: VideoId={body['VideoId']},等待轉碼...")

        elif body['EventType'] == 'TranscodeComplete' and body['Status'] == 'success':
            # 全部轉碼完成 → 擷取播放地址
            play_info = get_play_info(body['VideoId'])
            for stream in play_info['PlayInfoList']['PlayInfo']:
                print(f"  [{stream['Definition']}] {stream['PlayURL']}")

擷取播放地址

request = GetPlayInfoRequest()
request.set_VideoId(video_id)
request.set_Formats('mp4,m3u8')
request.set_AuthTimeout(3600)  # 地址有效期間 1 小時
response = json.loads(client.do_action_with_exception(request))
# response['PlayInfoList']['PlayInfo'] → 各清晰度的 PlayURL

執行結果

提交批量上傳

$ python vod_batch_upload.py

[1/2] 初始化VOD用戶端... ✓ (region: cn-shanghai)
[2/2] 提交批量上傳任務 (3個視頻)... ✓
  RequestId: 25818875-5F78-4AF6-D7393642CA58****
  JobId: ad90a501b1b9****  ← sample1.mp4
  JobId: bd81b612c2c8****  ← sample2.mp4
  JobId: ce72c723d3d9****  ← sample3.mp4

後續流程通過事件通知自動觸發:
  UploadByURLComplete → 自動轉碼 → TranscodeComplete → GetPlayInfo

回調服務接收事件

$ python callback_server.py
回調服務啟動: http://0.0.0.0:8080 | 等待回調...

[UploadByURLComplete] VideoId=93ab850b**** | Status=success
  → 上傳完成,等待自動轉碼...

[StreamTranscodeComplete] VideoId=93ab850b**** | Status=success
  → LD清晰度轉碼完成(已可播放)

[TranscodeComplete] VideoId=93ab850b**** | Status=success
  → 全部轉碼完成,擷取播放地址:
    [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=****

Agent 文檔檢索可視化

以下流程圖展示 Agent 基於 llms.txt 索引自動檢索文檔並產生代碼的過程(僅供理解,無需手動操作):

Prompt: "批量上傳+轉碼+播放"
  │
  ▼
┌── llms.txt (Quick start 匹配 + Common mistakes 約束) ────────┐
│  ✓ "URL 批量上傳"  → 媒體上傳/URL拉取上傳.md                  │
│  ✓ "事件通知"       → 事件通知/事件通知_2.md                  │
│  ✓ "播放地址"       → API參考/擷取音視頻播放地址.md           │
│  ✓ "Python 初始化" → API參考/初始化_2.md                      │
│  ✓ "自動轉碼"       → ApsaraVideo for Media Processing/工作流程.md                      │
│  ✗ #1 不用主帳號  ✗ #3 不輪詢  ✗ #5 URL編碼  ✗ #6 QPS退避     │
└────────────────────────┬─────────────────────────────────────┘
                         │
   ┌─────────────────────┼─────────────────────┐
   ▼                     ▼                     ▼
URL拉取上傳.md       事件通知_2.md       擷取音視頻播放地址.md
- UploadMediaByURL  - 事件類型列表       - 請求/返回參數
- URL必須編碼        - HTTP回調格式       - 清晰度枚舉
- 地區限制           - 逾時&重試策略      - QPS=360/s
- 最多20個URL        - 5秒逾時           - Status=Normal
   │                     │                     │
   ▼                     ▼                     ▼
   └────► 初始化_2.md(SDK 用戶端構造) ◄────┘
                         │
                         ▼
                  工作流程.md(TemplateGroupId 自動轉碼)
                         │
                         ▼
                產生完整可運行代碼

關鍵設計決策

下表為本 Demo 的具體決策追溯,與角色分工對應:角色分工說明誰做什麼,本表說明針對本情境,為什麼這樣選。

決策點

Agent 的選擇

文檔依據

上傳方式

UploadMediaByURL

需求是 URL 列表,文檔明確此 API 適用公網 URL 情境

轉碼觸發

上傳時指定 TemplateGroupId

文檔:設定後上傳完自動轉碼,無需額外 API 呼叫

狀態感知

HTTP 事件回調

Common Mistakes 第 3 條:不要輪詢

播放地址時機

TranscodeComplete 後

文檔:Status=Normal 才能 GetPlayInfo

AccessKey 管理

環境變數

Common Mistakes 第 1 條:不寫入程式碼

URL 處理

quote(url, safe='')

Common Mistakes 第 5 條:必須編碼

地區

cn-shanghai

URL 拉取僅支援華東 2(上海)

容錯

指數退避重試

Common Mistakes 第 6 條:Throttling 退避

服務端約束執行清單

Agent 在產生服務端代碼時 MUST 逐條自檢以下約束:

層級

約束

自檢方式

NEVER

使用主帳號 AccessKey(C-1)

檢查代碼中無寫入程式碼 AK,改用環境變數 + RAM 子帳號

NEVER

輪詢任務狀態(C-3)

檢查代碼中無 while/sleep 迴圈查詢狀態,改用 HTTP 回調

MUST

URL 編碼(C-4)

檢查所有傳入 UploadMediaByURL 的 URL 已調用 quote(url, safe='')

MUST

複用 Credential(C-5)

檢查 AcsClient 初始化僅一次,後續請求複用

MUST

QPS 退避(C-6)

檢查程式碼封裝含 Throttling 異常的指數退避重試邏輯

MUST

region=cn-shanghai(C-8)

檢查 AcsClient region_id 為 cn-shanghai

MUST

回調 5 秒內返回 200(C-9)

檢查 do_POST 在解析 body 後立即 send_response(200)

MUST

TranscodeComplete 後才 GetPlayInfo(C-10)

檢查 GetPlayInfo 僅在 TranscodeComplete 事件回調中調用

用戶端 Demo:Android 播放器與互動直播觀眾端

在打通服務端鏈路的基礎上,本模組以 Android 平台為例提供用戶端可運行 Demo:涵蓋"20 行代碼起播 MP4"到"基於 AUI Kits 接入互動直播觀眾端"。同時示範 Agent 如何利用用戶端 llms.txt 索引按情境定位文檔並產生代碼。

提交給 Agent 的 Prompt

請基於用戶端llms索引(端側播放器/、端側上傳/、端側短視頻/ 等目錄),用 Android 實現一個 Demo App。
具體需求為:
1.選取最小依賴包,播放服務端Demo GetPlayInfo 返回的播放地址。
2.在同一個 App 中再加一個入口,使用 AUI Kits 進入互動直播觀眾端(拉流播放 + 彈幕 + 點贊 + 禮物)。
3.包名、License、AppServer 網域名稱均做成可配置,避免寫入程式碼。

Agent 執行過程

階段 1:讀取用戶端 llms 索引,情境匹配

Quick start 匹配結果:
  "Android 起播 VOD"   → 端側播放器/player-atomic-android.md
  "包/依賴怎麼選"       → 端側播放器/player-overview.md
  "License 怎麼接"     → 端側播放器/player-common-license.md
  "互動直播觀眾端"      → 端側播放器/player-kits-aui.md
  "錯誤碼 4400/4013"   → 端側播放器/player-common-error-codes.md

Common mistakes 命中的約束:
  ✗ License 在播放器建立之後才 init      → 必須在 Application.onCreate
  ✗ Surface 還沒 ready 就 setSurface     → 必須等 surfaceCreated
  ✗ 直接拿全功能包                        → 按情境選最小包
  ✗ AppServer 網域名稱寫入程式碼                  → 抽到 BuildConfig / gradle.properties
  ✗ 同一直播間多次 PrivateService.init   → 僅在 SampleApp 裡 init 一次

階段 2:按需載入詳細文檔

文檔

擷取目的

端側播放器/player-overview.md

選包決策(BasicLive / UGC / InteractiveLive / Standard)

端側播放器/player-atomic-android.md

gradle 依賴、Manifest 許可權、混淆、最小起播代碼

端側播放器/player-common-license.md

License 接入時機與 4400/4013 錯誤排查

端側播放器/player-kits-aui.md

AUI Kits 用戶端模組、AppServer 協作模型

端側播放器/player-common-error-codes.md

起播失敗錯誤碼速查

階段 3:提取關鍵決策

選包:
  僅播放          → AliVCSDK_BasicLive
  播放 + 短視頻    → AliVCSDK_UGC
  含連麥/互動直播  → AliVCSDK_InteractiveLive   ← 本 Demo 選它
  全功能          → AliVCSDK_Standard / Premium

License:
  PrivateService.initService(ctx, licenseFile, licenseKey)
  必須在第一次 createAliPlayer 之前完成

AUI Kits:
  AppServer + 用戶端雙側;AppServer 頒發 身份Token / 直播間Token / IMS Token
  用戶端:AUILiveRoomCore + AUILiveRoomViewer + AUIInteraction 三個 module

執行結果

播放器快速整合——工程依賴

在專案級 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.**

License 初始化

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>

單視頻起播介面

產生 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();
        // 來源:2.1 節 GetPlayInfo 返回的 PlayURL(已帶 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 App]                                [AppServer]
   │                                              │
   │── GET /api/v1/live/viewer/token?roomId=xxx ─►│
   │                                              ├── 鑒權 + 頒發:
   │                                              │     userId / nick / pullUrl / imsToken
   │◄──────── 200 OK + ViewerTokenInfo ───────────┤
   │
   │── LiveRoomViewerActivity.start(params) ──►(AUI Kits 內部)
   │                                              │
   │   (AliPlayer 拉流 + IMS 彈幕)                 │
  • Android App 攜帶登入態向 AppServer 發起 GET /api/v1/live/viewer/token?roomId=xxx

  • AppServer 校正登入態後,頒發 userId/nick/pullUrl/imsToken

  • App 收到 ViewerTokenInfo 後,調用 LiveRoomViewerActivity.start(params) 進入直播間。

  • AUI Kits 內部用 AliPlayer 拉流,並通過 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;  // 互動訊息 SDK Token
}

兩個 Demo 入口合一

產生 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="單視頻起播" />

    <Button android:id="@+id/btnLiveRoom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="互動直播觀眾端" />
</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);
            // 實際使用時把 2.1 節 GetPlayInfo 的 PlayURL 透傳過來
            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, "進入失敗:" + msg, Toast.LENGTH_LONG).show();
            }
        });
    }
}

驗證清單

完整 Demo 跑通後預期看到的介面與日誌:

  • 點擊單視頻起播:進入 SimplePlayerActivity,黑色背景上的 220dp SurfaceView 正常出畫,畫面流暢、無黑屏。

  • 點擊互動直播觀眾端:AUI Kits 的 LiveRoomViewerActivity 被拉起;AliPlayer 顯示主播畫面;右下方彈幕、點贊、禮物面板均可正常使用。

  • logcat 關鍵日誌:AliPlayer: preparedAliPlayer: first frameIMS: connected 三條同時出現,代表拉流互動兩側均就緒。

Agent 文檔檢索可視化

以下流程圖展示 Agent 基於用戶端 llms.txt 索引自動檢索文檔並產生 Android Demo 的過程(僅供理解,無需手動操作):

Prompt: "Android 起播 + 進入互動直播間"
  │
  ▼
┌── 用戶端llms.txt(Quick start + Common mistakes) ───────────┐
│  ✓ "Android 起播"        → player-atomic-android.md         │
│  ✓ "包怎麼選"             → player-overview.md               │
│  ✓ "License"             → player-common-license.md          │
│  ✓ "AUI Kits 互動直播"    → player-kits-aui.md               │
│  ✓ "錯誤碼 4400/4013"     → player-common-error-codes.md     │
│  ✗ License 必須在播放器建立前 init                            │
│  ✗ Surface 未 ready 不可 setSurface                          │
│  ✗ 不要直接拿全功能包                                          │
│  ✗ AppServer 網域名稱不要寫入程式碼                                    │
│  ✗ PrivateService.init 僅在 Application 內一次               │
└────────────────────────┬────────────────────────────────────┘
                         │
  ┌─────────────┬────────┴─────────┬─────────────┐
  ▼             ▼                  ▼             ▼
player-      player-atomic-   player-common-  player-kits-aui.md
overview.md  android.md       license.md      - AppServer 協作
- 選包矩陣   - gradle 依賴     - PrivateService - 三類 Token
- 情境對應   - Manifest 許可權   - 4400/4013     - 模組拆分
- SDK 體積   - 起播代碼        - 包名/BundleID  - 用戶端 4 步整合
  └─────────────┴────────┬─────────┴─────────────┘
                         │
                         ▼
              player-common-error-codes.md
              (4400/4013/4034/4036 處理指引)
                         │
                         ▼
              產生完整可運行 Android Demo

關鍵設計決策

決策點

Agent 的選擇

文檔依據

SDK 組合包

AliVCSDK_InteractiveLive

player-overview.md 選包矩陣:含連麥/互動直播

License 時機

Application.onCreate

player-common-license.md:必須早於 createAliPlayer

Surface 綁定

surfaceCreated 回調內 setSurface

player-atomic-android.md 標準模板

AppServer 網域名稱

BuildConfig + gradle.properties

Common Mistake:不寫入程式碼

與服務端聯動

複用服務端 Demo GetPlayInfo 返回的 PlayURL

端到端一致性

互動直播觀眾端

AUI Kits 三模組(Core / Viewer / Interaction)

player-kits-aui.md:用戶端整合主線

錯誤處理

setOnErrorListener 通用回調打點;4400/4013/4034/4036 留作業務側分支擴充

player-common-error-codes.md

用戶端約束執行清單

Agent 在產生用戶端代碼時 MUST 逐條自檢以下約束:

層級

約束

自檢方式

MUST

License 在 createAliPlayer 之前初始化(C-11)

檢查 PrivateService.initService 在 Application.onCreate 中調用

MUST

Surface 綁定在 surfaceCreated 回調內(C-12)

檢查 setSurface 在 SurfaceHolder.Callback.surfaceCreated 內調用

NEVER

直接使用全功能 SDK 包(C-13)

檢查 gradle 依賴為情境最小包(如 InteractiveLive 而非 Standard)

NEVER

寫入程式碼 AppServer 網域名稱(C-14)

檢查網域名稱通過 BuildConfig / gradle.properties 配置

MUST

PrivateService.initService 僅調用一次(C-15)

檢查僅在 Application.onCreate 中調用,不在 Activity 中重複調用

MUST

錯誤碼監聽(4400/4013/4034/4036)

檢查 setOnErrorListener 登入並對嚴重錯誤碼做分支處理

PREFER

abiFilters 限制為 armv7 + arm64

檢查 ndk { abiFilters } 配置,避免包含 x86 增大包體

上線前 Checklist(Android)

層級

維度

檢查項

MUST

License

測試與生產 License 已分別申請並填入對應 BuildConfig

MUST

包體

abiFilters 限制為 armv7 + arm64;按情境選擇最小 SDK 組合包

MUST

起播

監聽 4400/4013/4034/4036 並打點

MUST

直播間

IMS Token 到期自動續簽;AppServer 已開啟 HTTPS 與 CORS

MUST

隱私

錄音、網路、媒體讀取許可權均於隱私協議明示

PREFER

灰階

新版本先 5% 灰階,48 小時觀察首幀與失敗率再放量

PREFER

監控

啟用阿里雲播放品質服務(QoS)與單點追查

最終目錄結構

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

至此,服務端 Demo用戶端 Demo 構成完整閉環:涵蓋上傳、自動轉碼、擷取播放地址、單視頻起播及 AUI Kits 互動直播觀眾端,全流程均由 Agent 基於 llms.txt 索引按需檢索文檔產生。

附錄:Coding Agent 選型與自檢

本附錄面向尚未使用或安裝 Coding Agent 的開發人員。

何時需要 Coding Agent

使用情境

是否需要

想讓 AI 直接讀 llms.txt、按需要展開子文檔、產生可運行專案工程

需要

僅想看本文檔瞭解架構、複製代碼後人工修改

不需要,瀏覽器即可

想讓 AI 在本地倉庫裡直接增刪改檔案、運行命令進行驗證

需要

推薦選項

分類

名稱

形態

說明

推薦

Qoder / Cursor / Claude Code

案頭 IDE 或 CLI

原生支援 llms.txt 類長文檔檢索,對國內雲廠商 SDK 適配較好;本文 Demo 基於 Qoder 驗證。

其他

-

-

任何能連網讀取 URL、能在本地寫檔案、能調用 Shell 的 Coding Agent 均可。

說明

安裝連結以產品官網為準,避免使用第三方鏡像。

安裝後的最小自檢

將以下 Prompt 發送給安裝好的 Coding Agent,對照預期答案驗證能力。MUST 三項全部通過才視為具備運行本文 Demo 的能力:

請幫我讀取 https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/vod/llms/llms.txt,
告訴我:
1.這份索引一共有幾個一級模組?分別是什嗎?
2."批量上傳 + 自動轉碼 + 擷取播放地址"這條鏈路涉及哪幾個子文檔?請列出相對路徑。
3.Common mistakes to avoid 中和 "AccessKey" 相關的有哪幾條?

預期答案

  • MUST 正確列出 llms.txt 的一級模組清單。

  • MUST 給出與服務端 Demo 中按需載入詳細文檔表大致一致的子文檔清單(媒體上傳、事件通知、擷取音視頻播放地址、Python SDK 初始化、ApsaraVideo for Media Processing工作流程)。

  • MUST 明確指出不要使用主帳號 AccessKey,應使用 RAM 子帳號。

如果三項都通過,則 Coding Agent 已具備運行本文 Demo 的能力。