Skip to content

Commit d8fb722

Browse files
jhomlalapinguluktinusneethlingcreativeblaqdanielz-nenda
authored
Feature/august changes (#649)
* Added licenseUrl support for iOS DRM. * Fixed RTL text direction issue in player controls. * Updated normal player page * Added currently displayed subtitle * Added additional check in `postControllerEvent` to handle scenario where event stream is closed. * Updated ExoPlayer version * Buffering fix in Android, video list update * * Added `setMixWithOthers` method in `BetterPlayerListVideoPlayerController`. * Fixed broken ling in cover page of documentatio * Fixed progress bar issue where position could be set above video duration * Fixed iOS remote notification command issu * Update welcome_page.dart (#607) Removed duplicated page * Removed duplicated page in example app (by https://github.com/pinguluk) * Added support for ClearKey DRM for File Datasource. (#566) * Added support for ClearKey DRM for File Datasource. * Formatting of ClearKey implementation code. * Added ClearKey support for BetterPlayerDataSourceType.network and BetterPlayerDataSourceType.memory for Android. * Added ClearKey DRM to new documentation format. * Clear key DRM update * Better UI Changes (#594) * Better UI Changes * Update ci.yml * Feature/july changes (#598) * Fixed play after seeking issue on iOS * Fixed audio track selection issue on iOS/Android * Fixed issue where speed which couldn't be applied on iOS was saved in player state. * Added support for D-pad navigation using a Android TV remote control (#586) * Exposes all active eventListener (#585) * Updated changelog * Added docs * Updated documentation * Updated documentation * Added BetterPlayerMultipleGestureDetector, general refactor * General refactor Co-authored-by: Daniel Zarins <[email protected]> Co-authored-by: Letalus <[email protected]> * Updated docs * Updated docs * Updated docs * Updated docs * fixed conflicts * Duplicated named argument 'onLongPress' removed * Better UI Changes * Feature/july changes (#598) * Fixed play after seeking issue on iOS * Fixed audio track selection issue on iOS/Android * Fixed issue where speed which couldn't be applied on iOS was saved in player state. * Added support for D-pad navigation using a Android TV remote control (#586) * Exposes all active eventListener (#585) * Updated changelog * Added docs * Updated documentation * Updated documentation * Added BetterPlayerMultipleGestureDetector, general refactor * General refactor Co-authored-by: Daniel Zarins <[email protected]> Co-authored-by: Letalus <[email protected]> * fixed conflicts Co-authored-by: Jakub <[email protected]> Co-authored-by: Daniel Zarins <[email protected]> Co-authored-by: Letalus <[email protected]> * Updated changelog * Added `sigmaX` and `sigmaY` parameters in BetterPlayerControlsConfiguration * Updated lint and format * Updated versions Co-authored-by: Pinguluk <[email protected]> Co-authored-by: tinusneethling <[email protected]> Co-authored-by: creativeblaq <[email protected]> Co-authored-by: Daniel Zarins <[email protected]> Co-authored-by: Letalus <[email protected]>
1 parent 178474e commit d8fb722

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1152
-541
lines changed

CHANGELOG.md

+18
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
## 0.0.73
2+
* Added `licenseUrl` support for iOS DRM.
3+
* Fixed RTL text direction issue in player controls.
4+
* Added `renderedSubtitle` in `BetterPlayerController`.
5+
* Added additional check in `postControllerEvent` to handle scenario where event stream is closed.
6+
* Updated ExoPlayer version
7+
* Fixed `bufferingUpdate` event triggered too often.
8+
* Updated video list example with bufering configuration.
9+
* Updated video list documentation.
10+
* Added `setMixWithOthers` method in `BetterPlayerListVideoPlayerController`.
11+
* Fixed broken link in cover page of documentation.
12+
* Fixed progress bar issue where position could be set above video duration.
13+
* Fixed iOS remote notification command issue.
14+
* Removed duplicated page in example app (by https://github.com/pinguluk)
15+
* Added support for clear key DRM (by https://github.com/tinusneethling)
16+
* Refreshed look and feel of the player UI (by https://github.com/creativeblaq)
17+
* Added `sigmaX` and `sigmaY` parameters in BetterPlayerControlsConfiguration to control blur of cupertino controls (original idea by: https://github.com/YeFei572)
18+
119
## 0.0.72
220
* Updated ExoPlayer version
321

analysis_options.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ linter:
1717
sized_box_for_whitespace: false
1818
invalid_dependency: false
1919
sort_pub_dependencies: false
20+
avoid_unnecessary_containers: false
2021

android/build.gradle

+6-6
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ android {
3939
}
4040

4141
dependencies {
42-
implementation 'com.google.android.exoplayer:exoplayer-core:2.14.1'
43-
implementation 'com.google.android.exoplayer:exoplayer-hls:2.14.1'
44-
implementation 'com.google.android.exoplayer:exoplayer-dash:2.14.1'
45-
implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.14.1'
46-
implementation 'com.google.android.exoplayer:exoplayer-ui:2.14.1'
47-
implementation 'com.google.android.exoplayer:extension-mediasession:2.14.1'
42+
implementation 'com.google.android.exoplayer:exoplayer-core:2.14.2'
43+
implementation 'com.google.android.exoplayer:exoplayer-hls:2.14.2'
44+
implementation 'com.google.android.exoplayer:exoplayer-dash:2.14.2'
45+
implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.14.2'
46+
implementation 'com.google.android.exoplayer:exoplayer-ui:2.14.2'
47+
implementation 'com.google.android.exoplayer:extension-mediasession:2.14.2'
4848
implementation "android.arch.lifecycle:runtime:1.1.1"
4949
implementation "android.arch.lifecycle:common:1.1.1"
5050
implementation "android.arch.lifecycle:common-java8:1.1.1"

android/src/main/java/com/jhomlala/better_player/BetterPlayer.java

+26-9
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import com.google.android.exoplayer2.drm.DummyExoMediaDrm;
4545
import com.google.android.exoplayer2.drm.FrameworkMediaDrm;
4646
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
47+
import com.google.android.exoplayer2.drm.LocalMediaDrmCallback;
4748
import com.google.android.exoplayer2.drm.UnsupportedDrmException;
4849
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
4950
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
@@ -111,6 +112,7 @@ final class BetterPlayer {
111112
private WorkManager workManager;
112113
private HashMap<UUID, Observer<WorkInfo>> workerObserverMap;
113114
private CustomDefaultLoadControl customDefaultLoadControl;
115+
private long lastSendBufferedPosition = 0L;
114116

115117

116118
BetterPlayer(
@@ -147,7 +149,7 @@ void setDataSource(
147149
Context context, String key, String dataSource, String formatHint, Result result,
148150
Map<String, String> headers, boolean useCache, long maxCacheSize, long maxCacheFileSize,
149151
long overriddenDuration, String licenseUrl, Map<String, String> drmHeaders,
150-
String cacheKey) {
152+
String cacheKey, String clearKey) {
151153
this.key = key;
152154
isInitialized = false;
153155

@@ -189,6 +191,16 @@ void setDataSource(
189191
.build(httpMediaDrmCallback);
190192
}
191193
}
194+
} else if (clearKey != null && !clearKey.isEmpty()) {
195+
if (Util.SDK_INT < 18) {
196+
Log.e(TAG, "Protected content not supported on API levels below 18");
197+
drmSessionManager = null;
198+
} else {
199+
drmSessionManager = new DefaultDrmSessionManager.Builder()
200+
.setUuidAndExoMediaDrmProvider(C.CLEARKEY_UUID, FrameworkMediaDrm.DEFAULT_PROVIDER).
201+
build(new LocalMediaDrmCallback(clearKey.getBytes()));
202+
}
203+
192204
} else {
193205
drmSessionManager = null;
194206
}
@@ -556,8 +568,9 @@ public void onCancel(Object o) {
556568
exoPlayer.addListener(new Player.Listener() {
557569
@Override
558570
public void onPlaybackStateChanged(int playbackState) {
571+
559572
if (playbackState == Player.STATE_BUFFERING) {
560-
sendBufferingUpdate();
573+
sendBufferingUpdate(true);
561574
Map<String, Object> event = new HashMap<>();
562575
event.put("event", "bufferingStart");
563576
eventSink.success(event);
@@ -590,13 +603,17 @@ public void onPlayerError(final ExoPlaybackException error) {
590603
result.success(reply);
591604
}
592605

593-
void sendBufferingUpdate() {
594-
Map<String, Object> event = new HashMap<>();
595-
event.put("event", "bufferingUpdate");
596-
List<? extends Number> range = Arrays.asList(0, exoPlayer.getBufferedPosition());
597-
// iOS supports a list of buffered ranges, so here is a list with a single range.
598-
event.put("values", Collections.singletonList(range));
599-
eventSink.success(event);
606+
void sendBufferingUpdate(boolean isFromBufferingStart) {
607+
long bufferedPosition = exoPlayer.getBufferedPosition();
608+
if (isFromBufferingStart || bufferedPosition != lastSendBufferedPosition) {
609+
Map<String, Object> event = new HashMap<>();
610+
event.put("event", "bufferingUpdate");
611+
List<? extends Number> range = Arrays.asList(0, bufferedPosition);
612+
// iOS supports a list of buffered ranges, so here is a list with a single range.
613+
event.put("values", Collections.singletonList(range));
614+
eventSink.success(event);
615+
lastSendBufferedPosition = bufferedPosition;
616+
}
600617
}
601618

602619
private void setAudioAttributes(SimpleExoPlayer exoPlayer, Boolean mixWithOthers) {

android/src/main/java/com/jhomlala/better_player/BetterPlayerPlugin.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public class BetterPlayerPlugin implements FlutterPlugin, ActivityAware, MethodC
6565
private static final String INDEX_PARAMETER = "index";
6666
private static final String LICENSE_URL_PARAMETER = "licenseUrl";
6767
private static final String DRM_HEADERS_PARAMETER = "drmHeaders";
68+
private static final String DRM_CLEARKEY_PARAMETER = "clearKey";
6869
private static final String MIX_WITH_OTHERS_PARAMETER = "mixWithOthers";
6970
public static final String URL_PARAMETER = "url";
7071
public static final String PRE_CACHE_SIZE_PARAMETER = "preCacheSize";
@@ -238,7 +239,7 @@ private void onMethodCall(MethodCall call, Result result, long textureId, Better
238239
break;
239240
case POSITION_METHOD:
240241
result.success(player.getPosition());
241-
player.sendBufferingUpdate();
242+
player.sendBufferingUpdate(false);
242243
break;
243244
case ABSOLUTE_POSITION_METHOD:
244245
result.success(player.getAbsolutePosition());
@@ -305,7 +306,7 @@ private void setDataSource(MethodCall call, Result result, BetterPlayer player)
305306
assetLookupKey = flutterState.keyForAsset.get(asset);
306307
}
307308

308-
player.setDataSource(
309+
player.setDataSource(
309310
flutterState.applicationContext,
310311
key,
311312
"asset:///" + assetLookupKey,
@@ -317,7 +318,7 @@ private void setDataSource(MethodCall call, Result result, BetterPlayer player)
317318
0L,
318319
overriddenDuration.longValue(),
319320
null,
320-
null, null
321+
null, null, null
321322
);
322323
} else {
323324
boolean useCache = getParameter(dataSource, USE_CACHE_PARAMETER, false);
@@ -329,6 +330,7 @@ private void setDataSource(MethodCall call, Result result, BetterPlayer player)
329330
String cacheKey = getParameter(dataSource, CACHE_KEY_PARAMETER, null);
330331
String formatHint = getParameter(dataSource, FORMAT_HINT_PARAMETER, null);
331332
String licenseUrl = getParameter(dataSource, LICENSE_URL_PARAMETER, null);
333+
String clearKey = getParameter(dataSource, DRM_CLEARKEY_PARAMETER, null);
332334
Map<String, String> drmHeaders = getParameter(dataSource, DRM_HEADERS_PARAMETER, new HashMap<>());
333335
player.setDataSource(
334336
flutterState.applicationContext,
@@ -343,7 +345,8 @@ private void setDataSource(MethodCall call, Result result, BetterPlayer player)
343345
overriddenDuration.longValue(),
344346
licenseUrl,
345347
drmHeaders,
346-
cacheKey
348+
cacheKey,
349+
clearKey
347350
);
348351
}
349352
}

docs/_coverpage.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@
1212
- Supports both Android and iOS
1313

1414
[GitHub](https://github.com/jhomlala/betterplayer)
15-
[Get Started](#gettingstarted)
15+
[Get Started](#home)

docs/controlsconfiguration.md

+6
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,10 @@ final Widget loadingWidget;
157157
158158
///Color of the background, when no frame is displayed.
159159
final Color backgroundColor;
160+
161+
///Quality of Gaussian Blur for x (iOS only).
162+
final double sigmaX;
163+
164+
///Quality of Gaussian Blur for y (iOS only).
165+
final double sigmaY;
160166
```

docs/drmconfiguration.md

+44-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Supported DRMs:
44

55
* Token based (authorization header): Android/iOS
66
* Widevine (licensue url + headers): Android
7-
* Fairplay EZDRM (certificate url): iOS
7+
* Fairplay EZDRM (certificate url, license url): iOS
88

99
Additional DRM types may be added in the future.
1010

@@ -42,6 +42,48 @@ BetterPlayerDataSource _fairplayDataSource = BetterPlayerDataSource(
4242
drmConfiguration: BetterPlayerDrmConfiguration(
4343
drmType: BetterPlayerDrmType.fairplay,
4444
certificateUrl: Constants.fairplayCertificateUrl,
45+
licenseUrl: Constants.fairplayLicenseUrl,
4546
),
4647
);
47-
```
48+
```
49+
50+
ClearKey (only supported in Android):
51+
52+
A ClearKey MP4 file can be generated with MP4Box as follow:
53+
54+
- Create drm_file.xml with the following contents.
55+
```xml
56+
<GPACDRM type="CENC AES-CTR">
57+
<DRMInfo type="pssh" version="1">
58+
<BS ID128="1077efecc0b24d02ace33c1e52e2fb4b"/>
59+
<BS bits="32" value="1"/>
60+
<BS ID128="cd7eb9ff88f34caeb06185b00024e4c2"/>
61+
</DRMInfo>
62+
<CrypTrack IV_size="8" first_IV="0xbb5738fe08f11341" isEncrypted="1" saiSavedBox="senc" trackID="1">
63+
<key KID="f3c5e0361e6654b28f8049c778b23946" value="a4631a153a443df9eed0593043db7519"/>
64+
</CrypTrack>
65+
<CrypTrack IV_size="8" first_IV="0xbb5738fe08f11341" isEncrypted="1" saiSavedBox="senc" trackID="2">
66+
<key KID="f3c5e0361e6654b28f8049c778b23946" value="a4631a153a443df9eed0593043db7519"/>
67+
</CrypTrack>
68+
69+
</GPACDRM>
70+
71+
72+
```
73+
- Create the mp4 container using [MP4Box](https://gpac.wp.imt.fr/)
74+
- MP4Box -crypt drm_file.xml testvideo.mp4 -out testvideo_encrypt_tmp.mp4
75+
- MP4Box -frag 240000 testvideo_encrypt_tmp.mp4 -out testvideo_encrypt.mp4 (need to create multi segment mp4 file as ExoPlayer does not read the pssh block on a single segment mp4 file)
76+
```dart
77+
78+
var _clearKeyDataSourceFile = BetterPlayerDataSource(
79+
BetterPlayerDataSourceType.file,
80+
await Utils.getFileUrl(Constants.fileTestVideoEncryptUrl),
81+
drmConfiguration: BetterPlayerDrmConfiguration(
82+
drmType: BetterPlayerDrmType.clearKey,
83+
clearKey: BetterPlayerClearKeyUtils.generate({
84+
"f3c5e0361e6654b28f8049c778b23946":
85+
"a4631a153a443df9eed0593043db7519",
86+
"abba271e8bcf552bbd2e86a434a9a5d9":
87+
"69eaa802a6763af979e8d1940fb88392"
88+
})),
89+
);

docs/install.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
```yaml
66
dependencies:
7-
better_player: ^0.0.72
7+
better_player: ^0.0.73
88
```
99
1010
2. Install it

docs/listplayerusage.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@
2121

2222
You can control `BetterPlayerListViewPlayer` with `BetterPlayerListViewPlayerController`. You need to pass `BetterPlayerListViewPlayerController` to `BetterPlayerListVideoPlayer`. See more in example app.
2323

24-
`BetterPlayerListViewPlayer` is good solution if you know that your list will be not too long. If you know that your list of videos will be long then you need to recycle `BetterPlayerController` instances. This is required because each creation of `BetterPlayerController` requires a lot of resources of the device. You need to remember that there are some devices which allows to create 2-3 instances of `BetterPlayerController` due to low hardware specification. To handle problem like this, you should use **recycle/reusable** techniques, where you will create 2-3 instances of `BetterPlayerController` and simply reuse them in list cell. See reusable video list example here: https://github.com/jhomlala/betterplayer/tree/master/example/lib/pages/reusable_video_list
24+
`BetterPlayerListViewPlayer` is good solution if you know that your list will be not too long. If you know that your list of videos will be long then you need to recycle `BetterPlayerController` instances. This is required because each creation of `BetterPlayerController` requires a lot of resources of the device. You need to remember that there are some devices which allows to create 2-3 instances of `BetterPlayerController` due to low hardware specification. To handle problem like this, you should use **recycle/reusable** techniques, where you will create 2-3 instances of `BetterPlayerController` and simply reuse them in list cell. See reusable video list example here: https://github.com/jhomlala/betterplayer/tree/master/example/lib/pages/reusable_video_list
25+
26+
To resolve random OOM issues, try to lower values in `bufferingConfiguration` in `BetterPlayerDataSource`.

docs/media/logo.png

15.4 KB
Loading

docs/media/style.css

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
2+
body .app-nav {
3+
color: #34495e !important;
4+
}
5+
.cover {
6+
color: #34495e !important;
7+
}
8+
9+
.cover #better-player .anchor span {
10+
color: #34495e !important;
11+
}
12+
13+
section.cover {
14+
min-height: 100vh;
15+
height: auto !important;
16+
padding: 2em 0.5em;
17+
}
18+
19+
section.cover .cover-main {
20+
z-index: 0;
21+
}
22+
23+
section.cover .cover-main > .buttons a {
24+
border-radius: 2rem;
25+
border: 1px solid var(--theme-color, #42b983);
26+
box-sizing: border-box;
27+
color: var(--theme-color, #42b983);
28+
display: inline-block;
29+
font-size: 1.05rem;
30+
letter-spacing: 0.1rem;
31+
margin: 0.5rem 1rem;
32+
padding: 0.75em 2rem;
33+
text-decoration: none;
34+
transition: all 0.15s ease;
35+
}
36+
section.cover .cover-main > .buttons a:last-child {
37+
background-color: var(--theme-color, #42b983);
38+
color: #fff;
39+
}
40+
section.cover .cover-main > .buttons a:last-child:hover {
41+
color: inherit;
42+
opacity: 0.8;
43+
}
44+
section.cover .cover-main > .buttons a:hover {
45+
color: inherit;
46+
}
47+
section.cover blockquote > .buttons > a {
48+
border-bottom: 2px solid var(--theme-color, #42b983);
49+
transition: color 0.3s;
50+
}
51+
section.cover blockquote > .buttons > a:hover {
52+
color: var(--theme-color, #42b983);
53+
}

docs/subtitlesconfiguration.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,8 @@ final Color backgroundColor;
140140
141141
///Subtitles selected by default, without user interaction
142142
final bool selectedByDefault;
143-
```
143+
```
144+
145+
## Current subtitle
146+
147+
To get currently displayed subtitle, use `renderedSubtitle` in `BetterPlayerController`.

0 commit comments

Comments
 (0)