Merge pull request #236 from thoblr/master

Added possibility to play video in background and when app is inactive.
This commit is contained in:
Stanisław Chmiela 2016-06-08 11:05:58 +02:00
commit 24c24cd962
6 changed files with 73 additions and 4 deletions

View File

@ -40,6 +40,8 @@ static NSString *const playbackRate = @"rate";
BOOL _paused; BOOL _paused;
BOOL _repeat; BOOL _repeat;
BOOL _playbackStalled; BOOL _playbackStalled;
BOOL _playInBackground;
BOOL _playWhenInactive;
NSString * _resizeMode; NSString * _resizeMode;
BOOL _fullscreenPlayerPresented; BOOL _fullscreenPlayerPresented;
UIViewController * _presentingViewController; UIViewController * _presentingViewController;
@ -61,12 +63,19 @@ static NSString *const playbackRate = @"rate";
_progressUpdateInterval = 250; _progressUpdateInterval = 250;
_controls = NO; _controls = NO;
_playerBufferEmpty = YES; _playerBufferEmpty = YES;
_playInBackground = false;
_playWhenInactive = false;
[[NSNotificationCenter defaultCenter] addObserver:self [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillResignActive:) selector:@selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification name:UIApplicationWillResignActiveNotification
object:nil]; object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillEnterForeground:) selector:@selector(applicationWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification name:UIApplicationWillEnterForegroundNotification
@ -123,15 +132,26 @@ static NSString *const playbackRate = @"rate";
- (void)applicationWillResignActive:(NSNotification *)notification - (void)applicationWillResignActive:(NSNotification *)notification
{ {
if (!_paused) { if (_playInBackground || _playWhenInactive || _paused) return;
[_player pause]; [_player pause];
[_player setRate:0.0]; [_player setRate:0.0];
} }
- (void)applicationDidEnterBackground:(NSNotification *)notification
{
if (_playInBackground) {
// Needed to play sound in background. See https://developer.apple.com/library/ios/qa/qa1668/_index.html
[_playerLayer setPlayer:nil];
}
} }
- (void)applicationWillEnterForeground:(NSNotification *)notification - (void)applicationWillEnterForeground:(NSNotification *)notification
{ {
[self applyModifiers]; [self applyModifiers];
if (_playInBackground) {
[_playerLayer setPlayer:_player];
}
} }
#pragma mark - Progress #pragma mark - Progress
@ -403,6 +423,16 @@ static NSString *const playbackRate = @"rate";
_resizeMode = mode; _resizeMode = mode;
} }
- (void)setPlayInBackground:(BOOL)playInBackground
{
_playInBackground = playInBackground;
}
- (void)setPlayWhenInactive:(BOOL)playWhenInactive
{
_playWhenInactive = playWhenInactive;
}
- (void)setPaused:(BOOL)paused - (void)setPaused:(BOOL)paused
{ {
if (paused) { if (paused) {

View File

@ -48,6 +48,8 @@ RCT_EXPORT_VIEW_PROPERTY(paused, BOOL);
RCT_EXPORT_VIEW_PROPERTY(muted, BOOL); RCT_EXPORT_VIEW_PROPERTY(muted, BOOL);
RCT_EXPORT_VIEW_PROPERTY(controls, BOOL); RCT_EXPORT_VIEW_PROPERTY(controls, BOOL);
RCT_EXPORT_VIEW_PROPERTY(volume, float); RCT_EXPORT_VIEW_PROPERTY(volume, float);
RCT_EXPORT_VIEW_PROPERTY(playInBackground, BOOL);
RCT_EXPORT_VIEW_PROPERTY(playWhenInactive, BOOL);
RCT_EXPORT_VIEW_PROPERTY(rate, float); RCT_EXPORT_VIEW_PROPERTY(rate, float);
RCT_EXPORT_VIEW_PROPERTY(seek, float); RCT_EXPORT_VIEW_PROPERTY(seek, float);
RCT_EXPORT_VIEW_PROPERTY(currentTime, float); RCT_EXPORT_VIEW_PROPERTY(currentTime, float);

View File

@ -71,6 +71,8 @@ Under `.addPackage(new MainReactPackage())`:
paused={false} // Pauses playback entirely. paused={false} // Pauses playback entirely.
resizeMode="cover" // Fill the whole screen at aspect ratio. resizeMode="cover" // Fill the whole screen at aspect ratio.
repeat={true} // Repeat forever. repeat={true} // Repeat forever.
playInBackground={false} // Audio continues to play when app entering background.
playWhenInactive={false} // [iOS] Video continues to play when control or notification center are shown.
onLoadStart={this.loadStart} // Callback when video starts to load onLoadStart={this.loadStart} // Callback when video starts to load
onLoad={this.setDuration} // Callback when video loads onLoad={this.setDuration} // Callback when video loads
onProgress={this.setTime} // Callback every ~250ms with currentTime onProgress={this.setTime} // Callback every ~250ms with currentTime
@ -90,6 +92,10 @@ var styles = StyleSheet.create({
}); });
``` ```
### Play in background on iOS
To enable audio to play in background on iOS the audio session needs to be set to `AVAudioSessionCategoryPlayback`. See [Apple documentation][3].
## Static Methods ## Static Methods
`seek(seconds)` `seek(seconds)`
@ -120,6 +126,7 @@ Seeks the video to the specified time (in seconds). Access using a ref to the co
[1]: https://github.com/brentvatne/react-native-login/blob/56c47a5d1e23781e86e19b27e10427fd6391f666/App/Screens/UserInfoScreen.js#L32-L35 [1]: https://github.com/brentvatne/react-native-login/blob/56c47a5d1e23781e86e19b27e10427fd6391f666/App/Screens/UserInfoScreen.js#L32-L35
[2]: https://github.com/brentvatne/react-native-video/tree/master/Examples/VideoPlayer [2]: https://github.com/brentvatne/react-native-video/tree/master/Examples/VideoPlayer
[3]: https://developer.apple.com/library/ios/qa/qa1668/_index.html
--- ---

View File

@ -222,6 +222,8 @@ Video.propTypes = {
muted: PropTypes.bool, muted: PropTypes.bool,
volume: PropTypes.number, volume: PropTypes.number,
rate: PropTypes.number, rate: PropTypes.number,
playInBackground: PropTypes.bool,
playWhenInactive: PropTypes.bool,
controls: PropTypes.bool, controls: PropTypes.bool,
currentTime: PropTypes.number, currentTime: PropTypes.number,
onLoadStart: PropTypes.func, onLoadStart: PropTypes.func,

View File

@ -9,13 +9,14 @@ import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.events.RCTEventEmitter; import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.yqritc.scalablevideoview.ScalableType; import com.yqritc.scalablevideoview.ScalableType;
import com.yqritc.scalablevideoview.ScalableVideoView; import com.yqritc.scalablevideoview.ScalableVideoView;
public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnPreparedListener, MediaPlayer public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnPreparedListener, MediaPlayer
.OnErrorListener, MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnCompletionListener { .OnErrorListener, MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnCompletionListener, LifecycleEventListener {
public enum Events { public enum Events {
EVENT_LOAD_START("onVideoLoadStart"), EVENT_LOAD_START("onVideoLoadStart"),
@ -73,6 +74,7 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
private boolean mMuted = false; private boolean mMuted = false;
private float mVolume = 1.0f; private float mVolume = 1.0f;
private float mRate = 1.0f; private float mRate = 1.0f;
private boolean mPlayInBackground = false;
private boolean mMediaPlayerValid = false; // True if mMediaPlayer is in prepared, started, or paused state. private boolean mMediaPlayerValid = false; // True if mMediaPlayer is in prepared, started, or paused state.
private int mVideoDuration = 0; private int mVideoDuration = 0;
@ -83,6 +85,7 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
mThemedReactContext = themedReactContext; mThemedReactContext = themedReactContext;
mEventEmitter = themedReactContext.getJSModule(RCTEventEmitter.class); mEventEmitter = themedReactContext.getJSModule(RCTEventEmitter.class);
themedReactContext.addLifecycleEventListener(this);
initializeMediaPlayerIfNeeded(); initializeMediaPlayerIfNeeded();
setSurfaceTextureListener(this); setSurfaceTextureListener(this);
@ -248,6 +251,10 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
// setRateModifier(mRate); // setRateModifier(mRate);
} }
public void setPlayInBackground(final boolean playInBackground) {
mPlayInBackground = playInBackground;
}
@Override @Override
public void onPrepared(MediaPlayer mp) { public void onPrepared(MediaPlayer mp) {
mMediaPlayerValid = true; mMediaPlayerValid = true;
@ -324,4 +331,19 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
super.onAttachedToWindow(); super.onAttachedToWindow();
setSrc(mSrcUriString, mSrcType, mSrcIsNetwork, mSrcIsAsset); setSrc(mSrcUriString, mSrcType, mSrcIsNetwork, mSrcIsAsset);
} }
@Override
public void onHostPause() {
if (mMediaPlayer != null && !mPlayInBackground) {
mMediaPlayer.pause();
}
}
@Override
public void onHostResume() {
}
@Override
public void onHostDestroy() {
}
} }

View File

@ -30,6 +30,7 @@ public class ReactVideoViewManager extends SimpleViewManager<ReactVideoView> {
public static final String PROP_VOLUME = "volume"; public static final String PROP_VOLUME = "volume";
public static final String PROP_SEEK = "seek"; public static final String PROP_SEEK = "seek";
public static final String PROP_RATE = "rate"; public static final String PROP_RATE = "rate";
public static final String PROP_PLAY_IN_BACKGROUND = "playInBackground";
@Override @Override
public String getName() { public String getName() {
@ -106,4 +107,9 @@ public class ReactVideoViewManager extends SimpleViewManager<ReactVideoView> {
public void setRate(final ReactVideoView videoView, final float rate) { public void setRate(final ReactVideoView videoView, final float rate) {
videoView.setRateModifier(rate); videoView.setRateModifier(rate);
} }
@ReactProp(name = PROP_PLAY_IN_BACKGROUND, defaultBoolean = false)
public void setPlayInBackground(final ReactVideoView videoView, final boolean playInBackground) {
videoView.setPlayInBackground(playInBackground);
}
} }