Skip to content

Java API 说明 ​

使用 Java SDK 连接设备、接收媒体帧、保存文件并收发消息。工程依赖、JDK 和系统要求见 Java SDK 接入。

Client 与 Connection ​

ClientOptions.builder() 设置 appId、accessKeyId、accessKeySecret 和可写的绝对 cacheDir(Path)。可选 endpoint 省略时使用默认地址,显式空字符串无效。同一进程一个 RtcClient,可以创建多条连接。

操作方法
创建连接client.newConnection() 或 client.newConnection(listener)
连接connection.connect(deviceId) → CompletableFuture<Void>,无需传 Token
状态与断开state()、disconnect();后续断线由 Connection.Listener.onStateChanged 通知
订阅subscribeAudio(id)、unsubscribeAudio(id)、subscribeVideo(id)、unsubscribeVideo(id)
请求关键帧requestVideoKeyframe(id)
命令sendCommand(long commandId, byte[] data);Listener 的 onCommand 接收
流消息sendStreamMessage(int id, Duration timestamp, byte[] data);Listener 的 onStreamMessage 接收

Stream ID 为 0..15,设备端发送、Output 绑定和订阅使用同一编号;同方向音频和视频使用不同编号。自定义命令字从 0x2001 起。流消息时间戳使用非负毫秒,最大为 uint32 毫秒。

接收第一帧视频 ​

设置环境变量 TIRTC_APP_ID、TIRTC_ACCESS_KEY_ID、TIRTC_SECRET_KEY_ID、TIRTC_DEVICE_ID。设备发送视频流 11;将下面的类加入已配置依赖的应用并运行。收到首帧输出尺寸,失败或超时抛出异常。

java
import com.tange.ai.tirtc.*;
import java.nio.file.Paths;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;

public final class ReceiveVideo {
    public static void main(String[] args) throws Exception {
        ClientOptions options = ClientOptions.builder()
            .appId(System.getenv("TIRTC_APP_ID"))
            .accessKeyId(System.getenv("TIRTC_ACCESS_KEY_ID"))
            .accessKeySecret(System.getenv("TIRTC_SECRET_KEY_ID"))
            .cacheDir(Paths.get(".tirtc-cache").toAbsolutePath())
            .build();
        CountDownLatch received = new CountDownLatch(1);
        AtomicReference<TiRtcException> failure = new AtomicReference<>();
        AtomicReference<VideoFrame> first = new AtomicReference<>();
        try (RtcClient client = new RtcClient(options);
             Connection connection = client.newConnection(new Connection.Listener() {
                 @Override public void onStateChanged(ConnectionState state, TiRtcException error) {
                     if (error != null) { failure.set(error); received.countDown(); }
                 }
             });
             VideoOutput video = new VideoOutput(connection, 11, new VideoOutput.Listener() {
                 @Override public void onFrame(VideoFrame frame) {
                     first.compareAndSet(null, frame);
                     received.countDown();
                 }
                 @Override public void onError(TiRtcException error) {
                     failure.set(error); received.countDown();
                 }
             })) {
            CompletableFuture<Void> attempt = connection.connect(System.getenv("TIRTC_DEVICE_ID"));
            try {
                attempt.get(30, TimeUnit.SECONDS);
            } catch (TimeoutException error) {
                attempt.cancel(false);
                throw error;
            }
            connection.subscribeVideo(11);
            connection.requestVideoKeyframe(11);
            if (!received.await(30, TimeUnit.SECONDS)) {
                throw new TimeoutException("30 秒内未收到视频帧");
            }
            if (failure.get() != null) { throw failure.get(); }
            VideoFrame frame = first.get();
            System.out.printf("video: %dx%d%n", frame.width(), frame.height());
        }
    }
}

get(timeout, unit) 自身超时不会取消连接;示例显式取消 Future。初次连接失败由 Future 交付,之后的断线仍需监听状态回调。

Output 与 Frame ​

四类 Output 为 AudioOutput、VideoOutput、EncodedAudioOutput、EncodedVideoOutput。构造时传 Connection、Stream ID 和对应 Listener,立即绑定到该连接。每个 Listener 的 onFrame 必填,另有 onStateChanged、onError。

detach() 解除当前绑定,attach(id) 只能重新绑定同一来源的 Stream ID;最终调用 close()。不要将一个 Output 重挂到另一条 Connection。SDK 回调帧,播放、渲染和后续处理由应用完成;不提供本地音视频输入。

Frame 是不可变快照,可在回调后保留。音频通过 frame.data() 读取只读 ByteBuffer,视频通过 frame.planes() 的数据和 stride() 读取各平面;按帧实际格式、尺寸、采样率和声道解释数据。

MP4 与 JPEG ​

java
RecordingFile file;
try (RecordingTask task = connection.startRecording(new StartRecordingOptions(11, 10))) {
    // 保持接收,业务需要结束保存时调用:
    file = task.stop();
}

上面为签名示意,connection 已连接并订阅相应流。可省略音频,只保存视频。解码视频有帧后调用 video.takeSnapshot() 取得 SnapshotFile。

两类文件的 path() 返回临时文件 Path;用 Files.copy 保存到业务目录,再调用 delete() 或 close() 删除临时源。成功领取的文件独立于任务和 Client;不要依赖关闭连接来取得录制结果,应先显式 stop()。stop() 领取文件后仍需关闭 RecordingTask,上例由 try-with-resources 完成。

释放与错误处理 ​

资源实现 AutoCloseable,使用上例的 try-with-resources 按 Output、Connection、Client 逆序关闭。父对象关闭会级联释放后代,close() 幂等。活动录制先显式停止并处理文件。

持续 Listener 中不能同步控制同一资源树,包括发送消息、订阅和关闭;重入会抛出 IN_USE。将收到的消息或状态投递到业务线程,再调用 SDK,回调保持简短。

同步失败抛出 TiRtcException,通过 category()、code() 查类别和错误码。Future 失败通过 ExecutionException.getCause() 查看原因。记录操作、异常和 client.uploadLogs() 返回的日志 ID,便于排查。

TiRTC 开发文档