Skip to content

Commit

Permalink
Merge branch 'main' into feature/web-cast-support
Browse files Browse the repository at this point in the history
  • Loading branch information
hawk23 authored Nov 29, 2024
2 parents 57677f4 + 6083522 commit e7b1e6a
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 14 deletions.
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ allprojects {
}

dependencies {
api 'com.bitmovin.player:player:3.94.0+jason'
api 'com.bitmovin.player:player:3.95.0+jason'
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.14.1'
implementation 'androidx.concurrent:concurrent-futures-ktx:1.1.0'
}
36 changes: 26 additions & 10 deletions example/lib/pages/casting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:bitmovin_player/bitmovin_player.dart';
import 'package:bitmovin_player_example/controls.dart';
import 'package:bitmovin_player_example/env/env.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';
Expand Down Expand Up @@ -34,18 +35,20 @@ final SourceConfig _sourceConfig = isIOS
: const SourceConfig(url: artOfMotionDash, type: SourceType.dash);

class _CastingState extends State<Casting> {
factory _CastingState() {
final logger = Logger();
void eventListener(Event event) => _onEvent(logger, event);

return _CastingState._(createPlayerState(_sourceConfig, eventListener));
_CastingState() {
_playerState = createPlayerState(
_sourceConfig,
(Event event) {
_onEvent(logger, event);
},
);
}

_CastingState._(this._playerState);
final logger = Logger();
late final Future<_PlayerState> _playerState;
final _playerInfoKey = GlobalKey<PlayerInfoState>();

final Future<_PlayerState> _playerState;

static Future<_PlayerState> createPlayerState(
Future<_PlayerState> createPlayerState(
SourceConfig sourceConfig,
void Function(Event event) eventListener,
) async {
Expand Down Expand Up @@ -82,10 +85,17 @@ class _CastingState extends State<Casting> {
return _PlayerState(player, castManager);
}

static void _onEvent(
void _onEvent(
Logger logger,
Event event,
) {
_playerState.then(
(state) => _playerInfoKey.currentState?.updatePlayerInfo(
state.player,
event,
),
);

final eventName = '${event.runtimeType}';
final eventData = '$eventName ${event.toJson()}';
logger.d(eventData);
Expand Down Expand Up @@ -166,6 +176,12 @@ class _CastingState extends State<Casting> {
),
],
),
Expanded(
child: Container(
margin: const EdgeInsets.fromLTRB(10, 10, 10, 10),
child: PlayerInfo(key: _playerInfoKey),
),
),
],
);
}
Expand Down
12 changes: 11 additions & 1 deletion example/lib/pages/event_subscription.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -17,6 +18,7 @@ class EventSubscription extends StatefulWidget {

class _EventSubscriptionState extends State<EventSubscription> {
final _eventsKey = GlobalKey<EventsState>();
final _playerInfoKey = GlobalKey<PlayerInfoState>();
final _sourceConfig = SourceConfig(
url: isIOS
? 'https://bitmovin-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8'
Expand All @@ -32,6 +34,8 @@ class _EventSubscriptionState extends State<EventSubscription> {
final _logger = Logger();

void _onEvent(Event event) {
_playerInfoKey.currentState?.updatePlayerInfo(_player, event);

final eventName = '${event.runtimeType}';
final eventData = '$eventName ${event.toJson()}';
_logger.d(eventData);
Expand Down Expand Up @@ -115,10 +119,16 @@ class _EventSubscriptionState extends State<EventSubscription> {
),
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),
),
),
],
),
);
Expand Down
98 changes: 98 additions & 0 deletions example/lib/player_info.dart
Original file line number Diff line number Diff line change
@@ -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<StatefulWidget> createState() => PlayerInfoState();
}

class PlayerInfoState extends State<PlayerInfo> {
final Map<String, dynamic> _data = {};

Future<void> 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<dynamic> 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()),
),
],
),
);
},
);
}
}
4 changes: 2 additions & 2 deletions lib/src/platform/web/player_platform_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ class PlayerPlatformWeb extends PlayerPlatformInterface {
return div;
}

// TODO(mario): implement subtitle tracks API for Web
@override
Future<List<SubtitleTrack>> get availableSubtitles async =>
throw UnimplementedError();
Future<List<SubtitleTrack>> get availableSubtitles async => [];

@override
Future<void> castStop() async => _player.castStop();
Expand Down

0 comments on commit e7b1e6a

Please sign in to comment.