From 435b041eb286b81fa2936f23e8e374e927563394 Mon Sep 17 00:00:00 2001 From: Mario Graf Date: Fri, 29 Nov 2024 14:28:13 +0100 Subject: [PATCH] Introduce player info widget and add it to event subscription sample --- example/lib/pages/event_subscription.dart | 12 ++- example/lib/player_info.dart | 98 +++++++++++++++++++ lib/src/platform/web/player_platform_web.dart | 4 +- 3 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 example/lib/player_info.dart diff --git a/example/lib/pages/event_subscription.dart b/example/lib/pages/event_subscription.dart index 9d18a953..ee306ffc 100644 --- a/example/lib/pages/event_subscription.dart +++ b/example/lib/pages/event_subscription.dart @@ -3,6 +3,7 @@ import 'package:bitmovin_player_example/controls.dart'; import 'package:bitmovin_player_example/env/env.dart'; import 'package:bitmovin_player_example/events.dart'; import 'package:bitmovin_player_example/platform.dart'; +import 'package:bitmovin_player_example/player_info.dart'; import 'package:bitmovin_player_example/player_view_container.dart'; import 'package:flutter/material.dart'; import 'package:logger/logger.dart'; @@ -17,6 +18,7 @@ class EventSubscription extends StatefulWidget { class _EventSubscriptionState extends State { final _eventsKey = GlobalKey(); + final _playerInfoKey = GlobalKey(); final _sourceConfig = SourceConfig( url: isIOS ? 'https://bitmovin-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8' @@ -32,6 +34,8 @@ class _EventSubscriptionState extends State { final _logger = Logger(); void _onEvent(Event event) { + _playerInfoKey.currentState?.updatePlayerInfo(_player, event); + final eventName = '${event.runtimeType}'; final eventData = '$eventName ${event.toJson()}'; _logger.d(eventData); @@ -115,10 +119,16 @@ class _EventSubscriptionState extends State { ), Expanded( child: Container( - margin: const EdgeInsets.fromLTRB(10, 10, 10, 40), + margin: const EdgeInsets.fromLTRB(10, 10, 10, 10), child: Events(key: _eventsKey), ), ), + Expanded( + child: Container( + margin: const EdgeInsets.fromLTRB(10, 10, 10, 10), + child: PlayerInfo(key: _playerInfoKey), + ), + ), ], ), ); diff --git a/example/lib/player_info.dart b/example/lib/player_info.dart new file mode 100644 index 00000000..be4220ac --- /dev/null +++ b/example/lib/player_info.dart @@ -0,0 +1,98 @@ +import 'package:bitmovin_player/bitmovin_player.dart'; +import 'package:flutter/material.dart'; + +/// Maintains a table of player information that is updated dynamically based +/// on received player events. Player state only gets added to the table if at +/// least one event has been received for the corresponding state. +class PlayerInfo extends StatefulWidget { + const PlayerInfo({super.key}); + + @override + State createState() => PlayerInfoState(); +} + +class PlayerInfoState extends State { + final Map _data = {}; + + Future updatePlayerInfo(Player player, Event event) async { + if (event is ReadyEvent) { + final isLive = await player.isLive; + _updatePlayerInfoForField('isLive', Future.value(isLive)); + + final availableSubtitles = await player.availableSubtitles; + final subtitleLanguages = availableSubtitles.map((element) { + return element.label; + }).toList(); + + if (subtitleLanguages.isNotEmpty) { + _updatePlayerInfoForField( + 'availableSubtitles', + Future.value( + subtitleLanguages.join(', '), + ), + ); + } + + if (isLive) { + _updatePlayerInfoForField('maxTimeShift', player.maxTimeShift); + } else { + _updatePlayerInfoForField('duration', player.duration); + } + } + if (event is PlayingEvent || event is PausedEvent) { + _updatePlayerInfoForField('isPlaying', player.isPlaying); + } + if (event is TimeChangedEvent) { + _updatePlayerInfoForField('currentTime', player.currentTime); + final isLive = await player.isLive; + if (isLive) { + _updatePlayerInfoForField('timeShift', player.timeShift); + } + } + if (event is SubtitleChangedEvent) { + final subtitle = await player.subtitle; + _updatePlayerInfoForField('subtitle', Future.value(subtitle.label)); + } + if (event is CastAvailableEvent) { + _updatePlayerInfoForField('isCastAvailable', player.isCastAvailable); + } + if (event is CastStartedEvent || event is CastStoppedEvent) { + _updatePlayerInfoForField('isCasting', player.isCasting); + } + } + + void _updatePlayerInfoForField(String field, Future value) { + value.then((dynamic value) { + setState(() { + _data[field] = value.toString(); + }); + }); + } + + @override + Widget build(BuildContext context) { + return ListView.builder( + itemCount: _data.length, + itemBuilder: (context, index) { + final key = _data.keys.elementAt(index); + final value = _data[key]; + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 1, horizontal: 1), + child: Row( + children: [ + Expanded( + flex: 2, + child: Text(key), + ), + Expanded( + flex: 3, + child: Text(value.toString()), + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/src/platform/web/player_platform_web.dart b/lib/src/platform/web/player_platform_web.dart index 1a6495a7..c3ed1056 100644 --- a/lib/src/platform/web/player_platform_web.dart +++ b/lib/src/platform/web/player_platform_web.dart @@ -41,9 +41,9 @@ class PlayerPlatformWeb extends PlayerPlatformInterface { return div; } + // TODO(mario): implement subtitle tracks API for Web @override - Future> get availableSubtitles async => - throw UnimplementedError(); + Future> get availableSubtitles async => []; @override Future castStop() async => _player.castStop();