Skip to content

Commit

Permalink
Feature/may changes 3 (jhomlala#497)
Browse files Browse the repository at this point in the history
* Segmented ASMS subtitles loading

* Skip parsing subtitle files with no cues (jhomlala#492)

* Fix parsing subtitle timestamps with no hour component (jhomlala#491)

Co-authored-by: Jakub <jhomlala@gmail.com>

* General refactor, updated documentation and changelog

* General refactor, updated documentation and changelog

* Added spanish translation (jhomlala#494)

* Added spanish translation

* Changed "EN VIVO" for "EN DIRECTO"

Co-authored-by: Koldo <kolod@byvapps.com>

* Lint fix, format, updated dependencies, general refactor

* Lint fix, format, updated dependencies, general refactor

* Updated changelog and normal player page

* Updated iOS example configuration, updated changelog

* Updated iOS example configuration, updated changelog

* Fixed iOS Picture in Picture play/pause state

* Format

* Readme and changelog update

Co-authored-by: Alex Page <50582909+trms-alex@users.noreply.github.com>
Co-authored-by: Koldo <koldoru92@gmail.com>
Co-authored-by: Koldo <kolod@byvapps.com>
  • Loading branch information
4 people authored May 22, 2021
1 parent ab1e080 commit c6f0b48
Show file tree
Hide file tree
Showing 18 changed files with 266 additions and 66 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## 0.0.68
* Added support for segmented subtitles.
* Added new fields in in BetterPlayerSubtitlesSource: `asmsIsSegmented`, `asmsSegmentsTime` and ` asmsSegments`. These fields shouldn't be configured
manually.
* Fixed parsing VTT subtitle timestamps with no hour component (by https://github.com/trms-alex).
* Fixed parsing VTT subtitles when there's no subtitles in the file (by https://github.com/trms-alex).
* Added ES translations (by https://github.com/koldo92).
* Fixed iOS Picture in Picture play/pause state.
* Updated dependencies.
* Updated iOS example configuration.

## 0.0.67
* Added support for DASH adaptive stream subtitles, audio tracks, tracks (by https://github.com/adrianByv)
* [BREAKING_CHANGE] Changed useHlsSubtitles, useHlsTracks, useHlsAudio to useAsmsSubtitles, useAsmsTracks, useAsmsAudio.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ This plugin is based on [Chewie](https://github.com/brianegan/chewie). Chewie is
✔️ HTTP Headers support
✔️ BoxFit of video support
✔️ Playback speed support
✔️ HLS support (track, subtitles, audio track selection)
✔️ HLS support (track, subtitles (also segmented), audio track selection)
✔️ DASH support (track, subtitles, audio track selection)
✔️ Alternative resolution support
✔️ Cache support
Expand All @@ -95,7 +95,7 @@ This plugin is based on [Chewie](https://github.com/brianegan/chewie). Chewie is

```yaml
dependencies:
better_player: ^0.0.67
better_player: ^0.0.68
```
2. Install it
Expand Down
4 changes: 4 additions & 0 deletions example/ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>NSBonjourServices</key>
<array>
<string>_dartobservatory._tcp</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
Expand Down
2 changes: 2 additions & 0 deletions example/lib/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,6 @@ class Constants {
"https://img.webmd.com/dtmcms/live/webmd/consumer_assets/site_images/article_thumbnails/other/cat_relaxing_on_patio_other/1800x1200_cat_relaxing_on_patio_other.jpg";
static String dashStreamUrl =
"https://bitmovin-a.akamaihd.net/content/sintel/sintel.mpd";
static String segmentedSubtitlesHlsUrl =
"https://eng-demo.cablecast.tv/segmented-captions/vod.m3u8";
}
39 changes: 10 additions & 29 deletions example/lib/pages/normal_player_page.dart
Original file line number Diff line number Diff line change
@@ -1,32 +1,14 @@
import 'package:better_player/better_player.dart';
import 'package:better_player_example/constants.dart';
import 'package:better_player_example/utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class NormalPlayerPage extends StatefulWidget {
@override
_NormalPlayerPageState createState() => _NormalPlayerPageState();
}

class _NormalPlayerPageState extends State<NormalPlayerPage> {
late BetterPlayerController _betterPlayerController;

@override
void initState() {
BetterPlayerConfiguration betterPlayerConfiguration =
BetterPlayerConfiguration(
aspectRatio: 16 / 9,
fit: BoxFit.contain,
autoPlay: true,
);
_betterPlayerController = BetterPlayerController(betterPlayerConfiguration);
_betterPlayerController.setupDataSource(BetterPlayerDataSource(
BetterPlayerDataSourceType.network,
Constants.bugBuckBunnyVideoUrl,
));
super.initState();
}

@override
Widget build(BuildContext context) {
return Scaffold(
Expand All @@ -45,16 +27,15 @@ class _NormalPlayerPageState extends State<NormalPlayerPage> {
),
AspectRatio(
aspectRatio: 16 / 9,
child: BetterPlayer(controller: _betterPlayerController),
),
ElevatedButton(
child: Text("Play file data source"),
onPressed: () async {
String url = await Utils.getFileUrl(Constants.fileTestVideoUrl);
BetterPlayerDataSource dataSource =
BetterPlayerDataSource(BetterPlayerDataSourceType.file, url);
_betterPlayerController.setupDataSource(dataSource);
},
child: BetterPlayer.network(
Constants.forBiggerBlazesUrl,
betterPlayerConfiguration: BetterPlayerConfiguration(
deviceOrientationsAfterFullScreen: [
DeviceOrientation.portraitUp
],
aspectRatio: 16 / 9,
fullScreenAspectRatio: 16 / 9),
),
),
],
),
Expand Down
22 changes: 21 additions & 1 deletion ios/Classes/FLTBetterPlayerPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ @interface FLTBetterPlayer : NSObject <FlutterTexture, FlutterStreamHandler, AVP
@property(nonatomic) int stalledCount;
@property(nonatomic) bool isStalledCheckStarted;
@property(nonatomic) float playerRate;
@property(nonatomic) AVPlayerTimeControlStatus lastAvPlayerTimeControlStatus;
- (void)play;
- (void)pause;
- (void)setIsLooping:(bool)isLooping;
Expand Down Expand Up @@ -407,6 +408,25 @@ - (void)observeValueForKeyPath:(NSString*)path
context:(void*)context {

if ([path isEqualToString:@"rate"]) {
if (@available(iOS 10.0, *)) {
if (_pipController.pictureInPictureActive == true){
if (_lastAvPlayerTimeControlStatus != [NSNull null] && _lastAvPlayerTimeControlStatus == _player.timeControlStatus){
return;
}

if (_player.timeControlStatus == AVPlayerTimeControlStatusPaused){
_lastAvPlayerTimeControlStatus = _player.timeControlStatus;
_eventSink(@{@"event" : @"pause"});
return;

}
if (_player.timeControlStatus == AVPlayerTimeControlStatusPlaying){
_lastAvPlayerTimeControlStatus = _player.timeControlStatus;
_eventSink(@{@"event" : @"play"});
}
}
}

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
Expand Down Expand Up @@ -479,14 +499,14 @@ - (void)observeValueForKeyPath:(NSString*)path

- (void)updatePlayingState {
if (!_isInitialized || !_key) {
NSLog(@"not initalized and paused!!");
_displayLink.paused = YES;
return;
}
if (!self._observersAdded){
[self addObservers:[_player currentItem]];
}


if (_isPlaying) {
if (@available(iOS 10.0, *)) {
[_player playImmediatelyAtRate:1.0];
Expand Down
30 changes: 23 additions & 7 deletions lib/src/asms/better_player_asms_subtitle.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:better_player/src/asms/better_player_asms_subtitle_segment.dart';

///Representation of HLS / DASH subtitle element.
class BetterPlayerAsmsSubtitle {
///Language of the subtitle
Expand All @@ -18,11 +20,25 @@ class BetterPlayerAsmsSubtitle {
///Urls of specific files
final List<String>? realUrls;

BetterPlayerAsmsSubtitle(
{this.language,
this.name,
this.mimeType,
this.segmentAlignment,
this.url,
this.realUrls});
///Should subtitles be loaded with segments.
final bool? isSegmented;

///Max value between segments. In HLS defined as #EXT-X-TARGETDURATION.
///Only used when [isSegmented] is true.
final int? segmentsTime;

///List of subtitle segments. Only used when [isSegmented] is true.
final List<BetterPlayerAsmsSubtitleSegment>? segments;

BetterPlayerAsmsSubtitle({
this.language,
this.name,
this.mimeType,
this.segmentAlignment,
this.url,
this.realUrls,
this.isSegmented,
this.segmentsTime,
this.segments,
});
}
15 changes: 15 additions & 0 deletions lib/src/asms/better_player_asms_subtitle_segment.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
///Class which represents one segment of subtitles. It consists of start time
///and end time which are relative from start of the video and real url of the
///video (with domain and all paths).
class BetterPlayerAsmsSubtitleSegment {
///Start of the subtitles counting from the start of the video.
final Duration startTime;

///End of the subtitles counting from the start of the video.
final Duration endTime;

///Real url of the subtitles (with all domains and paths).
final String realUrl;

BetterPlayerAsmsSubtitleSegment(this.startTime, this.endTime, this.realUrl);
}
15 changes: 15 additions & 0 deletions lib/src/configuration/better_player_translations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,19 @@ class BetterPlayerTranslations {
overflowMenuAudioTracks: "Âm thanh",
qualityAuto: "Tự động",
);

factory BetterPlayerTranslations.spanish() => BetterPlayerTranslations(
languageCode: "es",
generalDefaultError: "No se puede reproducir el video",
generalNone: "Ninguno",
generalDefault: "Por defecto",
generalRetry: "Reintentar",
controlsLive: "EN DIRECTO",
playlistLoadingNextVideo: "Cargando siguiente video",
controlsNextVideoIn: "Siguiente video en",
overflowMenuPlaybackSpeed: "Velocidad",
overflowMenuSubtitles: "Subtítulos",
overflowMenuQuality: "Calidad",
qualityAuto: "Automática",
);
}
82 changes: 78 additions & 4 deletions lib/src/core/better_player_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,12 @@ class BetterPlayerController {
Stream<BetterPlayerControllerEvent> get controllerEventStream =>
_controllerEventStreamController.stream;

///Flag which determines whether are ASMS segments loading
bool _asmsSegmentsLoading = false;

///List of loaded ASMS segments
final List<String> _asmsSegmentsLoaded = [];

BetterPlayerController(
this.betterPlayerConfiguration, {
this.betterPlayerPlaylistConfiguration,
Expand Down Expand Up @@ -309,9 +315,13 @@ class BetterPlayerController {
asmsSubtitles.forEach((BetterPlayerAsmsSubtitle asmsSubtitle) {
_betterPlayerSubtitlesSourceList.add(
BetterPlayerSubtitlesSource(
type: BetterPlayerSubtitlesSourceType.network,
name: asmsSubtitle.name,
urls: asmsSubtitle.realUrls),
type: BetterPlayerSubtitlesSourceType.network,
name: asmsSubtitle.name,
urls: asmsSubtitle.realUrls,
asmsIsSegmented: asmsSubtitle.isSegmented,
asmsSegmentsTime: asmsSubtitle.segmentsTime,
asmsSegments: asmsSubtitle.segments,
),
);
});
}
Expand All @@ -327,12 +337,20 @@ class BetterPlayerController {
}
}

///Setup subtitles to be displayed from given subtitle source
///Setup subtitles to be displayed from given subtitle source.
///If subtitles source is segmented then don't load videos at start. Videos
///will load with just in time policy.
Future<void> setupSubtitleSource(BetterPlayerSubtitlesSource subtitlesSource,
{bool sourceInitialize = false}) async {
_betterPlayerSubtitlesSource = subtitlesSource;
subtitlesLines.clear();
_asmsSegmentsLoaded.clear();
_asmsSegmentsLoading = false;

if (subtitlesSource.type != BetterPlayerSubtitlesSourceType.none) {
if (subtitlesSource.asmsIsSegmented == true) {
return;
}
final subtitlesParsed =
await BetterPlayerSubtitlesFactory.parseSubtitles(subtitlesSource);
subtitlesLines.addAll(subtitlesParsed);
Expand All @@ -344,6 +362,56 @@ class BetterPlayerController {
}
}

///Load ASMS subtitles segments for given [position].
///Segments are being loaded within range (current video position;endPosition)
///where endPosition is based on time segment detected in HLS playlist. If
///time segment is not present then 5000 ms will be used. Also time segment
///is multiplied by 5 to increase window of duration.
///Segments are also cached, so same segment won't load twice. Only one
///pack of segments can be load at given time.
Future _loadAsmsSubtitlesSegments(Duration position) async {
try {
if (_asmsSegmentsLoading) {
return;
}
_asmsSegmentsLoading = true;
final BetterPlayerSubtitlesSource? source = _betterPlayerSubtitlesSource;
final Duration loadDurationEnd = Duration(
milliseconds: position.inMilliseconds +
5 * (_betterPlayerSubtitlesSource?.asmsSegmentsTime ?? 5000));

final segmentsToLoad = _betterPlayerSubtitlesSource?.asmsSegments
?.where((segment) {
return segment.startTime > position &&
segment.endTime < loadDurationEnd &&
!_asmsSegmentsLoaded.contains(segment.realUrl);
})
.map((segment) => segment.realUrl)
.toList();

if (segmentsToLoad != null && segmentsToLoad.isNotEmpty) {
final subtitlesParsed =
await BetterPlayerSubtitlesFactory.parseSubtitles(
BetterPlayerSubtitlesSource(
type: _betterPlayerSubtitlesSource!.type,
headers: _betterPlayerSubtitlesSource!.headers,
urls: segmentsToLoad,
));

///Additional check if current source of subtitles is same as source
///used to start loading subtitles. It can be different when user
///changes subtitles and there was already pending load.
if (source == _betterPlayerSubtitlesSource) {
subtitlesLines.addAll(subtitlesParsed);
_asmsSegmentsLoaded.addAll(segmentsToLoad);
}
}
_asmsSegmentsLoading = false;
} catch (exception) {
BetterPlayerUtils.log("Load ASMS subtitle segments failed: $exception");
}
}

///Get VideoFormat from BetterPlayerVideoFormat (adapter method which translates
///to video_player supported format).
VideoFormat? _getVideoFormat(
Expand Down Expand Up @@ -698,6 +766,10 @@ class BetterPlayerController {
videoPlayerController?.refresh();
}

if (_betterPlayerSubtitlesSource?.asmsIsSegmented == true) {
_loadAsmsSubtitlesSegments(currentVideoPlayerValue.position);
}

final int now = DateTime.now().millisecondsSinceEpoch;
if (now - _lastPositionSelection > 500) {
_lastPositionSelection = now;
Expand Down Expand Up @@ -876,6 +948,8 @@ class BetterPlayerController {
return BetterPlayerTranslations.turkish();
case "vi":
return BetterPlayerTranslations.vietnamese();
case "es":
return BetterPlayerTranslations.spanish();
default:
return BetterPlayerTranslations();
}
Expand Down
Loading

0 comments on commit c6f0b48

Please sign in to comment.