Skip to content

Commit

Permalink
Feature/february changes 9 (jhomlala#330)
Browse files Browse the repository at this point in the history
* Added overflowModalColor and overflowModalTextColor in BetterPlayerControlsConfiguration.

* Disabled picture in picture in fullscreen mode.

* Updated reusable video example

* Fixed enabled parameter for skip back and forward

* Fixed enabled parameter for skip back and forward

* Updated changelog

* Added AES DRM support

* Added AES DRM support

* Updated documentation

* Updated token based DRM

* Added widevine DRM support

* Dart lint fixes, Dart format

* Dart lint fixes, Dart format

* Dart lint fixes, Dart format
  • Loading branch information
jhomlala authored Feb 24, 2021
1 parent 726f095 commit 1a42e87
Show file tree
Hide file tree
Showing 24 changed files with 462 additions and 203 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 0.0.58
* Added overflowModalColor and overflowModalTextColor in BetterPlayerControlsConfiguration.
* Disabled picture in picture in fullscreen mode.
* Fixed enabled parameter for skip back and forward.
* Fixed notification configuration null issue (by https://github.com/bounty1342)
* Added token based and widevine DRM support.
* Updated documentation.

## 0.0.57
* Fixed iOS HLS initialization issue.
* Fixed issue where video plays after resume even if it's not visible.
Expand Down
44 changes: 42 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ This plugin is based on [Chewie](https://github.com/brianegan/chewie). Chewie is
✔️ Alternative resolution support
✔️ Cache support
✔️ Notifications support
✔️ Picture in Picture support
✔️ Picture in Picture support
✔️ DRM support (token, Widevine)
✔️ ... and much more!


Expand All @@ -36,7 +37,7 @@ This plugin is based on [Chewie](https://github.com/brianegan/chewie). Chewie is

```yaml
dependencies:
better_player: ^0.0.57
better_player: ^0.0.58
```
2. Install it
Expand Down Expand Up @@ -938,6 +939,45 @@ will have incorrect orientation.
betterPlayerController.setControlsAlwaysVisible(true);
```

### DRM
To configure DRM for your data source, use drmConfiguration parameter.
Supported DRMs:

* Token based (authorization header): Android/iOS
* Widevine (licensue url + headers): Android

Additional DRM types may be added in the future.

Token based:
```dart
BetterPlayerDataSource dataSource = BetterPlayerDataSource(
BetterPlayerDataSourceType.network,
"url",
videoFormat: BetterPlayerVideoFormat.hls,
drmConfiguration: BetterPlayerDrmConfiguration(
drmType: BetterPlayerDrmType.token,
token:
"Bearer=token",
),
);
````
Widevine (license url based):
```dart
BetterPlayerDataSource _widevineDataSource = BetterPlayerDataSource(
BetterPlayerDataSourceType.network,
"url",
drmConfiguration: BetterPlayerDrmConfiguration(
drmType: BetterPlayerDrmType.widevine,
licenseUrl:
"licenseUrl",
headers: {"header": "value"}
),
);
_widevineController.setupDataSource(_widevineDataSource);
```


### More documentation
https://pub.dev/documentation/better_player/latest/better_player/better_player-library.html
Expand Down
26 changes: 19 additions & 7 deletions android/src/main/java/com/jhomlala/better_player/BetterPlayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.source.ClippingMediaSource;
import com.google.android.exoplayer2.source.ClippingMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.source.TrackGroup;
Expand All @@ -44,12 +46,8 @@
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource;
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectionUtil;
import com.google.android.exoplayer2.ui.DefaultTrackNameProvider;
import com.google.android.exoplayer2.ui.TrackNameProvider;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
Expand Down Expand Up @@ -104,6 +102,7 @@ final class BetterPlayer {
private EventListener exoPlayerEventListener;
private Bitmap bitmap;
private MediaSessionCompat mediaSession;
private DrmSessionManager drmSessionManager;


BetterPlayer(
Expand All @@ -122,7 +121,7 @@ final class BetterPlayer {
void setDataSource(
Context context, String key, String dataSource, String formatHint, Result result,
Map<String, String> headers, boolean useCache, long maxCacheSize, long maxCacheFileSize,
long overriddenDuration) {
long overriddenDuration, String licenseUrl, Map<String, String> drmHeaders) {
this.key = key;
isInitialized = false;

Expand All @@ -137,6 +136,15 @@ void setDataSource(
}
}

if (licenseUrl != null && !licenseUrl.isEmpty()) {
HttpMediaDrmCallback httpMediaDrmCallback =
new HttpMediaDrmCallback(licenseUrl, new DefaultHttpDataSourceFactory());
drmSessionManager = new DefaultDrmSessionManager.Builder()
.setKeyRequestParameters(drmHeaders).build(httpMediaDrmCallback);
} else {
drmSessionManager = null;
}

if (isHTTP(uri)) {
DefaultHttpDataSourceFactory defaultHttpDataSourceFactory =
new DefaultHttpDataSourceFactory(
Expand Down Expand Up @@ -430,18 +438,22 @@ private MediaSource buildMediaSource(
return new SsMediaSource.Factory(
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
new DefaultDataSourceFactory(context, null, mediaDataSourceFactory))
.setDrmSessionManager(drmSessionManager)
.createMediaSource(MediaItem.fromUri(uri));
case C.TYPE_DASH:
return new DashMediaSource.Factory(
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
new DefaultDataSourceFactory(context, null, mediaDataSourceFactory))
.setDrmSessionManager(drmSessionManager)
.createMediaSource(MediaItem.fromUri(uri));
case C.TYPE_HLS:
return new HlsMediaSource.Factory(mediaDataSourceFactory)
.setDrmSessionManager(drmSessionManager)
.createMediaSource(MediaItem.fromUri(uri));
case C.TYPE_OTHER:
return new ProgressiveMediaSource.Factory(mediaDataSourceFactory,
new DefaultExtractorsFactory())
.setDrmSessionManager(drmSessionManager)
.createMediaSource(MediaItem.fromUri(uri));
default: {
throw new IllegalStateException("Unsupported type: " + type);
Expand Down Expand Up @@ -560,7 +572,7 @@ void setTrackParameters(int width, int height, int bitrate) {
if (bitrate != 0) {
parametersBuilder.setMaxVideoBitrate(bitrate);
}
if (width == 0 && height == 0 && bitrate == 0){
if (width == 0 && height == 0 && bitrate == 0) {
parametersBuilder.clearVideoSizeConstraints();
parametersBuilder.setMaxVideoBitrate(Integer.MAX_VALUE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ public class BetterPlayerPlugin implements FlutterPlugin, ActivityAware, MethodC
private static final String OVERRIDDEN_DURATION_PARAMETER = "overriddenDuration";
private static final String NAME_PARAMETER = "name";
private static final String INDEX_PARAMETER = "index";
private static final String LICENSE_URL_PARAMETER = "licenseUrl";
private static final String DRM_HEADERS_PARAMETER = "drmHeaders";

private static final String INIT_METHOD = "init";
private static final String CREATE_METHOD = "create";
Expand Down Expand Up @@ -309,7 +311,9 @@ private void setDataSource(MethodCall call, Result result, BetterPlayer player)
false,
0L,
0L,
overriddenDuration.longValue()
overriddenDuration.longValue(),
null,
null
);
} else {
boolean useCache = getParameter(dataSource, USE_CACHE_PARAMETER, false);
Expand All @@ -319,6 +323,8 @@ private void setDataSource(MethodCall call, Result result, BetterPlayer player)
long maxCacheFileSize = maxCacheFileSizeNumber.longValue();
String uri = getParameter(dataSource, URI_PARAMETER, "");
String formatHint = getParameter(dataSource, FORMAT_HINT_PARAMETER, null);
String licenseUrl = getParameter(dataSource, LICENSE_URL_PARAMETER, null);
Map<String, String> drmHeaders = getParameter(dataSource, DRM_HEADERS_PARAMETER, new HashMap<>());
player.setDataSource(
flutterState.applicationContext,
key,
Expand All @@ -329,7 +335,9 @@ private void setDataSource(MethodCall call, Result result, BetterPlayer player)
useCache,
maxCacheSize,
maxCacheFileSize,
overriddenDuration.longValue()
overriddenDuration.longValue(),
licenseUrl,
drmHeaders
);
}
}
Expand Down
8 changes: 8 additions & 0 deletions example/lib/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,12 @@ class Constants {
"https://imgix.bustle.com/uploads/image/2020/8/5/23905b9c-6b8c-47fa-bc0f-434de1d7e9bf-avengers-5.jpg";
static String elephantDreamStreamUrl =
"http://cdn.theoplayer.com/video/elephants-dream/playlist.m3u8";
static String tokenEncodedHlsUrl =
"https://amssamples.streaming.mediaservices.windows.net/830584f8-f0c8-4e41-968b-6538b9380aa5/TearsOfSteelTeaser.ism/manifest(format=m3u8-aapl)";
static String tokenEncodedHlsToken =
"Bearer=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cm46bWljcm9zb2Z0OmF6dXJlOm1lZGlhc2VydmljZXM6Y29udGVudGtleWlkZW50aWZpZXIiOiI5ZGRhMGJjYy01NmZiLTQxNDMtOWQzMi0zYWI5Y2M2ZWE4MGIiLCJpc3MiOiJodHRwOi8vdGVzdGFjcy5jb20vIiwiYXVkIjoidXJuOnRlc3QiLCJleHAiOjE3MTA4MDczODl9.lJXm5hmkp5ArRIAHqVJGefW2bcTzd91iZphoKDwa6w8";
static String widevineVideoUrl =
"https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_sd.mpd";
static String widevineLicenseUrl =
"https://proxy.uat.widevine.com/proxy?provider=widevine_test";
}
2 changes: 2 additions & 0 deletions example/lib/model/video_list_data.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
class VideoListData {
final String videoTitle;
final String videoUrl;
Duration lastPosition;
bool wasPlaying = false;

VideoListData(this.videoTitle, this.videoUrl);
}
3 changes: 3 additions & 0 deletions example/lib/pages/controls_configuration_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class _ControlsConfigurationPageState extends State<ControlsConfigurationPage> {
enableFullscreen: false,
controlBarHeight: 60,
loadingColor: Colors.red,
overflowModalColor: Colors.indigo,
overflowModalTextColor: Colors.white,
overflowMenuIconsColor: Colors.white,
);

BetterPlayerConfiguration betterPlayerConfiguration =
Expand Down
83 changes: 83 additions & 0 deletions example/lib/pages/drm_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import 'package:better_player/better_player.dart';
import 'package:better_player_example/constants.dart';
import 'package:flutter/material.dart';

class DrmPage extends StatefulWidget {
@override
_DrmPageState createState() => _DrmPageState();
}

class _DrmPageState extends State<DrmPage> {
BetterPlayerController _tokenController;
BetterPlayerController _widevineController;

@override
void initState() {
BetterPlayerConfiguration betterPlayerConfiguration =
BetterPlayerConfiguration(
aspectRatio: 16 / 9,
fit: BoxFit.contain,
);
BetterPlayerDataSource _tokenDataSource = BetterPlayerDataSource(
BetterPlayerDataSourceType.network,
Constants.tokenEncodedHlsUrl,
videoFormat: BetterPlayerVideoFormat.hls,
drmConfiguration: BetterPlayerDrmConfiguration(
drmType: BetterPlayerDrmType.token,
token: Constants.tokenEncodedHlsToken),
);
_tokenController = BetterPlayerController(betterPlayerConfiguration);
_tokenController.setupDataSource(_tokenDataSource);

_widevineController = BetterPlayerController(betterPlayerConfiguration);
BetterPlayerDataSource _widevineDataSource = BetterPlayerDataSource(
BetterPlayerDataSourceType.network,
Constants.widevineVideoUrl,
drmConfiguration: BetterPlayerDrmConfiguration(
drmType: BetterPlayerDrmType.widevine,
licenseUrl: Constants.widevineLicenseUrl,
headers: {"Test": "Test2"}),
);
_widevineController.setupDataSource(_widevineDataSource);

super.initState();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("DRM player"),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
"Auth token based DRM.",
style: TextStyle(fontSize: 16),
),
),
AspectRatio(
aspectRatio: 16 / 9,
child: BetterPlayer(controller: _tokenController),
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
"Widevine - license url based DRM. Works only for Android.",
style: TextStyle(fontSize: 16),
),
),
AspectRatio(
aspectRatio: 16 / 9,
child: BetterPlayer(controller: _widevineController),
),
],
),
);
}
}
12 changes: 9 additions & 3 deletions example/lib/pages/normal_player_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ class _NormalPlayerPageState extends State<NormalPlayerPage> {
);
BetterPlayerDataSource dataSource = BetterPlayerDataSource(
BetterPlayerDataSourceType.network,
Constants.bugBuckBunnyVideoUrl,
"https://amssamples.streaming.mediaservices.windows.net/830584f8-f0c8-4e41-968b-6538b9380aa5/TearsOfSteelTeaser.ism/manifest(format=m3u8-aapl)",
videoFormat: BetterPlayerVideoFormat.hls,
headers: {
"Authorization":
"Bearer=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cm46bWljcm9zb2Z0OmF6dXJlOm1lZGlhc2VydmljZXM6Y29udGVudGtleWlkZW50aWZpZXIiOiI5ZGRhMGJjYy01NmZiLTQxNDMtOWQzMi0zYWI5Y2M2ZWE4MGIiLCJpc3MiOiJodHRwOi8vdGVzdGFjcy5jb20vIiwiYXVkIjoidXJuOnRlc3QiLCJleHAiOjE3MTA4MDczODl9.lJXm5hmkp5ArRIAHqVJGefW2bcTzd91iZphoKDwa6w8"
},
drmConfiguration: BetterPlayerDrmConfiguration(),
);
_betterPlayerController = BetterPlayerController(betterPlayerConfiguration);
_betterPlayerController.setupDataSource(dataSource);
Expand All @@ -31,15 +37,15 @@ class _NormalPlayerPageState extends State<NormalPlayerPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Normal player"),
title: Text("DRM page"),
),
body: Column(
children: [
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
"Normal player with configuration managed by developer.",
"AES DRM example.",
style: TextStyle(fontSize: 16),
),
),
Expand Down
Loading

0 comments on commit 1a42e87

Please sign in to comment.