1821 lines
72 KiB
Java
Raw Normal View History

2017-01-11 12:51:45 +00:00
package com.brentvatne.exoplayer;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityManager;
2017-01-11 12:51:45 +00:00
import android.content.Context;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Handler;
2022-03-30 14:29:08 +03:00
import android.os.Looper;
2017-01-11 12:51:45 +00:00
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.accessibility.CaptioningManager;
2017-01-11 12:51:45 +00:00
import android.widget.FrameLayout;
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;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Dynamic;
2017-01-11 12:51:45 +00:00
import com.facebook.react.bridge.LifecycleEventListener;
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;
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;
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.drm.MediaDrmCallbackException;
2017-01-11 12:51:45 +00:00
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackParameters;
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;
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.MediaDrmCallbackException;
Add iOS and Android basic DRM support (#1445) This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey) I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR. **Test stream for ANDROID:** ``` testStream = { uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)', type: 'mpd', drm: { type: DRMType.PLAYREADY, licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)' } }; ``` or ``` { uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd', drm: { type: 'widevine', //or DRMType.WIDEVINE licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense', headers: { 'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU' }, } } ``` **Test stream for iOS:** Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them. It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
import com.google.android.exoplayer2.drm.UnsupportedDrmException;
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
2017-01-11 12:51:45 +00:00
import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer;
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
import com.google.android.exoplayer2.metadata.Metadata;
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;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
2018-06-11 15:23:43 -07:00
import com.google.android.exoplayer2.source.SingleSampleMediaSource;
import com.google.android.exoplayer2.source.TrackGroup;
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;
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;
import com.google.android.exoplayer2.trackselection.ExoTrackSelection;
2017-01-11 12:51:45 +00:00
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
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;
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;
import com.google.android.exoplayer2.util.Assertions;
2017-01-11 12:51:45 +00:00
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.source.dash.DashUtil;
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
import com.google.android.exoplayer2.source.dash.manifest.Period;
import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet;
import com.google.android.exoplayer2.source.dash.manifest.Representation;
import com.google.android.exoplayer2.source.dash.manifest.Descriptor;
2017-01-11 12:51:45 +00:00
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
2018-06-11 15:23:43 -07:00
import java.util.ArrayList;
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;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.List;
import java.lang.Thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.lang.Integer;
2017-01-11 12:51:45 +00:00
@SuppressLint("ViewConstructor")
class ReactExoplayerView extends FrameLayout implements
LifecycleEventListener,
Player.EventListener,
2018-11-01 21:41:57 +05:30
BandwidthMeter.EventListener,
2017-01-11 12:51:45 +00:00
BecomingNoisyListener,
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,
DrmSessionEventListener {
2017-01-11 12:51:45 +00:00
public static final double DEFAULT_MAX_HEAP_ALLOCATION_PERCENT = 1;
public static final double DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE = 0;
public static final double DEFAULT_MIN_BUFFER_MEMORY_RESERVE = 0;
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;
private final ReactExoplayerConfig config;
private final DefaultBandwidthMeter bandwidthMeter;
private PlayerControlView playerControlView;
2019-02-04 19:18:29 +05:30
private View playPauseControlContainer;
private Player.EventListener eventListener;
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;
private boolean isFullscreen;
private boolean isInBackground;
private boolean isPaused;
2017-01-11 12:51:45 +00:00
private boolean isBuffering;
private boolean muted = false;
private boolean hasAudioFocus = false;
private float rate = 1f;
private float audioVolume = 1f;
private int minLoadRetryCount = 3;
private int maxBitRate = 0;
private long seekTime = C.TIME_UNSET;
private boolean hasDrmFailed = false;
private boolean isUsingContentResolution = false;
private boolean selectTrackWhenReady = false;
2017-01-11 12:51:45 +00: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;
private double maxHeapAllocationPercent = ReactExoplayerView.DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
private double minBackBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE;
private double minBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
Add iOS and Android basic DRM support (#1445) This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey) I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR. **Test stream for ANDROID:** ``` testStream = { uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)', type: 'mpd', drm: { type: DRMType.PLAYREADY, licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)' } }; ``` or ``` { uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd', drm: { type: 'widevine', //or DRMType.WIDEVINE licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense', headers: { 'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU' }, } } ``` **Test stream for iOS:** Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them. It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
private Handler mainHandler;
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
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;
private String audioTrackType;
private Dynamic audioTrackValue;
private String videoTrackType;
private Dynamic videoTrackValue;
private String textTrackType;
private Dynamic textTrackValue;
private ReadableArray textTracks;
2017-01-11 12:51:45 +00:00
private boolean disableFocus;
private boolean disableBuffering;
private long contentStartTime;
private boolean disableDisconnectError;
private boolean preventsDisplaySleepDuringVideoPlayback = true;
private float mProgressUpdateInterval = 250.0f;
private boolean playInBackground = false;
private Map<String, String> requestHeaders;
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
&& player.getPlaybackState() == Player.STATE_READY
2017-01-11 12:51:45 +00:00
&& player.getPlayWhenReady()
) {
long pos = player.getCurrentPosition();
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);
sendMessageDelayed(msg, Math.round(mProgressUpdateInterval));
2017-01-11 12:51:45 +00:00
}
break;
}
}
};
2020-05-15 12:55:19 +05:30
public double getPositionInFirstPeriodMsForCurrentWindow(long currentPosition) {
Timeline.Window window = new Timeline.Window();
if(!player.getCurrentTimeline().isEmpty()) {
2020-05-15 12:55:19 +05:30
player.getCurrentTimeline().getWindow(player.getCurrentWindowIndex(), window);
}
return window.windowStartTimeMs + currentPosition;
}
2017-01-11 12:51:45 +00:00
public ReactExoplayerView(ThemedReactContext context, ReactExoplayerConfig config) {
2017-01-11 12:51:45 +00:00
super(context);
this.themedReactContext = context;
2018-11-01 21:41:57 +05:30
this.eventEmitter = new VideoEventEmitter(context);
this.config = config;
this.bandwidthMeter = config.getBandwidthMeter();
2017-01-11 12:51:45 +00:00
createViews();
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() {
if (!playInBackground || !isInBackground) {
setPlayWhenReady(!isPaused);
}
isInBackground = false;
2017-01-11 12:51:45 +00:00
}
@Override
public void onHostPause() {
isInBackground = true;
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) {
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
}
}
2017-01-11 12:51:45 +00:00
2018-11-01 21:41:57 +05:30
// Internal methods
/**
* Toggling the visibility of the player control view
*/
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()) {
playerControlView.hide();
} else {
playerControlView.show();
}
}
/**
2019-01-16 23:47:32 +05:30
* Initializing Player control
*/
2019-01-16 23:47:32 +05:30
private void initializePlayerControl() {
2019-02-10 18:15:30 -08:00
if (playerControlView == null) {
playerControlView = new PlayerControlView(getContext());
}
2019-02-10 18:15:30 -08:00
// Setting the player for the playerControlView
playerControlView.setPlayer(player);
playerControlView.show();
2019-02-04 19:18:29 +05:30
playPauseControlContainer = playerControlView.findViewById(R.id.exo_play_pause_container);
2019-02-10 18:15:30 -08:00
// Invoking onClick event for exoplayerView
exoPlayerView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
togglePlayerControlVisibility();
}
});
2019-02-04 19:18:29 +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);
//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);
}
/**
* Adding Player control to the frame layout
*/
private void addPlayerControl() {
2019-07-07 22:17:15 +02:00
if(player == null) return;
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);
}
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());
}
private class RNVLoadControl extends DefaultLoadControl {
private int availableHeapInBytes = 0;
private Runtime runtime;
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);
runtime = Runtime.getRuntime();
ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(themedReactContext.ACTIVITY_SERVICE);
availableHeapInBytes = (int) Math.floor(activityManager.getMemoryClass() * maxHeapAllocationPercent * 1024 * 1024);
}
@Override
public boolean shouldContinueLoading(long playbackPositionUs, long bufferedDurationUs, float playbackSpeed) {
if (ReactExoplayerView.this.disableBuffering) {
return false;
}
int loadedBytes = getAllocator().getTotalBytesAllocated();
boolean isHeapReached = availableHeapInBytes > 0 && loadedBytes >= availableHeapInBytes;
if (isHeapReached) {
return false;
}
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
long freeMemory = runtime.maxMemory() - usedMemory;
long reserveMemory = (long)minBufferMemoryReservePercent * runtime.maxMemory();
long bufferedMs = bufferedDurationUs / (long)1000;
if (reserveMemory > freeMemory && bufferedMs > 2000) {
// We don't have enough memory in reserve so we stop buffering to allow other components to use it instead
return false;
}
if (runtime.freeMemory() == 0) {
2021-11-16 21:40:38 -04:00
Log.w("ExoPlayer Warning", "Free memory reached 0, forcing garbage collection");
runtime.gc();
return false;
}
return super.shouldContinueLoading(playbackPositionUs, bufferedDurationUs, playbackSpeed);
}
}
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() {
ReactExoplayerView self = this;
// This ensures all props have been settled, to avoid async racing conditions.
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
try {
if (player == null) {
2022-03-30 14:35:48 +03:00
// Initialize core configuration and listeners
initializePlayerCore(self);
}
2022-03-30 14:29:08 +03:00
if (playerNeedsSource && srcUri != null) {
exoPlayerView.invalidateAspectRatio();
2022-03-30 14:29:08 +03:00
// DRM session manager creation must be done on a different thread to prevent crashes so we start a new thread
ExecutorService es = Executors.newSingleThreadExecutor();
es.execute(new Runnable() {
@Override
public void run() {
// DRM initialization must run on a different thread
2022-03-30 14:35:48 +03:00
initializePlayerDrm(self);
2022-03-30 14:29:08 +03:00
// Initialize handler to run on the main thread
new Handler(Looper.getMainLooper()).post(new Runnable () {
@Override
public void run () {
// Source initialization must run on the main thread
2022-03-30 14:35:48 +03:00
initializePlayerSource(self);
2022-03-30 14:29:08 +03:00
}
});
}
2022-03-30 14:29:08 +03:00
});
} else {
2022-03-30 14:35:48 +03:00
initializePlayerSource(self);
}
2022-03-30 14:29:08 +03:00
} catch (Exception ex) {
self.playerNeedsSource = true;
Log.e("ExoPlayer Exception", "Failed to initialize Player!");
Log.e("ExoPlayer Exception", ex.toString());
eventEmitter.error(ex.toString(), ex, "1001");
}
}
}, 1);
2017-01-11 12:51:45 +00:00
}
2022-03-30 14:35:48 +03:00
private void initializePlayerCore(ReactExoplayerView self) {
ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
trackSelector.setParameters(trackSelector.buildUponParameters()
.setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate));
DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
RNVLoadControl loadControl = new RNVLoadControl(
allocator,
minBufferMs,
maxBufferMs,
bufferForPlaybackMs,
bufferForPlaybackAfterRebufferMs,
-1,
true,
backBufferDurationMs,
DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME
);
DefaultRenderersFactory renderersFactory =
new DefaultRenderersFactory(getContext())
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
player = new SimpleExoPlayer.Builder(getContext(), renderersFactory)
.setTrackSelector(trackSelector)
.setBandwidthMeter(bandwidthMeter)
.setLoadControl(loadControl)
.build();
player.addListener(self);
player.addMetadataOutput(self);
exoPlayerView.setPlayer(player);
audioBecomingNoisyReceiver.setListener(self);
bandwidthMeter.addEventListener(new Handler(), self);
setPlayWhenReady(!isPaused);
playerNeedsSource = true;
PlaybackParameters params = new PlaybackParameters(rate, 1f);
player.setPlaybackParameters(params);
}
private void initializePlayerDrm(ReactExoplayerView self) {
2022-03-30 14:29:08 +03:00
DrmSessionManager drmSessionManager = null;
if (self.drmUUID != null) {
try {
drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl,
self.drmLicenseHeader);
} catch (UnsupportedDrmException e) {
int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported
: (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
eventEmitter.error(getResources().getString(errorStringId), e, "3003");
return;
}
}
if (drmSessionManager == null) {
// DRM Session Manager failed to instantiate, no other work can be done - error has been handled by the instantiating method
return;
}
}
2022-03-30 14:35:48 +03:00
private void initializePlayerSource(ReactExoplayerView self) {
2022-03-30 14:29:08 +03:00
ArrayList<MediaSource> mediaSourceList = buildTextSources();
MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager);
MediaSource mediaSource;
if (mediaSourceList.size() == 0) {
mediaSource = videoSource;
} else {
mediaSourceList.add(0, videoSource);
MediaSource[] textSourceArray = mediaSourceList.toArray(
new MediaSource[mediaSourceList.size()]
);
mediaSource = new MergingMediaSource(textSourceArray);
}
boolean haveResumePosition = resumeWindow != C.INDEX_UNSET;
if (haveResumePosition) {
player.seekTo(resumeWindow, resumePosition);
}
player.prepare(mediaSource, !haveResumePosition, false);
playerNeedsSource = false;
reLayout(exoPlayerView);
eventEmitter.loadStart();
loadVideoStarted = true;
finishPlayerInitialization();
}
private void finishPlayerInitialization() {
// Initializing the playerControlView
initializePlayerControl();
setControls(controls);
applyModifiers();
startBufferCheckTimer();
}
private DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray, int retryCount) throws UnsupportedDrmException {
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 (Util.SDK_INT < 18) {
return null;
}
try {
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]);
}
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 mediaDrm = FrameworkMediaDrm.newInstance(uuid);
if (hasDrmFailed) {
// When DRM fails using L1 we want to switch to L3
mediaDrm.setPropertyString("securityLevel", "L3");
}
return new DefaultDrmSessionManager(uuid, mediaDrm, drmCallback, null, false, 3);
} catch(UnsupportedDrmException e) {
// Unsupported DRM exceptions are handled by the calling method
throw e;
} catch (Exception e) {
if (retryCount < 3) {
// Attempt retry 3 times in case where the OS Media DRM Framework fails for whatever reason
return buildDrmSessionManager(uuid, licenseUrl, keyRequestPropertiesArray, ++retryCount);
}
// Handle the unknow exception and emit to JS
eventEmitter.error(ex.toString(), ex, "3006");
return 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
}
}
private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) {
if (uri == null) {
throw new IllegalStateException("Invalid video uri");
}
2017-01-11 12:51:45 +00:00
int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension
: uri.getLastPathSegment());
config.setDisableDisconnectError(this.disableDisconnectError);
2017-01-11 12:51:45 +00:00
switch (type) {
case C.TYPE_SS:
return new SsMediaSource.Factory(
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
buildDataSourceFactory(false)
).setDrmSessionManager(drmSessionManager)
.setLoadErrorHandlingPolicy(
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
).createMediaSource(uri);
2017-01-11 12:51:45 +00:00
case C.TYPE_DASH:
return new DashMediaSource.Factory(
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
buildDataSourceFactory(false)
).setDrmSessionManager(drmSessionManager)
.setLoadErrorHandlingPolicy(
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
).createMediaSource(uri);
2017-01-11 12:51:45 +00:00
case C.TYPE_HLS:
return new HlsMediaSource.Factory(
mediaDataSourceFactory
).setDrmSessionManager(drmSessionManager)
.setLoadErrorHandlingPolicy(
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
).createMediaSource(uri);
2017-01-11 12:51:45 +00:00
case C.TYPE_OTHER:
return new ProgressiveMediaSource.Factory(
mediaDataSourceFactory
).setDrmSessionManager(drmSessionManager)
.setLoadErrorHandlingPolicy(
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
).createMediaSource(uri);
2017-01-11 12:51:45 +00:00
default: {
throw new IllegalStateException("Unsupported type: " + type);
}
}
}
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) {
Format textFormat = Format.createTextSampleFormat(title, mimeType, Format.NO_VALUE, language);
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) {
stopBufferCheckTimer();
2017-03-21 20:25:17 +00:00
updateResumePosition();
2017-01-11 12:51:45 +00:00
player.release();
player.removeMetadataOutput(this);
2017-01-11 12:51:45 +00:00
trackSelector = null;
player = null;
2017-01-11 12:51:45 +00:00
}
progressHandler.removeMessages(SHOW_PROGRESS);
themedReactContext.removeLifecycleEventListener(this);
audioBecomingNoisyReceiver.removeListener();
bandwidthMeter.removeEventListener(this);
2017-01-11 12:51:45 +00:00
}
private boolean requestAudioFocus() {
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) {
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()) {
case Player.STATE_IDLE:
case Player.STATE_ENDED:
2017-01-11 12:51:45 +00:00
initializePlayer();
break;
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) {
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() {
if (isFullscreen) {
setFullscreen(false);
}
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.
*
* @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) {
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:
this.hasAudioFocus = false;
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:
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
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
if (!muted) {
player.setVolume(audioVolume * 1);
}
2017-01-11 12:51:45 +00:00
}
}
}
// AudioBecomingNoisyListener implementation
@Override
public void onAudioBecomingNoisy() {
eventEmitter.audioBecomingNoisy();
}
// 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) {
case Player.STATE_IDLE:
2017-01-11 12:51:45 +00:00
text += "idle";
eventEmitter.idle();
clearProgressMessageHandler();
if (!playWhenReady) {
setKeepScreenOn(false);
}
2017-01-11 12:51:45 +00:00
break;
case Player.STATE_BUFFERING:
2017-01-11 12:51:45 +00:00
text += "buffering";
onBuffering(true);
clearProgressMessageHandler();
setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback);
2017-01-11 12:51:45 +00:00
break;
case Player.STATE_READY:
2017-01-11 12:51:45 +00:00
text += "ready";
eventEmitter.ready();
onBuffering(false);
startProgressHandler();
videoLoaded();
if (selectTrackWhenReady && isUsingContentResolution) {
selectTrackWhenReady = false;
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
}
// Setting the visibility for the playerControlView
if (playerControlView != null) {
playerControlView.show();
}
setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback);
2017-01-11 12:51:45 +00:00
break;
case Player.STATE_ENDED:
2017-01-11 12:51:45 +00:00
text += "ended";
eventEmitter.end();
onStopPlayback();
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);
}
/*
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;
setSelectedAudioTrack(audioTrackType, audioTrackValue);
setSelectedVideoTrack(videoTrackType, videoTrackValue);
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";
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
}
}
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 : "");
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);
}
return audioTracks;
}
private WritableArray getVideoTrackInfo() {
WritableArray contentVideoTracks = this.getVideoTrackInfoFromManifest();
if (contentVideoTracks != null) {
isUsingContentResolution = true;
return contentVideoTracks;
}
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();
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);
if (isFormatSupported(format)) {
videoTracks.pushMap(videoTrack);
}
}
}
return videoTracks;
}
private WritableArray getVideoTrackInfoFromManifest() {
ExecutorService es = Executors.newSingleThreadExecutor();
final DataSource dataSource = this.mediaDataSourceFactory.createDataSource();
final Uri sourceUri = this.srcUri;
final Timeline timelineRef = this.player.getCurrentTimeline();
final long startTime = this.contentStartTime * 1000 - 100; // s -> ms with 100ms offset
Future<WritableArray> result = es.submit(new Callable<WritableArray>() {
DataSource ds = dataSource;
Uri uri = sourceUri;
Timeline timeline = timelineRef;
long startTimeUs = startTime * 1000; // ms -> us
public WritableArray call() throws Exception {
WritableArray videoTracks = Arguments.createArray();
try {
DashManifest manifest = DashUtil.loadManifest(this.ds, this.uri);
int periodCount = manifest.getPeriodCount();
for (int i = 0; i < periodCount; i++) {
Period period = manifest.getPeriod(i);
for (int adaptationIndex = 0; adaptationIndex < period.adaptationSets.size(); adaptationIndex++) {
AdaptationSet adaptation = period.adaptationSets.get(adaptationIndex);
if (adaptation.type != C.TRACK_TYPE_VIDEO) {
continue;
}
boolean hasFoundContentPeriod = false;
for (int representationIndex = 0; representationIndex < adaptation.representations.size(); representationIndex++) {
Representation representation = adaptation.representations.get(representationIndex);
Format format = representation.format;
if (representation.presentationTimeOffsetUs <= startTimeUs) {
break;
}
hasFoundContentPeriod = true;
WritableMap videoTrack = Arguments.createMap();
videoTrack.putInt("width", format.width == Format.NO_VALUE ? 0 : format.width);
videoTrack.putInt("height",format.height == Format.NO_VALUE ? 0 : format.height);
videoTrack.putInt("bitrate", format.bitrate == Format.NO_VALUE ? 0 : format.bitrate);
videoTrack.putString("codecs", format.codecs != null ? format.codecs : "");
videoTrack.putString("trackId",
format.id == null ? String.valueOf(representationIndex) : format.id);
if (isFormatSupported(format)) {
videoTracks.pushMap(videoTrack);
}
}
if (hasFoundContentPeriod) {
return videoTracks;
}
}
}
} catch (Exception e) {}
return null;
}
});
try {
WritableArray results = result.get();
es.shutdown();
return results;
} catch (Exception e) {}
return null;
}
private WritableArray getTextTrackInfo() {
WritableArray textTracks = Arguments.createArray();
MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
int index = getTrackRendererIndex(C.TRACK_TYPE_TEXT);
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);
textTrack.putString("title", format.id != null ? format.id : "");
textTrack.putString("type", format.sampleMimeType);
textTrack.putString("language", format.language != null ? format.language : "");
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();
}
if (isUsingContentResolution) {
// Discontinuity events might have a different track list so we update the selected track
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
selectTrackWhenReady = true;
}
// When repeat is turned on, reaching the end of the video will not cause a state change
// so we need to explicitly detect it.
if (reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION
&& 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() {
eventEmitter.seek(player.getCurrentPosition(), seekTime);
seekTime = C.TIME_UNSET;
if (isUsingContentResolution) {
// We need to update the selected track to make sure that it still matches user selection if track list has changed in this period
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
}
2018-04-03 12:19:04 -05:00
}
@Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
// Do nothing.
}
@Override
public void onRepeatModeChanged(int repeatMode) {
2017-03-21 20:25:17 +00:00
// Do nothing.
2017-01-11 12:51:45 +00:00
}
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
// Do Nothing.
}
@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;
String errorCode = "2001"; // Playback error code 2xxx (2001 - unknown playback exception)
boolean needsReInitialization = false;
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) {
errorCode = "2011";
2017-01-11 12:51:45 +00:00
errorString = getResources().getString(R.string.error_querying_decoders);
} else if (decoderInitializationException.secureDecoderRequired) {
errorCode = "2012";
2017-01-11 12:51:45 +00:00
errorString = getResources().getString(R.string.error_no_secure_decoder,
decoderInitializationException.mimeType);
} else {
errorCode = "2013";
2017-01-11 12:51:45 +00:00
errorString = getResources().getString(R.string.error_no_decoder,
decoderInitializationException.mimeType);
}
} else {
errorCode = "2014";
2017-01-11 12:51:45 +00:00
errorString = getResources().getString(R.string.error_instantiating_decoder,
2020-06-11 14:17:33 +01:00
decoderInitializationException.codecInfo.name);
2017-01-11 12:51:45 +00:00
}
}
}
else if (e.type == ExoPlaybackException.TYPE_SOURCE) {
// Re-initialization improves recovery speed and properly resumes
needsReInitialization = true;
errorString = getResources().getString(R.string.unrecognized_media_format);
Exception cause = e.getSourceException();
if (cause instanceof DefaultDrmSessionManager.MissingSchemeDataException) {
errorCode = "3004";
errorString = getResources().getString(R.string.unrecognized_media_format);
} else if(cause instanceof MediaDrmCallbackException) {
errorCode = "3005";
errorString = getResources().getString(R.string.unrecognized_media_format);
} else {
errorCode = "2021";
errorString = getResources().getString(R.string.unrecognized_media_format);
}
if (cause != null) {
Throwable rootCause = cause.getCause();
if (rootCause instanceof MediaDrmCallbackException) {
errorCode = "3005";
errorString = getResources().getString(R.string.unrecognized_media_format);
if (!hasDrmFailed) {
// When DRM fails to reach the app level certificate server it will fail with a source error so we assume that it is DRM related and try one more time
hasDrmFailed = true;
playerNeedsSource = true;
updateResumePosition();
initializePlayer();
return;
}
}
}
}
eventEmitter.error(errorString, ex, errorCode);
2017-01-11 12:51:45 +00:00
playerNeedsSource = true;
2017-03-21 20:25:17 +00:00
if (isBehindLiveWindow(e)) {
clearResumePosition();
initializePlayer();
} else {
updateResumePosition();
if (needsReInitialization) {
initializePlayer();
}
2017-03-21 20:25:17 +00:00
}
}
private static boolean isBehindLiveWindow(ExoPlaybackException e) {
2020-05-15 12:55:19 +05:30
Log.e("ExoPlayer Exception", e.toString());
2017-03-21 20:25:17 +00:00
if (e.type != ExoPlaybackException.TYPE_SOURCE) {
return false;
}
Throwable cause = e.getSourceException();
while (cause != null) {
2020-05-15 12:55:19 +05:30
if (cause instanceof BehindLiveWindowException ||
cause instanceof HttpDataSource.HttpDataSourceException) {
2017-03-21 20:25:17 +00:00
return true;
}
cause = cause.getCause();
}
return false;
2017-01-11 12:51:45 +00:00
}
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;
}
}
}
return C.INDEX_UNSET;
}
@Override
public void onMetadata(Metadata metadata) {
eventEmitter.timedMetadata(metadata);
}
2017-01-11 12:51:45 +00:00
// ReactExoplayerViewManager public api
public void setSrc(final Uri uri, final String extension, Map<String, String> headers) {
2017-01-11 12:51:45 +00:00
if (uri != null) {
boolean isSourceEqual = uri.equals(srcUri);
2017-01-11 12:51:45 +00:00
this.srcUri = uri;
this.extension = extension;
this.requestHeaders = headers;
this.mediaDataSourceFactory =
DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, bandwidthMeter,
this.requestHeaders);
if (!isSourceEqual) {
reloadSource();
}
2017-01-11 12:51:45 +00:00
}
}
public void clearSrc() {
if (srcUri != null) {
player.stop(true);
this.srcUri = null;
this.extension = null;
this.requestHeaders = null;
this.mediaDataSourceFactory = null;
clearResumePosition();
}
}
public void setProgressUpdateInterval(final float progressUpdateInterval) {
mProgressUpdateInterval = progressUpdateInterval;
}
2018-11-01 21:41:57 +05:30
public void setReportBandwidth(boolean reportBandwidth) {
mReportBandwidth = reportBandwidth;
}
2017-01-11 12:51:45 +00:00
public void setRawSrc(final Uri uri, final String extension) {
if (uri != null) {
boolean isSourceEqual = uri.equals(srcUri);
2017-01-11 12:51:45 +00:00
this.srcUri = uri;
this.extension = extension;
this.mediaDataSourceFactory = buildDataSourceFactory(true);
if (!isSourceEqual) {
reloadSource();
}
2017-01-11 12:51:45 +00:00
}
}
public void setTextTracks(ReadableArray textTracks) {
this.textTracks = textTracks;
reloadSource();
}
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);
setMutedModifier(muted);
2019-07-08 12:47:05 +02:00
}
2017-01-11 12:51:45 +00:00
public void setRepeatModifier(boolean repeat) {
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;
}
public void setPreventsDisplaySleepDuringVideoPlayback(boolean preventsDisplaySleepDuringVideoPlayback) {
this.preventsDisplaySleepDuringVideoPlayback = preventsDisplaySleepDuringVideoPlayback;
}
public void setSelectedTrack(int trackType, String type, Dynamic value) {
if (player == null) return;
int rendererIndex = getTrackRendererIndex(trackType);
if (rendererIndex == C.INDEX_UNSET) {
return;
}
MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
if (info == null) {
return;
}
TrackGroupArray groups = info.getTrackGroups(rendererIndex);
int groupIndex = C.INDEX_UNSET;
int[] tracks = {0} ;
if (TextUtils.isEmpty(type)) {
type = "default";
}
2018-08-07 23:10:03 -07:00
DefaultTrackSelector.Parameters disableParameters = trackSelector.getParameters()
.buildUpon()
.setRendererDisabled(rendererIndex, true)
.build();
if (type.equals("disabled")) {
2018-08-07 23:10:03 -07:00
trackSelector.setParameters(disableParameters);
return;
} 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())) {
groupIndex = i;
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())) {
groupIndex = i;
break;
}
}
} else if (type.equals("index")) {
if (value.asInt() < groups.length) {
groupIndex = value.asInt();
}
} 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);
Format closestFormat = null;
int closestTrackIndex = -1;
boolean usingExactMatch = false;
for (int j = 0; j < group.length; j++) {
Format format = group.getFormat(j);
if (format.height == height) {
groupIndex = i;
tracks[0] = j;
closestFormat = null;
closestTrackIndex = -1;
usingExactMatch = true;
break;
} else if (isUsingContentResolution) {
// When using content resolution rather than ads, we need to try and find the closest match if there is no exact match
if (closestFormat != null) {
if ((format.bitrate > closestFormat.bitrate || format.height > closestFormat.height) && format.height < height) {
// Higher quality match
closestFormat = format;
closestTrackIndex = j;
}
} else if(format.height < height) {
closestFormat = format;
closestTrackIndex = j;
}
}
}
// This is a fallback if the new period contains only higher resolutions than the user has selected
if (closestFormat == null && isUsingContentResolution && !usingExactMatch) {
// No close match found - so we pick the lowest quality
int minHeight = Integer.MAX_VALUE;
for (int j = 0; j < group.length; j++) {
Format format = group.getFormat(j);
if (format.height < minHeight) {
minHeight = format.height;
groupIndex = i;
tracks[0] = j;
}
}
}
// Selecting the closest match found
if (closestFormat != null && closestTrackIndex != -1) {
// We found the closest match instead of an exact one
groupIndex = i;
tracks[0] = closestTrackIndex;
}
}
} 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()) {
groupIndex = getGroupIndexForDefaultLocale(groups);
}
} else if (rendererIndex == C.TRACK_TYPE_AUDIO) { // Audio default
groupIndex = getGroupIndexForDefaultLocale(groups);
}
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);
int[] allTracks = new int[group.length];
groupIndex = 0;
for (int j = 0; j < group.length; j++) {
allTracks[j] = j;
}
// Valiate list of all tracks and add only supported formats
int supportedFormatLength = 0;
ArrayList<Integer> supportedTrackList = new ArrayList<Integer>();
for (int g = 0; g < allTracks.length; g++) {
Format format = group.getFormat(g);
if (isFormatSupported(format)) {
supportedFormatLength++;
}
}
tracks = new int[supportedFormatLength + 1];
int o = 0;
for (int k = 0; k < allTracks.length; k++) {
Format format = group.getFormat(k);
if (isFormatSupported(format)) {
tracks[o] = allTracks[k];
supportedTrackList.add(allTracks[k]);
o++;
}
}
}
if (groupIndex == C.INDEX_UNSET) {
2018-08-07 23:10:03 -07:00
trackSelector.setParameters(disableParameters);
return;
}
2018-08-07 23:10:03 -07:00
DefaultTrackSelector.Parameters selectionParameters = trackSelector.getParameters()
.buildUpon()
.setRendererDisabled(rendererIndex, false)
.setSelectionOverride(rendererIndex, groups,
new DefaultTrackSelector.SelectionOverride(groupIndex, tracks))
2018-08-07 23:10:03 -07:00
.build();
trackSelector.setParameters(selectionParameters);
}
private boolean isFormatSupported(Format format) {
int width = format.width == Format.NO_VALUE ? 0 : format.width;
int height = format.height == Format.NO_VALUE ? 0 : format.height;
float frameRate = format.frameRate == Format.NO_VALUE ? 0 : format.frameRate;
String mimeType = format.sampleMimeType;
if (mimeType == null) {
return true;
}
boolean isSupported = false;
try {
MediaCodecInfo codecInfo = MediaCodecUtil.getDecoderInfo(mimeType, false, false);
isSupported = codecInfo.isVideoSizeAndRateSupportedV21(width, height, frameRate);
} catch (Exception e) {
// Failed to get decoder info - assume it is supported
isSupported = true;
}
return isSupported;
}
private int getGroupIndexForDefaultLocale(TrackGroupArray groups) {
if (groups.length == 0){
return C.INDEX_UNSET;
}
int groupIndex = 0; // default if no match
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))) {
groupIndex = i;
break;
}
}
return groupIndex;
}
public void setSelectedVideoTrack(String type, Dynamic value) {
videoTrackType = type;
videoTrackValue = value;
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
}
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);
}
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) {
this.muted = muted;
audioVolume = muted ? 0.f : 1.f;
2017-01-11 12:51:45 +00:00
if (player != null) {
player.setVolume(audioVolume);
2017-01-11 12:51:45 +00:00
}
}
public void setVolumeModifier(float volume) {
audioVolume = volume;
2017-01-11 12:51:45 +00:00
if (player != null) {
player.setVolume(audioVolume);
2017-01-11 12:51:45 +00:00
}
}
public void seekTo(long positionMs) {
if (player != null) {
seekTime = positionMs;
2017-01-11 12:51:45 +00:00
player.seekTo(positionMs);
}
}
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;
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
}
public void setMaxBitRateModifier(int newMaxBitRate) {
maxBitRate = newMaxBitRate;
if (player != null) {
trackSelector.setParameters(trackSelector.buildUponParameters()
.setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate));
}
}
public void setMinLoadRetryCountModifier(int newMinLoadRetryCount) {
minLoadRetryCount = newMinLoadRetryCount;
releasePlayer();
initializePlayer();
}
2017-01-11 12:51:45 +00:00
public void setPlayInBackground(boolean playInBackground) {
this.playInBackground = playInBackground;
2017-01-11 12:51:45 +00:00
}
public void setDisableFocus(boolean disableFocus) {
this.disableFocus = disableFocus;
}
public void setBackBufferDurationMs(int backBufferDurationMs) {
Runtime runtime = Runtime.getRuntime();
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
long freeMemory = runtime.maxMemory() - usedMemory;
long reserveMemory = (long)minBackBufferMemoryReservePercent * runtime.maxMemory();
if (reserveMemory > freeMemory) {
// We don't have enough memory in reserve so we will
Log.w("ExoPlayer Warning", "Not enough reserve memory, setting back buffer to 0ms to reduce memory pressure!");
this.backBufferDurationMs = 0;
return;
}
this.backBufferDurationMs = backBufferDurationMs;
}
public void setContentStartTime(int contentStartTime) {
this.contentStartTime = (long)contentStartTime;
}
public void setDisableBuffering(boolean disableBuffering) {
this.disableBuffering = disableBuffering;
}
public void setDisableDisconnectError(boolean disableDisconnectError) {
this.disableDisconnectError = disableDisconnectError;
}
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();
}
}
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);
}
public void useSecureView(boolean useSecureView) {
exoPlayerView.useSecureView(useSecureView);
}
public void setHideShutterView(boolean hideShutterView) {
exoPlayerView.setHideShutterView(hideShutterView);
}
public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs, double newMaxHeapAllocationPercent, double newMinBackBufferMemoryReservePercent, double newMinBufferMemoryReservePercent) {
minBufferMs = newMinBufferMs;
maxBufferMs = newMaxBufferMs;
bufferForPlaybackMs = newBufferForPlaybackMs;
bufferForPlaybackAfterRebufferMs = newBufferForPlaybackAfterRebufferMs;
maxHeapAllocationPercent = newMaxHeapAllocationPercent;
minBackBufferMemoryReservePercent = newMinBackBufferMemoryReservePercent;
minBufferMemoryReservePercent = newMinBufferMemoryReservePercent;
releasePlayer();
initializePlayer();
}
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
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
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, "3002");
Add iOS and Android basic DRM support (#1445) This PR adds support for DRM streams on iOS (Fairplay) and Android (Playready, Widevine, Clearkey) I am neither Android nor iOS developer, so feel free to provide feedback to improve this PR. **Test stream for ANDROID:** ``` testStream = { uri: 'http://profficialsite.origin.mediaservices.windows.net/c51358ea-9a5e-4322-8951-897d640fdfd7/tearsofsteel_4k.ism/manifest(format=mpd-time-csf)', type: 'mpd', drm: { type: DRMType.PLAYREADY, licenseServer: 'http://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:150)' } }; ``` or ``` { uri: 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd', drm: { type: 'widevine', //or DRMType.WIDEVINE licenseServer: 'https://drm-widevine-licensing.axtest.net/AcquireLicense', headers: { 'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU' }, } } ``` **Test stream for iOS:** Sorry but I can not provide free streams to test. If anyone can provide test streams, or found some we can use, please let me know to also test them. It has been tested with a private provider and they work, at least with the `getLicense` override method. (An example implementation is provided in the README)
2020-08-13 03:56:21 +02:00
}
@Override
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
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");
}
/**
* Handling controls prop
*
2019-02-10 18:15:30 -08:00
* @param controls Controls prop, if true enable controls, if false disable them
*/
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);
}
}
}
2017-01-11 12:51:45 +00:00
}