tobias 2016-05-23 13:52:08 +02:00
commit 36f62d333b
9 changed files with 165 additions and 22 deletions

View File

@ -114,5 +114,5 @@ dependencies {
compile project(':react-native-video') compile project(':react-native-video')
compile fileTree(dir: "libs", include: ["*.jar"]) compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.0.1" compile "com.android.support:appcompat-v7:23.0.1"
compile "com.facebook.react:react-native:0.19.+" compile "com.facebook.react:react-native:0.20.+"
} }

View File

@ -1,8 +1,11 @@
'use strict'; 'use strict';
import React, { import {
Component
} from 'react';
import {
AppRegistry, AppRegistry,
Component,
StyleSheet, StyleSheet,
Text, Text,
TouchableOpacity, TouchableOpacity,

View File

@ -1,9 +1,11 @@
'use strict'; 'use strict';
import React, { import React, {
Component
} from 'react';
import {
AlertIOS, AlertIOS,
AppRegistry, AppRegistry,
Component,
StyleSheet, StyleSheet,
Text, Text,
TouchableOpacity, TouchableOpacity,

View File

@ -7,6 +7,8 @@
static NSString *const statusKeyPath = @"status"; static NSString *const statusKeyPath = @"status";
static NSString *const playbackLikelyToKeepUpKeyPath = @"playbackLikelyToKeepUp"; static NSString *const playbackLikelyToKeepUpKeyPath = @"playbackLikelyToKeepUp";
static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty"; static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
static NSString *const readyForDisplayKeyPath = @"readyForDisplay";
static NSString *const playbackRate = @"rate";
@implementation RCTVideo @implementation RCTVideo
{ {
@ -36,6 +38,7 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
BOOL _muted; BOOL _muted;
BOOL _paused; BOOL _paused;
BOOL _repeat; BOOL _repeat;
BOOL _playbackStalled;
BOOL _playInBackground; BOOL _playInBackground;
BOOL _playWhenInactive; BOOL _playWhenInactive;
NSString * _resizeMode; NSString * _resizeMode;
@ -48,6 +51,7 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
if ((self = [super init])) { if ((self = [super init])) {
_eventDispatcher = eventDispatcher; _eventDispatcher = eventDispatcher;
_playbackStalled = NO;
_rate = 1.0; _rate = 1.0;
_volume = 1.0; _volume = 1.0;
_resizeMode = @"AVLayerVideoGravityResizeAspectFill"; _resizeMode = @"AVLayerVideoGravityResizeAspectFill";
@ -233,13 +237,13 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
[self addPlayerItemObservers]; [self addPlayerItemObservers];
[_player pause]; [_player pause];
[_playerLayer removeFromSuperlayer]; [self removePlayerLayer];
_playerLayer = nil;
[_playerViewController.view removeFromSuperview]; [_playerViewController.view removeFromSuperview];
_playerViewController = nil; _playerViewController = nil;
_player = [AVPlayer playerWithPlayerItem:_playerItem]; _player = [AVPlayer playerWithPlayerItem:_playerItem];
_player.actionAtItemEnd = AVPlayerActionAtItemEndNone; _player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[_player addObserver:self forKeyPath:playbackRate options:0 context:nil];
const Float64 progressUpdateIntervalMS = _progressUpdateInterval / 1000; const Float64 progressUpdateIntervalMS = _progressUpdateInterval / 1000;
// @see endScrubbing in AVPlayerDemoPlaybackViewController.m of https://developer.apple.com/library/ios/samplecode/AVPlayerDemo/Introduction/Intro.html // @see endScrubbing in AVPlayerDemoPlaybackViewController.m of https://developer.apple.com/library/ios/samplecode/AVPlayerDemo/Introduction/Intro.html
@ -290,9 +294,21 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
NSObject *width = @"undefined"; NSObject *width = @"undefined";
NSObject *height = @"undefined"; NSObject *height = @"undefined";
NSString *orientation = @"undefined";
if ([_playerItem.asset tracksWithMediaType:AVMediaTypeVideo].count > 0) { if ([_playerItem.asset tracksWithMediaType:AVMediaTypeVideo].count > 0) {
width = [NSNumber numberWithFloat:[_playerItem.asset tracksWithMediaType:AVMediaTypeVideo][0].naturalSize.width]; AVAssetTrack *videoTrack = [[_playerItem.asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
height = [NSNumber numberWithFloat:[_playerItem.asset tracksWithMediaType:AVMediaTypeVideo][0].naturalSize.height]; width = [NSNumber numberWithFloat:videoTrack.naturalSize.width];
height = [NSNumber numberWithFloat:videoTrack.naturalSize.height];
CGAffineTransform preferredTransform = [videoTrack preferredTransform];
if ((videoTrack.naturalSize.width == preferredTransform.tx
&& videoTrack.naturalSize.height == preferredTransform.ty)
|| (preferredTransform.tx == 0 && preferredTransform.ty == 0))
{
orientation = @"landscape";
} else
orientation = @"portrait";
} }
[_eventDispatcher sendInputEventWithName:@"onVideoLoad" [_eventDispatcher sendInputEventWithName:@"onVideoLoad"
@ -306,7 +322,8 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
@"canStepForward": [NSNumber numberWithBool:_playerItem.canStepForward], @"canStepForward": [NSNumber numberWithBool:_playerItem.canStepForward],
@"naturalSize": @{ @"naturalSize": @{
@"width": width, @"width": width,
@"height": height @"height": height,
@"orientation": orientation
}, },
@"target": self.reactTag}]; @"target": self.reactTag}];
@ -328,6 +345,25 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
} }
_playerBufferEmpty = NO; _playerBufferEmpty = NO;
} }
} else if (object == _playerLayer) {
if([keyPath isEqualToString:readyForDisplayKeyPath] && [change objectForKey:NSKeyValueChangeNewKey]) {
if([change objectForKey:NSKeyValueChangeNewKey]) {
[_eventDispatcher sendInputEventWithName:@"onReadyForDisplay"
body:@{@"target": self.reactTag}];
}
}
} else if (object == _player) {
if([keyPath isEqualToString:playbackRate]) {
[_eventDispatcher sendInputEventWithName:@"onPlaybackRateChange"
body:@{@"playbackRate": [NSNumber numberWithFloat:_player.rate],
@"target": self.reactTag}];
if(_playbackStalled && _player.rate > 0) {
[_eventDispatcher sendInputEventWithName:@"onPlaybackResume"
body:@{@"playbackRate": [NSNumber numberWithFloat:_player.rate],
@"target": self.reactTag}];
_playbackStalled = NO;
}
}
} else { } else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
} }
@ -340,6 +376,16 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
selector:@selector(playerItemDidReachEnd:) selector:@selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification name:AVPlayerItemDidPlayToEndTimeNotification
object:[_player currentItem]]; object:[_player currentItem]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(playbackStalled:)
name:AVPlayerItemPlaybackStalledNotification
object:nil];
}
- (void)playbackStalled:(NSNotification *)notification
{
[_eventDispatcher sendInputEventWithName:@"onPlaybackStalled" body:@{@"target": self.reactTag}];
_playbackStalled = YES;
} }
- (void)playerItemDidReachEnd:(NSNotification *)notification - (void)playerItemDidReachEnd:(NSNotification *)notification
@ -535,6 +581,8 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
_playerLayer.frame = self.bounds; _playerLayer.frame = self.bounds;
_playerLayer.needsDisplayOnBoundsChange = YES; _playerLayer.needsDisplayOnBoundsChange = YES;
[_playerLayer addObserver:self forKeyPath:readyForDisplayKeyPath options:NSKeyValueObservingOptionNew context:nil];
[self.layer addSublayer:_playerLayer]; [self.layer addSublayer:_playerLayer];
self.layer.needsDisplayOnBoundsChange = YES; self.layer.needsDisplayOnBoundsChange = YES;
} }
@ -547,8 +595,7 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
_controls = controls; _controls = controls;
if( _controls ) if( _controls )
{ {
[_playerLayer removeFromSuperlayer]; [self removePlayerLayer];
_playerLayer = nil;
[self usePlayerViewController]; [self usePlayerViewController];
} }
else else
@ -560,6 +607,13 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
} }
} }
- (void)removePlayerLayer
{
[_playerLayer removeFromSuperlayer];
[_playerLayer removeObserver:self forKeyPath:readyForDisplayKeyPath];
_playerLayer = nil;
}
#pragma mark - RCTVideoPlayerViewControllerDelegate #pragma mark - RCTVideoPlayerViewControllerDelegate
- (void)videoPlayerViewControllerWillDismiss:(AVPlayerViewController *)playerViewController - (void)videoPlayerViewControllerWillDismiss:(AVPlayerViewController *)playerViewController
@ -643,10 +697,10 @@ static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
- (void)removeFromSuperview - (void)removeFromSuperview
{ {
[_player pause]; [_player pause];
[_player removeObserver:self forKeyPath:playbackRate];
_player = nil; _player = nil;
[_playerLayer removeFromSuperlayer]; [self removePlayerLayer];
_playerLayer = nil;
[_playerViewController.view removeFromSuperview]; [_playerViewController.view removeFromSuperview];
_playerViewController = nil; _playerViewController = nil;

View File

@ -28,7 +28,11 @@ RCT_EXPORT_MODULE();
@"onVideoFullscreenPlayerWillPresent", @"onVideoFullscreenPlayerWillPresent",
@"onVideoFullscreenPlayerDidPresent", @"onVideoFullscreenPlayerDidPresent",
@"onVideoFullscreenPlayerWillDismiss", @"onVideoFullscreenPlayerWillDismiss",
@"onVideoFullscreenPlayerDidDismiss" @"onVideoFullscreenPlayerDidDismiss",
@"onReadyForDisplay",
@"onPlaybackStalled",
@"onPlaybackResume",
@"onPlaybackRateChange"
]; ];
} }

View File

@ -1,6 +1,6 @@
## react-native-video ## react-native-video
A <Video> component for react-native, as seen in A `<Video>` component for react-native, as seen in
[react-native-login](https://github.com/brentvatne/react-native-login)! [react-native-login](https://github.com/brentvatne/react-native-login)!
Requires react-native >= 0.19.0 Requires react-native >= 0.19.0
@ -13,6 +13,20 @@ Run `npm install react-native-video --save`
Install [rnpm](https://github.com/rnpm/rnpm) and run `rnpm link react-native-video` Install [rnpm](https://github.com/rnpm/rnpm) and run `rnpm link react-native-video`
If you would like to allow other apps to play music over your video component, add:
**AppDelegate.m**
```
#import <AVFoundation/AVFoundation.h> // import
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil]; // allow
...
}
```
#### Android #### Android
First, copy your video file to `android/app/src/main/res/raw/`, then First, copy your video file to `android/app/src/main/res/raw/`, then

View File

@ -1,14 +1,18 @@
import React from 'react-native'; import React from 'react';
import ReactNative from 'react-native';
import VideoResizeMode from './VideoResizeMode.js'; import VideoResizeMode from './VideoResizeMode.js';
const { const {
Component, Component,
PropTypes,
} = React;
const {
StyleSheet, StyleSheet,
requireNativeComponent, requireNativeComponent,
PropTypes,
NativeModules, NativeModules,
View, View,
} = React; } = ReactNative;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
base: { base: {
@ -34,6 +38,10 @@ export default class Video extends Component {
this._onFullscreenPlayerDidPresent = this._onFullscreenPlayerDidPresent.bind(this); this._onFullscreenPlayerDidPresent = this._onFullscreenPlayerDidPresent.bind(this);
this._onFullscreenPlayerWillDismiss = this._onFullscreenPlayerWillDismiss.bind(this); this._onFullscreenPlayerWillDismiss = this._onFullscreenPlayerWillDismiss.bind(this);
this._onFullscreenPlayerDidDismiss = this._onFullscreenPlayerDidDismiss.bind(this); this._onFullscreenPlayerDidDismiss = this._onFullscreenPlayerDidDismiss.bind(this);
this._onReadyForDisplay = this._onReadyForDisplay.bind(this);
this._onPlaybackStalled = this._onPlaybackStalled.bind(this);
this._onPlaybackResume = this._onPlaybackResume.bind(this);
this._onPlaybackRateChange = this._onPlaybackRateChange.bind(this);
} }
setNativeProps(nativeProps) { setNativeProps(nativeProps) {
@ -116,6 +124,30 @@ export default class Video extends Component {
} }
} }
_onReadyForDisplay(event) {
if (this.props.onReadyForDisplay) {
this.props.onReadyForDisplay(event.nativeEvent);
}
}
_onPlaybackStalled(event) {
if (this.props.onPlaybackStalled) {
this.props.onPlaybackStalled(event.nativeEvent);
}
}
_onPlaybackResume(event) {
if (this.props.onPlaybackResume) {
this.props.onPlaybackResume(event.nativeEvent);
}
}
_onPlaybackRateChange(event) {
if (this.props.onPlaybackRateChange) {
this.props.onPlaybackRateChange(event.nativeEvent);
}
}
render() { render() {
const { const {
source, source,
@ -161,6 +193,10 @@ export default class Video extends Component {
onVideoFullscreenPlayerDidPresent: this._onFullscreenPlayerDidPresent, onVideoFullscreenPlayerDidPresent: this._onFullscreenPlayerDidPresent,
onVideoFullscreenPlayerWillDismiss: this._onFullscreenPlayerWillDismiss, onVideoFullscreenPlayerWillDismiss: this._onFullscreenPlayerWillDismiss,
onVideoFullscreenPlayerDidDismiss: this._onFullscreenPlayerDidDismiss, onVideoFullscreenPlayerDidDismiss: this._onFullscreenPlayerDidDismiss,
onReadyForDisplay: this._onReadyForDisplay,
onPlaybackStalled: this._onPlaybackStalled,
onPlaybackResume: this._onPlaybackResume,
onPlaybackRateChange: this._onPlaybackRateChange,
}); });
return ( return (
@ -200,6 +236,10 @@ Video.propTypes = {
onFullscreenPlayerDidPresent: PropTypes.func, onFullscreenPlayerDidPresent: PropTypes.func,
onFullscreenPlayerWillDismiss: PropTypes.func, onFullscreenPlayerWillDismiss: PropTypes.func,
onFullscreenPlayerDidDismiss: PropTypes.func, onFullscreenPlayerDidDismiss: PropTypes.func,
onReadyForDisplay: PropTypes.func,
onPlaybackStalled: PropTypes.func,
onPlaybackResume: PropTypes.func,
onPlaybackRateChange: PropTypes.func,
/* Required by react-native */ /* Required by react-native */
scaleX: React.PropTypes.number, scaleX: React.PropTypes.number,

View File

@ -3,6 +3,10 @@ package com.brentvatne.react;
import android.media.MediaPlayer; import android.media.MediaPlayer;
import android.os.Handler; import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.net.Uri;
import android.webkit.CookieManager;
import java.util.Map;
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.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ThemedReactContext;
@ -122,8 +126,30 @@ public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnP
mMediaPlayer.reset(); mMediaPlayer.reset();
try { try {
if (isNetwork || isAsset) { if (isNetwork) {
setDataSource(uriString); // Use the shared CookieManager to access the cookies
// set by WebViews inside the same app
CookieManager cookieManager = CookieManager.getInstance();
Uri parsedUrl = Uri.parse(uriString);
Uri.Builder builtUrl = parsedUrl.buildUpon();
String cookie = cookieManager.getCookie(builtUrl.build().toString());
Map<String, String> headers = new HashMap<String, String>();
if (cookie != null) {
headers.put("Cookie", cookie);
}
setDataSource(mThemedReactContext, parsedUrl, headers);
} else if (isAsset) {
if (uriString.startsWith("content://")) {
Uri parsedUrl = Uri.parse(uriString);
setDataSource(mThemedReactContext, parsedUrl);
} else {
setDataSource(uriString);
}
} else { } else {
setRawData(mThemedReactContext.getResources().getIdentifier( setRawData(mThemedReactContext.getResources().getIdentifier(
uriString, uriString,

View File

@ -1,6 +1,6 @@
{ {
"name": "react-native-video", "name": "react-native-video",
"version": "0.8.0-beta", "version": "0.8.0-rc",
"description": "A <Video /> element for react-native", "description": "A <Video /> element for react-native",
"main": "Video.js", "main": "Video.js",
"license": "MIT", "license": "MIT",