Skip to content

Real-Time Audio and Video ​

This page explains how a device sends audio and video by stream_id, and how the client binds the same stream IDs to audio and video outputs after a connection is established.

If you have not started the device or connected the client yet, read Device Integration, Client Integration, and Connection first.

Agree on stream_id ​

stream_id identifies one media stream inside a connection.

  • The public range is 0 to 15.
  • Audio and video must not use the same stream_id in the same connection.
  • The media type tells TiRTC whether a frame is audio or video; stream_id only identifies the stream.

If you start the local device example with the CLI tool, it uses these defaults:

Purposestream_id
Audio10
Video11

The examples below use the same values. You can choose your own values as long as device sending and client binding stay aligned.

Client Playback ​

On the client, create the connection and the audio/video outputs, bind each output to the agreed stream_id, then connect.

dart
final TiRtcConn conn = TiRtcConn();
final TiRtcAudioOutput audioOutput = TiRtcAudioOutput();
final TiRtcVideoOutput videoOutput = TiRtcVideoOutput();

Widget buildVideoView() => videoOutput.view();

void startPlayback({
  required String remoteId,
  required String token,
}) {
  conn.onStateChanged = (TiRtcConnState state, int errorCode) {
    debugPrint('conn state=$state error=$errorCode');
  };
  audioOutput.onStateChanged = (TiRtcAudioOutputState state) {
    debugPrint('audio state=$state');
  };
  videoOutput.onStateChanged = (TiRtcVideoOutputState state) {
    debugPrint('video state=$state');
  };
  videoOutput.onRenderSizeChanged = (Size size) {
    debugPrint('video size=${size.width}x${size.height}');
  };

  conn.connect(remoteId: remoteId, token: token);
  audioOutput.attach(connection: conn, streamId: 10);
  videoOutput.attach(connection: conn, streamId: 11);
}

void stopPlayback() {
  videoOutput.detach();
  audioOutput.detach();
  conn.disconnect();
}

void disposePlayback() {
  videoOutput.dispose();
  audioOutput.dispose();
  conn.dispose();
}

For HarmonyOS native apps, mount TiRtcVideoOutputView with the TiRtcVideoOutput object:

ts
import {
  TiRtcAudioOutput,
  TiRtcAudioOutputState,
  TiRtcConn,
  TiRtcConnState,
  TiRtcVideoFit,
  TiRtcVideoOutput,
  TiRtcVideoOutputState,
  TiRtcVideoOutputView,
} from 'tirtc-av/Index';

const conn = new TiRtcConn();
const audioOutput = new TiRtcAudioOutput();
const videoOutput = new TiRtcVideoOutput();

@Builder
function RemoteVideoView() {
  TiRtcVideoOutputView({
    output: videoOutput,
    fit: TiRtcVideoFit.contain,
    width: '100%',
    height: '100%',
  });
}

function startPlayback(remoteId: string, token: string): void {
  conn.onStateChanged = (state: TiRtcConnState, errorCode: number): void => {
    console.info(`conn state=${state} error=${errorCode}`);
  };
  audioOutput.onStateChanged = (state: TiRtcAudioOutputState): void => {
    console.info(`audio state=${state}`);
  };
  videoOutput.onStateChanged = (state: TiRtcVideoOutputState): void => {
    console.info(`video state=${state}`);
  };
  videoOutput.onRenderSizeChanged = (size): void => {
    console.info(`video size=${size.width}x${size.height}`);
  };

  conn.connect({ remoteId, token });
  const audioCode = audioOutput.attach({ connection: conn, streamId: 10 });
  const videoCode = videoOutput.attach({ connection: conn, streamId: 11 });

  console.info(`audio=${audioCode} video=${videoCode}`);
}

function stopPlayback(): void {
  videoOutput.detach();
  audioOutput.detach();
  conn.disconnect();
}

function disposePlayback(): void {
  videoOutput.dispose();
  audioOutput.dispose();
  conn.dispose();
}

Success Signals ​

  • The connection reaches connected.
  • The audio output reaches playing.
  • The video output reaches rendering, or the video size callback fires.

If the connection is established but there is no video, check:

  • whether the client stream IDs match the device stream IDs;
  • whether the first video frame from the device is a key frame;
  • whether the video output widget is mounted in a visible view tree.

TiRTC