2017-01-11 12:51:45 +00:00
|
|
|
|
package com.brentvatne.exoplayer;
|
|
|
|
|
|
|
|
|
|
import android.annotation.SuppressLint;
|
2018-05-17 15:42:44 -07:00
|
|
|
|
import android.app.Activity;
|
2021-11-03 19:14:19 -04:00
|
|
|
|
import android.app.ActivityManager;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
import android.content.Context;
|
|
|
|
|
import android.media.AudioManager;
|
|
|
|
|
import android.net.Uri;
|
|
|
|
|
import android.os.Handler;
|
|
|
|
|
import android.os.Message;
|
|
|
|
|
import android.text.TextUtils;
|
|
|
|
|
import android.util.Log;
|
2018-05-17 15:42:44 -07:00
|
|
|
|
import android.view.View;
|
|
|
|
|
import android.view.Window;
|
2018-06-21 09:08:37 -07:00
|
|
|
|
import android.view.accessibility.CaptioningManager;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
import android.widget.FrameLayout;
|
2020-02-20 19:53:23 +05:30
|
|
|
|
import android.widget.ImageButton;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
|
|
|
|
|
import com.brentvatne.react.R;
|
|
|
|
|
import com.brentvatne.receiver.AudioBecomingNoisyReceiver;
|
|
|
|
|
import com.brentvatne.receiver.BecomingNoisyListener;
|
2018-06-11 21:25:58 -07:00
|
|
|
|
import com.facebook.react.bridge.Arguments;
|
2018-06-02 02:24:13 -07:00
|
|
|
|
import com.facebook.react.bridge.Dynamic;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
import com.facebook.react.bridge.LifecycleEventListener;
|
2018-06-11 21:25:58 -07:00
|
|
|
|
import com.facebook.react.bridge.ReadableArray;
|
|
|
|
|
import com.facebook.react.bridge.ReadableMap;
|
|
|
|
|
import com.facebook.react.bridge.WritableArray;
|
|
|
|
|
import com.facebook.react.bridge.WritableMap;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
import com.facebook.react.uimanager.ThemedReactContext;
|
2021-03-18 03:58:04 -07:00
|
|
|
|
import com.facebook.react.util.RNLog;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
import com.google.android.exoplayer2.C;
|
|
|
|
|
import com.google.android.exoplayer2.DefaultLoadControl;
|
2019-09-16 16:29:31 -04:00
|
|
|
|
import com.google.android.exoplayer2.DefaultRenderersFactory;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
2021-10-13 18:03:29 +03:00
|
|
|
|
import com.google.android.exoplayer2.drm.MediaDrmCallbackException;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
import com.google.android.exoplayer2.ExoPlayerFactory;
|
|
|
|
|
import com.google.android.exoplayer2.Format;
|
2017-06-14 00:45:12 +02:00
|
|
|
|
import com.google.android.exoplayer2.PlaybackParameters;
|
2018-05-28 23:25:33 -07:00
|
|
|
|
import com.google.android.exoplayer2.Player;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
import com.google.android.exoplayer2.SimpleExoPlayer;
|
|
|
|
|
import com.google.android.exoplayer2.Timeline;
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
|
2021-03-17 17:49:10 +02:00
|
|
|
|
import com.google.android.exoplayer2.drm.DrmSessionEventListener;
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
|
|
|
|
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
|
|
|
|
|
import com.google.android.exoplayer2.drm.FrameworkMediaDrm;
|
|
|
|
|
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
|
2021-10-12 14:56:50 +03:00
|
|
|
|
import com.google.android.exoplayer2.drm.MediaDrmCallbackException;
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
import com.google.android.exoplayer2.drm.UnsupportedDrmException;
|
2021-09-26 16:38:10 +03:00
|
|
|
|
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer;
|
|
|
|
|
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
|
2017-02-14 03:38:02 +01:00
|
|
|
|
import com.google.android.exoplayer2.metadata.Metadata;
|
2019-09-16 16:29:31 -04:00
|
|
|
|
import com.google.android.exoplayer2.metadata.MetadataOutput;
|
2017-03-21 20:25:17 +00:00
|
|
|
|
import com.google.android.exoplayer2.source.BehindLiveWindowException;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
import com.google.android.exoplayer2.source.MediaSource;
|
2018-06-11 15:23:43 -07:00
|
|
|
|
import com.google.android.exoplayer2.source.MergingMediaSource;
|
2019-09-16 16:29:31 -04:00
|
|
|
|
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
2018-06-11 15:23:43 -07:00
|
|
|
|
import com.google.android.exoplayer2.source.SingleSampleMediaSource;
|
2018-08-24 15:33:46 +05:30
|
|
|
|
import com.google.android.exoplayer2.source.TrackGroup;
|
2019-09-16 16:29:31 -04:00
|
|
|
|
import com.google.android.exoplayer2.source.TrackGroupArray;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
import com.google.android.exoplayer2.source.dash.DashMediaSource;
|
|
|
|
|
import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource;
|
|
|
|
|
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
|
|
|
|
|
import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource;
|
|
|
|
|
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
|
2017-06-14 00:45:12 +02:00
|
|
|
|
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
|
|
|
|
import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
|
2021-03-17 17:49:10 +02:00
|
|
|
|
import com.google.android.exoplayer2.trackselection.ExoTrackSelection;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
2019-09-16 16:29:31 -04:00
|
|
|
|
import com.google.android.exoplayer2.ui.PlayerControlView;
|
|
|
|
|
import com.google.android.exoplayer2.upstream.BandwidthMeter;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
import com.google.android.exoplayer2.upstream.DataSource;
|
2018-07-31 17:23:20 +02:00
|
|
|
|
import com.google.android.exoplayer2.upstream.DefaultAllocator;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
2020-05-15 12:55:19 +05:30
|
|
|
|
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
2021-03-18 03:58:04 -07:00
|
|
|
|
import com.google.android.exoplayer2.util.Assertions;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
import com.google.android.exoplayer2.util.Util;
|
2021-11-09 14:22:32 +02:00
|
|
|
|
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
|
|
|
|
import com.google.android.exoplayer2.source.dash.DashUtil;
|
|
|
|
|
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
|
|
|
|
|
import com.google.android.exoplayer2.source.dash.manifest.Period;
|
|
|
|
|
import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet;
|
|
|
|
|
import com.google.android.exoplayer2.source.dash.manifest.Representation;
|
|
|
|
|
import com.google.android.exoplayer2.source.dash.manifest.Descriptor;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
|
|
|
|
|
import java.net.CookieHandler;
|
|
|
|
|
import java.net.CookieManager;
|
|
|
|
|
import java.net.CookiePolicy;
|
2018-06-11 15:23:43 -07:00
|
|
|
|
import java.util.ArrayList;
|
2018-07-09 11:36:35 -07:00
|
|
|
|
import java.util.Locale;
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
import java.util.UUID;
|
2019-09-16 16:29:31 -04:00
|
|
|
|
import java.util.Map;
|
2021-07-07 18:59:55 +03:00
|
|
|
|
import java.util.Timer;
|
|
|
|
|
import java.util.TimerTask;
|
2021-11-09 14:22:32 +02:00
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.lang.Thread;
|
|
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
|
|
import java.util.concurrent.Executors;
|
|
|
|
|
import java.util.concurrent.Callable;
|
|
|
|
|
import java.util.concurrent.Future;
|
|
|
|
|
import java.lang.Integer;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
|
|
|
|
|
@SuppressLint("ViewConstructor")
|
|
|
|
|
class ReactExoplayerView extends FrameLayout implements
|
|
|
|
|
LifecycleEventListener,
|
2019-09-16 16:29:31 -04:00
|
|
|
|
Player.EventListener,
|
2018-11-01 21:41:57 +05:30
|
|
|
|
BandwidthMeter.EventListener,
|
2017-01-11 12:51:45 +00:00
|
|
|
|
BecomingNoisyListener,
|
2017-02-14 03:38:02 +01:00
|
|
|
|
AudioManager.OnAudioFocusChangeListener,
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
MetadataOutput,
|
2021-03-17 17:49:10 +02:00
|
|
|
|
DrmSessionEventListener {
|
2017-01-11 12:51:45 +00:00
|
|
|
|
|
2021-11-04 13:54:43 -04:00
|
|
|
|
public static final double DEFAULT_MAX_HEAP_ALLOCATION_PERCENT = 1;
|
2022-01-21 14:10:22 +02:00
|
|
|
|
public static final double DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE = 0;
|
|
|
|
|
public static final double DEFAULT_MIN_BUFFER_MEMORY_RESERVE = 0;
|
2021-11-04 13:54:43 -04:00
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
private static final String TAG = "ReactExoplayerView";
|
|
|
|
|
|
|
|
|
|
private static final CookieManager DEFAULT_COOKIE_MANAGER;
|
|
|
|
|
private static final int SHOW_PROGRESS = 1;
|
|
|
|
|
|
|
|
|
|
static {
|
|
|
|
|
DEFAULT_COOKIE_MANAGER = new CookieManager();
|
|
|
|
|
DEFAULT_COOKIE_MANAGER.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private final VideoEventEmitter eventEmitter;
|
2019-09-16 16:29:31 -04:00
|
|
|
|
private final ReactExoplayerConfig config;
|
|
|
|
|
private final DefaultBandwidthMeter bandwidthMeter;
|
2019-01-04 14:58:32 +05:30
|
|
|
|
private PlayerControlView playerControlView;
|
2019-02-04 19:18:29 +05:30
|
|
|
|
private View playPauseControlContainer;
|
|
|
|
|
private Player.EventListener eventListener;
|
2019-09-16 16:29:31 -04:00
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
private ExoPlayerView exoPlayerView;
|
|
|
|
|
|
|
|
|
|
private DataSource.Factory mediaDataSourceFactory;
|
|
|
|
|
private SimpleExoPlayer player;
|
2018-08-07 23:10:03 -07:00
|
|
|
|
private DefaultTrackSelector trackSelector;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
private boolean playerNeedsSource;
|
|
|
|
|
|
2017-03-21 20:25:17 +00:00
|
|
|
|
private int resumeWindow;
|
|
|
|
|
private long resumePosition;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
private boolean loadVideoStarted;
|
2018-05-17 15:42:44 -07:00
|
|
|
|
private boolean isFullscreen;
|
2018-06-09 16:11:18 -07:00
|
|
|
|
private boolean isInBackground;
|
2018-07-09 16:18:42 -07:00
|
|
|
|
private boolean isPaused;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
private boolean isBuffering;
|
2019-08-01 11:33:28 -07:00
|
|
|
|
private boolean muted = false;
|
2021-03-15 16:51:16 +02:00
|
|
|
|
private boolean hasAudioFocus = false;
|
2017-06-14 00:45:12 +02:00
|
|
|
|
private float rate = 1f;
|
2018-11-14 12:33:28 +01:00
|
|
|
|
private float audioVolume = 1f;
|
2019-02-10 19:45:31 -08:00
|
|
|
|
private int minLoadRetryCount = 3;
|
2018-11-26 14:50:31 -08:00
|
|
|
|
private int maxBitRate = 0;
|
2018-11-27 19:48:41 -08:00
|
|
|
|
private long seekTime = C.TIME_UNSET;
|
2021-10-13 18:03:29 +03:00
|
|
|
|
private boolean hasDrmFailed = false;
|
2021-11-09 14:22:32 +02:00
|
|
|
|
private boolean isUsingContentResolution = false;
|
|
|
|
|
private boolean selectTrackWhenReady = false;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
|
2018-08-01 15:58:02 +02:00
|
|
|
|
private int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
|
|
|
|
|
private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
|
|
|
|
|
private int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
|
|
|
|
|
private int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
|
2021-11-04 13:54:43 -04:00
|
|
|
|
private double maxHeapAllocationPercent = ReactExoplayerView.DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
|
2022-01-21 14:10:22 +02:00
|
|
|
|
private double minBackBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE;
|
|
|
|
|
private double minBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
private Handler mainHandler;
|
2021-07-07 18:59:55 +03:00
|
|
|
|
private Timer bufferCheckTimer;
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
// Props from React
|
2021-05-17 13:09:09 +03:00
|
|
|
|
private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
private Uri srcUri;
|
|
|
|
|
private String extension;
|
|
|
|
|
private boolean repeat;
|
2018-07-17 14:14:21 -07:00
|
|
|
|
private String audioTrackType;
|
|
|
|
|
private Dynamic audioTrackValue;
|
2018-08-24 15:33:46 +05:30
|
|
|
|
private String videoTrackType;
|
2019-09-16 16:29:31 -04:00
|
|
|
|
private Dynamic videoTrackValue;
|
2018-06-04 11:48:59 -07:00
|
|
|
|
private String textTrackType;
|
|
|
|
|
private Dynamic textTrackValue;
|
2018-06-11 21:25:58 -07:00
|
|
|
|
private ReadableArray textTracks;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
private boolean disableFocus;
|
2021-03-18 03:58:04 -07:00
|
|
|
|
private boolean disableBuffering;
|
2021-11-09 14:22:32 +02:00
|
|
|
|
private long contentStartTime;
|
2021-05-17 13:09:09 +03:00
|
|
|
|
private boolean disableDisconnectError;
|
2020-06-16 14:31:23 +02:00
|
|
|
|
private boolean preventsDisplaySleepDuringVideoPlayback = true;
|
2017-03-31 18:15:39 +02:00
|
|
|
|
private float mProgressUpdateInterval = 250.0f;
|
2017-05-09 06:30:45 +10:00
|
|
|
|
private boolean playInBackground = false;
|
2017-10-02 20:11:41 +02:00
|
|
|
|
private Map<String, String> requestHeaders;
|
2018-08-25 21:53:11 +05:30
|
|
|
|
private boolean mReportBandwidth = false;
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
private UUID drmUUID = null;
|
|
|
|
|
private String drmLicenseUrl = null;
|
|
|
|
|
private String[] drmLicenseHeader = null;
|
2019-07-07 22:17:15 +02:00
|
|
|
|
private boolean controls;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
// \ End props
|
|
|
|
|
|
|
|
|
|
// React
|
|
|
|
|
private final ThemedReactContext themedReactContext;
|
|
|
|
|
private final AudioManager audioManager;
|
|
|
|
|
private final AudioBecomingNoisyReceiver audioBecomingNoisyReceiver;
|
|
|
|
|
|
|
|
|
|
private final Handler progressHandler = new Handler() {
|
|
|
|
|
@Override
|
|
|
|
|
public void handleMessage(Message msg) {
|
|
|
|
|
switch (msg.what) {
|
|
|
|
|
case SHOW_PROGRESS:
|
|
|
|
|
if (player != null
|
2019-09-16 16:29:31 -04:00
|
|
|
|
&& player.getPlaybackState() == Player.STATE_READY
|
2017-01-11 12:51:45 +00:00
|
|
|
|
&& player.getPlayWhenReady()
|
|
|
|
|
) {
|
|
|
|
|
long pos = player.getCurrentPosition();
|
2018-06-25 12:25:14 -07:00
|
|
|
|
long bufferedDuration = player.getBufferedPercentage() * player.getDuration() / 100;
|
2020-05-15 12:55:19 +05:30
|
|
|
|
eventEmitter.progressChanged(pos, bufferedDuration, player.getDuration(), getPositionInFirstPeriodMsForCurrentWindow(pos));
|
2017-01-11 12:51:45 +00:00
|
|
|
|
msg = obtainMessage(SHOW_PROGRESS);
|
2017-03-31 18:15:39 +02:00
|
|
|
|
sendMessageDelayed(msg, Math.round(mProgressUpdateInterval));
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
2021-03-18 03:58:04 -07:00
|
|
|
|
|
2020-05-15 12:55:19 +05:30
|
|
|
|
public double getPositionInFirstPeriodMsForCurrentWindow(long currentPosition) {
|
|
|
|
|
Timeline.Window window = new Timeline.Window();
|
2021-11-03 19:14:19 -04:00
|
|
|
|
if(!player.getCurrentTimeline().isEmpty()) {
|
2020-05-15 12:55:19 +05:30
|
|
|
|
player.getCurrentTimeline().getWindow(player.getCurrentWindowIndex(), window);
|
|
|
|
|
}
|
|
|
|
|
return window.windowStartTimeMs + currentPosition;
|
|
|
|
|
}
|
2017-01-11 12:51:45 +00:00
|
|
|
|
|
2019-09-16 16:29:31 -04:00
|
|
|
|
public ReactExoplayerView(ThemedReactContext context, ReactExoplayerConfig config) {
|
2017-01-11 12:51:45 +00:00
|
|
|
|
super(context);
|
2018-01-29 13:25:58 -07:00
|
|
|
|
this.themedReactContext = context;
|
2018-11-01 21:41:57 +05:30
|
|
|
|
this.eventEmitter = new VideoEventEmitter(context);
|
2019-09-16 16:29:31 -04:00
|
|
|
|
this.config = config;
|
|
|
|
|
this.bandwidthMeter = config.getBandwidthMeter();
|
2018-11-01 15:18:59 +05:30
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
createViews();
|
2019-09-16 16:29:31 -04:00
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
|
|
|
|
themedReactContext.addLifecycleEventListener(this);
|
|
|
|
|
audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void setId(int id) {
|
|
|
|
|
super.setId(id);
|
|
|
|
|
eventEmitter.setViewId(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void createViews() {
|
2017-03-21 20:25:17 +00:00
|
|
|
|
clearResumePosition();
|
2017-01-11 12:51:45 +00:00
|
|
|
|
mediaDataSourceFactory = buildDataSourceFactory(true);
|
|
|
|
|
if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) {
|
|
|
|
|
CookieHandler.setDefault(DEFAULT_COOKIE_MANAGER);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LayoutParams layoutParams = new LayoutParams(
|
|
|
|
|
LayoutParams.MATCH_PARENT,
|
|
|
|
|
LayoutParams.MATCH_PARENT);
|
|
|
|
|
exoPlayerView = new ExoPlayerView(getContext());
|
|
|
|
|
exoPlayerView.setLayoutParams(layoutParams);
|
|
|
|
|
|
|
|
|
|
addView(exoPlayerView, 0, layoutParams);
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
|
|
|
|
|
mainHandler = new Handler();
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onAttachedToWindow() {
|
|
|
|
|
super.onAttachedToWindow();
|
|
|
|
|
initializePlayer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onDetachedFromWindow() {
|
|
|
|
|
super.onDetachedFromWindow();
|
2018-10-13 20:36:12 -07:00
|
|
|
|
/* We want to be able to continue playing audio when switching tabs.
|
|
|
|
|
* Leave this here in case it causes issues.
|
|
|
|
|
*/
|
|
|
|
|
// stopPlayback();
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LifecycleEventListener implementation
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onHostResume() {
|
2018-06-09 16:11:18 -07:00
|
|
|
|
if (!playInBackground || !isInBackground) {
|
|
|
|
|
setPlayWhenReady(!isPaused);
|
|
|
|
|
}
|
|
|
|
|
isInBackground = false;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onHostPause() {
|
2018-06-09 16:11:18 -07:00
|
|
|
|
isInBackground = true;
|
2017-05-09 06:30:45 +10:00
|
|
|
|
if (playInBackground) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-01-11 12:51:45 +00:00
|
|
|
|
setPlayWhenReady(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onHostDestroy() {
|
|
|
|
|
stopPlayback();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void cleanUpResources() {
|
|
|
|
|
stopPlayback();
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-01 21:41:57 +05:30
|
|
|
|
//BandwidthMeter.EventListener implementation
|
|
|
|
|
@Override
|
|
|
|
|
public void onBandwidthSample(int elapsedMs, long bytes, long bitrate) {
|
2018-12-31 21:33:02 -08:00
|
|
|
|
if (mReportBandwidth) {
|
2020-05-15 12:55:19 +05:30
|
|
|
|
if (player == null) {
|
|
|
|
|
eventEmitter.bandwidthReport(bitrate, 0, 0, "-1");
|
|
|
|
|
} else {
|
|
|
|
|
Format videoFormat = player.getVideoFormat();
|
|
|
|
|
int width = videoFormat != null ? videoFormat.width : 0;
|
|
|
|
|
int height = videoFormat != null ? videoFormat.height : 0;
|
|
|
|
|
String trackId = videoFormat != null ? videoFormat.id : "-1";
|
|
|
|
|
eventEmitter.bandwidthReport(bitrate, height, width, trackId);
|
|
|
|
|
}
|
2018-11-01 21:41:57 +05:30
|
|
|
|
}
|
2018-11-01 15:18:59 +05:30
|
|
|
|
}
|
2017-01-11 12:51:45 +00:00
|
|
|
|
|
2018-11-01 21:41:57 +05:30
|
|
|
|
// Internal methods
|
2019-01-04 14:58:32 +05:30
|
|
|
|
|
|
|
|
|
/**
|
2019-09-16 16:29:31 -04:00
|
|
|
|
* Toggling the visibility of the player control view
|
2019-01-04 14:58:32 +05:30
|
|
|
|
*/
|
|
|
|
|
private void togglePlayerControlVisibility() {
|
2019-07-07 22:17:15 +02:00
|
|
|
|
if(player == null) return;
|
2019-02-04 19:18:29 +05:30
|
|
|
|
reLayout(playerControlView);
|
2019-02-10 18:15:30 -08:00
|
|
|
|
if (playerControlView.isVisible()) {
|
2019-01-28 14:50:51 +05:30
|
|
|
|
playerControlView.hide();
|
2019-01-04 14:58:32 +05:30
|
|
|
|
} else {
|
2019-01-28 14:50:51 +05:30
|
|
|
|
playerControlView.show();
|
2019-01-04 14:58:32 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2019-01-16 23:47:32 +05:30
|
|
|
|
* Initializing Player control
|
2019-01-04 14:58:32 +05:30
|
|
|
|
*/
|
2019-01-16 23:47:32 +05:30
|
|
|
|
private void initializePlayerControl() {
|
2019-02-10 18:15:30 -08:00
|
|
|
|
if (playerControlView == null) {
|
2019-01-25 15:54:53 +05:30
|
|
|
|
playerControlView = new PlayerControlView(getContext());
|
|
|
|
|
}
|
2019-01-04 14:58:32 +05:30
|
|
|
|
|
2019-02-10 18:15:30 -08:00
|
|
|
|
// Setting the player for the playerControlView
|
2019-01-04 14:58:32 +05:30
|
|
|
|
playerControlView.setPlayer(player);
|
2019-01-28 14:50:51 +05:30
|
|
|
|
playerControlView.show();
|
2019-02-04 19:18:29 +05:30
|
|
|
|
playPauseControlContainer = playerControlView.findViewById(R.id.exo_play_pause_container);
|
2019-01-04 14:58:32 +05:30
|
|
|
|
|
2019-02-10 18:15:30 -08:00
|
|
|
|
// Invoking onClick event for exoplayerView
|
2019-01-04 14:58:32 +05:30
|
|
|
|
exoPlayerView.setOnClickListener(new OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(View v) {
|
|
|
|
|
togglePlayerControlVisibility();
|
|
|
|
|
}
|
|
|
|
|
});
|
2019-02-04 19:18:29 +05:30
|
|
|
|
|
2020-02-20 19:53:23 +05:30
|
|
|
|
//Handling the playButton click event
|
|
|
|
|
ImageButton playButton = playerControlView.findViewById(R.id.exo_play);
|
|
|
|
|
playButton.setOnClickListener(new View.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(View v) {
|
|
|
|
|
if (player != null && player.getPlaybackState() == Player.STATE_ENDED) {
|
|
|
|
|
player.seekTo(0);
|
|
|
|
|
}
|
|
|
|
|
setPausedModifier(false);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//Handling the pauseButton click event
|
|
|
|
|
ImageButton pauseButton = playerControlView.findViewById(R.id.exo_pause);
|
|
|
|
|
pauseButton.setOnClickListener(new View.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(View v) {
|
|
|
|
|
setPausedModifier(true);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2019-02-10 18:15:30 -08:00
|
|
|
|
// Invoking onPlayerStateChanged event for Player
|
2019-02-04 19:18:29 +05:30
|
|
|
|
eventListener = new Player.EventListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
|
|
|
|
reLayout(playPauseControlContainer);
|
2019-02-06 03:22:06 +05:30
|
|
|
|
//Remove this eventListener once its executed. since UI will work fine once after the reLayout is done
|
2019-02-04 19:18:29 +05:30
|
|
|
|
player.removeListener(eventListener);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
player.addListener(eventListener);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 03:22:06 +05:30
|
|
|
|
/**
|
|
|
|
|
* Adding Player control to the frame layout
|
|
|
|
|
*/
|
|
|
|
|
private void addPlayerControl() {
|
2019-07-07 22:17:15 +02:00
|
|
|
|
if(player == null) return;
|
2019-02-06 03:22:06 +05:30
|
|
|
|
LayoutParams layoutParams = new LayoutParams(
|
|
|
|
|
LayoutParams.MATCH_PARENT,
|
|
|
|
|
LayoutParams.MATCH_PARENT);
|
|
|
|
|
playerControlView.setLayoutParams(layoutParams);
|
2019-07-07 22:17:15 +02:00
|
|
|
|
int indexOfPC = indexOfChild(playerControlView);
|
|
|
|
|
if (indexOfPC != -1) {
|
|
|
|
|
removeViewAt(indexOfPC);
|
|
|
|
|
}
|
2019-02-06 03:22:06 +05:30
|
|
|
|
addView(playerControlView, 1, layoutParams);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-04 19:18:29 +05:30
|
|
|
|
/**
|
|
|
|
|
* Update the layout
|
2019-02-10 18:15:30 -08:00
|
|
|
|
* @param view view needs to update layout
|
2019-02-04 19:18:29 +05:30
|
|
|
|
*
|
|
|
|
|
* This is a workaround for the open bug in react-native: https://github.com/facebook/react-native/issues/17968
|
|
|
|
|
*/
|
|
|
|
|
private void reLayout(View view) {
|
2019-02-10 18:15:30 -08:00
|
|
|
|
if (view == null) return;
|
2019-02-04 19:18:29 +05:30
|
|
|
|
view.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
|
|
|
|
|
MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
|
|
|
|
|
view.layout(view.getLeft(), view.getTop(), view.getMeasuredWidth(), view.getMeasuredHeight());
|
2019-01-04 14:58:32 +05:30
|
|
|
|
}
|
|
|
|
|
|
2021-03-18 03:58:04 -07:00
|
|
|
|
private class RNVLoadControl extends DefaultLoadControl {
|
2021-11-03 19:14:19 -04:00
|
|
|
|
private int availableHeapInBytes = 0;
|
2021-11-16 20:35:30 -04:00
|
|
|
|
private Runtime runtime;
|
2021-03-18 03:58:04 -07:00
|
|
|
|
public RNVLoadControl(DefaultAllocator allocator, int minBufferMs, int maxBufferMs, int bufferForPlaybackMs, int bufferForPlaybackAfterRebufferMs, int targetBufferBytes, boolean prioritizeTimeOverSizeThresholds, int backBufferDurationMs, boolean retainBackBufferFromKeyframe) {
|
|
|
|
|
super(allocator,
|
|
|
|
|
minBufferMs,
|
|
|
|
|
maxBufferMs,
|
|
|
|
|
bufferForPlaybackMs,
|
|
|
|
|
bufferForPlaybackAfterRebufferMs,
|
|
|
|
|
targetBufferBytes,
|
|
|
|
|
prioritizeTimeOverSizeThresholds,
|
|
|
|
|
backBufferDurationMs,
|
|
|
|
|
retainBackBufferFromKeyframe);
|
2021-11-16 20:35:30 -04:00
|
|
|
|
runtime = Runtime.getRuntime();
|
2021-11-04 13:54:43 -04:00
|
|
|
|
ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(themedReactContext.ACTIVITY_SERVICE);
|
|
|
|
|
availableHeapInBytes = (int) Math.floor(activityManager.getMemoryClass() * maxHeapAllocationPercent * 1024 * 1024);
|
2021-03-18 03:58:04 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean shouldContinueLoading(long playbackPositionUs, long bufferedDurationUs, float playbackSpeed) {
|
|
|
|
|
if (ReactExoplayerView.this.disableBuffering) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-11-04 13:54:43 -04:00
|
|
|
|
int loadedBytes = getAllocator().getTotalBytesAllocated();
|
|
|
|
|
boolean isHeapReached = availableHeapInBytes > 0 && loadedBytes >= availableHeapInBytes;
|
|
|
|
|
if (isHeapReached) {
|
2021-03-18 03:58:04 -07:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-01-21 14:10:22 +02:00
|
|
|
|
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
|
|
|
|
|
long freeMemory = runtime.maxMemory() - usedMemory;
|
|
|
|
|
long reserveMemory = (long)minBufferMemoryReservePercent * runtime.maxMemory();
|
|
|
|
|
long bufferedMs = bufferedDurationUs / (long)1000;
|
|
|
|
|
if (reserveMemory > freeMemory && bufferedMs > 2000) {
|
|
|
|
|
// We don't have enough memory in reserve so we stop buffering to allow other components to use it instead
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-11-16 20:35:30 -04:00
|
|
|
|
if (runtime.freeMemory() == 0) {
|
2021-11-16 21:40:38 -04:00
|
|
|
|
Log.w("ExoPlayer Warning", "Free memory reached 0, forcing garbage collection");
|
2021-11-16 20:35:30 -04:00
|
|
|
|
runtime.gc();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-03-18 03:58:04 -07:00
|
|
|
|
return super.shouldContinueLoading(playbackPositionUs, bufferedDurationUs, playbackSpeed);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-07 18:59:55 +03:00
|
|
|
|
private void startBufferCheckTimer() {
|
|
|
|
|
SimpleExoPlayer player = this.player;
|
|
|
|
|
VideoEventEmitter eventEmitter = this.eventEmitter;
|
|
|
|
|
Handler mainHandler = this.mainHandler;
|
|
|
|
|
|
|
|
|
|
if (this.bufferCheckTimer != null) {
|
|
|
|
|
this.stopBufferCheckTimer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.bufferCheckTimer = new Timer();
|
|
|
|
|
TimerTask bufferCheckTimerTask = new TimerTask() {
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
if (mainHandler != null) {
|
|
|
|
|
mainHandler.post(new Runnable() {
|
|
|
|
|
public void run() {
|
|
|
|
|
if (player != null) {
|
|
|
|
|
double bufferedDuration = (double) (player.getBufferedPercentage() * player.getDuration() / 100);
|
|
|
|
|
eventEmitter.bufferProgress(0d, bufferedDuration);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.bufferCheckTimer.scheduleAtFixedRate(bufferCheckTimerTask, 500, 1000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void stopBufferCheckTimer() {
|
|
|
|
|
this.bufferCheckTimer.cancel();
|
|
|
|
|
this.bufferCheckTimer = null;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
private void initializePlayer() {
|
2019-07-07 10:21:23 +02:00
|
|
|
|
ReactExoplayerView self = this;
|
2019-09-16 16:29:31 -04:00
|
|
|
|
// This ensures all props have been settled, to avoid async racing conditions.
|
2019-07-07 10:21:23 +02:00
|
|
|
|
new Handler().postDelayed(new Runnable() {
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
2021-10-12 14:56:50 +03:00
|
|
|
|
try {
|
|
|
|
|
if (player == null) {
|
|
|
|
|
ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
|
|
|
|
|
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
|
|
|
|
trackSelector.setParameters(trackSelector.buildUponParameters()
|
|
|
|
|
.setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate));
|
|
|
|
|
|
|
|
|
|
DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
|
|
|
|
|
RNVLoadControl loadControl = new RNVLoadControl(
|
|
|
|
|
allocator,
|
|
|
|
|
minBufferMs,
|
|
|
|
|
maxBufferMs,
|
|
|
|
|
bufferForPlaybackMs,
|
|
|
|
|
bufferForPlaybackAfterRebufferMs,
|
|
|
|
|
-1,
|
|
|
|
|
true,
|
|
|
|
|
backBufferDurationMs,
|
|
|
|
|
DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME
|
2019-07-07 10:21:23 +02:00
|
|
|
|
);
|
2021-10-12 14:56:50 +03:00
|
|
|
|
DefaultRenderersFactory renderersFactory =
|
|
|
|
|
new DefaultRenderersFactory(getContext())
|
|
|
|
|
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
|
|
|
|
|
player = new SimpleExoPlayer.Builder(getContext(), renderersFactory)
|
|
|
|
|
.setTrackSelector(trackSelector)
|
|
|
|
|
.setBandwidthMeter(bandwidthMeter)
|
|
|
|
|
.setLoadControl(loadControl)
|
|
|
|
|
.build();
|
|
|
|
|
player.addListener(self);
|
|
|
|
|
player.addMetadataOutput(self);
|
|
|
|
|
exoPlayerView.setPlayer(player);
|
|
|
|
|
audioBecomingNoisyReceiver.setListener(self);
|
|
|
|
|
bandwidthMeter.addEventListener(new Handler(), self);
|
|
|
|
|
setPlayWhenReady(!isPaused);
|
|
|
|
|
playerNeedsSource = true;
|
|
|
|
|
|
|
|
|
|
PlaybackParameters params = new PlaybackParameters(rate, 1f);
|
|
|
|
|
player.setPlaybackParameters(params);
|
2019-07-07 10:21:23 +02:00
|
|
|
|
}
|
2021-10-12 14:56:50 +03:00
|
|
|
|
if (playerNeedsSource && srcUri != null) {
|
|
|
|
|
exoPlayerView.invalidateAspectRatio();
|
|
|
|
|
|
|
|
|
|
// DRM
|
|
|
|
|
DrmSessionManager drmSessionManager = null;
|
|
|
|
|
if (self.drmUUID != null) {
|
|
|
|
|
try {
|
|
|
|
|
drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl,
|
|
|
|
|
self.drmLicenseHeader);
|
|
|
|
|
} catch (UnsupportedDrmException e) {
|
|
|
|
|
int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported
|
|
|
|
|
: (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
|
|
|
|
|
? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
|
|
|
|
|
eventEmitter.error(getResources().getString(errorStringId), e, "3003");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// End DRM
|
|
|
|
|
|
|
|
|
|
ArrayList<MediaSource> mediaSourceList = buildTextSources();
|
|
|
|
|
MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager);
|
|
|
|
|
MediaSource mediaSource;
|
|
|
|
|
if (mediaSourceList.size() == 0) {
|
|
|
|
|
mediaSource = videoSource;
|
|
|
|
|
} else {
|
|
|
|
|
mediaSourceList.add(0, videoSource);
|
|
|
|
|
MediaSource[] textSourceArray = mediaSourceList.toArray(
|
|
|
|
|
new MediaSource[mediaSourceList.size()]
|
|
|
|
|
);
|
|
|
|
|
mediaSource = new MergingMediaSource(textSourceArray);
|
|
|
|
|
}
|
2018-06-11 15:23:43 -07:00
|
|
|
|
|
2021-10-12 14:56:50 +03:00
|
|
|
|
boolean haveResumePosition = resumeWindow != C.INDEX_UNSET;
|
|
|
|
|
if (haveResumePosition) {
|
|
|
|
|
player.seekTo(resumeWindow, resumePosition);
|
|
|
|
|
}
|
|
|
|
|
player.prepare(mediaSource, !haveResumePosition, false);
|
|
|
|
|
playerNeedsSource = false;
|
|
|
|
|
|
|
|
|
|
reLayout(exoPlayerView);
|
|
|
|
|
eventEmitter.loadStart();
|
|
|
|
|
loadVideoStarted = true;
|
2019-07-07 10:21:23 +02:00
|
|
|
|
}
|
2017-01-11 12:51:45 +00:00
|
|
|
|
|
2021-10-12 14:56:50 +03:00
|
|
|
|
// Initializing the playerControlView
|
|
|
|
|
initializePlayerControl();
|
|
|
|
|
setControls(controls);
|
|
|
|
|
applyModifiers();
|
|
|
|
|
startBufferCheckTimer();
|
|
|
|
|
|
|
|
|
|
} catch (Exception ex) {
|
|
|
|
|
self.playerNeedsSource = true;
|
|
|
|
|
Log.e("ExoPlayer Exception", "Failed to initialize Player!");
|
|
|
|
|
Log.e("ExoPlayer Exception", ex.toString());
|
|
|
|
|
eventEmitter.error(ex.toString(), ex, "1001");
|
2019-07-07 10:21:23 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}, 1);
|
2021-10-12 14:56:50 +03:00
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-17 17:49:10 +02:00
|
|
|
|
private DrmSessionManager buildDrmSessionManager(UUID uuid,
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
String licenseUrl, String[] keyRequestPropertiesArray) throws UnsupportedDrmException {
|
|
|
|
|
if (Util.SDK_INT < 18) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl,
|
|
|
|
|
buildHttpDataSourceFactory(false));
|
|
|
|
|
if (keyRequestPropertiesArray != null) {
|
|
|
|
|
for (int i = 0; i < keyRequestPropertiesArray.length - 1; i += 2) {
|
|
|
|
|
drmCallback.setKeyRequestProperty(keyRequestPropertiesArray[i],
|
|
|
|
|
keyRequestPropertiesArray[i + 1]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-13 18:03:29 +03:00
|
|
|
|
FrameworkMediaDrm mediaDrm = FrameworkMediaDrm.newInstance(uuid);
|
|
|
|
|
if (hasDrmFailed) {
|
|
|
|
|
// When DRM fails using L1 we want to switch to L3
|
|
|
|
|
mediaDrm.setPropertyString("securityLevel", "L3");
|
|
|
|
|
}
|
2021-03-17 17:49:10 +02:00
|
|
|
|
return new DefaultDrmSessionManager(uuid,
|
2021-10-13 18:03:29 +03:00
|
|
|
|
mediaDrm, drmCallback, null, false, 3);
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-17 17:49:10 +02:00
|
|
|
|
private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) {
|
2021-10-12 14:56:50 +03:00
|
|
|
|
if (uri == null) {
|
|
|
|
|
throw new IllegalStateException("Invalid video uri");
|
|
|
|
|
}
|
2017-01-11 12:51:45 +00:00
|
|
|
|
int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension
|
|
|
|
|
: uri.getLastPathSegment());
|
2021-05-17 13:09:09 +03:00
|
|
|
|
config.setDisableDisconnectError(this.disableDisconnectError);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
switch (type) {
|
|
|
|
|
case C.TYPE_SS:
|
2019-09-16 16:29:31 -04:00
|
|
|
|
return new SsMediaSource.Factory(
|
|
|
|
|
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
|
|
|
|
|
buildDataSourceFactory(false)
|
2021-03-17 17:49:10 +02:00
|
|
|
|
).setDrmSessionManager(drmSessionManager)
|
|
|
|
|
.setLoadErrorHandlingPolicy(
|
2019-09-16 16:29:31 -04:00
|
|
|
|
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
|
|
|
|
).createMediaSource(uri);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
case C.TYPE_DASH:
|
2019-09-16 16:29:31 -04:00
|
|
|
|
return new DashMediaSource.Factory(
|
|
|
|
|
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
|
|
|
|
|
buildDataSourceFactory(false)
|
2021-03-17 17:49:10 +02:00
|
|
|
|
).setDrmSessionManager(drmSessionManager)
|
|
|
|
|
.setLoadErrorHandlingPolicy(
|
2019-09-16 16:29:31 -04:00
|
|
|
|
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
|
|
|
|
).createMediaSource(uri);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
case C.TYPE_HLS:
|
2019-09-16 16:29:31 -04:00
|
|
|
|
return new HlsMediaSource.Factory(
|
|
|
|
|
mediaDataSourceFactory
|
2021-03-17 17:49:10 +02:00
|
|
|
|
).setDrmSessionManager(drmSessionManager)
|
|
|
|
|
.setLoadErrorHandlingPolicy(
|
2019-09-16 16:29:31 -04:00
|
|
|
|
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
|
|
|
|
).createMediaSource(uri);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
case C.TYPE_OTHER:
|
2019-09-16 16:29:31 -04:00
|
|
|
|
return new ProgressiveMediaSource.Factory(
|
|
|
|
|
mediaDataSourceFactory
|
2021-03-17 17:49:10 +02:00
|
|
|
|
).setDrmSessionManager(drmSessionManager)
|
|
|
|
|
.setLoadErrorHandlingPolicy(
|
2019-09-16 16:29:31 -04:00
|
|
|
|
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
|
|
|
|
|
).createMediaSource(uri);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
default: {
|
|
|
|
|
throw new IllegalStateException("Unsupported type: " + type);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-11 21:25:58 -07:00
|
|
|
|
private ArrayList<MediaSource> buildTextSources() {
|
|
|
|
|
ArrayList<MediaSource> textSources = new ArrayList<>();
|
|
|
|
|
if (textTracks == null) {
|
|
|
|
|
return textSources;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < textTracks.size(); ++i) {
|
|
|
|
|
ReadableMap textTrack = textTracks.getMap(i);
|
|
|
|
|
String language = textTrack.getString("language");
|
|
|
|
|
String title = textTrack.hasKey("title")
|
|
|
|
|
? textTrack.getString("title") : language + " " + i;
|
|
|
|
|
Uri uri = Uri.parse(textTrack.getString("uri"));
|
|
|
|
|
MediaSource textSource = buildTextSource(title, uri, textTrack.getString("type"),
|
|
|
|
|
language);
|
|
|
|
|
if (textSource != null) {
|
|
|
|
|
textSources.add(textSource);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return textSources;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-11 15:23:43 -07:00
|
|
|
|
private MediaSource buildTextSource(String title, Uri uri, String mimeType, String language) {
|
2018-06-12 20:57:30 -07:00
|
|
|
|
Format textFormat = Format.createTextSampleFormat(title, mimeType, Format.NO_VALUE, language);
|
2019-09-16 16:29:31 -04:00
|
|
|
|
return new SingleSampleMediaSource.Factory(mediaDataSourceFactory)
|
|
|
|
|
.createMediaSource(uri, textFormat, C.TIME_UNSET);
|
2018-06-11 15:23:43 -07:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
private void releasePlayer() {
|
|
|
|
|
if (player != null) {
|
2021-07-07 18:59:55 +03:00
|
|
|
|
stopBufferCheckTimer();
|
2017-03-21 20:25:17 +00:00
|
|
|
|
updateResumePosition();
|
2017-01-11 12:51:45 +00:00
|
|
|
|
player.release();
|
2019-09-16 16:29:31 -04:00
|
|
|
|
player.removeMetadataOutput(this);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
trackSelector = null;
|
2019-07-07 10:21:23 +02:00
|
|
|
|
player = null;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
progressHandler.removeMessages(SHOW_PROGRESS);
|
|
|
|
|
themedReactContext.removeLifecycleEventListener(this);
|
|
|
|
|
audioBecomingNoisyReceiver.removeListener();
|
2019-09-16 16:29:31 -04:00
|
|
|
|
bandwidthMeter.removeEventListener(this);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean requestAudioFocus() {
|
2021-03-15 16:51:16 +02:00
|
|
|
|
if (disableFocus || srcUri == null || this.hasAudioFocus) {
|
2017-01-11 12:51:45 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
int result = audioManager.requestAudioFocus(this,
|
|
|
|
|
AudioManager.STREAM_MUSIC,
|
|
|
|
|
AudioManager.AUDIOFOCUS_GAIN);
|
|
|
|
|
return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setPlayWhenReady(boolean playWhenReady) {
|
|
|
|
|
if (player == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (playWhenReady) {
|
2021-03-15 16:51:16 +02:00
|
|
|
|
this.hasAudioFocus = requestAudioFocus();
|
|
|
|
|
if (this.hasAudioFocus) {
|
2017-01-11 12:51:45 +00:00
|
|
|
|
player.setPlayWhenReady(true);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
player.setPlayWhenReady(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void startPlayback() {
|
|
|
|
|
if (player != null) {
|
|
|
|
|
switch (player.getPlaybackState()) {
|
2019-09-16 16:29:31 -04:00
|
|
|
|
case Player.STATE_IDLE:
|
|
|
|
|
case Player.STATE_ENDED:
|
2017-01-11 12:51:45 +00:00
|
|
|
|
initializePlayer();
|
|
|
|
|
break;
|
2019-09-16 16:29:31 -04:00
|
|
|
|
case Player.STATE_BUFFERING:
|
|
|
|
|
case Player.STATE_READY:
|
2017-01-11 12:51:45 +00:00
|
|
|
|
if (!player.getPlayWhenReady()) {
|
|
|
|
|
setPlayWhenReady(true);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
initializePlayer();
|
|
|
|
|
}
|
|
|
|
|
if (!disableFocus) {
|
2020-06-16 14:31:23 +02:00
|
|
|
|
setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void pausePlayback() {
|
|
|
|
|
if (player != null) {
|
|
|
|
|
if (player.getPlayWhenReady()) {
|
|
|
|
|
setPlayWhenReady(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
setKeepScreenOn(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void stopPlayback() {
|
|
|
|
|
onStopPlayback();
|
|
|
|
|
releasePlayer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void onStopPlayback() {
|
2018-05-17 15:42:44 -07:00
|
|
|
|
if (isFullscreen) {
|
2020-06-11 16:44:13 +02:00
|
|
|
|
setFullscreen(false);
|
2018-05-17 15:42:44 -07:00
|
|
|
|
}
|
2017-01-11 12:51:45 +00:00
|
|
|
|
audioManager.abandonAudioFocus(this);
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-21 20:25:17 +00:00
|
|
|
|
private void updateResumePosition() {
|
|
|
|
|
resumeWindow = player.getCurrentWindowIndex();
|
|
|
|
|
resumePosition = player.isCurrentWindowSeekable() ? Math.max(0, player.getCurrentPosition())
|
|
|
|
|
: C.TIME_UNSET;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void clearResumePosition() {
|
|
|
|
|
resumeWindow = C.INDEX_UNSET;
|
|
|
|
|
resumePosition = C.TIME_UNSET;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
/**
|
|
|
|
|
* Returns a new DataSource factory.
|
|
|
|
|
*
|
2019-09-16 16:29:31 -04:00
|
|
|
|
* @param useBandwidthMeter Whether to set {@link #bandwidthMeter} as a listener to the new
|
2017-01-11 12:51:45 +00:00
|
|
|
|
* DataSource factory.
|
|
|
|
|
* @return A new DataSource factory.
|
|
|
|
|
*/
|
|
|
|
|
private DataSource.Factory buildDataSourceFactory(boolean useBandwidthMeter) {
|
2019-09-16 16:29:31 -04:00
|
|
|
|
return DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext,
|
|
|
|
|
useBandwidthMeter ? bandwidthMeter : null, requestHeaders);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
/**
|
|
|
|
|
* Returns a new HttpDataSource factory.
|
|
|
|
|
*
|
|
|
|
|
* @param useBandwidthMeter Whether to set {@link #bandwidthMeter} as a listener to the new
|
|
|
|
|
* DataSource factory.
|
|
|
|
|
* @return A new HttpDataSource factory.
|
|
|
|
|
*/
|
|
|
|
|
private HttpDataSource.Factory buildHttpDataSourceFactory(boolean useBandwidthMeter) {
|
|
|
|
|
return DataSourceUtil.getDefaultHttpDataSourceFactory(this.themedReactContext, useBandwidthMeter ? bandwidthMeter : null, requestHeaders);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
// AudioManager.OnAudioFocusChangeListener implementation
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onAudioFocusChange(int focusChange) {
|
|
|
|
|
switch (focusChange) {
|
|
|
|
|
case AudioManager.AUDIOFOCUS_LOSS:
|
2021-03-15 16:51:16 +02:00
|
|
|
|
this.hasAudioFocus = false;
|
2020-02-17 18:53:56 +02:00
|
|
|
|
eventEmitter.audioFocusChanged(false);
|
|
|
|
|
pausePlayback();
|
|
|
|
|
audioManager.abandonAudioFocus(this);
|
|
|
|
|
break;
|
|
|
|
|
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
|
2017-01-11 12:51:45 +00:00
|
|
|
|
eventEmitter.audioFocusChanged(false);
|
|
|
|
|
break;
|
|
|
|
|
case AudioManager.AUDIOFOCUS_GAIN:
|
2021-03-15 16:51:16 +02:00
|
|
|
|
this.hasAudioFocus = true;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
eventEmitter.audioFocusChanged(true);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (player != null) {
|
|
|
|
|
if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
|
|
|
|
|
// Lower the volume
|
2019-08-01 11:33:28 -07:00
|
|
|
|
if (!muted) {
|
|
|
|
|
player.setVolume(audioVolume * 0.8f);
|
|
|
|
|
}
|
2017-01-11 12:51:45 +00:00
|
|
|
|
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
|
|
|
|
|
// Raise it back to normal
|
2019-08-01 11:33:28 -07:00
|
|
|
|
if (!muted) {
|
|
|
|
|
player.setVolume(audioVolume * 1);
|
|
|
|
|
}
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AudioBecomingNoisyListener implementation
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onAudioBecomingNoisy() {
|
|
|
|
|
eventEmitter.audioBecomingNoisy();
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-16 16:29:31 -04:00
|
|
|
|
// Player.EventListener implementation
|
2017-01-11 12:51:45 +00:00
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onLoadingChanged(boolean isLoading) {
|
|
|
|
|
// Do nothing.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
|
|
|
|
String text = "onStateChanged: playWhenReady=" + playWhenReady + ", playbackState=";
|
|
|
|
|
switch (playbackState) {
|
2019-09-16 16:29:31 -04:00
|
|
|
|
case Player.STATE_IDLE:
|
2017-01-11 12:51:45 +00:00
|
|
|
|
text += "idle";
|
|
|
|
|
eventEmitter.idle();
|
2019-09-30 13:27:08 -04:00
|
|
|
|
clearProgressMessageHandler();
|
2020-06-16 14:31:23 +02:00
|
|
|
|
if (!playWhenReady) {
|
|
|
|
|
setKeepScreenOn(false);
|
|
|
|
|
}
|
2017-01-11 12:51:45 +00:00
|
|
|
|
break;
|
2019-09-16 16:29:31 -04:00
|
|
|
|
case Player.STATE_BUFFERING:
|
2017-01-11 12:51:45 +00:00
|
|
|
|
text += "buffering";
|
|
|
|
|
onBuffering(true);
|
2019-09-30 13:27:08 -04:00
|
|
|
|
clearProgressMessageHandler();
|
2020-06-16 14:31:23 +02:00
|
|
|
|
setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
break;
|
2019-09-16 16:29:31 -04:00
|
|
|
|
case Player.STATE_READY:
|
2017-01-11 12:51:45 +00:00
|
|
|
|
text += "ready";
|
|
|
|
|
eventEmitter.ready();
|
|
|
|
|
onBuffering(false);
|
|
|
|
|
startProgressHandler();
|
|
|
|
|
videoLoaded();
|
2021-11-09 14:22:32 +02:00
|
|
|
|
if (selectTrackWhenReady && isUsingContentResolution) {
|
|
|
|
|
selectTrackWhenReady = false;
|
|
|
|
|
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
|
|
|
|
|
}
|
2019-09-30 13:27:08 -04:00
|
|
|
|
// Setting the visibility for the playerControlView
|
2019-09-16 16:29:31 -04:00
|
|
|
|
if (playerControlView != null) {
|
2019-01-28 14:50:51 +05:30
|
|
|
|
playerControlView.show();
|
2019-01-04 14:58:32 +05:30
|
|
|
|
}
|
2020-06-16 14:31:23 +02:00
|
|
|
|
setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
break;
|
2019-09-16 16:29:31 -04:00
|
|
|
|
case Player.STATE_ENDED:
|
2017-01-11 12:51:45 +00:00
|
|
|
|
text += "ended";
|
|
|
|
|
eventEmitter.end();
|
|
|
|
|
onStopPlayback();
|
2020-06-16 14:31:23 +02:00
|
|
|
|
setKeepScreenOn(false);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
text += "unknown";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
Log.d(TAG, text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void startProgressHandler() {
|
|
|
|
|
progressHandler.sendEmptyMessage(SHOW_PROGRESS);
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-30 13:27:08 -04:00
|
|
|
|
/*
|
|
|
|
|
The progress message handler will duplicate recursions of the onProgressMessage handler
|
|
|
|
|
on change of player state from any state to STATE_READY with playWhenReady is true (when
|
|
|
|
|
the video is not paused). This clears all existing messages.
|
|
|
|
|
*/
|
|
|
|
|
private void clearProgressMessageHandler() {
|
|
|
|
|
progressHandler.removeMessages(SHOW_PROGRESS);
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
private void videoLoaded() {
|
|
|
|
|
if (loadVideoStarted) {
|
|
|
|
|
loadVideoStarted = false;
|
2018-07-17 14:14:21 -07:00
|
|
|
|
setSelectedAudioTrack(audioTrackType, audioTrackValue);
|
2018-08-24 15:33:46 +05:30
|
|
|
|
setSelectedVideoTrack(videoTrackType, videoTrackValue);
|
2018-06-04 11:48:59 -07:00
|
|
|
|
setSelectedTextTrack(textTrackType, textTrackValue);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
Format videoFormat = player.getVideoFormat();
|
|
|
|
|
int width = videoFormat != null ? videoFormat.width : 0;
|
|
|
|
|
int height = videoFormat != null ? videoFormat.height : 0;
|
2020-05-15 12:55:19 +05:30
|
|
|
|
String trackId = videoFormat != null ? videoFormat.id : "-1";
|
2018-06-11 21:25:58 -07:00
|
|
|
|
eventEmitter.load(player.getDuration(), player.getCurrentPosition(), width, height,
|
2020-05-15 12:55:19 +05:30
|
|
|
|
getAudioTrackInfo(), getTextTrackInfo(), getVideoTrackInfo(), trackId);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-17 14:14:21 -07:00
|
|
|
|
private WritableArray getAudioTrackInfo() {
|
|
|
|
|
WritableArray audioTracks = Arguments.createArray();
|
|
|
|
|
|
|
|
|
|
MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
|
|
|
|
|
int index = getTrackRendererIndex(C.TRACK_TYPE_AUDIO);
|
|
|
|
|
if (info == null || index == C.INDEX_UNSET) {
|
|
|
|
|
return audioTracks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TrackGroupArray groups = info.getTrackGroups(index);
|
|
|
|
|
for (int i = 0; i < groups.length; ++i) {
|
|
|
|
|
Format format = groups.get(i).getFormat(0);
|
2018-08-07 23:10:03 -07:00
|
|
|
|
WritableMap audioTrack = Arguments.createMap();
|
|
|
|
|
audioTrack.putInt("index", i);
|
|
|
|
|
audioTrack.putString("title", format.id != null ? format.id : "");
|
|
|
|
|
audioTrack.putString("type", format.sampleMimeType);
|
|
|
|
|
audioTrack.putString("language", format.language != null ? format.language : "");
|
2018-08-25 11:21:01 +05:30
|
|
|
|
audioTrack.putString("bitrate", format.bitrate == Format.NO_VALUE ? ""
|
|
|
|
|
: String.format(Locale.US, "%.2fMbps", format.bitrate / 1000000f));
|
2018-08-07 23:10:03 -07:00
|
|
|
|
audioTracks.pushMap(audioTrack);
|
2018-07-17 14:14:21 -07:00
|
|
|
|
}
|
|
|
|
|
return audioTracks;
|
|
|
|
|
}
|
2018-08-24 15:33:46 +05:30
|
|
|
|
private WritableArray getVideoTrackInfo() {
|
2021-11-09 14:22:32 +02:00
|
|
|
|
|
|
|
|
|
WritableArray contentVideoTracks = this.getVideoTrackInfoFromManifest();
|
|
|
|
|
if (contentVideoTracks != null) {
|
|
|
|
|
isUsingContentResolution = true;
|
|
|
|
|
return contentVideoTracks;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 15:33:46 +05:30
|
|
|
|
WritableArray videoTracks = Arguments.createArray();
|
|
|
|
|
|
|
|
|
|
MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
|
|
|
|
|
int index = getTrackRendererIndex(C.TRACK_TYPE_VIDEO);
|
|
|
|
|
if (info == null || index == C.INDEX_UNSET) {
|
|
|
|
|
return videoTracks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TrackGroupArray groups = info.getTrackGroups(index);
|
|
|
|
|
for (int i = 0; i < groups.length; ++i) {
|
|
|
|
|
TrackGroup group = groups.get(i);
|
|
|
|
|
|
|
|
|
|
for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
|
|
|
|
|
Format format = group.getFormat(trackIndex);
|
|
|
|
|
WritableMap videoTrack = Arguments.createMap();
|
2018-12-31 21:33:02 -08:00
|
|
|
|
videoTrack.putInt("width", format.width == Format.NO_VALUE ? 0 : format.width);
|
|
|
|
|
videoTrack.putInt("height",format.height == Format.NO_VALUE ? 0 : format.height);
|
|
|
|
|
videoTrack.putInt("bitrate", format.bitrate == Format.NO_VALUE ? 0 : format.bitrate);
|
|
|
|
|
videoTrack.putString("codecs", format.codecs != null ? format.codecs : "");
|
|
|
|
|
videoTrack.putString("trackId",
|
|
|
|
|
format.id == null ? String.valueOf(trackIndex) : format.id);
|
2021-09-26 16:38:10 +03:00
|
|
|
|
if (isFormatSupported(format)) {
|
|
|
|
|
videoTracks.pushMap(videoTrack);
|
|
|
|
|
}
|
2018-08-24 15:33:46 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
2021-11-09 14:22:32 +02:00
|
|
|
|
|
2018-08-24 15:33:46 +05:30
|
|
|
|
return videoTracks;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-09 14:22:32 +02:00
|
|
|
|
private WritableArray getVideoTrackInfoFromManifest() {
|
|
|
|
|
ExecutorService es = Executors.newSingleThreadExecutor();
|
|
|
|
|
final DataSource dataSource = this.mediaDataSourceFactory.createDataSource();
|
|
|
|
|
final Uri sourceUri = this.srcUri;
|
|
|
|
|
final Timeline timelineRef = this.player.getCurrentTimeline();
|
|
|
|
|
final long startTime = this.contentStartTime * 1000 - 100; // s -> ms with 100ms offset
|
|
|
|
|
|
|
|
|
|
Future<WritableArray> result = es.submit(new Callable<WritableArray>() {
|
|
|
|
|
DataSource ds = dataSource;
|
|
|
|
|
Uri uri = sourceUri;
|
|
|
|
|
Timeline timeline = timelineRef;
|
|
|
|
|
long startTimeUs = startTime * 1000; // ms -> us
|
|
|
|
|
|
|
|
|
|
public WritableArray call() throws Exception {
|
|
|
|
|
WritableArray videoTracks = Arguments.createArray();
|
|
|
|
|
try {
|
|
|
|
|
DashManifest manifest = DashUtil.loadManifest(this.ds, this.uri);
|
|
|
|
|
int periodCount = manifest.getPeriodCount();
|
|
|
|
|
for (int i = 0; i < periodCount; i++) {
|
|
|
|
|
Period period = manifest.getPeriod(i);
|
|
|
|
|
for (int adaptationIndex = 0; adaptationIndex < period.adaptationSets.size(); adaptationIndex++) {
|
|
|
|
|
AdaptationSet adaptation = period.adaptationSets.get(adaptationIndex);
|
|
|
|
|
if (adaptation.type != C.TRACK_TYPE_VIDEO) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
boolean hasFoundContentPeriod = false;
|
|
|
|
|
for (int representationIndex = 0; representationIndex < adaptation.representations.size(); representationIndex++) {
|
|
|
|
|
Representation representation = adaptation.representations.get(representationIndex);
|
|
|
|
|
Format format = representation.format;
|
|
|
|
|
if (representation.presentationTimeOffsetUs <= startTimeUs) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
hasFoundContentPeriod = true;
|
|
|
|
|
WritableMap videoTrack = Arguments.createMap();
|
|
|
|
|
videoTrack.putInt("width", format.width == Format.NO_VALUE ? 0 : format.width);
|
|
|
|
|
videoTrack.putInt("height",format.height == Format.NO_VALUE ? 0 : format.height);
|
|
|
|
|
videoTrack.putInt("bitrate", format.bitrate == Format.NO_VALUE ? 0 : format.bitrate);
|
|
|
|
|
videoTrack.putString("codecs", format.codecs != null ? format.codecs : "");
|
|
|
|
|
videoTrack.putString("trackId",
|
|
|
|
|
format.id == null ? String.valueOf(representationIndex) : format.id);
|
|
|
|
|
if (isFormatSupported(format)) {
|
|
|
|
|
videoTracks.pushMap(videoTrack);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (hasFoundContentPeriod) {
|
|
|
|
|
return videoTracks;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
WritableArray results = result.get();
|
|
|
|
|
es.shutdown();
|
|
|
|
|
return results;
|
|
|
|
|
} catch (Exception e) {}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-11 21:25:58 -07:00
|
|
|
|
private WritableArray getTextTrackInfo() {
|
|
|
|
|
WritableArray textTracks = Arguments.createArray();
|
|
|
|
|
|
|
|
|
|
MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
|
2018-07-17 14:14:21 -07:00
|
|
|
|
int index = getTrackRendererIndex(C.TRACK_TYPE_TEXT);
|
2018-06-11 21:25:58 -07:00
|
|
|
|
if (info == null || index == C.INDEX_UNSET) {
|
|
|
|
|
return textTracks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TrackGroupArray groups = info.getTrackGroups(index);
|
|
|
|
|
for (int i = 0; i < groups.length; ++i) {
|
|
|
|
|
Format format = groups.get(i).getFormat(0);
|
|
|
|
|
WritableMap textTrack = Arguments.createMap();
|
|
|
|
|
textTrack.putInt("index", i);
|
2018-06-20 15:46:04 -07:00
|
|
|
|
textTrack.putString("title", format.id != null ? format.id : "");
|
2018-06-11 21:25:58 -07:00
|
|
|
|
textTrack.putString("type", format.sampleMimeType);
|
2018-06-20 15:46:04 -07:00
|
|
|
|
textTrack.putString("language", format.language != null ? format.language : "");
|
2018-06-11 21:25:58 -07:00
|
|
|
|
textTracks.pushMap(textTrack);
|
|
|
|
|
}
|
|
|
|
|
return textTracks;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
private void onBuffering(boolean buffering) {
|
|
|
|
|
if (isBuffering == buffering) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isBuffering = buffering;
|
|
|
|
|
if (buffering) {
|
|
|
|
|
eventEmitter.buffering(true);
|
|
|
|
|
} else {
|
|
|
|
|
eventEmitter.buffering(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2018-04-03 12:19:04 -05:00
|
|
|
|
public void onPositionDiscontinuity(int reason) {
|
2017-03-21 20:25:17 +00:00
|
|
|
|
if (playerNeedsSource) {
|
|
|
|
|
// This will only occur if the user has performed a seek whilst in the error state. Update the
|
|
|
|
|
// resume position so that if the user then retries, playback will resume from the position to
|
|
|
|
|
// which they seeked.
|
|
|
|
|
updateResumePosition();
|
|
|
|
|
}
|
2021-11-09 14:22:32 +02:00
|
|
|
|
if (isUsingContentResolution) {
|
|
|
|
|
// Discontinuity events might have a different track list so we update the selected track
|
|
|
|
|
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
|
|
|
|
|
selectTrackWhenReady = true;
|
|
|
|
|
}
|
2018-05-28 21:26:23 -07:00
|
|
|
|
// When repeat is turned on, reaching the end of the video will not cause a state change
|
|
|
|
|
// so we need to explicitly detect it.
|
2019-09-16 16:29:31 -04:00
|
|
|
|
if (reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION
|
2018-05-28 21:22:47 -07:00
|
|
|
|
&& player.getRepeatMode() == Player.REPEAT_MODE_ONE) {
|
|
|
|
|
eventEmitter.end();
|
|
|
|
|
}
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2018-04-03 12:19:04 -05:00
|
|
|
|
public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
|
|
|
|
|
// Do nothing.
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2018-04-03 12:19:04 -05:00
|
|
|
|
public void onSeekProcessed() {
|
2018-11-27 19:48:41 -08:00
|
|
|
|
eventEmitter.seek(player.getCurrentPosition(), seekTime);
|
|
|
|
|
seekTime = C.TIME_UNSET;
|
2021-11-09 14:22:32 +02:00
|
|
|
|
if (isUsingContentResolution) {
|
|
|
|
|
// We need to update the selected track to make sure that it still matches user selection if track list has changed in this period
|
|
|
|
|
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
|
|
|
|
|
}
|
2018-04-03 12:19:04 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
|
|
|
|
|
// Do nothing.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onRepeatModeChanged(int repeatMode) {
|
2017-03-21 20:25:17 +00:00
|
|
|
|
// Do nothing.
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
|
|
|
|
// Do Nothing.
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-14 00:45:12 +02:00
|
|
|
|
@Override
|
|
|
|
|
public void onPlaybackParametersChanged(PlaybackParameters params) {
|
|
|
|
|
eventEmitter.playbackRateChange(params.speed);
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
@Override
|
|
|
|
|
public void onPlayerError(ExoPlaybackException e) {
|
2020-05-15 12:55:19 +05:30
|
|
|
|
String errorString = "ExoPlaybackException type : " + e.type;
|
2021-10-12 14:56:50 +03:00
|
|
|
|
String errorCode = "2001"; // Playback error code 2xxx (2001 - unknown playback exception)
|
2021-10-13 18:03:29 +03:00
|
|
|
|
boolean needsReInitialization = false;
|
2017-12-06 08:56:41 -08:00
|
|
|
|
Exception ex = e;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
if (e.type == ExoPlaybackException.TYPE_RENDERER) {
|
|
|
|
|
Exception cause = e.getRendererException();
|
|
|
|
|
if (cause instanceof MediaCodecRenderer.DecoderInitializationException) {
|
|
|
|
|
// Special case for decoder initialization failures.
|
|
|
|
|
MediaCodecRenderer.DecoderInitializationException decoderInitializationException =
|
|
|
|
|
(MediaCodecRenderer.DecoderInitializationException) cause;
|
2020-06-11 14:17:33 +01:00
|
|
|
|
if (decoderInitializationException.codecInfo.name == null) {
|
2017-01-11 12:51:45 +00:00
|
|
|
|
if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) {
|
2021-10-12 14:56:50 +03:00
|
|
|
|
errorCode = "2011";
|
2017-01-11 12:51:45 +00:00
|
|
|
|
errorString = getResources().getString(R.string.error_querying_decoders);
|
|
|
|
|
} else if (decoderInitializationException.secureDecoderRequired) {
|
2021-10-12 14:56:50 +03:00
|
|
|
|
errorCode = "2012";
|
2017-01-11 12:51:45 +00:00
|
|
|
|
errorString = getResources().getString(R.string.error_no_secure_decoder,
|
|
|
|
|
decoderInitializationException.mimeType);
|
|
|
|
|
} else {
|
2021-10-12 14:56:50 +03:00
|
|
|
|
errorCode = "2013";
|
2017-01-11 12:51:45 +00:00
|
|
|
|
errorString = getResources().getString(R.string.error_no_decoder,
|
|
|
|
|
decoderInitializationException.mimeType);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2021-10-12 14:56:50 +03:00
|
|
|
|
errorCode = "2014";
|
2017-01-11 12:51:45 +00:00
|
|
|
|
errorString = getResources().getString(R.string.error_instantiating_decoder,
|
2020-06-11 14:17:33 +01:00
|
|
|
|
decoderInitializationException.codecInfo.name);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-06-02 02:24:13 -07:00
|
|
|
|
else if (e.type == ExoPlaybackException.TYPE_SOURCE) {
|
2021-10-13 18:03:29 +03:00
|
|
|
|
// Re-initialization improves recovery speed and properly resumes
|
|
|
|
|
needsReInitialization = true;
|
|
|
|
|
errorString = getResources().getString(R.string.unrecognized_media_format);
|
2021-10-12 14:56:50 +03:00
|
|
|
|
Exception cause = e.getSourceException();
|
|
|
|
|
if (cause instanceof DefaultDrmSessionManager.MissingSchemeDataException) {
|
|
|
|
|
errorCode = "3004";
|
|
|
|
|
errorString = getResources().getString(R.string.unrecognized_media_format);
|
|
|
|
|
} else if(cause instanceof MediaDrmCallbackException) {
|
|
|
|
|
errorCode = "3005";
|
|
|
|
|
errorString = getResources().getString(R.string.unrecognized_media_format);
|
|
|
|
|
} else {
|
|
|
|
|
errorCode = "2021";
|
|
|
|
|
errorString = getResources().getString(R.string.unrecognized_media_format);
|
|
|
|
|
}
|
|
|
|
|
if (cause != null) {
|
|
|
|
|
Throwable rootCause = cause.getCause();
|
|
|
|
|
if (rootCause instanceof MediaDrmCallbackException) {
|
|
|
|
|
errorCode = "3005";
|
|
|
|
|
errorString = getResources().getString(R.string.unrecognized_media_format);
|
2021-10-13 18:03:29 +03:00
|
|
|
|
if (!hasDrmFailed) {
|
|
|
|
|
// When DRM fails to reach the app level certificate server it will fail with a source error so we assume that it is DRM related and try one more time
|
|
|
|
|
hasDrmFailed = true;
|
|
|
|
|
playerNeedsSource = true;
|
|
|
|
|
updateResumePosition();
|
|
|
|
|
initializePlayer();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-10-12 14:56:50 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-12-06 08:56:41 -08:00
|
|
|
|
}
|
2021-10-12 14:56:50 +03:00
|
|
|
|
eventEmitter.error(errorString, ex, errorCode);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
playerNeedsSource = true;
|
2017-03-21 20:25:17 +00:00
|
|
|
|
if (isBehindLiveWindow(e)) {
|
|
|
|
|
clearResumePosition();
|
|
|
|
|
initializePlayer();
|
|
|
|
|
} else {
|
|
|
|
|
updateResumePosition();
|
2021-10-13 18:03:29 +03:00
|
|
|
|
if (needsReInitialization) {
|
|
|
|
|
initializePlayer();
|
|
|
|
|
}
|
2017-03-21 20:25:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static boolean isBehindLiveWindow(ExoPlaybackException e) {
|
2020-05-15 12:55:19 +05:30
|
|
|
|
Log.e("ExoPlayer Exception", e.toString());
|
2017-03-21 20:25:17 +00:00
|
|
|
|
if (e.type != ExoPlaybackException.TYPE_SOURCE) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
Throwable cause = e.getSourceException();
|
|
|
|
|
while (cause != null) {
|
2020-05-15 12:55:19 +05:30
|
|
|
|
if (cause instanceof BehindLiveWindowException ||
|
|
|
|
|
cause instanceof HttpDataSource.HttpDataSourceException) {
|
2017-03-21 20:25:17 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
cause = cause.getCause();
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-17 14:14:21 -07:00
|
|
|
|
public int getTrackRendererIndex(int trackType) {
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
if (player != null) {
|
|
|
|
|
int rendererCount = player.getRendererCount();
|
|
|
|
|
for (int rendererIndex = 0; rendererIndex < rendererCount; rendererIndex++) {
|
|
|
|
|
if (player.getRendererType(rendererIndex) == trackType) {
|
|
|
|
|
return rendererIndex;
|
|
|
|
|
}
|
2018-06-02 02:24:13 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return C.INDEX_UNSET;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-14 03:38:02 +01:00
|
|
|
|
@Override
|
|
|
|
|
public void onMetadata(Metadata metadata) {
|
|
|
|
|
eventEmitter.timedMetadata(metadata);
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
// ReactExoplayerViewManager public api
|
|
|
|
|
|
2017-10-02 20:11:41 +02:00
|
|
|
|
public void setSrc(final Uri uri, final String extension, Map<String, String> headers) {
|
2017-01-11 12:51:45 +00:00
|
|
|
|
if (uri != null) {
|
2017-03-21 20:26:23 +00:00
|
|
|
|
boolean isSourceEqual = uri.equals(srcUri);
|
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
this.srcUri = uri;
|
|
|
|
|
this.extension = extension;
|
2017-10-02 20:11:41 +02:00
|
|
|
|
this.requestHeaders = headers;
|
2019-09-16 16:29:31 -04:00
|
|
|
|
this.mediaDataSourceFactory =
|
|
|
|
|
DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, bandwidthMeter,
|
|
|
|
|
this.requestHeaders);
|
2017-03-21 20:26:23 +00:00
|
|
|
|
|
2021-06-30 10:24:21 +09:00
|
|
|
|
if (!isSourceEqual) {
|
2017-03-21 20:26:23 +00:00
|
|
|
|
reloadSource();
|
|
|
|
|
}
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-30 10:24:21 +09:00
|
|
|
|
public void clearSrc() {
|
|
|
|
|
if (srcUri != null) {
|
|
|
|
|
player.stop(true);
|
|
|
|
|
this.srcUri = null;
|
|
|
|
|
this.extension = null;
|
|
|
|
|
this.requestHeaders = null;
|
|
|
|
|
this.mediaDataSourceFactory = null;
|
|
|
|
|
clearResumePosition();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-31 18:15:39 +02:00
|
|
|
|
public void setProgressUpdateInterval(final float progressUpdateInterval) {
|
|
|
|
|
mProgressUpdateInterval = progressUpdateInterval;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-01 21:41:57 +05:30
|
|
|
|
public void setReportBandwidth(boolean reportBandwidth) {
|
2018-08-25 21:53:11 +05:30
|
|
|
|
mReportBandwidth = reportBandwidth;
|
2019-09-16 16:29:31 -04:00
|
|
|
|
}
|
2018-08-25 21:53:11 +05:30
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
public void setRawSrc(final Uri uri, final String extension) {
|
|
|
|
|
if (uri != null) {
|
2017-03-21 20:26:23 +00:00
|
|
|
|
boolean isSourceEqual = uri.equals(srcUri);
|
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
this.srcUri = uri;
|
|
|
|
|
this.extension = extension;
|
2018-12-09 00:40:05 +01:00
|
|
|
|
this.mediaDataSourceFactory = buildDataSourceFactory(true);
|
2017-03-21 20:26:23 +00:00
|
|
|
|
|
2021-06-30 10:24:21 +09:00
|
|
|
|
if (!isSourceEqual) {
|
2017-03-21 20:26:23 +00:00
|
|
|
|
reloadSource();
|
|
|
|
|
}
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-11 21:25:58 -07:00
|
|
|
|
public void setTextTracks(ReadableArray textTracks) {
|
|
|
|
|
this.textTracks = textTracks;
|
|
|
|
|
reloadSource();
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-21 20:26:23 +00:00
|
|
|
|
private void reloadSource() {
|
|
|
|
|
playerNeedsSource = true;
|
|
|
|
|
initializePlayer();
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
public void setResizeModeModifier(@ResizeMode.Mode int resizeMode) {
|
|
|
|
|
exoPlayerView.setResizeMode(resizeMode);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-08 12:47:05 +02:00
|
|
|
|
private void applyModifiers() {
|
|
|
|
|
setRepeatModifier(repeat);
|
2019-08-01 11:33:28 -07:00
|
|
|
|
setMutedModifier(muted);
|
2019-07-08 12:47:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
public void setRepeatModifier(boolean repeat) {
|
2018-05-28 21:01:22 -07:00
|
|
|
|
if (player != null) {
|
|
|
|
|
if (repeat) {
|
|
|
|
|
player.setRepeatMode(Player.REPEAT_MODE_ONE);
|
|
|
|
|
} else {
|
|
|
|
|
player.setRepeatMode(Player.REPEAT_MODE_OFF);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-11 12:51:45 +00:00
|
|
|
|
this.repeat = repeat;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-16 14:31:23 +02:00
|
|
|
|
public void setPreventsDisplaySleepDuringVideoPlayback(boolean preventsDisplaySleepDuringVideoPlayback) {
|
|
|
|
|
this.preventsDisplaySleepDuringVideoPlayback = preventsDisplaySleepDuringVideoPlayback;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-17 14:14:21 -07:00
|
|
|
|
public void setSelectedTrack(int trackType, String type, Dynamic value) {
|
2019-07-07 10:21:23 +02:00
|
|
|
|
if (player == null) return;
|
2018-07-17 14:14:21 -07:00
|
|
|
|
int rendererIndex = getTrackRendererIndex(trackType);
|
|
|
|
|
if (rendererIndex == C.INDEX_UNSET) {
|
2018-06-02 02:24:13 -07:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
|
|
|
|
|
if (info == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-17 14:14:21 -07:00
|
|
|
|
TrackGroupArray groups = info.getTrackGroups(rendererIndex);
|
2018-08-24 15:33:46 +05:30
|
|
|
|
int groupIndex = C.INDEX_UNSET;
|
|
|
|
|
int[] tracks = {0} ;
|
2018-07-09 16:18:42 -07:00
|
|
|
|
|
2018-06-02 02:24:13 -07:00
|
|
|
|
if (TextUtils.isEmpty(type)) {
|
2018-07-17 14:14:21 -07:00
|
|
|
|
type = "default";
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-07 23:10:03 -07:00
|
|
|
|
DefaultTrackSelector.Parameters disableParameters = trackSelector.getParameters()
|
|
|
|
|
.buildUpon()
|
|
|
|
|
.setRendererDisabled(rendererIndex, true)
|
|
|
|
|
.build();
|
|
|
|
|
|
2018-07-17 14:14:21 -07:00
|
|
|
|
if (type.equals("disabled")) {
|
2018-08-07 23:10:03 -07:00
|
|
|
|
trackSelector.setParameters(disableParameters);
|
2018-06-02 19:41:50 -07:00
|
|
|
|
return;
|
2018-06-02 02:24:13 -07:00
|
|
|
|
} else if (type.equals("language")) {
|
|
|
|
|
for (int i = 0; i < groups.length; ++i) {
|
|
|
|
|
Format format = groups.get(i).getFormat(0);
|
|
|
|
|
if (format.language != null && format.language.equals(value.asString())) {
|
2018-08-24 15:33:46 +05:30
|
|
|
|
groupIndex = i;
|
2018-06-02 02:24:13 -07:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (type.equals("title")) {
|
|
|
|
|
for (int i = 0; i < groups.length; ++i) {
|
|
|
|
|
Format format = groups.get(i).getFormat(0);
|
|
|
|
|
if (format.id != null && format.id.equals(value.asString())) {
|
2018-08-24 15:33:46 +05:30
|
|
|
|
groupIndex = i;
|
2018-06-02 02:24:13 -07:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (type.equals("index")) {
|
2018-07-17 14:14:21 -07:00
|
|
|
|
if (value.asInt() < groups.length) {
|
2018-08-24 15:33:46 +05:30
|
|
|
|
groupIndex = value.asInt();
|
|
|
|
|
}
|
2018-12-31 21:33:02 -08:00
|
|
|
|
} else if (type.equals("resolution")) {
|
|
|
|
|
int height = value.asInt();
|
|
|
|
|
for (int i = 0; i < groups.length; ++i) { // Search for the exact height
|
|
|
|
|
TrackGroup group = groups.get(i);
|
2021-11-09 14:22:32 +02:00
|
|
|
|
Format closestFormat = null;
|
|
|
|
|
int closestTrackIndex = -1;
|
|
|
|
|
boolean usingExactMatch = false;
|
2018-12-31 21:33:02 -08:00
|
|
|
|
for (int j = 0; j < group.length; j++) {
|
|
|
|
|
Format format = group.getFormat(j);
|
2019-01-30 22:43:12 +05:30
|
|
|
|
if (format.height == height) {
|
2018-12-31 21:33:02 -08:00
|
|
|
|
groupIndex = i;
|
|
|
|
|
tracks[0] = j;
|
2021-11-09 14:22:32 +02:00
|
|
|
|
closestFormat = null;
|
|
|
|
|
closestTrackIndex = -1;
|
|
|
|
|
usingExactMatch = true;
|
2018-12-31 21:33:02 -08:00
|
|
|
|
break;
|
2021-11-09 14:22:32 +02:00
|
|
|
|
} else if (isUsingContentResolution) {
|
|
|
|
|
// When using content resolution rather than ads, we need to try and find the closest match if there is no exact match
|
|
|
|
|
if (closestFormat != null) {
|
|
|
|
|
if ((format.bitrate > closestFormat.bitrate || format.height > closestFormat.height) && format.height < height) {
|
|
|
|
|
// Higher quality match
|
|
|
|
|
closestFormat = format;
|
|
|
|
|
closestTrackIndex = j;
|
|
|
|
|
}
|
|
|
|
|
} else if(format.height < height) {
|
|
|
|
|
closestFormat = format;
|
|
|
|
|
closestTrackIndex = j;
|
|
|
|
|
}
|
2018-08-24 15:33:46 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
2021-11-09 14:22:32 +02:00
|
|
|
|
// This is a fallback if the new period contains only higher resolutions than the user has selected
|
|
|
|
|
if (closestFormat == null && isUsingContentResolution && !usingExactMatch) {
|
|
|
|
|
// No close match found - so we pick the lowest quality
|
|
|
|
|
int minHeight = Integer.MAX_VALUE;
|
|
|
|
|
for (int j = 0; j < group.length; j++) {
|
|
|
|
|
Format format = group.getFormat(j);
|
|
|
|
|
if (format.height < minHeight) {
|
|
|
|
|
minHeight = format.height;
|
|
|
|
|
groupIndex = i;
|
|
|
|
|
tracks[0] = j;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Selecting the closest match found
|
|
|
|
|
if (closestFormat != null && closestTrackIndex != -1) {
|
|
|
|
|
// We found the closest match instead of an exact one
|
|
|
|
|
groupIndex = i;
|
|
|
|
|
tracks[0] = closestTrackIndex;
|
|
|
|
|
}
|
2018-07-17 14:14:21 -07:00
|
|
|
|
}
|
2018-12-31 21:33:02 -08:00
|
|
|
|
} else if (rendererIndex == C.TRACK_TYPE_TEXT && Util.SDK_INT > 18) { // Text default
|
|
|
|
|
// Use system settings if possible
|
|
|
|
|
CaptioningManager captioningManager
|
|
|
|
|
= (CaptioningManager)themedReactContext.getSystemService(Context.CAPTIONING_SERVICE);
|
|
|
|
|
if (captioningManager != null && captioningManager.isEnabled()) {
|
2018-08-24 15:33:46 +05:30
|
|
|
|
groupIndex = getGroupIndexForDefaultLocale(groups);
|
2018-07-17 14:14:21 -07:00
|
|
|
|
}
|
2018-12-31 21:33:02 -08:00
|
|
|
|
} else if (rendererIndex == C.TRACK_TYPE_AUDIO) { // Audio default
|
|
|
|
|
groupIndex = getGroupIndexForDefaultLocale(groups);
|
2018-06-02 02:24:13 -07:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-03 23:24:02 -07:00
|
|
|
|
if (groupIndex == C.INDEX_UNSET && trackType == C.TRACK_TYPE_VIDEO && groups.length != 0) { // Video auto
|
|
|
|
|
// Add all tracks as valid options for ABR to choose from
|
|
|
|
|
TrackGroup group = groups.get(0);
|
2021-09-26 16:38:10 +03:00
|
|
|
|
int[] allTracks = new int[group.length];
|
2019-04-03 23:24:02 -07:00
|
|
|
|
groupIndex = 0;
|
2021-09-26 16:38:10 +03:00
|
|
|
|
|
2019-04-03 23:24:02 -07:00
|
|
|
|
for (int j = 0; j < group.length; j++) {
|
2021-09-26 16:38:10 +03:00
|
|
|
|
allTracks[j] = j;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Valiate list of all tracks and add only supported formats
|
|
|
|
|
int supportedFormatLength = 0;
|
|
|
|
|
ArrayList<Integer> supportedTrackList = new ArrayList<Integer>();
|
|
|
|
|
for (int g = 0; g < allTracks.length; g++) {
|
|
|
|
|
Format format = group.getFormat(g);
|
|
|
|
|
if (isFormatSupported(format)) {
|
|
|
|
|
supportedFormatLength++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
tracks = new int[supportedFormatLength + 1];
|
|
|
|
|
int o = 0;
|
|
|
|
|
for (int k = 0; k < allTracks.length; k++) {
|
|
|
|
|
Format format = group.getFormat(k);
|
|
|
|
|
if (isFormatSupported(format)) {
|
|
|
|
|
tracks[o] = allTracks[k];
|
|
|
|
|
supportedTrackList.add(allTracks[k]);
|
|
|
|
|
o++;
|
|
|
|
|
}
|
2018-12-31 21:33:02 -08:00
|
|
|
|
}
|
2019-09-16 16:29:31 -04:00
|
|
|
|
}
|
2019-03-19 13:46:01 -04:00
|
|
|
|
|
|
|
|
|
if (groupIndex == C.INDEX_UNSET) {
|
2018-08-07 23:10:03 -07:00
|
|
|
|
trackSelector.setParameters(disableParameters);
|
2018-06-02 02:24:13 -07:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-07 23:10:03 -07:00
|
|
|
|
DefaultTrackSelector.Parameters selectionParameters = trackSelector.getParameters()
|
|
|
|
|
.buildUpon()
|
|
|
|
|
.setRendererDisabled(rendererIndex, false)
|
|
|
|
|
.setSelectionOverride(rendererIndex, groups,
|
2018-08-24 15:33:46 +05:30
|
|
|
|
new DefaultTrackSelector.SelectionOverride(groupIndex, tracks))
|
2018-08-07 23:10:03 -07:00
|
|
|
|
.build();
|
|
|
|
|
trackSelector.setParameters(selectionParameters);
|
2018-07-17 14:14:21 -07:00
|
|
|
|
}
|
|
|
|
|
|
2021-09-26 16:38:10 +03:00
|
|
|
|
private boolean isFormatSupported(Format format) {
|
|
|
|
|
int width = format.width == Format.NO_VALUE ? 0 : format.width;
|
|
|
|
|
int height = format.height == Format.NO_VALUE ? 0 : format.height;
|
|
|
|
|
float frameRate = format.frameRate == Format.NO_VALUE ? 0 : format.frameRate;
|
|
|
|
|
String mimeType = format.sampleMimeType;
|
|
|
|
|
if (mimeType == null) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
boolean isSupported = false;
|
|
|
|
|
try {
|
|
|
|
|
MediaCodecInfo codecInfo = MediaCodecUtil.getDecoderInfo(mimeType, false, false);
|
|
|
|
|
isSupported = codecInfo.isVideoSizeAndRateSupportedV21(width, height, frameRate);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
// Failed to get decoder info - assume it is supported
|
|
|
|
|
isSupported = true;
|
|
|
|
|
}
|
|
|
|
|
return isSupported;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 15:33:46 +05:30
|
|
|
|
private int getGroupIndexForDefaultLocale(TrackGroupArray groups) {
|
2018-12-31 21:33:02 -08:00
|
|
|
|
if (groups.length == 0){
|
|
|
|
|
return C.INDEX_UNSET;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 15:33:46 +05:30
|
|
|
|
int groupIndex = 0; // default if no match
|
2018-07-17 14:14:21 -07:00
|
|
|
|
String locale2 = Locale.getDefault().getLanguage(); // 2 letter code
|
|
|
|
|
String locale3 = Locale.getDefault().getISO3Language(); // 3 letter code
|
|
|
|
|
for (int i = 0; i < groups.length; ++i) {
|
|
|
|
|
Format format = groups.get(i).getFormat(0);
|
|
|
|
|
String language = format.language;
|
|
|
|
|
if (language != null && (language.equals(locale2) || language.equals(locale3))) {
|
2018-08-24 15:33:46 +05:30
|
|
|
|
groupIndex = i;
|
2018-07-17 14:14:21 -07:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-24 15:33:46 +05:30
|
|
|
|
return groupIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setSelectedVideoTrack(String type, Dynamic value) {
|
|
|
|
|
videoTrackType = type;
|
|
|
|
|
videoTrackValue = value;
|
|
|
|
|
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
|
2018-07-17 14:14:21 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setSelectedAudioTrack(String type, Dynamic value) {
|
|
|
|
|
audioTrackType = type;
|
|
|
|
|
audioTrackValue = value;
|
|
|
|
|
setSelectedTrack(C.TRACK_TYPE_AUDIO, audioTrackType, audioTrackValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setSelectedTextTrack(String type, Dynamic value) {
|
|
|
|
|
textTrackType = type;
|
|
|
|
|
textTrackValue = value;
|
|
|
|
|
setSelectedTrack(C.TRACK_TYPE_TEXT, textTrackType, textTrackValue);
|
2018-06-02 02:24:13 -07:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-11 12:51:45 +00:00
|
|
|
|
public void setPausedModifier(boolean paused) {
|
|
|
|
|
isPaused = paused;
|
|
|
|
|
if (player != null) {
|
|
|
|
|
if (!paused) {
|
|
|
|
|
startPlayback();
|
|
|
|
|
} else {
|
|
|
|
|
pausePlayback();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setMutedModifier(boolean muted) {
|
2019-08-01 11:33:28 -07:00
|
|
|
|
this.muted = muted;
|
2018-11-14 12:33:28 +01:00
|
|
|
|
audioVolume = muted ? 0.f : 1.f;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
if (player != null) {
|
2018-11-14 12:33:28 +01:00
|
|
|
|
player.setVolume(audioVolume);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void setVolumeModifier(float volume) {
|
2018-11-14 12:33:28 +01:00
|
|
|
|
audioVolume = volume;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
if (player != null) {
|
2018-11-14 12:33:28 +01:00
|
|
|
|
player.setVolume(audioVolume);
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void seekTo(long positionMs) {
|
|
|
|
|
if (player != null) {
|
2018-11-27 19:48:41 -08:00
|
|
|
|
seekTime = positionMs;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
player.seekTo(positionMs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-14 00:45:12 +02:00
|
|
|
|
public void setRateModifier(float newRate) {
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
rate = newRate;
|
2017-06-14 00:45:12 +02:00
|
|
|
|
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
if (player != null) {
|
|
|
|
|
PlaybackParameters params = new PlaybackParameters(rate, 1f);
|
|
|
|
|
player.setPlaybackParameters(params);
|
|
|
|
|
}
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-26 14:50:31 -08:00
|
|
|
|
public void setMaxBitRateModifier(int newMaxBitRate) {
|
|
|
|
|
maxBitRate = newMaxBitRate;
|
2018-10-29 09:53:52 -07:00
|
|
|
|
if (player != null) {
|
|
|
|
|
trackSelector.setParameters(trackSelector.buildUponParameters()
|
2018-12-13 09:50:43 -08:00
|
|
|
|
.setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate));
|
2018-10-29 09:53:52 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-10 19:45:31 -08:00
|
|
|
|
public void setMinLoadRetryCountModifier(int newMinLoadRetryCount) {
|
|
|
|
|
minLoadRetryCount = newMinLoadRetryCount;
|
2019-01-24 18:49:37 +05:30
|
|
|
|
releasePlayer();
|
|
|
|
|
initializePlayer();
|
|
|
|
|
}
|
2017-01-11 12:51:45 +00:00
|
|
|
|
|
|
|
|
|
public void setPlayInBackground(boolean playInBackground) {
|
2017-05-09 06:30:45 +10:00
|
|
|
|
this.playInBackground = playInBackground;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setDisableFocus(boolean disableFocus) {
|
|
|
|
|
this.disableFocus = disableFocus;
|
|
|
|
|
}
|
2018-05-17 15:42:44 -07:00
|
|
|
|
|
2021-05-17 13:09:09 +03:00
|
|
|
|
public void setBackBufferDurationMs(int backBufferDurationMs) {
|
2022-01-21 14:10:22 +02:00
|
|
|
|
Runtime runtime = Runtime.getRuntime();
|
|
|
|
|
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
|
|
|
|
|
long freeMemory = runtime.maxMemory() - usedMemory;
|
|
|
|
|
long reserveMemory = (long)minBackBufferMemoryReservePercent * runtime.maxMemory();
|
|
|
|
|
if (reserveMemory > freeMemory) {
|
|
|
|
|
// We don't have enough memory in reserve so we will
|
|
|
|
|
Log.w("ExoPlayer Warning", "Not enough reserve memory, setting back buffer to 0ms to reduce memory pressure!");
|
|
|
|
|
this.backBufferDurationMs = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-05-17 13:09:09 +03:00
|
|
|
|
this.backBufferDurationMs = backBufferDurationMs;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-09 14:22:32 +02:00
|
|
|
|
public void setContentStartTime(int contentStartTime) {
|
|
|
|
|
this.contentStartTime = (long)contentStartTime;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-18 03:58:04 -07:00
|
|
|
|
public void setDisableBuffering(boolean disableBuffering) {
|
|
|
|
|
this.disableBuffering = disableBuffering;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-17 13:09:09 +03:00
|
|
|
|
public void setDisableDisconnectError(boolean disableDisconnectError) {
|
|
|
|
|
this.disableDisconnectError = disableDisconnectError;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-17 15:42:44 -07:00
|
|
|
|
public void setFullscreen(boolean fullscreen) {
|
|
|
|
|
if (fullscreen == isFullscreen) {
|
|
|
|
|
return; // Avoid generating events when nothing is changing
|
|
|
|
|
}
|
|
|
|
|
isFullscreen = fullscreen;
|
|
|
|
|
|
|
|
|
|
Activity activity = themedReactContext.getCurrentActivity();
|
|
|
|
|
if (activity == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Window window = activity.getWindow();
|
|
|
|
|
View decorView = window.getDecorView();
|
|
|
|
|
int uiOptions;
|
|
|
|
|
if (isFullscreen) {
|
|
|
|
|
if (Util.SDK_INT >= 19) { // 4.4+
|
|
|
|
|
uiOptions = SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
|
|
|
|
| SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
|
|
|
|
| SYSTEM_UI_FLAG_FULLSCREEN;
|
|
|
|
|
} else {
|
|
|
|
|
uiOptions = SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
|
|
|
|
| SYSTEM_UI_FLAG_FULLSCREEN;
|
|
|
|
|
}
|
|
|
|
|
eventEmitter.fullscreenWillPresent();
|
|
|
|
|
decorView.setSystemUiVisibility(uiOptions);
|
|
|
|
|
eventEmitter.fullscreenDidPresent();
|
|
|
|
|
} else {
|
|
|
|
|
uiOptions = View.SYSTEM_UI_FLAG_VISIBLE;
|
|
|
|
|
eventEmitter.fullscreenWillDismiss();
|
|
|
|
|
decorView.setSystemUiVisibility(uiOptions);
|
|
|
|
|
eventEmitter.fullscreenDidDismiss();
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-06-08 00:01:13 -07:00
|
|
|
|
|
|
|
|
|
public void setUseTextureView(boolean useTextureView) {
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
boolean finallyUseTextureView = useTextureView && this.drmUUID == null;
|
|
|
|
|
exoPlayerView.setUseTextureView(finallyUseTextureView);
|
2018-06-08 00:01:13 -07:00
|
|
|
|
}
|
2018-07-31 17:23:20 +02:00
|
|
|
|
|
2022-02-14 21:17:22 -04:00
|
|
|
|
public void useSecureView(boolean useSecureView) {
|
|
|
|
|
exoPlayerView.useSecureView(useSecureView);
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-28 14:56:58 +02:00
|
|
|
|
public void setHideShutterView(boolean hideShutterView) {
|
|
|
|
|
exoPlayerView.setHideShutterView(hideShutterView);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-21 14:10:22 +02:00
|
|
|
|
public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs, double newMaxHeapAllocationPercent, double newMinBackBufferMemoryReservePercent, double newMinBufferMemoryReservePercent) {
|
2018-08-02 09:20:08 +02:00
|
|
|
|
minBufferMs = newMinBufferMs;
|
|
|
|
|
maxBufferMs = newMaxBufferMs;
|
|
|
|
|
bufferForPlaybackMs = newBufferForPlaybackMs;
|
|
|
|
|
bufferForPlaybackAfterRebufferMs = newBufferForPlaybackAfterRebufferMs;
|
2021-11-04 13:54:43 -04:00
|
|
|
|
maxHeapAllocationPercent = newMaxHeapAllocationPercent;
|
2022-01-21 14:10:22 +02:00
|
|
|
|
minBackBufferMemoryReservePercent = newMinBackBufferMemoryReservePercent;
|
|
|
|
|
minBufferMemoryReservePercent = newMinBufferMemoryReservePercent;
|
2018-08-03 15:54:18 -07:00
|
|
|
|
releasePlayer();
|
2018-08-01 15:58:02 +02:00
|
|
|
|
initializePlayer();
|
2018-07-31 17:23:20 +02:00
|
|
|
|
}
|
2019-01-04 14:58:32 +05:30
|
|
|
|
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
public void setDrmType(UUID drmType) {
|
|
|
|
|
this.drmUUID = drmType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setDrmLicenseUrl(String licenseUrl){
|
|
|
|
|
this.drmLicenseUrl = licenseUrl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setDrmLicenseHeader(String[] header){
|
|
|
|
|
this.drmLicenseHeader = header;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
2021-03-17 17:49:10 +02:00
|
|
|
|
public void onDrmKeysLoaded(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
Log.d("DRM Info", "onDrmKeysLoaded");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2021-03-17 17:49:10 +02:00
|
|
|
|
public void onDrmSessionManagerError(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId, Exception e) {
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
Log.d("DRM Info", "onDrmSessionManagerError");
|
2021-10-12 14:56:50 +03:00
|
|
|
|
eventEmitter.error("onDrmSessionManagerError", e, "3002");
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2021-03-17 17:49:10 +02:00
|
|
|
|
public void onDrmKeysRestored(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
Log.d("DRM Info", "onDrmKeysRestored");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2021-03-17 17:49:10 +02:00
|
|
|
|
public void onDrmKeysRemoved(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
|
Add iOS and Android basic DRM support (#1445)
This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey)
I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR.
**Test stream for ANDROID:**
```
testStream = {
uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)',
type: 'mpd',
drm: {
type: DRMType.PLAYREADY,
licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)'
}
};
```
or
```
{
uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd',
drm: {
type: 'widevine', //or DRMType.WIDEVINE
licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
headers: {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
},
}
}
```
**Test stream for iOS:**
Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them.
It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
|
|
|
|
Log.d("DRM Info", "onDrmKeysRemoved");
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-04 14:58:32 +05:30
|
|
|
|
/**
|
|
|
|
|
* Handling controls prop
|
2019-09-16 16:29:31 -04:00
|
|
|
|
*
|
2019-02-10 18:15:30 -08:00
|
|
|
|
* @param controls Controls prop, if true enable controls, if false disable them
|
2019-01-04 14:58:32 +05:30
|
|
|
|
*/
|
|
|
|
|
public void setControls(boolean controls) {
|
2019-07-07 22:17:15 +02:00
|
|
|
|
this.controls = controls;
|
|
|
|
|
if (player == null || exoPlayerView == null) return;
|
|
|
|
|
if (controls) {
|
2019-02-10 18:15:30 -08:00
|
|
|
|
addPlayerControl();
|
2019-07-07 22:17:15 +02:00
|
|
|
|
} else {
|
|
|
|
|
int indexOfPC = indexOfChild(playerControlView);
|
|
|
|
|
if (indexOfPC != -1) {
|
|
|
|
|
removeViewAt(indexOfPC);
|
|
|
|
|
}
|
2019-01-04 14:58:32 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-11 12:51:45 +00:00
|
|
|
|
}
|