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;
|
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;
|
|
|
|
|
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;
|
|
|
|
|
import com.google.android.exoplayer2.drm.UnsupportedDrmException;
|
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;
|
|
|
|
|
|
|
|
|
|
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;
|
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
|
|
|
|
|
|
|
|
|
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;
|
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;
|
|
|
|
|
|
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-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();
|
|
|
|
|
if(!player.getCurrentTimeline().isEmpty()) {
|
|
|
|
|
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 {
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean shouldContinueLoading(long playbackPositionUs, long bufferedDurationUs, float playbackSpeed) {
|
|
|
|
|
if (ReactExoplayerView.this.disableBuffering) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
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() {
|
|
|
|
|
if (player == null) {
|
2021-03-17 17:49:10 +02:00
|
|
|
|
ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
|
2019-07-07 10:21:23 +02:00
|
|
|
|
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
|
|
|
|
trackSelector.setParameters(trackSelector.buildUponParameters()
|
|
|
|
|
.setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate));
|
2017-06-14 00:45:12 +02:00
|
|
|
|
|
2019-07-07 10:21:23 +02:00
|
|
|
|
DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
|
2021-03-18 03:58:04 -07:00
|
|
|
|
RNVLoadControl loadControl = new RNVLoadControl(
|
|
|
|
|
allocator,
|
|
|
|
|
minBufferMs,
|
|
|
|
|
maxBufferMs,
|
|
|
|
|
bufferForPlaybackMs,
|
|
|
|
|
bufferForPlaybackAfterRebufferMs,
|
|
|
|
|
-1,
|
|
|
|
|
true,
|
2021-05-17 13:09:09 +03:00
|
|
|
|
backBufferDurationMs,
|
2021-03-18 03:58:04 -07:00
|
|
|
|
DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME
|
|
|
|
|
);
|
2019-09-16 16:29:31 -04:00
|
|
|
|
DefaultRenderersFactory renderersFactory =
|
|
|
|
|
new DefaultRenderersFactory(getContext())
|
|
|
|
|
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
|
2021-03-17 17:49:10 +02:00
|
|
|
|
player = new SimpleExoPlayer.Builder(getContext(), renderersFactory)
|
|
|
|
|
.setTrackSelector(trackSelector)
|
|
|
|
|
.setBandwidthMeter(bandwidthMeter)
|
2021-03-18 03:58:04 -07:00
|
|
|
|
.setLoadControl(loadControl)
|
2021-03-17 17:49:10 +02:00
|
|
|
|
.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);
|
|
|
|
|
}
|
|
|
|
|
if (playerNeedsSource && srcUri != null) {
|
|
|
|
|
exoPlayerView.invalidateAspectRatio();
|
|
|
|
|
|
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
|
|
|
|
// DRM
|
2021-03-17 17:49:10 +02:00
|
|
|
|
DrmSessionManager drmSessionManager = null;
|
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 (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);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// End DRM
|
2020-06-30 14:00:16 -03:00
|
|
|
|
|
2019-07-07 10:21:23 +02:00
|
|
|
|
ArrayList<MediaSource> mediaSourceList = buildTextSources();
|
2021-03-17 17:49:10 +02:00
|
|
|
|
MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager);
|
2019-07-07 10:21:23 +02:00
|
|
|
|
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
|
|
|
|
|
2019-07-07 10:21:23 +02:00
|
|
|
|
boolean haveResumePosition = resumeWindow != C.INDEX_UNSET;
|
|
|
|
|
if (haveResumePosition) {
|
|
|
|
|
player.seekTo(resumeWindow, resumePosition);
|
|
|
|
|
}
|
|
|
|
|
player.prepare(mediaSource, !haveResumePosition, false);
|
|
|
|
|
playerNeedsSource = false;
|
2017-01-11 12:51:45 +00:00
|
|
|
|
|
2021-06-30 10:24:21 +09:00
|
|
|
|
reLayout(exoPlayerView);
|
2019-07-07 10:21:23 +02:00
|
|
|
|
eventEmitter.loadStart();
|
|
|
|
|
loadVideoStarted = true;
|
|
|
|
|
}
|
2019-02-06 03:22:06 +05:30
|
|
|
|
|
2019-07-07 10:21:23 +02:00
|
|
|
|
// Initializing the playerControlView
|
|
|
|
|
initializePlayerControl();
|
2019-07-07 22:17:15 +02:00
|
|
|
|
setControls(controls);
|
2019-07-08 12:47:05 +02:00
|
|
|
|
applyModifiers();
|
2021-07-07 18:59:55 +03:00
|
|
|
|
startBufferCheckTimer();
|
2019-07-07 10:21:23 +02:00
|
|
|
|
}
|
|
|
|
|
}, 1);
|
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-03-17 17:49:10 +02:00
|
|
|
|
return new DefaultDrmSessionManager(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
|
|
|
|
FrameworkMediaDrm.newInstance(uuid), drmCallback, null, false, 3);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-17 17:49:10 +02:00
|
|
|
|
private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) {
|
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();
|
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() {
|
|
|
|
|
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);
|
2018-08-24 15:33:46 +05:30
|
|
|
|
videoTracks.pushMap(videoTrack);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return videoTracks;
|
|
|
|
|
}
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
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;
|
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;
|
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) {
|
|
|
|
|
errorString = getResources().getString(R.string.error_querying_decoders);
|
|
|
|
|
} else if (decoderInitializationException.secureDecoderRequired) {
|
|
|
|
|
errorString = getResources().getString(R.string.error_no_secure_decoder,
|
|
|
|
|
decoderInitializationException.mimeType);
|
|
|
|
|
} else {
|
|
|
|
|
errorString = getResources().getString(R.string.error_no_decoder,
|
|
|
|
|
decoderInitializationException.mimeType);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
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) {
|
2017-12-06 08:56:41 -08:00
|
|
|
|
errorString = getResources().getString(R.string.unrecognized_media_format);
|
|
|
|
|
}
|
2020-05-15 12:55:19 +05:30
|
|
|
|
eventEmitter.error(errorString, ex);
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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;
|
|
|
|
|
break;
|
2018-08-24 15:33:46 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
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);
|
|
|
|
|
tracks = new int[group.length];
|
|
|
|
|
groupIndex = 0;
|
|
|
|
|
for (int j = 0; j < group.length; j++) {
|
|
|
|
|
tracks[j] = j;
|
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
|
|
|
|
}
|
|
|
|
|
|
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) {
|
|
|
|
|
this.backBufferDurationMs = backBufferDurationMs;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
2018-11-28 14:56:58 +02:00
|
|
|
|
public void setHideShutterView(boolean hideShutterView) {
|
|
|
|
|
exoPlayerView.setHideShutterView(hideShutterView);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-02 09:20:08 +02:00
|
|
|
|
public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs) {
|
|
|
|
|
minBufferMs = newMinBufferMs;
|
|
|
|
|
maxBufferMs = newMaxBufferMs;
|
|
|
|
|
bufferForPlaybackMs = newBufferForPlaybackMs;
|
|
|
|
|
bufferForPlaybackAfterRebufferMs = newBufferForPlaybackAfterRebufferMs;
|
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");
|
|
|
|
|
eventEmitter.error("onDrmSessionManagerError", e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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
|
|
|
|
}
|