Skip to content

Commit 49e3815

Browse files
author
Shahen Hovhannisyan
authored
Merge pull request #35 from shahen94/android-fixes
fix(VideoPlayer, TrimmerManager): a bunch of fixes
2 parents c3c0ee5 + 6fa1562 commit 49e3815

11 files changed

Lines changed: 815 additions & 646 deletions

File tree

README.md

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,16 @@ class App extends Component {
5151
const options = {
5252
startTime: 0,
5353
endTime: 15,
54-
quality: VideoPlayer.Constants.quality.QUALITY_1280x720,
55-
saveToCameraRoll: true, // default is false
56-
saveWithCurrentDate: true, // default is false
54+
quality: VideoPlayer.Constants.quality.QUALITY_1280x720, // iOS only
55+
saveToCameraRoll: true, // default is false // iOS only
56+
saveWithCurrentDate: true, // default is false // iOS only
5757
};
58-
this.videoPlayerRef.trim(require('./videoFile.mp4'), options)
58+
this.videoPlayerRef.trim(options)
5959
.then((newSource) => console.log(newSource))
6060
.catch(console.warn);
6161
}
6262

63+
// iOS only
6364
compressVideo() {
6465
const options = {
6566
width: 720,
@@ -69,20 +70,20 @@ class App extends Component {
6970
saveWithCurrentDate: true, // default is false
7071
minimumBitrate: 300000
7172
};
72-
this.videoPlayerRef.compress(require('./videoFile.mp4'), options)
73+
this.videoPlayerRef.compress(options)
7374
.then((newSource) => console.log(newSource))
7475
.catch(console.warn);
7576
}
7677

7778
getPreviewImageForSecond(second) {
78-
const maximumSize = { width: 640, height: 1024 }; // default is { width: 1080, height: 1080 }
79-
this.videoPlayerRef.getPreviewForSecond(require('./videoFile.mp4'), second, maximumSize)
79+
const maximumSize = { width: 640, height: 1024 }; // default is { width: 1080, height: 1080 } iOS only
80+
this.videoPlayerRef.getPreviewForSecond(second, maximumSize) // maximumSize is iOS only
8081
.then((base64String) => console.log('This is BASE64 of image', base64String))
8182
.catch(console.warn);
8283
}
8384

8485
getVideoInfo() {
85-
this.videoPlayerRef.getVideoInfo(require('./videoFile.mp4'))
86+
this.videoPlayerRef.getVideoInfo()
8687
.then((info) => console.log(info))
8788
.catch(console.warn);
8889
}
@@ -96,20 +97,20 @@ class App extends Component {
9697
endTime={120} // seconds
9798
play={true} // default false
9899
replay={true} // should player play video again if it's ended
99-
rotate={true} // use this prop to rotate video if it captured in landscape mode
100-
source={require('./videoFile.mp4')}
101-
playerWidth={300}
102-
playerHeight={500}
100+
rotate={true} // use this prop to rotate video if it captured in landscape mode iOS only
101+
source={{ uri: 'file:///sdcard/DCIM/....' }}
102+
playerWidth={300} // iOS only
103+
playerHeight={500} // iOS only
103104
style={{ backgroundColor: 'black' }}
104-
onChange={({ nativeEvent }) => console.log({ nativeEvent })}
105+
onChange={({ nativeEvent }) => console.log({ nativeEvent })} // get Current time on every second
105106
/>
106107
<Trimmer
107-
source={require('./videoFile.mp4')}
108+
source={{ uri: 'file:///sdcard/DCIM/....' }}
108109
height={100}
109110
width={300}
110-
currentTime={this.video.currentTime} // use this prop to set tracker position
111-
themeColor={'white'}
112-
trackerColor={'green'}
111+
currentTime={this.video.currentTime} // use this prop to set tracker position iOS only
112+
themeColor={'white'} // iOS only
113+
trackerColor={'green'} // iOS only
113114
onChange={(e) => console.log(e.startTime, e.endTime)}
114115
/>
115116
</View>
@@ -125,3 +126,10 @@ class App extends Component {
125126

126127
1. Please follow the eslint style guide.
127128
2. Please commit with `$ npm run commit`
129+
130+
## Roadmap
131+
1. [ ] Use FFMpeg instead of MP4Parser
132+
2. [ ] Add ability to add GLSL filters
133+
3. [ ] Android should be able to compress video
134+
4. [ ] More processing options
135+
5. [ ] Create native trimmer component for Android

android/build.gradle

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,26 @@
22
apply plugin: 'com.android.library'
33

44
android {
5-
compileSdkVersion 23
6-
buildToolsVersion "23.0.1"
5+
compileSdkVersion 23
6+
buildToolsVersion "23.0.1"
77

8-
defaultConfig {
9-
minSdkVersion 16
10-
targetSdkVersion 22
11-
versionCode 1
12-
versionName "1.0"
13-
ndk {
14-
abiFilters "armeabi-v7a", "x86"
15-
}
16-
}
17-
lintOptions {
18-
warning 'InvalidPackage'
8+
defaultConfig {
9+
minSdkVersion 16
10+
targetSdkVersion 22
11+
versionCode 1
12+
versionName "1.0"
13+
ndk {
14+
abiFilters "armeabi-v7a", "x86"
1915
}
16+
}
17+
lintOptions {
18+
warning 'InvalidPackage'
19+
}
2020
}
2121

2222
dependencies {
23-
compile 'com.facebook.react:react-native:0.20.+'
24-
compile 'com.yqritc:android-scalablevideoview:1.0.4'
25-
compile 'com.googlecode.mp4parser:isoparser:1.1.20'
23+
compile 'com.facebook.react:react-native:0.20.+'
24+
compile 'com.yqritc:android-scalablevideoview:1.0.4'
25+
compile 'com.googlecode.mp4parser:isoparser:1.1.20'
26+
compile 'com.github.wseemann:FFmpegMediaMetadataRetriever:1.0.14'
2627
}
27-

android/src/main/java/com/shahenlibrary/Trimmer/Trimmer.java

Lines changed: 76 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -26,67 +26,107 @@
2626

2727
import android.annotation.TargetApi;
2828
import android.graphics.Bitmap;
29+
import android.graphics.Matrix;
2930
import android.media.MediaMetadataRetriever;
31+
import android.net.Uri;
3032
import android.os.Build;
3133
import android.util.Base64;
34+
import android.util.Log;
3235

3336
import com.facebook.react.bridge.Arguments;
3437
import com.facebook.react.bridge.Promise;
38+
import com.facebook.react.bridge.ReactApplicationContext;
3539
import com.facebook.react.bridge.WritableArray;
3640
import com.facebook.react.bridge.WritableMap;
3741
import com.facebook.react.uimanager.events.Event;
3842
import com.shahenlibrary.Events.Events;
43+
import com.shahenlibrary.utils.VideoEdit;
3944

4045
import java.io.ByteArrayOutputStream;
4146

47+
import wseemann.media.FFmpegMediaMetadataRetriever;
48+
4249
public class Trimmer {
4350

44-
public static void getPreviewImages(String path, Promise promise) {
45-
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
46-
retriever.setDataSource(path);
51+
public static void getPreviewImages(String path, Promise promise, ReactApplicationContext ctx) {
52+
FFmpegMediaMetadataRetriever retriever = new FFmpegMediaMetadataRetriever();
53+
if (VideoEdit.shouldUseURI(path)) {
54+
retriever.setDataSource(ctx, Uri.parse(path));
55+
} else {
56+
retriever.setDataSource(path);
57+
}
58+
59+
WritableArray images = Arguments.createArray();
60+
int duration = Integer.parseInt(retriever.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_DURATION));
61+
int width = Integer.parseInt(retriever.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
62+
int height = Integer.parseInt(retriever.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
63+
int orientation = Integer.parseInt(retriever.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION));
64+
65+
float aspectRatio = width / height;
66+
67+
int resizeWidth = 200;
68+
int resizeHeight = Math.round(resizeWidth / aspectRatio);
4769

48-
WritableArray images = Arguments.createArray();
49-
int duration = Integer.parseInt(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
50-
int width = Integer.parseInt(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
51-
int height = Integer.parseInt(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
70+
float scaleWidth = ((float) resizeWidth) / width;
71+
float scaleHeight = ((float) resizeHeight) / height;
5272

53-
int aspectRatio = Math.round(width / height);
54-
int resizeWidth = 200;
55-
int resizeHeight = resizeWidth / aspectRatio;
73+
Log.d(TrimmerManager.REACT_PACKAGE, "getPreviewImages: \n\tduration: " + duration +
74+
"\n\twidth: " + width +
75+
"\n\theight: " + height +
76+
"\n\torientation: " + orientation +
77+
"\n\taspectRatio: " + aspectRatio +
78+
"\n\tresizeWidth: " + resizeWidth +
79+
"\n\tresizeHeight: " + resizeHeight
80+
);
5681

57-
for (int i = 0; i < duration; i += duration / 10) {
58-
Bitmap currBmp = Bitmap.createScaledBitmap(retriever.getFrameAtTime(i * 1000), resizeWidth, resizeHeight, false);
59-
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
60-
currBmp.compress(Bitmap.CompressFormat.PNG, 90, byteArrayOutputStream);
61-
byte[] byteArray = byteArrayOutputStream .toByteArray();
62-
String encoded = "data:image/png;base64," + Base64.encodeToString(byteArray, Base64.DEFAULT);
63-
images.pushString(encoded);
64-
}
82+
Matrix mx = new Matrix();
6583

66-
WritableMap event = Arguments.createMap();
84+
mx.postScale(scaleWidth, scaleHeight);
85+
mx.postRotate(orientation - 360);
6786

68-
event.putArray("images", images);
87+
for (int i = 0; i < duration; i += duration / 10) {
88+
Bitmap frame = retriever.getFrameAtTime(i * 1000);
89+
Bitmap currBmp = Bitmap.createScaledBitmap(frame, resizeWidth, resizeHeight, false);
6990

70-
promise.resolve(event);
71-
retriever.release();
91+
Bitmap normalizedBmp = Bitmap.createBitmap(currBmp, 0, 0, resizeWidth, resizeHeight, mx, true);
92+
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
93+
normalizedBmp.compress(Bitmap.CompressFormat.PNG, 90, byteArrayOutputStream);
94+
byte[] byteArray = byteArrayOutputStream .toByteArray();
95+
String encoded = "data:image/png;base64," + Base64.encodeToString(byteArray, Base64.DEFAULT);
96+
images.pushString(encoded);
7297
}
7398

74-
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
75-
public static void getVideoInfo(String path, Promise promise) {
76-
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
77-
mmr.setDataSource(path);
99+
WritableMap event = Arguments.createMap();
78100

79-
int duration = Integer.parseInt(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
80-
int width = Integer.parseInt(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
81-
int height = Integer.parseInt(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
82-
int orientation = Integer.parseInt(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION));
101+
event.putArray("images", images);
83102

84-
WritableMap event = Arguments.createMap();
85-
event.putInt(Events.DURATION, duration);
86-
event.putInt(Events.WIDTH, width);
87-
event.putInt(Events.HEIGHT, height);
88-
event.putInt(Events.ORIENTATION, orientation);
103+
promise.resolve(event);
104+
retriever.release();
105+
}
89106

90-
promise.resolve(event);
107+
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
108+
public static void getVideoInfo(String path, Promise promise, ReactApplicationContext ctx) {
109+
FFmpegMediaMetadataRetriever mmr = new FFmpegMediaMetadataRetriever();
110+
111+
if (VideoEdit.shouldUseURI(path)) {
112+
mmr.setDataSource(ctx, Uri.parse(path));
113+
} else {
114+
mmr.setDataSource(path);
91115
}
116+
117+
int duration = Integer.parseInt(mmr.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_DURATION));
118+
int width = Integer.parseInt(mmr.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
119+
int height = Integer.parseInt(mmr.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
120+
int orientation = Integer.parseInt(mmr.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION));
121+
122+
WritableMap event = Arguments.createMap();
123+
event.putInt(Events.DURATION, duration);
124+
event.putInt(Events.WIDTH, width);
125+
event.putInt(Events.HEIGHT, height);
126+
event.putInt(Events.ORIENTATION, orientation);
127+
128+
promise.resolve(event);
129+
130+
mmr.release();
131+
}
92132
}

android/src/main/java/com/shahenlibrary/Trimmer/TrimmerManager.java

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,29 +32,29 @@
3232
import com.facebook.react.bridge.ReactMethod;
3333

3434
public class TrimmerManager extends ReactContextBaseJavaModule {
35-
public static final String REACT_PACKAGE = "RNTrimmerManager";
36-
37-
private final ReactApplicationContext reactContext;
38-
39-
public TrimmerManager(ReactApplicationContext reactContext) {
40-
super(reactContext);
41-
this.reactContext = reactContext;
42-
}
43-
44-
@Override
45-
public String getName() {
46-
return REACT_PACKAGE;
47-
}
48-
49-
@ReactMethod
50-
public void getPreviewImages(String path, Promise promise) {
51-
Log.d(REACT_PACKAGE, "getPreviewImages: " + path);
52-
Trimmer.getPreviewImages(path, promise);
53-
}
54-
55-
@ReactMethod
56-
public void getVideoInfo(String path, Promise promise) {
57-
Log.d(REACT_PACKAGE, "getVideoInfo: " + path);
58-
Trimmer.getVideoInfo(path, promise);
59-
}
35+
public static final String REACT_PACKAGE = "RNTrimmerManager";
36+
37+
private final ReactApplicationContext reactContext;
38+
39+
public TrimmerManager(ReactApplicationContext reactContext) {
40+
super(reactContext);
41+
this.reactContext = reactContext;
42+
}
43+
44+
@Override
45+
public String getName() {
46+
return REACT_PACKAGE;
47+
}
48+
49+
@ReactMethod
50+
public void getPreviewImages(String path, Promise promise) {
51+
Log.d(REACT_PACKAGE, "getPreviewImages: " + path);
52+
Trimmer.getPreviewImages(path, promise, reactContext);
53+
}
54+
55+
@ReactMethod
56+
public void getVideoInfo(String path, Promise promise) {
57+
Log.d(REACT_PACKAGE, "getVideoInfo: " + path);
58+
Trimmer.getVideoInfo(path, promise, reactContext);
59+
}
6060
}

0 commit comments

Comments
 (0)