# Flutter

> **Warning**
>
> This SDK is no longer being developed. If you would like to use a Flutter SDK with OptiView Real-Time (Millicast) please [contact OptiView Sales](https://optiview.dolby.com/contact/). If you are an existing customer using this SDK, please contact us via our support channels for ongoing support and migration options.

OptiView Real-time Streaming APIs support creating iOS and Android applications using the Flutter SDK.

## [![](/pr-863/img/github.svg)![](/pr-863/img/github_dark.svg)GitHub](https://github.com/millicast/flutter-sdk/releases)

[Access the SDK releases](https://github.com/millicast/flutter-sdk/releases)

## [📖Reference](https://pub.dev/documentation/millicast_flutter_sdk/latest/index.html)

[Class and object reference](https://pub.dev/documentation/millicast_flutter_sdk/latest/index.html)

## [📱Sample app](https://github.com/millicast/flutter-sdk/tree/develop/example)

[Test features and capabilities](https://github.com/millicast/flutter-sdk/tree/develop/example)

## Getting started

### Importing the library

You can import the SDK through flutter pub dev.

#### Import as a dependency

install sdk

```shell
flutter pub add millicast_flutter_sdk
```

For windows you may need to allow installing apps from any source:

You should now see the following information in pubspec.yaml file:

```yaml
dependencies:
  # ...
  # More dependencies
  millicast_flutter_sdk: ^1.1.x
```

And then using it with the `import` statement.

Import SDK

```dart
import 'package:millicast_flutter_sdk/millicast_flutter_sdk.dart';
```

### Publish a stream

> **Getting started**
>
> You will need to find or create a new stream name with a token in your OptiView Real-time Streaming Dashboard. You can do that following this [link](https://docs-preview.optiview.dolby.com/pr-863/millicast/managing-your-tokens.md).

The main module to publish a stream is the [Publish module](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/Publish-class.html).

#### Instance the Publish module

To create a new publisher, you have to instance the Publish class:

Dart

```dart
import 'package:millicast_flutter_sdk/millicast_flutter_sdk.dart';

// Setting subscriber options
DirectorPublisherOptions directorPublisherOptions =
  DirectorPublisherOptions(
  token: 'my-publishing-token', streamName: 'my-stream-name');

// Define callback for generate new token`
tokenGenerator() => Director.getPublisher(directorPublisherOptions);

// Create a new instance
Publish millicastPublish =
  Publish(streamName: 'my-stream-name', tokenGenerator: tokenGenerator);
```

In order to create a Publish instance, you have to send two params.

* The first one is the stream name ('my-stream-name') and it is used to set where do you want to publish your stream.
* The second param is a callback (tokenGenerator) and it is used to get the necessary data of the stream name where you want to stream. Also, it is used to set auto reconnection if you are streaming and then lose connection. In order for the Director class to be able to call the getPublisher method for the tokenGenerator, you must first set the stream name ('my-stream-name') and token('my-publishing-token').

More information here:

* [Publish](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/Publish-class.html)
* [.getPublisher()](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/Director/getPublisher.html)
* [Token generator .callback()](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/Publish/tokenGenerator.html)

#### Get your media information

You should get the media information (camera/microphone) you want to stream with. To do that, the SDK uses MediaStream object.

In this example, we are going to get the default user camera and microphone:

Dart

```dart
import 'package:flutter_webrtc/flutter_webrtc.dart';
// Get User camera and microphone
final _localRenderer = RTCVideoRenderer();
MediaStream? mediaStream = await navigator.mediaDevices.getUserMedia({
  'audio': true,
  'video': true,
});
```

This will ask permission in the mobile device to get the camera and microphone usage and will save the mediaStream information if the user accepts.

More information about using user media here:

* [.getUserMedia()](https://pub.dev/documentation/webrtc_interface/latest/webrtc_interface/MediaDevices/getUserMedia.html)

#### Initialize the stream

After all previous steps are completed, you can now initialize the stream using the `.connect()` method inside the Publish module.

Using the instanced Publisher and mediaStream, add the connect method:

Dart

```dart
// Publishing Options
Map<String, dynamic> broadcastOptions = {'mediaStream': mediaStream};

_localRenderer.srcObject = mediaStream;

// Start connection to publisher
try {
  await millicastPublish.connect(options: broadcastOptions);
} catch (e) {
  throw Exception(e);
}
```

Once the `.connect()` Future resolves, the stream is initialized correctly; otherwise it'll throw an error.

The `.connect()` can optionally receive more params, all of which are described in the [.connect()](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/Publish/connect.html) method.

For example:

* If you want to start your stream with a bitrate limit, you can use the `bandwidth` option.

* If your stream token in Millicast has the recording enabled, you can enable it with the `record` option. Once you have finished your stream, you can see the recording in the [Dashboard Recordings section](https://docs-preview.optiview.dolby.com/pr-863/millicast/distribution/stream-recordings.md).

* You can start a stream without audio or video setting the `disableAudio` or `disableVideo` respectively.

* You can select which codec you want to stream with using the `codec` option.

* More information here:

  * [.connect()](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/Publish/connect.html)
  * [Dashboard Recordings section](https://docs-preview.optiview.dolby.com/pr-863/millicast/distribution/stream-recordings.md)

#### Managing your active stream

> **Tip**
>
> The next samples use the `publisher` variable we created in the past steps. Please follow the tutorial.

The Publish instance internally uses most of the classes described in the [documentation](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/millicast_flutter_sdk-library.html).

If the method is not static (which means you can access it without a Publish instance), you can access it through the Publish instance.

Example:

Dart

```dart
publisher.signaling   // Corresponds to the Signaling module.
publisher.webRTCPeer  // Corresponds to the Peerconnection module.
```

Most of the important methods are located directly in the Publisher module but some are not, like updating the bitrate or getting the WebRTC stats.

#### Change bitrate

During an active stream, you can limit the maximum bitrate in kbps. To do that, you have to use the `.updateBitrate()` method of your Publish instance that is located inside a subclass called `webRTCPeer`.

Example:

Dart

```dart
publisher.webRTCPeer.updateBitrate(2000); // Set the active stream with 2000 kbps limit.
publisher.webRTCPeer.updateBitrate(0);    // Set the active stream with no limit.
```

More information here:

* [.updateBitrate()](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/PeerConnection/updateBitrate.html)

#### Stop stream

If you want to stop an active stream, you can use the `.stop()` method of your Publish instance.

```dart
publisher.stop()
```

More information here:

* [.stop()](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/Publish/stop.html)

### Viewer

> **Tip**
>
> In order to connect to a stream you'll need the account id and stream name of the broadcast. This information must be provided by the publish owner. For more information, see [Publishing API](https://docs-preview.optiview.dolby.com/pr-863/millicast/managing-your-tokens.md).

The main module to view a stream is the [View module](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/View-class.html).

#### Instance the View module

Creating a new View instance:

Dart

```dart
import 'dart:convert';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:millicast_flutter_sdk/millicast_flutter_sdk.dart';

// Define callback for generate new token
DirectorSubscriberOptions directorSubscriberOptions =
  DirectorSubscriberOptions(
  streamName: 'my-stream-name',
  streamAccountId: 'my-stream-accountId');
tokenGenerator() => Director.getSubscriber(directorSubscriberOptions);

RTCVideoRenderer mediaStream = RTCVideoRenderer();

/// Create a new instance
View view =
  View(streamName: 'my-stream-name', tokenGenerator: tokenGenerator);
```

Just like in the case of creating a Publish instance, in order to create a View instance you need two params and there's also an optional param. The first one is the stream name (`'my-stream-name'`) and it is used to set the stream you want to connect.

* The second parameter is a callback (`tokenGenerator`) and it is used to get the necessary data of the stream name where you want to connect. Also, it is used to set auto reconnection if you are streaming and then lose connection. You have to set the stream name('my-stream-name') and the token('my-subscriber-token') for the function to be able to be called correctly to generate the token.
* (Optional) `mediaElement`. This is the HTML media element where you want to mount the stream; for example a `video` element.
* (Optional) `autoReconnect`. The Default value is true, enabling auto reconnect to stream.

More information here:

* [View](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/View-class.html)
* [.getSubscriber()](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/Director/getSubscriber.html)
* [Token generator .callback()](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/View/tokenGenerator.html)

Dart

```dart
mediaStream = RTCVideoRenderer();

/// Start connection to publisher
try {
  await view.connect();
  view.webRTCPeer.initStats();
  return view;
} catch (e) {
  throw Exception(e);
}
```

#### Track event

In case the mediaElement param is not specified when creating a View instance, a track event containing the media track will be emitted when the stream starts.

Dart

```dart
view.on('stats', view, (ev, context) {
    MediaStream? stream = ev.eventData as MediaStream?;
    // Manage the track event.
});
```

#### Connecting to a stream

After the previous steps are done then you can connect to the stream using the `.connect()` method from the View instance.

Dart

```dart
try {
  await view.connect(options)
} catch (e) {
  print('Connection failed, handle error $e')
  view.reconnect();
}
```

We recommend using the `.reconnect()` method in the catch clause to make sure the View instance keeps trying to reconnect until the stream is live even when an error in the connect occurs.

### Reconnect

Both the instances of Publish and View have a reconnect event. This event emits when the connection is lost with a timeout and error message in the callback. This event is emitted by the .reconnect() method, this method is automatically called by default except specified with the [autoReconnect](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/View/autoReconnect.html) flag of the constructor. [SDK Reconnect event](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/View/setReconnect.html)

Example of a reconnect event in the View instance:

Dart

```dart
import 'package:millicast_flutter_sdk/millicast_flutter_sdk.dart';

View view = View(
    streamName: 'my-stream-name',
    tokenGenerator: tokenGenerator,
    mediaElement: localRenderer);

view.on('reconnect', view, (event, context) {
  print(event.eventData.toString());
});
```

* The timeout is the time, in milliseconds, informing on when it will retry to establish the connection. It starts at `2000 ms` and then `4000 ms` ... until `32000 ms` (`32 s`). After it reaches `32 s` it starts retrying at `32 s` intervals.
* The error message contains the cause of failure. More information here:
* [Publish reconnect](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/Publish/reconnect.html)
* [View reconnect](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/View/reconnect.html)

### Logger

You can get the logs of the connection, SDP, errors, and more. It can be enabled through the Logger module and you can activate it at any time (before/after a new connection).

Dart

```dart
import 'package:logger/logger.dart';
import 'package:millicast_flutter_sdk/millicast_flutter_sdk.dart';
Logger _logger = getLogger('main');
print(memory.getBuffer());
```

Where memory is a global variable in which all logs gets stored.

More information and examples here:

* [Logger](https://pub.dev/documentation/logger/latest/logger/Logger-class.html)

### WebRTC Stats

Stream stats can be accessed by both Publish and View instances. In order to capture them, you have to initialize the stats and then listen for the event through the `webRTCPeer` attribute.

> **WebRTC Statistics**
>
> Stats might defer between different mobile devices.

Example of usage:

Dart

```dart
import 'package:millicast_flutter_sdk/millicast_flutter_sdk.dart';

// Initialize and connect your Viewer
View view = View(
  streamName: Constants.streamName,
  tokenGenerator: tokenGenerator,
  mediaElement: localRenderer);

// Start connection to publisher
try {
  await view.connect();

  view.webRTCPeer.initStats();

  view.webRTCPeer.on('stats', view, (stats, context) {
    if (stats.eventData != null) {
      for (var report in stats.eventData as List<StatsReport>) {
        if (report.type != 'codec') {
          _logger.d('${report.id}, ${report.type}, ${report.timestamp}');
        }
      }
    }
  });
  return view;
} catch (e) {
  rethrow;
}
```

We highly recommend stopping the stats when they're not being used.

Example of stopping stats:

Dart

```dart
//...
view.webRTCPeer.stopStats();
```

More information here:

* [PeerConnection initStats](https://pub.dev/documentation/millicast_flutter_sdk/latest/millicast_flutter_sdk/PeerConnection/initStats.html)

### User count

The OptiView Real-time Streaming server provides the number of current viewers through broadcast events. To start listening to different broadcast events you have to add which events you want to listen to in the Publisher/Subscriber options.

Both Publisher and Subscriber follow the same workflow.

Example of usage:

Dart

```dart
import 'package:millicast_flutter_sdk/millicast_flutter_sdk.dart';
import 'dart:convert';

//...
int _viewers;

// Add viewercount to events list
Map<String, dynamic> options = {
  'events': ['active', 'inactive', 'layers', 'viewercount']
};

await publish.connect(options: options);

// Add listener of broacastEvent to get UserCount
_publisherMedia.on('broadcastEvent', this, (event, context) {
  var data = jsonEncode(event.eventData);
  Map<String, dynamic> dataMap = jsonDecode(data);
  if (dataMap['name'] == 'viewercount') {
    _viewers = dataMap['data']['viewercount'];
    // Use _viewers value as needed
  }
});
```
