Skip to content

Commit

Permalink
Feature/january changes 7 (jhomlala#259)
Browse files Browse the repository at this point in the history
* Fixed fullscreen dispose issue.

* Added VideoFormat support

* Added VideoFormat support

* Added retry feature

* Changed BetterPlayerEventType.openFullscreen and BetterPlayerEventType.hideFullscreen events behavior

* Removed closed caption support from original video_player codebase

* Format, DA fixes

* Better Player 0.0.49

Co-authored-by: jhomlala <j.homlala@gmail.com>
  • Loading branch information
jhomlala and jhomlala authored Jan 31, 2021
1 parent 6b2bda6 commit 68188ad
Show file tree
Hide file tree
Showing 19 changed files with 214 additions and 281 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## 0.0.49
* Fixed fullscreen dispose issue.
* Added videoFormat parameter in BetterPlayerDataSource (should be used when data source url has no extension).
* Added retry feature after video failed to load.
* Added enableRetry in BetterPlayerControlsConfiguration.
* Changed BetterPlayerEventType.openFullscreen and BetterPlayerEventType.hideFullscreen events behavior (now events trigger after route change).
* Removed closed caption support from original video_player codebase.
* Fixed chinese translation typo (fixed by https://github.com/Big7lion)

## 0.0.48
* Fixed loading large videos in iOS.
* Fixed partly progress bar jumping when seek issue in iOS.
Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ This plugin is based on [Chewie](https://github.com/brianegan/chewie). Chewie is

```yaml
dependencies:
better_player: ^0.0.48
better_player: ^0.0.49
```
2. Install it
Expand Down Expand Up @@ -607,6 +607,18 @@ Possible configuration options:
///Optional cache configuration, used only for network data sources
final BetterPlayerCacheConfiguration cacheConfiguration;
///List of bytes, used only in memory player
final List<int> bytes;
///Configuration of remote controls notification
final BetterPlayerNotificationConfiguration notificationConfiguration;
///Duration which will be returned instead of original duration
final Duration overriddenDuration;
///Video format hint when data source url has not valid extension.
final BetterPlayerVideoFormat videoFormat;
```


Expand Down
3 changes: 1 addition & 2 deletions example/lib/pages/normal_player_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ class _NormalPlayerPageState extends State<NormalPlayerPage> {
);
BetterPlayerDataSource dataSource = BetterPlayerDataSource(
BetterPlayerDataSourceType.network,
Constants.forBiggerBlazesUrl,
//overriddenDuration: Duration(seconds: 5),
Constants.bugBuckBunnyVideoUrl,
);
_betterPlayerController = BetterPlayerController(betterPlayerConfiguration);
_betterPlayerController.setupDataSource(dataSource);
Expand Down
64 changes: 49 additions & 15 deletions ios/Classes/FLTBetterPlayerPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ @interface FLTBetterPlayer : NSObject <FlutterTexture, FlutterStreamHandler, AVP
@property(nonatomic) AVPlayerLayer* _playerLayer;
@property(nonatomic) bool _pictureInPicture;
@property(nonatomic) bool _observersAdded;
@property(nonatomic) int stalledCount;
- (void)play;
- (void)pause;
- (void)setIsLooping:(bool)isLooping;
Expand All @@ -66,6 +67,7 @@ - (int64_t) duration;
- (int64_t) position;
@end


static void* timeRangeContext = &timeRangeContext;
static void* statusContext = &statusContext;
static void* playbackLikelyToKeepUpContext = &playbackLikelyToKeepUpContext;
Expand Down Expand Up @@ -106,6 +108,7 @@ - (void)addObservers:(AVPlayerItem*)item {
if (!self._observersAdded){
[item addObserver:self forKeyPath:@"loadedTimeRanges" options:0 context:timeRangeContext];
[item addObserver:self forKeyPath:@"status" options:0 context:statusContext];
[_player addObserver:self forKeyPath:@"rate" options:0 context:nil];
[item addObserver:self
forKeyPath:@"playbackLikelyToKeepUp"
options:0
Expand All @@ -122,8 +125,10 @@ - (void)addObservers:(AVPlayerItem*)item {
selector:@selector(itemDidPlayToEndTime:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:item];
///Currently disabled, because it leads to unexpected problems
//[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackStalled:) name:AVPlayerItemPlaybackStalledNotification object:item ];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(itemFailedToPlayToEndTime:)
name:AVPlayerItemFailedToPlayToEndTimeNotification
object:item];
self._observersAdded = true;
}
}
Expand Down Expand Up @@ -175,14 +180,13 @@ - (void) removeObservers{
[[_player currentItem] removeObserver:self
forKeyPath:@"playbackBufferFull"
context:playbackBufferFullContext];
///Currently disabled
///[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self];
self._observersAdded = false;
}
}

- (void)itemDidPlayToEndTime:(NSNotification*)notification {

if (_isLooping) {
AVPlayerItem* p = [notification object];
[p seekToTime:kCMTimeZero completionHandler:nil];
Expand All @@ -195,6 +199,7 @@ - (void)itemDidPlayToEndTime:(NSNotification*)notification {
}
}


static inline CGFloat radiansToDegrees(CGFloat radians) {
// Input range [-pi, pi] or [-180, 180]
CGFloat degrees = GLKMathRadiansToDegrees((float)radians);
Expand Down Expand Up @@ -313,6 +318,7 @@ - (void)setDataSourceURL:(NSURL*)url withKey:(NSString*)key withHeaders:(NSDicti

- (void)setDataSourcePlayerItem:(AVPlayerItem*)item withKey:(NSString*)key{
_key = key;
_stalledCount =0;
[_player replaceCurrentItemWithPlayerItem:item];

AVAsset* asset = [item asset];
Expand Down Expand Up @@ -348,20 +354,47 @@ - (void)setDataSourcePlayerItem:(AVPlayerItem*)item withKey:(NSString*)key{
[self addObservers:item];
}

- (void)playbackStalled:(NSNotification *)notification {
if (_eventSink != nil) {
_eventSink([FlutterError
errorWithCode:@"VideoError"
message:@"Failed to load video: playback stalled"
details:nil]);
-(void)handleStalled {
if (_player.currentItem.playbackLikelyToKeepUp ||
[self availableDuration] - CMTimeGetSeconds(_player.currentItem.currentTime) > 10.0) {
[self play];
} else {
_stalledCount++;
if (_stalledCount > 5){
_eventSink([FlutterError
errorWithCode:@"VideoError"
message:@"Failed to load video: playback stalled"
details:nil]);
return;
}
[self performSelector:@selector(handleStalled) withObject:nil afterDelay:1];
}
}

- (NSTimeInterval) availableDuration
{
NSArray *loadedTimeRanges = [[_player currentItem] loadedTimeRanges];
CMTimeRange timeRange = [[loadedTimeRanges objectAtIndex:0] CMTimeRangeValue];
Float64 startSeconds = CMTimeGetSeconds(timeRange.start);
Float64 durationSeconds = CMTimeGetSeconds(timeRange.duration);
NSTimeInterval result = startSeconds + durationSeconds;
return result;
}

- (void)observeValueForKeyPath:(NSString*)path
ofObject:(id)object
change:(NSDictionary*)change
context:(void*)context {

if ([path isEqualToString:@"rate"]) {
if (_player.rate == 0 && //if player rate dropped to 0
CMTIME_COMPARE_INLINE(_player.currentItem.currentTime, >, kCMTimeZero) && //if video was started
CMTIME_COMPARE_INLINE(_player.currentItem.currentTime, <, _player.currentItem.duration) && //but not yet finished
_isPlaying) { //instance variable to handle overall state (changed to YES when user triggers playback)
[self handleStalled];
}
}

if (context == timeRangeContext) {
if (_eventSink != nil) {
NSMutableArray<NSArray<NSNumber*>*>* values = [[NSMutableArray alloc] init];
Expand Down Expand Up @@ -475,6 +508,7 @@ - (void)onReadyToPlay {
}

- (void)play {
_stalledCount = 0;
_isPlaying = true;
[self updatePlayingState];
}
Expand All @@ -489,7 +523,7 @@ - (int64_t)position {
}

- (int64_t)absolutePosition {
return FLTNSTimeIntervalToMillis([[[_player currentItem] currentDate] timeIntervalSince1970]);
return FLTNSTimeIntervalToMillis([[[_player currentItem] currentDate] timeIntervalSince1970]);
}

- (int64_t)duration {
Expand All @@ -512,16 +546,16 @@ - (void)seekTo:(int)location {
if (wasPlaying){
[_player pause];
}

[_player seekToTime:CMTimeMake(location, 1000)
toleranceBefore:kCMTimeZero
toleranceAfter:kCMTimeZero
completionHandler:^(BOOL finished){
completionHandler:^(BOOL finished){
if (wasPlaying){
[self->_player play];
}
}];

}

- (void)setIsLooping:(bool)isLooping {
Expand Down Expand Up @@ -1093,7 +1127,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
} else if ([@"position" isEqualToString:call.method]) {
result(@([player position]));
} else if ([@"absolutePosition" isEqualToString:call.method]) {
result(@([player absolutePosition]));
result(@([player absolutePosition]));
} else if ([@"seekTo" isEqualToString:call.method]) {
[player seekTo:[argsMap[@"location"] intValue]];
result(nil);
Expand Down
1 change: 1 addition & 0 deletions lib/better_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export 'src/configuration/better_player_event_type.dart';
export 'src/configuration/better_player_notification_configuration.dart';
export 'src/configuration/better_player_theme.dart';
export 'src/configuration/better_player_translations.dart';
export 'src/configuration/better_player_video_format.dart';
export 'src/controls/better_player_overflow_menu_item.dart';
export 'src/controls/better_player_progress_colors.dart';
export 'src/core/better_player.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ class BetterPlayerControlsConfiguration {
///Flag used to show/hide PiP mode
final bool enablePip;

///Flag used to enable/disable retry feature
final bool enableRetry;

///Custom items of overflow menu
final List<BetterPlayerOverflowMenuItem> overflowMenuCustomItems;

Expand Down Expand Up @@ -178,6 +181,7 @@ class BetterPlayerControlsConfiguration {
this.enableSubtitles = true,
this.enableQualities = true,
this.enablePip = true,
this.enableRetry = true,
this.overflowMenuCustomItems = const [],
this.overflowMenuIcon = Icons.more_vert,
this.pipMenuIcon = Icons.picture_in_picture,
Expand Down
5 changes: 5 additions & 0 deletions lib/src/configuration/better_player_data_source.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Project imports:
import 'package:better_player/src/configuration/better_player_data_source_type.dart';
import 'package:better_player/src/configuration/better_player_notification_configuration.dart';
import 'package:better_player/src/configuration/better_player_video_format.dart';
import 'package:better_player/src/subtitles/better_player_subtitles_source.dart';

import 'better_player_cache_configuration.dart';
Expand Down Expand Up @@ -49,6 +50,9 @@ class BetterPlayerDataSource {
///Duration which will be returned instead of original duration
final Duration overriddenDuration;

///Video format hint when data source url has not valid extension.
final BetterPlayerVideoFormat videoFormat;

BetterPlayerDataSource(
this.type,
this.url, {
Expand All @@ -64,6 +68,7 @@ class BetterPlayerDataSource {
this.notificationConfiguration =
const BetterPlayerNotificationConfiguration(showNotification: false),
this.overriddenDuration,
this.videoFormat,
}) : assert(
((type == BetterPlayerDataSourceType.network ||
type == BetterPlayerDataSourceType.file) &&
Expand Down
5 changes: 5 additions & 0 deletions lib/src/configuration/better_player_translations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ class BetterPlayerTranslations {
final String generalDefaultError;
final String generalNone;
final String generalDefault;
final String generalRetry;
final String playlistLoadingNextVideo;
final String controlsLive;
final String controlsNextVideoIn;
Expand All @@ -16,6 +17,7 @@ class BetterPlayerTranslations {
this.generalDefaultError = "Video can't be played",
this.generalNone = "None",
this.generalDefault = "Default",
this.generalRetry = "Retry",
this.playlistLoadingNextVideo = "Loading next video",
this.controlsLive = "LIVE",
this.controlsNextVideoIn = "Next video in",
Expand All @@ -29,6 +31,7 @@ class BetterPlayerTranslations {
generalDefaultError: "Video nie może zostać odtworzone",
generalNone: "Brak",
generalDefault: "Domyślne",
generalRetry: "Spróbuj ponownie",
playlistLoadingNextVideo: "Ładowanie następnego filmu",
controlsNextVideoIn: "Następne video za",
overflowMenuPlaybackSpeed: "Szybkość odtwarzania",
Expand All @@ -41,6 +44,7 @@ class BetterPlayerTranslations {
generalDefaultError: "无法播放视频",
generalNone: "没有",
generalDefault: "默认",
generalRetry: "重試",
playlistLoadingNextVideo: "正在加载下一个视频",
controlsLive: "直播",
controlsNextVideoIn: "下一部影片",
Expand All @@ -54,6 +58,7 @@ class BetterPlayerTranslations {
generalDefaultError: "वीडियो नहीं चलाया जा सकता",
generalNone: "कोई नहीं",
generalDefault: "चूक",
generalRetry: "पुनः प्रयास करें",
playlistLoadingNextVideo: "अगला वीडियो लोड हो रहा है",
controlsLive: "लाइव",
controlsNextVideoIn: "में अगला वीडियो",
Expand Down
6 changes: 6 additions & 0 deletions lib/src/configuration/better_player_video_format.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
enum BetterPlayerVideoFormat {
dash,
hls,
ss,
other,
}
18 changes: 16 additions & 2 deletions lib/src/controls/better_player_cupertino_controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ class _BetterPlayerCupertinoControlsState
_betterPlayerController = BetterPlayerController.of(context);

if (_latestValue?.hasError == true) {
return _buildErrorWidget();
return Container(
color: Colors.black,
child: _buildErrorWidget(),
);
}

final backgroundColor = _controlsConfiguration.controlBarColor;
Expand Down Expand Up @@ -687,6 +690,7 @@ class _BetterPlayerCupertinoControlsState
return _betterPlayerController.errorBuilder(context,
_betterPlayerController.videoPlayerController.value.errorDescription);
} else {
final textStyle = TextStyle(color: _controlsConfiguration.textColor);
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
Expand All @@ -698,8 +702,18 @@ class _BetterPlayerCupertinoControlsState
),
Text(
_betterPlayerController.translations.generalDefaultError,
style: TextStyle(color: _controlsConfiguration.textColor),
style: textStyle,
),
if (_controlsConfiguration.enableRetry)
FlatButton(
onPressed: () {
_betterPlayerController.retryDataSource();
},
child: Text(
_betterPlayerController.translations.generalRetry,
style: textStyle.copyWith(fontWeight: FontWeight.bold),
),
)
],
),
);
Expand Down
18 changes: 16 additions & 2 deletions lib/src/controls/better_player_material_controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ class _BetterPlayerMaterialControlsState
Widget build(BuildContext context) {
_wasLoading = isLoading(_latestValue);
if (_latestValue?.hasError == true) {
return _buildErrorWidget();
return Container(
color: Colors.black,
child: _buildErrorWidget(),
);
}
return MouseRegion(
onHover: (_) {
Expand Down Expand Up @@ -137,6 +140,7 @@ class _BetterPlayerMaterialControlsState
return _betterPlayerController.errorBuilder(context,
_betterPlayerController.videoPlayerController.value.errorDescription);
} else {
final textStyle = TextStyle(color: _controlsConfiguration.textColor);
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
Expand All @@ -148,8 +152,18 @@ class _BetterPlayerMaterialControlsState
),
Text(
_betterPlayerController.translations.generalDefaultError,
style: TextStyle(color: _controlsConfiguration.textColor),
style: textStyle,
),
if (_controlsConfiguration.enableRetry)
FlatButton(
onPressed: () {
_betterPlayerController.retryDataSource();
},
child: Text(
_betterPlayerController.translations.generalRetry,
style: textStyle.copyWith(fontWeight: FontWeight.bold),
),
)
],
),
);
Expand Down
Loading

0 comments on commit 68188ad

Please sign in to comment.