PSDK-1029 - Задержки

This commit is contained in:
Elena Nazarova
2023-11-09 13:40:20 +03:00
committed by Jura Shikin
parent 4ade88a340
commit d8810095ac
15 changed files with 213 additions and 54 deletions
@@ -23,7 +23,7 @@ class SettingsRepository {
late bool isLoop;
late int manifest;
late int playlist;
late int track;
late int chunk;
@@ -44,7 +44,7 @@ class SettingsRepository {
required this.isSubtitlesAvailable,
required this.start,
required this.isLoop,
required this.manifest,
required this.playlist,
required this.track,
required this.chunk,
required this.log
@@ -66,7 +66,7 @@ class SettingsRepository {
isSubtitlesAvailable: true,
start: 0,
isLoop: false,
manifest: 5000,
playlist: 5000,
track: 3000,
chunk: 3000,
log: RepositoryLogType.info
@@ -1,38 +1,106 @@
import 'package:flutter/cupertino.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
class InputView extends StatelessWidget {
class InputView extends StatefulWidget {
final String title;
final int value;
final Function(int)? newSelection;
final TextEditingController _controller = TextEditingController();
InputView(this.title, this.value, this.newSelection, {super.key});
const InputView(this.title, this.value, this.newSelection, {super.key});
@override
State<InputView> createState() => _InputViewState();
}
class _InputViewState extends State<InputView> {
final TextEditingController _controller = TextEditingController();
final FocusNode _nodeText = FocusNode();
late String _textBefore;
@override
Widget build(BuildContext context) {
_controller.text = '$value';
_controller.text = '${widget.value}';
_textBefore = '${widget.value}';
return CupertinoListTile(
key: key,
key: widget.key,
trailing: SizedBox(
width: 155,
child: CupertinoTextField(
minLines: 1,
maxLines: 1,
maxLength: 10,
autocorrect: false,
keyboardType: TextInputType.number,
controller: _controller,
decoration: null,
onSubmitted: (newText) {
var newTime = int.tryParse(newText);
if (newTime != null) {
newSelection?.call(newTime);
}
},
),
width: 155,
height: 30,
child: KeyboardActions(
tapOutsideBehavior: TapOutsideBehavior.translucentDismiss,
config: _buildConfig(context),
child: Container(
padding: const EdgeInsets.only(left: 12),
child: CupertinoTextField(
minLines: 1,
maxLines: 1,
maxLength: 10,
autocorrect: false,
focusNode: _nodeText,
decoration: null,
keyboardType: TextInputType.number,
controller: _controller,
onSubmitted: _onSubmit,
onTapOutside: (_) {
_controller.text = _textBefore;
},
),
),
)
),
title: Text(title, style: const TextStyle(decoration: TextDecoration.none, fontSize: 17))
title: Text(widget.title, style: const TextStyle(decoration: TextDecoration.none, fontSize: 17)));
}
void _onSubmit(String newText) {
_textBefore = newText;
var newTime = int.tryParse(newText);
if (newTime != null) {
widget.newSelection?.call(newTime);
}
}
KeyboardActionsConfig _buildConfig(BuildContext context) {
return KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: CupertinoColors.white,
keyboardSeparatorColor: CupertinoColors.systemGrey4,
nextFocus: false,
actions: [
KeyboardActionsItem(
focusNode: _nodeText,
toolbarButtons: [
(node) {
return TextFieldTapRegion(
child: Container(
width: MediaQuery.of(context).size.width - 14,
margin: const EdgeInsets.symmetric(horizontal: 7),
child: Row(
children: [
CupertinoButton(
padding: const EdgeInsets.all(5),
onPressed: () {
_controller.text = _textBefore;
node.unfocus();
},
child: const Text("Отмена", style: TextStyle(fontWeight: FontWeight.w500))
),
const Spacer(),
CupertinoButton(
padding: const EdgeInsets.all(5),
onPressed: () {
_onSubmit(_controller.text);
node.unfocus();
},
child: const Text("Готово", style: TextStyle(fontWeight: FontWeight.w500))
)
],
),
),
);
}
],
),
],
);
}
}
}
@@ -11,7 +11,14 @@ class PlayerViewBloc extends Bloc<PlayerViewEvent, PlayerViewState> {
final VideoPlayerController controller;
PlayerViewBloc({required Provider provider, required SettingsRepository repository}):
controller = VideoPlayerController.provider(provider)..initialize(params: {'enablePip': true, 'enableAutostart': true}),
controller = VideoPlayerController.provider(provider)
..initialize(params: {'enablePip': true,
'enableAutostart': true,
'timeouts': {
'playlist': repository.playlist,
'track': repository.track,
'chunk': repository.chunk
}}),
super(PlayerViewController.createFromRepository(repository)) {
_onInitialize(repository);
on<DismissEvent>(_onDismissEvent);
@@ -24,6 +24,9 @@ class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
on<SubsChangedEvent>(_onSubsChangedEvent);
on<StartPositionChangedEvent>(_onStartPositionChangedEvent);
on<LoopChangedEvent>(_onLoopChangedEvent);
on<PlaylistTimeoutsChangedEvent>(_onPlaylistTimeoutsChangedEvent);
on<TrackTimeoutsChangedEvent>(_onTrackTimeoutsChangedEvent);
on<ChunkTimeoutsChangedEvent>(_onChunkTimeoutsChangedEvent);
}
_onDismissEvent(DismissEvent event, Emitter<SettingsState> emit) {
@@ -58,6 +61,27 @@ class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
_repository.start = event.value;
}
_onPlaylistTimeoutsChangedEvent(PlaylistTimeoutsChangedEvent event, Emitter<SettingsState> emit) {
final settingsState = state;
if (settingsState is! SettingsInitialState) { return; }
_repository.playlist = event.playlist;
}
_onTrackTimeoutsChangedEvent(TrackTimeoutsChangedEvent event, Emitter<SettingsState> emit) {
final settingsState = state;
if (settingsState is! SettingsInitialState) { return; }
_repository.track = event.track;
}
_onChunkTimeoutsChangedEvent(ChunkTimeoutsChangedEvent event, Emitter<SettingsState> emit) {
final settingsState = state;
if (settingsState is! SettingsInitialState) { return; }
_repository.chunk = event.chunk;
}
_onBrightnessChangedEvent(BrightnessChangedEvent event, Emitter<SettingsState> emit) {}
_onQualityChangedEvent(QualityChangedEvent event, Emitter<SettingsState> emit) {}
_onSubsChangedEvent(SubsChangedEvent event, Emitter<SettingsState> emit) {}
@@ -69,7 +93,19 @@ class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
_onFullscreenChangedEvent(FullscreenChangedEvent event, Emitter<SettingsState> emit) {}
_onColorChangedEvent(ColorChangedEvent event, Emitter<SettingsState> emit) {}
int get currentPosition => _repository.start;
int currentNumericValue(NumericOptionData setting) {
if (setting.key == const Key('PlaybackOptionStartPositionID')) {
return _repository.start;
} else if (setting.key == const Key('TimeoutsOptionManifestID')) {
return _repository.playlist;
} else if (setting.key == const Key('TimeoutsOptionTrackID')) {
return _repository.track;
} else if (setting.key == const Key('TimeoutsOptionChunkID')) {
return _repository.chunk;
} else {
return 0;
}
}
int selectedIndex(OptionDataContainer setting) {
if (setting.key == const Key('PlaybackOptionSpeedID')) {
@@ -68,4 +68,22 @@ class LoopChangedEvent extends SettingsEvent {
class StartPositionChangedEvent extends SettingsEvent {
final int value;
StartPositionChangedEvent(this.value);
}
class PlaylistTimeoutsChangedEvent extends SettingsEvent {
final int playlist;
PlaylistTimeoutsChangedEvent(this.playlist);
}
class TrackTimeoutsChangedEvent extends SettingsEvent {
final int track;
TrackTimeoutsChangedEvent(this.track);
}
class ChunkTimeoutsChangedEvent extends SettingsEvent {
final int chunk;
ChunkTimeoutsChangedEvent(this.chunk);
}
@@ -30,7 +30,7 @@ class SettingsInitialState extends SettingsState {
final bool isLoop;
final int manifest;
final int playlist;
final int track;
final int chunk;
@@ -51,7 +51,7 @@ class SettingsInitialState extends SettingsState {
required this.isSubtitlesAvailable,
required this.start,
required this.isLoop,
required this.manifest,
required this.playlist,
required this.track,
required this.chunk,
required this.log
@@ -76,7 +76,7 @@ class SettingsInitialState extends SettingsState {
bool? isLoop,
int? manifest,
int? playlist,
int? track,
int? chunk,
@@ -97,7 +97,7 @@ class SettingsInitialState extends SettingsState {
isSubtitlesAvailable: isSubtitlesAvailable ?? this.isSubtitlesAvailable,
start: start ?? this.start,
isLoop: isLoop ?? this.isLoop,
manifest: manifest ?? this.manifest,
playlist: playlist ?? this.playlist,
track: track ?? this.track,
chunk: chunk ?? this.chunk,
log: log ?? this.log
@@ -120,7 +120,7 @@ class SettingsInitialState extends SettingsState {
isSubtitlesAvailable: repository.isSubtitlesAvailable,
start: repository.start,
isLoop: repository.isLoop,
manifest: repository.manifest,
playlist: repository.playlist,
track: repository.track,
chunk: repository.chunk,
log: _logType(repository.log)
@@ -309,10 +309,25 @@ class SettingsInitialState extends SettingsState {
static const extraOption = OptionData(key: Key('ExtraOptionLoopID'), title: 'Зацикленность');
static const timeoutsOptions = [
NumericOptionData(key: Key('TimeoutsOptionManifestID'), title: 'Манифест (мс)', value: 5000),
NumericOptionData(key: Key('TimeoutsOptionTrackID'), title: 'Трек (мс)', value: 3000),
NumericOptionData(key: Key('TimeoutsOptionChunkID'), title: 'Сегмент (мс)', value: 3000)
static List<NumericOptionData> timeoutsOptions = [
NumericOptionData(
key: const Key('TimeoutsOptionManifestID'),
title: 'Манифест (мс)',
value: 5000,
onChange: (value) => PlaylistTimeoutsChangedEvent(value)
),
NumericOptionData(
key: const Key('TimeoutsOptionTrackID'),
title: 'Трек (мс)',
value: 3000,
onChange: (value) => TrackTimeoutsChangedEvent(value)
),
NumericOptionData(
key: const Key('TimeoutsOptionChunkID'),
title: 'Сегмент (мс)',
value: 3000,
onChange: (value) => ChunkTimeoutsChangedEvent(value)
)
];
static const logOptions = {
@@ -103,9 +103,7 @@ class SettingsView extends StatelessWidget {
style: const TextStyle(decoration: TextDecoration.none, color: CupertinoColors.systemGrey, fontSize: 13, fontWeight: FontWeight.w400)
),
),
children: <Widget>[...SettingsInitialState.timeoutsOptions.map((setting) {
return InputView(setting.title, setting.value, (_) {});
}).toList()]
children: _buildSettingsWidgets(SettingsInitialState.timeoutsOptions, bloc)
),
// Логирование
@@ -223,9 +221,10 @@ class SettingsView extends StatelessWidget {
} else if (setting is OptionData) {
return ToggleView(setting);
} else if (setting is NumericOptionData) {
final currentValue = bloc.currentNumericValue(setting);
return InputView(
setting.title,
bloc.currentPosition,
currentValue,
(newTime) {
final event = setting.onChange?.call(newTime);
if (event != null) {
+1
View File
@@ -18,6 +18,7 @@ dependencies:
sdk: flutter
flutter_bloc: ^8.0.1
package_info_plus: ^4.1.0
keyboard_actions: ^4.2.0
nut_player:
@@ -117,7 +117,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
}
}
Future<void> initialize({Map<String, bool>? params}) async {
Future<void> initialize({Map<String, Object>? params}) async {
const bool allowBackgroundPlayback = false;
if (!allowBackgroundPlayback && _lifeCycleObserver == null) {
_lifeCycleObserver = _VideoAppLifeCycleObserver(this);
@@ -45,7 +45,7 @@ class NutPlayerAndroidPlatform extends NutPlayerPlatform {
}
@override
Future<PlayerId> create({required PlatformPlayerContent content, Map<String, bool>? params}) async {
Future<PlayerId> create({required PlatformPlayerContent content, Map<String, Object>? params}) async {
final playerId = await _pluginChannel.invokeMethod("pluginCreate", [content.toJson(), params]);
return playerId;
}
@@ -209,7 +209,7 @@ public class NutPlayerIosPlugin: NSObject, FlutterPlugin, NutPlayerViewFactoryDe
let channel = FlutterEventChannel(name: "tech.nut/videoPlayer/videoEvents/\(playerId)",
binaryMessenger: self.registrar.messenger())
let player = PlayerFlutterPlatform(rawContent,
parameters: arguments[safe: 1] as? [String: Bool],
parameters: arguments[safe: 1] as? [String: Any],
playerId: playerId,
channel: channel)
@@ -11,7 +11,7 @@ import NutPlayer
protocol NutPlayerViewFactoryDelegate {
typealias Parameters = [String: Bool]
typealias Parameters = [String: Any]
func playerInstance(for id: Int64) -> NutPlayer?
func playerParams(for id: Int64) -> Parameters?
@@ -42,13 +42,13 @@ final class NutPlayerViewFactory: NSObject, FlutterPlatformViewFactory {
let settings: NutPlayerSkinPlugin.Settings
if let parameters {
settings = NutPlayerSkinPlugin.Settings(
onPip: parameters["enablePip"] ?? false
onPip: parameters["enablePip"] as? Bool ?? false
? { onEnter in
player.pipController.value >>- {
onEnter ? $0.start() : $0.stop()
}
} : nil,
onFullscreen: parameters["enableFullscreen"] ?? false
onFullscreen: parameters["enableFullscreen"] as? Bool ?? false
? { onEnter in
print("Fullscreen")
} : nil
@@ -33,11 +33,11 @@ final class PlayerFlutterPlatform: NSObject, FlutterStreamHandler {
private var cancellable = Set<AnyCancellable>()
private var playerContent: PlayerContent?
let parameters: [String: Bool]?
let parameters: [String: Any]?
// MARK: - Init
init(_ rawContent: String, parameters: [String: Bool]?, playerId: Int64, channel: FlutterEventChannel) {
init(_ rawContent: String, parameters: [String: Any]?, playerId: Int64, channel: FlutterEventChannel) {
self.playerId = playerId
self.channel = channel
@@ -132,12 +132,13 @@ final class PlayerFlutterPlatform: NSObject, FlutterStreamHandler {
let autostart: Bool
if let parameters {
autostart = parameters["enableAutostart"] ?? false
autostart = parameters["enableAutostart"] as? Bool ?? false
} else {
autostart = false
}
player.load(provider: CommonProvider(content: content), autoplay: autostart)
let timeouts = self.createTimeouts(from: parameters)
player.load(provider: CommonProvider(content: content), timeouts: timeouts, autoplay: autostart)
} else {
self.lastEvent = .error
let flutterError = FlutterError(code: "NutPlayerError",
@@ -147,6 +148,20 @@ final class PlayerFlutterPlatform: NSObject, FlutterStreamHandler {
}
}
private func createTimeouts(from params: [String: Any]?) -> PlayerTimeouts {
let timeouts = parameters?["timeouts"] as? [String: Any]
var playlistTimeout = timeouts?["playlist"] as? TimeInterval ?? 5000
if playlistTimeout <= 0 {
playlistTimeout = 5000
}
var trackTimeout = timeouts?["track"] as? TimeInterval ?? 3000
if trackTimeout <= 0 {
trackTimeout = 3000
}
return PlayerTimeouts(playlist: playlistTimeout, track: trackTimeout)
}
// MARK: - Interface
var currentTime: Double { self.player?.currentTime.value ?? 0.0 }
+1 -1
View File
@@ -46,7 +46,7 @@ class NutPlayerIosPlatform extends NutPlayerPlatform {
}
@override
Future<PlayerId> create({required PlatformPlayerContent content, Map<String, bool>? params}) async {
Future<PlayerId> create({required PlatformPlayerContent content, Map<String, Object>? params}) async {
final playerId = await _pluginChannel.invokeMethod("pluginCreate", [content.toJson(), params]);
return playerId;
}
@@ -47,7 +47,7 @@ abstract class NutPlayerPlatform extends PlatformInterface {
}
/// Creates an instance of a video player and returns its PlayerId.
Future<PlayerId> create({required PlatformPlayerContent content, Map<String, bool>? params}) {
Future<PlayerId> create({required PlatformPlayerContent content, Map<String, Object>? params}) {
throw UnimplementedError('create() has not been implemented.');
}