Skip to content

Commit 9c9f97c

Browse files
committed
lib update, drm, background audio fix, control lock screen
1 parent f19c0a3 commit 9c9f97c

8 files changed

+92
-8
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Conf
386386
| **`setSpeed`** | Sets the player speed. | `Double` |
387387
| **`setPlaylistIndex`** | Sets the current playing item in the loaded playlist. | `Int` |
388388
| **`setControls`** | Sets the display of the control buttons on the player. | `Boolean` |
389+
| **`setLockScreenControls`** | *(iOS only)* Sets the locks screen controls for the currently playing media, can be used to control what player to show the controls for. | `Boolean` |
389390
| **`setFullScreen`** | Set full screen. | `Boolean` |
390391
| **`loadPlaylist`** | Loads a playlist. (Using this function before the player has finished initializing may result in assert crash or blank screen, put in a timeout to make sure JWPlayer is mounted). | `[PlaylistItems]` |
391392
| **`loadPlaylistItem`** | Loads a playlist item. (Using this function before the player has finished initializing may result in assert crash or blank screen, put in a timeout to make sure JWPlayer is mounted). | [PlaylistItem](#PlaylistItem) |

index.d.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ declare module "react-native-jw-media-player" {
121121
| 'audiotracks_submenu'
122122
| 'casting_menu';
123123
interface Config {
124-
license: string,
124+
license: string;
125125
advertising?: Advertising;
126126
autostart?: boolean;
127127
controls?: boolean;
@@ -139,6 +139,9 @@ declare module "react-native-jw-media-player" {
139139
preload?: Preloads;
140140
interfaceBehavior?: InterfaceBehaviors;
141141
hideUIGroup?: UIGroups;
142+
processSpcUrl?: string;
143+
fairplayCertUrl?: string;
144+
contentUUID?: string;
142145
}
143146
interface PropsType {
144147
config: Config;
@@ -175,6 +178,7 @@ declare module "react-native-jw-media-player" {
175178
setSpeed(speed: number): void;
176179
setPlaylistIndex(index: number): void;
177180
setControls(show: boolean): void;
181+
setLockScreenControls(show: boolean): void;
178182
loadPlaylist(playlist: PlaylistItem[]): void;
179183
loadPlaylistItem(playlistItem: PlaylistItem): void;
180184
seekTo(time: number): void;

index.js

+9
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ export default class JWPlayer extends Component {
150150
forceLandscapeOnFullScreen: PropTypes.bool,
151151
enableLockScreenControls: PropTypes.bool,
152152
stretching: PropTypes.oneOf(['uniform', 'exactFit', 'fill', 'none']),
153+
processSpcUrl: PropTypes.string,
154+
fairplayCertUrl: PropTypes.string,
155+
contentUUID: PropTypes.string,
153156
}),
154157
onPlayerReady: PropTypes.func,
155158
onPlaylist: PropTypes.func,
@@ -159,6 +162,7 @@ export default class JWPlayer extends Component {
159162
setSpeed: PropTypes.func,
160163
setPlaylistIndex: PropTypes.func,
161164
setControls: PropTypes.func,
165+
setLockScreenControls: PropTypes.func,
162166
setFullscreen: PropTypes.func,
163167
setUpCastController: PropTypes.func,
164168
presentCastDialog: PropTypes.func,
@@ -231,6 +235,11 @@ export default class JWPlayer extends Component {
231235
RNJWPlayerManager.setControls(this.getRNJWPlayerBridgeHandle(), show);
232236
}
233237

238+
setLockScreenControls(show) {
239+
if (RNJWPlayerManager && Platform.OS === "ios")
240+
RNJWPlayerManager.setLockScreenControls(this.getRNJWPlayerBridgeHandle(), show);
241+
}
242+
234243
seekTo(time) {
235244
if (RNJWPlayerManager)
236245
RNJWPlayerManager.seekTo(this.getRNJWPlayerBridgeHandle(), time);

ios/RNJWPlayer.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Pod::Spec.new do |s|
1212
s.platform = :ios, "10.0"
1313
s.source = { :git => "https://github.com/chaimPaneth/react-native-jw-media-player.git", :tag => "v#{s.version}" }
1414
s.source_files = "RNJWPlayer/*.{h,m}"
15-
s.dependency 'JWPlayerKit', '~> 4.3.0'
15+
s.dependency 'JWPlayerKit', '~> 4.4.0'
1616
s.dependency 'google-cast-sdk', '~> 4.7.0'
1717
s.dependency 'React'
1818
# s.static_framework = true

ios/RNJWPlayer/RNJWPlayerView.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#import <JWPlayerKit/JWPlayerKit-swift.h>
1010
#import <GoogleCast/GoogleCast.h>
1111

12-
@interface RNJWPlayerView : UIView <JWPlayerDelegate, JWPlayerStateDelegate, JWAdDelegate, JWCastDelegate, JWAVDelegate, JWPlayerViewDelegate, JWPlayerViewControllerDelegate, AVPictureInPictureControllerDelegate>
12+
@interface RNJWPlayerView : UIView <JWPlayerDelegate, JWPlayerStateDelegate, JWAdDelegate, JWCastDelegate, JWAVDelegate, JWPlayerViewDelegate, JWPlayerViewControllerDelegate, JWDRMContentKeyDataSource, AVPictureInPictureControllerDelegate>
1313

1414
@property(nonatomic, strong)JWPlayerViewController* playerViewController;
1515
@property(nonatomic, strong)JWPlayerView *playerView;
@@ -22,6 +22,11 @@
2222

2323
@property(nonatomic)JWInterfaceBehavior interfaceBehavior;
2424

25+
/* DRM props */
26+
@property(nonatomic) NSString *fairplayCertUrl;
27+
@property(nonatomic) NSString *processSpcUrl;
28+
@property(nonatomic) NSString *contentUUID;
29+
2530
/* casting objects */
2631
@property(nonatomic, strong)JWCastController *castController;
2732
@property(nonatomic)BOOL isCasting;

ios/RNJWPlayer/RNJWPlayerView.m

+56-4
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ -(void)setConfig:(NSDictionary*)config
9090
} else {
9191
[self setupPlayerViewController:config :[self getPlayerConfiguration:config]];
9292
}
93+
94+
_processSpcUrl = config[@"processSpcUrl"];
95+
_fairplayCertUrl = config[@"fairplayCertUrl"];
96+
_contentUUID = config[@"contentUUID"];
9397
}
9498

9599
-(void)setControls:(BOOL)controls
@@ -612,8 +616,8 @@ -(void)setupPlayerViewController:config :(JWPlayerConfiguration*)playerConfig
612616
}
613617

614618
id enableLockScreenControls = config[@"enableLockScreenControls"];
615-
if (enableLockScreenControls != nil && enableLockScreenControls != (id)[NSNull null]) {
616-
_playerViewController.enableLockScreenControls = enableLockScreenControls;
619+
if ((enableLockScreenControls != nil && enableLockScreenControls != (id)[NSNull null]) || _backgroundAudioEnabled) {
620+
_playerViewController.enableLockScreenControls = YES;
617621
}
618622

619623
id styling = config[@"styling"];
@@ -687,6 +691,7 @@ -(void)presentPlayerViewController:(JWPlayerConfiguration*)configuration
687691
_playerViewController.player.playbackStateDelegate = self;
688692
_playerViewController.player.adDelegate = self;
689693
_playerViewController.player.avDelegate = self;
694+
_playerViewController.player.contentKeyDataSource = self;
690695
}
691696

692697
#pragma mark - JWPlayer View helpers
@@ -700,6 +705,7 @@ -(void)setupPlayerView:config :(JWPlayerConfiguration*)playerConfig
700705
_playerView.player.playbackStateDelegate = self;
701706
_playerView.player.adDelegate = self;
702707
_playerView.player.avDelegate = self;
708+
_playerView.player.contentKeyDataSource = self;
703709

704710
[_playerView.player configurePlayerWith:playerConfig];
705711

@@ -881,6 +887,36 @@ - (void)playerViewController:(JWPlayerViewController *)controller relatedItemBeg
881887

882888
}
883889

890+
#pragma mark - DRM Delegate
891+
892+
- (void)contentIdentifierForURL:(NSURL * _Nonnull)url completionHandler:(void (^ _Nonnull)(NSData * _Nullable))handler {
893+
NSData *uuidData = [_contentUUID dataUsingEncoding:NSUTF8StringEncoding];
894+
handler(uuidData);
895+
}
896+
897+
- (void)appIdentifierForURL:(NSURL * _Nonnull)url completionHandler:(void (^ _Nonnull)(NSData * _Nullable))handler {
898+
NSURL *certURL = [NSURL URLWithString:_fairplayCertUrl];
899+
NSData *certData = [NSData dataWithContentsOfURL:certURL];
900+
handler(certData);
901+
}
902+
903+
- (void)contentKeyWithSPCData:(NSData * _Nonnull)spcData completionHandler:(void (^ _Nonnull)(NSData * _Nullable, NSDate * _Nullable, NSString * _Nullable))handler {
904+
NSMutableURLRequest *ckcRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:_processSpcUrl]];
905+
[ckcRequest setHTTPMethod:@"POST"];
906+
[ckcRequest setHTTPBody:spcData];
907+
[ckcRequest addValue:@"application/octet-stream" forHTTPHeaderField:@"Content-Type"];
908+
909+
[[[NSURLSession sharedSession] dataTaskWithRequest:ckcRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
910+
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
911+
if (error != nil || (httpResponse != nil && httpResponse.statusCode != 200)) {
912+
handler(nil, nil, nil);
913+
return;
914+
}
915+
916+
handler(data, nil, nil);
917+
}] resume];
918+
}
919+
884920
#pragma mark - AV Picture In Picture Delegate
885921

886922
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *, id> *)change context:(void *)context
@@ -1438,10 +1474,14 @@ - (void)initializeAudioSession
14381474
object: audioSession];
14391475

14401476
NSError *setCategoryError = nil;
1441-
BOOL success = [audioSession setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
1477+
BOOL success = [audioSession setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers|AVAudioSessionCategoryOptionAllowBluetooth|AVAudioSessionCategoryOptionDefaultToSpeaker error:&setCategoryError];
14421478

14431479
NSError *activationError = nil;
1444-
success = [audioSession setActive:YES error:&activationError];
1480+
success = [audioSession setActive:YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&activationError];
1481+
1482+
[[NSNotificationCenter defaultCenter] addObserver:self
1483+
selector:@selector(applicationWillResignActive:)
1484+
name:UIApplicationWillResignActiveNotification object:nil];
14451485
}
14461486

14471487
-(void)audioSessionInterrupted:(NSNotification*)note
@@ -1477,4 +1517,16 @@ -(void)audioInterruptionsEnded:(NSNotification *)note {
14771517
}
14781518
}
14791519

1520+
// Inactive
1521+
1522+
-(void)applicationWillResignActive:(NSNotification *)notification {
1523+
if (!_userPaused && _backgroundAudioEnabled) {
1524+
if (_playerView != nil) {
1525+
[_playerView.player play];
1526+
} else if (_playerViewController != nil) {
1527+
[_playerViewController.player play];
1528+
}
1529+
}
1530+
}
1531+
14801532
@end

ios/RNJWPlayer/RNJWPlayerViewManager.m

+13
Original file line numberDiff line numberDiff line change
@@ -478,4 +478,17 @@ - (UIView*)view
478478
}];
479479
}
480480

481+
RCT_EXPORT_METHOD(setLockScreenControls: (nonnull NSNumber *)reactTag: (BOOL)show) {
482+
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RNJWPlayerView *> *viewRegistry) {
483+
RNJWPlayerView *view = viewRegistry[reactTag];
484+
if (![view isKindOfClass:[RNJWPlayerView class]] || (view.playerView == nil && view.playerViewController == nil)) {
485+
RCTLogError(@"Invalid view returned from registry, expecting RNJWPlayerView, got: %@", view);
486+
} else {
487+
if (view.playerViewController) {
488+
view.playerViewController.enableLockScreenControls = show;
489+
}
490+
}
491+
}];
492+
}
493+
481494
@end

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-native-jw-media-player",
3-
"version": "0.2.6",
3+
"version": "0.2.7",
44
"description": "React-native Android/iOS plugin for JWPlayer SDK (https://www.jwplayer.com/)",
55
"main": "index.js",
66
"types": "./index.d.ts",

0 commit comments

Comments
 (0)