# GT-Streaming JavaScript API 库

> [`GT-Streaming`](https://lucendar) 流媒体服务的(JavaScript)客户端接口类库，提供开发中常用的常量、数据结构和类，包括`HTTP API`和`WebSocket API`两套接口。

## 安装
```shell
npm install -S `strm-js`
```

## 更新日志
# v1.1.0 (2026-02-17)
- 增加 `StreamingFeatures` 媒体服务特性定义类
- 支持服务端`auto keep-alive`特性，`KeepAliveManager` 增加 `ServerSideAutoKeepAlive`属性。如果服务端已启用自动流请求保持(Auto KeepAlive)，则不再在客户端周期性调用 `/strm/keep` 接口来保持流请求
- 登录接口的请求体增加`baseOn`属性，用于声明客户端所期望的服务端版本
- `hls.js` 依赖升级到 `1.6.16`

# v1.0.11 (2025-12-02)
- 允许 `StrmApi.reqAvUpload2` 的 `fileSz` 参数值为0

# v1.0.10 (2025-07-15)
- `ApiReply` 结构增加 `attrs` 属性
- 增加 `qryCombinedTraceLog` 接口
- `mpegts.js` 依赖升级到 `1.8.0`
- `hls.js` 依赖升级到 `1.6.7`
- 增加巡检任务相关结构和HTTP接口
- 修正 StrmHttpApi 调用异常

# v1.0.9 (2025-05-03)
- ADAS附件任务增加 `排队中`、`由系统取消`、`由用户取消` 的任务状态常量，结构增加 `almTyp`, `almLvl`, `tm1` 属性
- `reqAlmAttUpload` 接口增加返回值定义
- 新增 `qryAlmAttUploaderStat` 接口
- `StrmNotif` 结构增加 `playedAvDumpUrl` 属性
- `StrmConsts` 增加 `mediaTypText` 静态方法
- `StrmApi` 增加更适合大文件下载的 `downloadUseIFrame` 方法，使用此方法需要在文档中定义一个隐藏的IFrame元素，然后将 url 和 IFrame 元素的ID传

# v1.0.8 (2025-04-21)
- 修正 PlayerWrapper.createPlayer 方法在 Safari 环境错报不支持 HLS 的问题
 
# v1.0.7 (2025-04-02)
- 修正 Safari 的兼容性问题

# v1.0.5 (2024-12-12)
- 修正 `StrmWsApi` 的 `qryAlmAtt()` 接口的 `id` 参数未实际提交到API服务

# v1.0.4 (2024-12-02)
- 注释订正
- 修正 qryAlmAtt() 接口的参数声明，补充遗漏的 `id` 参数，修正参数判断逻辑

# v1.0.3 (2024-09-21)
- 修正已知 BUG
- `qryAlmAtt()` 接口增加 `taskId` 参数
- 增加 `qryMqLogs()`, `qryMqAdaptorLogs()` 接口及相关类型

## 详细文档
请阅 https://lucendar.com/docs/strm-api-4/ws2-api/ws2-intro 文档。

## StrmWsApi 的基本用法
`StrmWsApi`类是用于操作`WebSocket API`接口的入口类。

### 登录
构造 `StrmWsApi` 对象，添加事件处理回调函数，并登录：
```ts
import {StrmWsApiConfig, StrmWsApi} from 'strm-js';

  // 连接配置
const config = new StrmWsApiConfig();
config.wsApiUrl = wsUrl.value; // wsApiUrl 是必要参数

// 创建 StrmWsApi 对象
strmWsApi = new StrmWsApi(config);

// 添加 媒体状态变更通知 监听器
strmWsApi.addStrmNotifListener(onStrmNotif);

// 登录
infoLog(`尝试登录到: ${config.wsApiUrl}`);
strmWsApi.login(username, password)
    .then(
        // login API 登录成功后，返回 GnssLoginResult 对象
        (_: GnssLoginResult) => {
            console.debug('登录成功');
        }
    )
    .catch((err) => {
        console.error('登录时遇到错误：' + err.toString());
        alert(err.toString());
    });
```

### 媒体状态变更通知的处理
简单的媒体状态变更通知，可以只处理 `StrmNotif.ACT__strmReady` 通知。当收到此通知时，开始创建播放器并播放：
```ts
import {StrmNotif} from 'strm-js';

/**
 * 收到 流媒体状态通知 时的处理
 * @param {StrmNotif} strmNotif 通知
 */
function onStrmNotif(strmNotif: StrmNotif) {
    switch (strmNotif.act) {
        case StrmNotif.ACT__strmReady:
            // 收到 `流已经准备好` 的通知，检查是否我们关注的终端号和通道号
            if (strmNotif.simNo === simNo.value && strmNotif.chan === channel.value) {
                console.debug(`流已经准备好：${strmNotif.simNo}/${strmNotif.chan}`);

                // 如果播放器尚未创建，则创建播放器并播放
                if (!player) {
                    createPlayerAndLoad(strmNotif);
                }
            }
            break;

        case StrmNotif.ACT__cmdSent:
            console.debug(`指令已经下发到：${strmNotif.simNo}/${strmNotif.chan}`);
            break;

        case StrmNotif.ACT__cmdFailed:
            console.debug(`指令失败：${strmNotif.simNo}/${strmNotif.chan}`);
            break;
    }
}
```

### 打开实时音视频播放
调用API，如打开实时音视频播放：

```ts
import {GnssOpenLiveParams, GnssOpenStrmResult} from "strm-js";

  // 准备参数
const params = new GnssOpenLiveParams();
params.simNo = simNo.value;                       // 终端识别号
params.channel = channel.value;                   // 通道号
params.dataType = selectedDataType.value;         // 数据类型
params.codeStream = selectedCodeStrm.value;       // 码流代码
params.async = true;                              // 采用异步方式
params.proto = StrmConsts.PROTO__FLV;             // 使用 FLV 格式

// 调用打开实时音视频接口
console.debug('尝试打开实时音视频：strm/liveOpen');
strmWsApi.liveOpen(params)
    .then(
        // liveOpen 接口成功后，返回结果
        (reply: ApiReply<GnssOpenStrmResult>) => {
            openStrmResult = reply.data![0];

            // 得到请求ID(reqId)后，保存起来，后续关闭通道时要用到
            reqId = openStrmResult.reqId;

            console.debug(`流已经创建，请求ID=${reqId}`);

            // 此时还不一定能直接播放，要检查返回结果的 `ready` 属性。
            // 如果`ready` == true（流已经准备好），且播放器尚未创建，则创建播放器并播放
            if (openStrmResult.ready && !player) {
                console.debug(`流已经准备好：${params.simNo}/${params.channel}`)
                createPlayerAndLoad(openStrmResult); // 创建播放器并播放
            }
        }
    )
    .catch((err: any) => {
        const msg = `打开实时音视频播放时遇到错误：${err.toString()}`;
        console.error(msg);        
    });
```

### 关闭媒体流

```ts
/**
 * 销毁播放器并释放流请求
 */
function stop() {
    // 关闭、销毁播放器
    if (player) {
        player.stop();
        player = null;
    }

    // 释放流请求
    if (strmWsApi && strmWsApi.loggedIn && reqId) {
        strmWsApi.releaseStrmReq(reqId);
        reqId = undefined;
    }

    console.debug('媒体流已经关闭');
}
```

## 播放器的用法
`strm-js` 库中包含一个抽象的播放器封装，可支持FLV，HLS两种格式。使用时，首先要定义一个回调处理对象，这个回调处理对象继承 `PlayerContainer` 接口，提供几个相关的回调方法，供播放器封装在相应的情况下调用。
```ts
/**
 * 播放器回调接口
 */
const playerContainer = new PlayerContainer(
                // id() 方法，播放器的ID，如一个页面内有多个时，用此ID区分。主要用于调试。
                () => 'test',

                // isOpenRequested() 回调方法，返回是否正在请求打开流媒体或已经打开流媒体，以下用本地缓存的openStrmResult对象是否为空来作为返回结果
                () => !!openStrmResult,

                // onPlayerError() 回调方法，当 PlayerWrapper 发生错误时，此方法被 PlayerWrapper 调用。实现类通常在此方法中在UI上提示用户。
                (err: string) => {
                   console.error(`播放时遇到错误：${err.toString()}`);
                },

                // onPlaying() 回调方法，当开始播放时，此方法被 PlayerWrapper 调用。
                () => {
                   console.debug('开始播放');
                },

                // onClose() 回调方法，当播放正常或异常结束时，此方法 PlayerWrapper 调用。实现类通常在此方法中做一些清理工作。
                () => {
                    console.debug('播放器已经关闭');
                }
        );
```

然后创建播放器对象可以直接根据需要 `new` 某一个播放器封装类（`FlvPlayerWrapper`, `HlsJsPlayerWrapper`, `HlsNativePlayer`），
```ts
import {FlvPlayerWrapper, StrmConsts} from 'strm-js';

const player = new FlvPlayerWrapper(
    playerContainer,
    videoElmt,
    StrmConsts.PROTO__FLV,
    openStrmResult.playUrl);
```

也可以使用 `PlayerWrapper.create()` 方法来创建：

```ts
import {PlayerWrapper, StrmConsts} from 'strm-js';
import {FlvPlayerWrapper} from "./player";

const player = PlayerWrapper.createPlayer(
    playerContainer,
    videoElmt,
    StrmConsts.PROTO__FLV,
    mediaNotif.mediaTyp!,
    openStrmResult.playUrl);   
```

加载流媒体时，只需要调用`load()`方法即可：
```ts
player.load();
```

