diff --git a/example/lib/widgets/controls.dart b/example/lib/widgets/controls.dart index ebde5a08..9adfa897 100644 --- a/example/lib/widgets/controls.dart +++ b/example/lib/widgets/controls.dart @@ -312,6 +312,7 @@ class _ControlsWidgetState extends State { else PopupMenuButton( icon: const Icon(Icons.settings_voice), + offset: Offset(0, -90), itemBuilder: (BuildContext context) { return [ PopupMenuItem( diff --git a/example/lib/widgets/participant.dart b/example/lib/widgets/participant.dart index 78d8ba19..4becd66a 100644 --- a/example/lib/widgets/participant.dart +++ b/example/lib/widgets/participant.dart @@ -148,13 +148,6 @@ abstract class _ParticipantWidgetState ) : const NoVideoWidget(), ), - if (widget.showStatsLayer) - Positioned( - top: 30, - right: 30, - child: ParticipantStatsWidget( - participant: widget.participant, - )), // Bottom bar Align( alignment: Alignment.bottomCenter, @@ -176,6 +169,13 @@ abstract class _ParticipantWidgetState ], ), ), + if (widget.showStatsLayer) + Positioned( + top: 130, + right: 30, + child: ParticipantStatsWidget( + participant: widget.participant, + )), ], ), ); diff --git a/example/lib/widgets/participant_stats.dart b/example/lib/widgets/participant_stats.dart index 24bd49e6..d1e322e7 100644 --- a/example/lib/widgets/participant_stats.dart +++ b/example/lib/widgets/participant_stats.dart @@ -19,7 +19,7 @@ class ParticipantStatsWidget extends StatefulWidget { class _ParticipantStatsWidgetState extends State { List> listeners = []; StatsType statsType = StatsType.kUnknown; - Map stats = {}; + Map> stats = {'audio': {}, 'video': {}}; void _setUpListener(Track track) { var listener = track.createListener(); @@ -27,8 +27,9 @@ class _ParticipantStatsWidgetState extends State { if (track is LocalVideoTrack) { statsType = StatsType.kLocalVideoSender; listener.on((event) { + Map stats = {}; setState(() { - stats['video tx'] = 'total sent ${event.currentBitrate.toInt()} kpbs'; + stats['tx'] = 'total sent ${event.currentBitrate.toInt()} kpbs'; event.stats.forEach((key, value) { stats['layer-$key'] = '${value.frameWidth ?? 0}x${value.frameHeight ?? 0} ${value.framesPerSecond?.toDouble() ?? 0} fps, ${event.bitrateForLayers[key] ?? 0} kbps'; @@ -37,52 +38,66 @@ class _ParticipantStatsWidgetState extends State { event.stats['f'] ?? event.stats['h'] ?? event.stats['q']; if (firstStats != null) { stats['encoder'] = firstStats.encoderImplementation ?? ''; - stats['video codec'] = - '${firstStats.mimeType}, ${firstStats.clockRate}hz, pt: ${firstStats.payloadType}'; + stats['codec'] = + '${firstStats.mimeType!.split('/')[1]}/${firstStats.clockRate}'; + stats['payload'] = '${firstStats.payloadType}'; stats['qualityLimitationReason'] = firstStats.qualityLimitationReason ?? ''; } + + this.stats['video']!.addEntries(stats.entries); }); }); } else if (track is RemoteVideoTrack) { statsType = StatsType.kRemoteVideoReceiver; listener.on((event) { + Map stats = {}; setState(() { - stats['video rx'] = '${event.currentBitrate.toInt()} kpbs'; - stats['video codec'] = - '${event.stats.mimeType}, ${event.stats.clockRate}hz, pt: ${event.stats.payloadType}'; - stats['video size'] = + stats['rx'] = '${event.currentBitrate.toInt()} kpbs'; + stats['codec'] = + '${event.stats.mimeType!.split('/')[1]}/${event.stats.clockRate}'; + stats['payload'] = '${event.stats.payloadType}'; + stats['size/fps'] = '${event.stats.frameWidth}x${event.stats.frameHeight} ${event.stats.framesPerSecond?.toDouble()}fps'; - stats['video jitter'] = '${event.stats.jitter} s'; - stats['video decoder'] = '${event.stats.decoderImplementation}'; + stats['jitter'] = '${event.stats.jitter} s'; + stats['decoder'] = '${event.stats.decoderImplementation}'; //stats['video packets lost'] = '${event.stats.packetsLost}'; //stats['video packets received'] = '${event.stats.packetsReceived}'; - stats['video frames received'] = '${event.stats.framesReceived}'; - stats['video frames decoded'] = '${event.stats.framesDecoded}'; - stats['video frames dropped'] = '${event.stats.framesDropped}'; + stats['frames received'] = '${event.stats.framesReceived}'; + stats['frames decoded'] = '${event.stats.framesDecoded}'; + stats['frames dropped'] = '${event.stats.framesDropped}'; + + this.stats['video']!.addEntries(stats.entries); }); }); } else if (track is LocalAudioTrack) { statsType = StatsType.kLocalAudioSender; listener.on((event) { + Map stats = {}; setState(() { - stats['audio tx'] = '${event.currentBitrate.toInt()} kpbs'; - stats['audio codec'] = - '${event.stats.mimeType}, ${event.stats.clockRate}hz, ${event.stats.channels}ch, pt: ${event.stats.payloadType}'; + stats['tx'] = '${event.currentBitrate.toInt()} kpbs'; + stats['codec'] = + '${event.stats.mimeType!.split('/')[1]}/${event.stats.clockRate}/${event.stats.channels}'; + stats['payload'] = '${event.stats.payloadType}'; + this.stats['audio']!.addEntries(stats.entries); }); }); } else if (track is RemoteAudioTrack) { statsType = StatsType.kRemoteAudioReceiver; listener.on((event) { + Map stats = {}; setState(() { - stats['audio rx'] = '${event.currentBitrate.toInt()} kpbs'; - stats['audio codec'] = - '${event.stats.mimeType}, ${event.stats.clockRate}hz, ${event.stats.channels}ch, pt: ${event.stats.payloadType}'; - stats['audio jitter'] = '${event.stats.jitter} s'; - //stats['audio concealed samples'] = + stats['rx'] = '${event.currentBitrate.toInt()} kpbs'; + stats['codec'] = + '${event.stats.mimeType!.split('/')[1]}/${event.stats.clockRate}/${event.stats.channels}'; + stats['payload'] = '${event.stats.payloadType}'; + stats['jitter'] = '${event.stats.jitter} s'; + //stats['concealed samples'] = // '${event.stats.concealedSamples} / ${event.stats.concealmentEvents}'; - stats['audio packets lost'] = '${event.stats.packetsLost}'; - stats['audio packets received'] = '${event.stats.packetsReceived}'; + stats['packets lost'] = '${event.stats.packetsLost}'; + stats['packets received'] = '${event.stats.packetsReceived}'; + + this.stats['audio']!.addEntries(stats.entries); }); }); } @@ -130,9 +145,14 @@ class _ParticipantStatsWidgetState extends State { vertical: 8, horizontal: 8, ), - child: Column( - children: - stats.entries.map((e) => Text('${e.key}: ${e.value}')).toList()), + child: Column(children: [ + const Text('audio stats', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), + ...stats['audio']!.entries.map((e) => Text('${e.key}: ${e.value}')), + const Text('video stats', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), + ...stats['video']!.entries.map((e) => Text('${e.key}: ${e.value}')), + ]), ); } }