diff --git a/README.md b/README.md index d1094d54..2915c898 100644 --- a/README.md +++ b/README.md @@ -276,6 +276,7 @@ var styles = StyleSheet.create({ ### Event props * [onAudioBecomingNoisy](#onaudiobecomingnoisy) * [onEnd](#onend) +* [onExternalPlaybackChange](#onexternalplaybackchange) * [onFullscreenPlayerWillPresent](#onfullscreenplayerwillpresent) * [onFullscreenPlayerDidPresent](#onfullscreenplayerdidpresent) * [onFullscreenPlayerWillDismiss](#onfullscreenplayerwilldismiss) @@ -644,6 +645,24 @@ Payload: none Platforms: all +#### onExternalPlaybackChange +Callback function that is called when external playback mode for current playing video has changed. Mostly useful when connecting/disconnecting to Apple TV – it's called on connection/disconnection. + +Payload: + +Property | Type | Description +--- | --- | --- +isExternalPlaybackActive | boolean | Boolean indicating whether external playback mode is active + +Example: +``` +{ + isExternalPlaybackActive: true +} +``` + +Platforms: iOS + #### onFullscreenPlayerWillPresent Callback function that is called when the player is about to enter fullscreen mode. diff --git a/Video.js b/Video.js index b53885bf..76f27dc3 100644 --- a/Video.js +++ b/Video.js @@ -172,6 +172,12 @@ export default class Video extends Component { this.props.onPlaybackRateChange(event.nativeEvent); } }; + + _onExternalPlaybackChange = (event) => { + if (this.props.onExternalPlaybackChange) { + this.props.onExternalPlaybackChange(event.nativeEvent); + } + } _onAudioBecomingNoisy = () => { if (this.props.onAudioBecomingNoisy) { @@ -246,6 +252,7 @@ export default class Video extends Component { onPlaybackRateChange: this._onPlaybackRateChange, onAudioFocusChanged: this._onAudioFocusChanged, onAudioBecomingNoisy: this._onAudioBecomingNoisy, + onExternalPlaybackChange: this._onExternalPlaybackChange, }); const posterStyle = { @@ -365,6 +372,7 @@ Video.propTypes = { onPlaybackRateChange: PropTypes.func, onAudioFocusChanged: PropTypes.func, onAudioBecomingNoisy: PropTypes.func, + onExternalPlaybackChange: PropTypes.func, /* Required by react-native */ scaleX: PropTypes.number, diff --git a/ios/Video/RCTVideo.h b/ios/Video/RCTVideo.h index b2296c5d..472cb2ff 100644 --- a/ios/Video/RCTVideo.h +++ b/ios/Video/RCTVideo.h @@ -35,6 +35,7 @@ @property (nonatomic, copy) RCTBubblingEventBlock onPlaybackStalled; @property (nonatomic, copy) RCTBubblingEventBlock onPlaybackResume; @property (nonatomic, copy) RCTBubblingEventBlock onPlaybackRateChange; +@property (nonatomic, copy) RCTBubblingEventBlock onExternalPlaybackChange; - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; diff --git a/ios/Video/RCTVideo.m b/ios/Video/RCTVideo.m index 9fe30b63..45389799 100644 --- a/ios/Video/RCTVideo.m +++ b/ios/Video/RCTVideo.m @@ -12,6 +12,7 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty"; static NSString *const readyForDisplayKeyPath = @"readyForDisplay"; static NSString *const playbackRate = @"rate"; static NSString *const timedMetadata = @"timedMetadata"; +static NSString *const externalPlaybackActive = @"externalPlaybackActive"; static int const RCTVideoUnset = -1; @@ -35,6 +36,7 @@ static int const RCTVideoUnset = -1; /* Required to publish events */ RCTEventDispatcher *_eventDispatcher; BOOL _playbackRateObserverRegistered; + BOOL _isExternalPlaybackActiveObserverRegistered; BOOL _videoLoadStarted; bool _pendingSeek; @@ -74,6 +76,7 @@ static int const RCTVideoUnset = -1; _eventDispatcher = eventDispatcher; _playbackRateObserverRegistered = NO; + _isExternalPlaybackActiveObserverRegistered = NO; _playbackStalled = NO; _rate = 1.0; _volume = 1.0; @@ -333,12 +336,19 @@ static int const RCTVideoUnset = -1; [_player removeObserver:self forKeyPath:playbackRate context:nil]; _playbackRateObserverRegistered = NO; } + if (_isExternalPlaybackActiveObserverRegistered) { + [_player removeObserver:self forKeyPath:externalPlaybackActive context:nil]; + _isExternalPlaybackActiveObserverRegistered = NO; + } _player = [AVPlayer playerWithPlayerItem:_playerItem]; _player.actionAtItemEnd = AVPlayerActionAtItemEndNone; [_player addObserver:self forKeyPath:playbackRate options:0 context:nil]; _playbackRateObserverRegistered = YES; + + [_player addObserver:self forKeyPath:externalPlaybackActive options:0 context:nil]; + _isExternalPlaybackActiveObserverRegistered = YES; [self addPlayerTimeObserver]; @@ -646,6 +656,12 @@ static int const RCTVideoUnset = -1; _playbackStalled = NO; } } + else if([keyPath isEqualToString:externalPlaybackActive]) { + if(self.onExternalPlaybackChange) { + self.onExternalPlaybackChange(@{@"isExternalPlaybackActive": [NSNumber numberWithBool:_player.isExternalPlaybackActive], + @"target": self.reactTag}); + } + } } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } @@ -1290,6 +1306,10 @@ static int const RCTVideoUnset = -1; [_player removeObserver:self forKeyPath:playbackRate context:nil]; _playbackRateObserverRegistered = NO; } + if (_isExternalPlaybackActiveObserverRegistered) { + [_player removeObserver:self forKeyPath:externalPlaybackActive context:nil]; + _isExternalPlaybackActiveObserverRegistered = NO; + } _player = nil; [self removePlayerLayer]; diff --git a/ios/Video/RCTVideoManager.m b/ios/Video/RCTVideoManager.m index e0e0162e..190a1663 100644 --- a/ios/Video/RCTVideoManager.m +++ b/ios/Video/RCTVideoManager.m @@ -56,6 +56,7 @@ RCT_EXPORT_VIEW_PROPERTY(onReadyForDisplay, RCTBubblingEventBlock); RCT_EXPORT_VIEW_PROPERTY(onPlaybackStalled, RCTBubblingEventBlock); RCT_EXPORT_VIEW_PROPERTY(onPlaybackResume, RCTBubblingEventBlock); RCT_EXPORT_VIEW_PROPERTY(onPlaybackRateChange, RCTBubblingEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onExternalPlaybackChange, RCTBubblingEventBlock); - (NSDictionary *)constantsToExport {