Broadcasting

The Stream Video Flutter SDK has support for HLS broadcasting. This allows you to broadcast a call over HLS, which can then be watched using any HLS-compatible player. You can also use RTMP to ingest streams from external tools like OBS.

Call type for broadcasting

Among the pre-built call types, the livestream type is best suited for broadcasting events. When a livestream call is created, it is set to backstage mode by default. Backstage mode lets hosts set up cameras and equipment before going live.

final call = StreamVideo.instance.makeCall(
  callType: StreamCallType.liveStream(),
  id: 'your_call_id',
);

await call.getOrCreate(
  members: [
    MemberRequest(
      userId: StreamVideo.instance.currentUser.id,
      role: 'host',
    ),
  ],
);

await call.join();

Start and stop HLS broadcasting

You can start HLS broadcasting by calling startHLS() on the Call object. The user must have the start-broadcast-call capability to perform this action.

final result = await call.startHLS();

if (result.isSuccess) {
  final hlsUrl = result.getDataOrNull();
  // hlsUrl contains the HLS playlist URL
}

To stop broadcasting:

await call.stopHLS();

After a few seconds of setup, broadcasting will start and the call state will be updated — the isBroadcasting flag will become true.

Starting HLS when going live

You can start HLS broadcasting automatically when the call transitions out of backstage mode by passing startHls: true to goLive():

await call.goLive(startHls: true);

This is a convenient way to ensure HLS broadcasting starts alongside the WebRTC stream.

Observing the broadcasting state

You can observe the isBroadcasting property from the call state to update your UI:

PartialCallStateBuilder(
  call: call,
  selector: (state) => state.isBroadcasting,
  builder: (context, isBroadcasting) {
    return Text(isBroadcasting ? 'Broadcasting' : 'Not broadcasting');
  },
);

Here is an example of a toggle button that starts and stops HLS broadcasting:

PartialCallStateBuilder(
  call: call,
  selector: (state) => state.isBroadcasting,
  builder: (context, isBroadcasting) {
    return ElevatedButton(
      onPressed: () async {
        if (isBroadcasting) {
          await call.stopHLS();
        } else {
          await call.startHLS();
        }
      },
      child: Text(isBroadcasting ? 'Stop Broadcasting' : 'Start Broadcasting'),
    );
  },
);

Listening to broadcasting events

You can listen to broadcasting-related events via the callEvents stream on the Call object:

call.callEvents.on<StreamCallBroadcastingStartedEvent>((event) {
  final hlsPlaylistUrl = event.hlsPlaylistUrl;
  // Broadcasting has started, use hlsPlaylistUrl to watch the stream
});

call.callEvents.on<StreamCallBroadcastingStoppedEvent>((event) {
  // Broadcasting has stopped
});

call.callEvents.on<StreamCallBroadcastingFailedEvent>((event) {
  // Broadcasting failed
});

See more about subscribing to events on the events page.

Retrieving the broadcast URL

The HLS playlist URL can be retrieved from the call state after broadcasting has started:

final hlsUrl = call.state.value.egress.hlsPlaylistUrl;

This URL can be shared with viewers to watch the broadcast in any HLS-compatible player.

Displaying an HLS stream

To play an HLS stream in your Flutter app, use a video player package that supports HLS playback, such as video_player or media_kit.

Using video_player

import 'package:video_player/video_player.dart';

final controller = VideoPlayerController.networkUrl(
  Uri.parse(hlsUrl),
);

await controller.initialize();
controller.play();

Then use the VideoPlayer widget to display the video:

AspectRatio(
  aspectRatio: controller.value.aspectRatio,
  child: VideoPlayer(controller),
);

Using media_kit

import 'package:media_kit/media_kit.dart';
import 'package:media_kit_video/media_kit_video.dart';

final player = Player();
final videoController = VideoController(player);

player.open(Media(hlsUrl));

Then use the Video widget to display the video:

Video(controller: videoController);

Remember to dispose the player when you no longer need it to free system resources.

RTMP-In

You can use RTMP streams as input for a call. This is useful for integrating professional broadcasting tools like OBS.

The RTMP address is available from the call state:

final rtmpAddress = call.state.value.rtmpIngress;

The streaming key for OBS and other tools uses the format: apiKey/userToken.

Configuring OBS

  1. Open OBS and go to Settings > Stream
  2. Select Custom as the service
  3. Set the Server to the rtmpAddress from the call state
  4. Set the Stream Key to your_api_key/user_token
  5. Click Start Streaming in OBS

The RTMP stream will now show up in the call just like a regular video participant.

A user corresponding to the token provided to OBS will appear as a participant in the call. Consider creating a dedicated user for OBS streaming.

Error handling

Broadcasting depends on many factors, including network conditions. Wrap your broadcasting actions with proper error handling:

final result = await call.startHLS();

if (result.isFailure) {
  // Show error to the user
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(content: Text('Failed to start broadcasting')),
  );
}

Permissions

Before a user can start or stop broadcasting, they must have the corresponding capabilities:

  • start-broadcast-call to start broadcasting
  • stop-broadcast-call to stop broadcasting

Permissions for each role can be configured in the Stream Dashboard under Roles & Permissions for your call type.

final hasPermission = call.state.value.ownCapabilities.contains(
  CallPermission.startBroadcastCall,
);