react-native-video-trim 4.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +124 -83
- package/VideoTrim.podspec +4 -4
- package/android/build.gradle +15 -54
- package/android/gradle.properties +2 -0
- package/android/src/main/AndroidManifest.xml +1 -1
- package/android/src/main/java/com/videotrim/VideoTrimModule.kt +660 -0
- package/android/src/main/java/com/videotrim/VideoTrimPackage.kt +33 -0
- package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/enums/ErrorCode.java +1 -1
- package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/interfaces/IVideoTrimmerView.java +1 -1
- package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/interfaces/VideoTrimListener.java +5 -4
- package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/utils/MediaMetadataUtil.java +1 -1
- package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/utils/StorageUtil.java +1 -1
- package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/utils/VideoTrimmerUtil.java +49 -39
- package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/widgets/VideoTrimmerView.java +49 -46
- package/ios/AssetLoader.h +19 -0
- package/ios/AssetLoader.mm +87 -0
- package/ios/ErrorCode.h +9 -0
- package/ios/ProgressAlertController.h +15 -0
- package/ios/ProgressAlertController.mm +78 -0
- package/ios/VideoTrim.h +31 -0
- package/ios/VideoTrim.mm +663 -0
- package/ios/VideoTrimmer.h +67 -0
- package/ios/VideoTrimmer.mm +863 -0
- package/ios/VideoTrimmerThumb.h +23 -0
- package/ios/VideoTrimmerThumb.mm +175 -0
- package/ios/VideoTrimmerViewController.h +52 -0
- package/ios/VideoTrimmerViewController.mm +533 -0
- package/lib/module/NativeVideoTrim.js +5 -0
- package/lib/module/NativeVideoTrim.js.map +1 -0
- package/lib/module/index.js +59 -34
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeVideoTrim.d.ts +107 -0
- package/lib/typescript/src/NativeVideoTrim.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +21 -10
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +15 -18
- package/src/NativeVideoTrim.ts +113 -0
- package/src/index.tsx +68 -39
- package/android/CMakeLists.txt +0 -24
- package/android/src/main/cpp/cpp-adapter.cpp +0 -6
- package/android/src/main/java/com/margelo/nitro/videotrim/VideoTrim.kt +0 -629
- package/android/src/main/java/com/margelo/nitro/videotrim/VideoTrimPackage.kt +0 -22
- package/ios/AssetLoader.swift +0 -99
- package/ios/ErrorCode.swift +0 -17
- package/ios/ProgressAlertController.swift +0 -100
- package/ios/VideoTrim.swift +0 -60
- package/ios/VideoTrimImpl.swift +0 -860
- package/ios/VideoTrimmer.swift +0 -872
- package/ios/VideoTrimmerThumb.swift +0 -175
- package/ios/VideoTrimmerViewController.swift +0 -557
- package/lib/module/VideoTrim.nitro.js +0 -4
- package/lib/module/VideoTrim.nitro.js.map +0 -1
- package/lib/typescript/src/VideoTrim.nitro.d.ts +0 -240
- package/lib/typescript/src/VideoTrim.nitro.d.ts.map +0 -1
- package/nitrogen/generated/android/c++/JEditorConfig.hpp +0 -229
- package/nitrogen/generated/android/c++/JFileValidationResult.hpp +0 -61
- package/nitrogen/generated/android/c++/JFunc_void.hpp +0 -74
- package/nitrogen/generated/android/c++/JFunc_void_std__string_std__unordered_map_std__string__std__string_.hpp +0 -89
- package/nitrogen/generated/android/c++/JHybridVideoTrimSpec.cpp +0 -131
- package/nitrogen/generated/android/c++/JHybridVideoTrimSpec.hpp +0 -67
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/EditorConfig.kt +0 -70
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/FileValidationResult.kt +0 -28
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/Func_void.kt +0 -80
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/Func_void_std__string_std__unordered_map_std__string__std__string_.kt +0 -80
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/HybridVideoTrimSpec.kt +0 -82
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/videotrim/videotrimOnLoad.kt +0 -35
- package/nitrogen/generated/android/videotrim+autolinking.cmake +0 -78
- package/nitrogen/generated/android/videotrim+autolinking.gradle +0 -27
- package/nitrogen/generated/android/videotrimOnLoad.cpp +0 -50
- package/nitrogen/generated/android/videotrimOnLoad.hpp +0 -25
- package/nitrogen/generated/ios/VideoTrim+autolinking.rb +0 -60
- package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Bridge.cpp +0 -88
- package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Bridge.hpp +0 -331
- package/nitrogen/generated/ios/VideoTrim-Swift-Cxx-Umbrella.hpp +0 -53
- package/nitrogen/generated/ios/VideoTrimAutolinking.mm +0 -33
- package/nitrogen/generated/ios/VideoTrimAutolinking.swift +0 -25
- package/nitrogen/generated/ios/c++/HybridVideoTrimSpecSwift.cpp +0 -11
- package/nitrogen/generated/ios/c++/HybridVideoTrimSpecSwift.hpp +0 -116
- package/nitrogen/generated/ios/swift/EditorConfig.swift +0 -519
- package/nitrogen/generated/ios/swift/FileValidationResult.swift +0 -57
- package/nitrogen/generated/ios/swift/Func_void.swift +0 -46
- package/nitrogen/generated/ios/swift/Func_void_FileValidationResult.swift +0 -46
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +0 -46
- package/nitrogen/generated/ios/swift/Func_void_double.swift +0 -46
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +0 -46
- package/nitrogen/generated/ios/swift/Func_void_std__string_std__unordered_map_std__string__std__string_.swift +0 -54
- package/nitrogen/generated/ios/swift/Func_void_std__vector_std__string_.swift +0 -46
- package/nitrogen/generated/ios/swift/HybridVideoTrimSpec.swift +0 -53
- package/nitrogen/generated/ios/swift/HybridVideoTrimSpec_cxx.swift +0 -222
- package/nitrogen/generated/shared/c++/EditorConfig.hpp +0 -245
- package/nitrogen/generated/shared/c++/FileValidationResult.hpp +0 -77
- package/nitrogen/generated/shared/c++/HybridVideoTrimSpec.cpp +0 -26
- package/nitrogen/generated/shared/c++/HybridVideoTrimSpec.hpp +0 -76
- package/src/VideoTrim.nitro.ts +0 -244
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
package com.
|
|
1
|
+
package com.videotrim.interfaces;
|
|
2
2
|
|
|
3
|
-
import com.
|
|
3
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
4
|
+
import com.videotrim.enums.ErrorCode;
|
|
4
5
|
|
|
5
6
|
import java.util.Map;
|
|
6
7
|
|
|
@@ -12,6 +13,6 @@ public interface VideoTrimListener {
|
|
|
12
13
|
void onError(String errorMessage, ErrorCode errorCode);
|
|
13
14
|
void onCancel();
|
|
14
15
|
void onSave();
|
|
15
|
-
void onLog(
|
|
16
|
-
void onStatistics(
|
|
16
|
+
void onLog(ReadableMap log);
|
|
17
|
+
void onStatistics(ReadableMap statistics);
|
|
17
18
|
}
|
package/android/src/main/java/com/{margelo/nitro/videotrim → videotrim}/utils/VideoTrimmerUtil.java
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
package com.
|
|
1
|
+
package com.videotrim.utils;
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint;
|
|
4
4
|
import android.graphics.Bitmap;
|
|
@@ -9,12 +9,16 @@ import com.arthenica.ffmpegkit.FFmpegKit;
|
|
|
9
9
|
import com.arthenica.ffmpegkit.FFmpegSession;
|
|
10
10
|
import com.arthenica.ffmpegkit.ReturnCode;
|
|
11
11
|
import com.arthenica.ffmpegkit.SessionState;
|
|
12
|
-
import com.
|
|
13
|
-
import com.
|
|
12
|
+
import com.facebook.react.bridge.Arguments;
|
|
13
|
+
import com.facebook.react.bridge.WritableMap;
|
|
14
|
+
import com.videotrim.enums.ErrorCode;
|
|
15
|
+
import com.videotrim.interfaces.VideoTrimListener;
|
|
14
16
|
|
|
15
17
|
import java.text.SimpleDateFormat;
|
|
18
|
+
import java.util.ArrayList;
|
|
16
19
|
import java.util.Date;
|
|
17
20
|
import java.util.HashMap;
|
|
21
|
+
import java.util.List;
|
|
18
22
|
import java.util.Map;
|
|
19
23
|
import java.util.TimeZone;
|
|
20
24
|
|
|
@@ -43,7 +47,7 @@ public class VideoTrimmerUtil {
|
|
|
43
47
|
public static final int THUMB_WIDTH = UnitConverter.dpToPx(25); // x2 for better resolution
|
|
44
48
|
private static final int THUMB_RESOLUTION_RES = 2; // double thumb resolution for better quality
|
|
45
49
|
|
|
46
|
-
public static FFmpegSession trim(String inputFile, String outputFile, int videoDuration, long startMs, long endMs, final VideoTrimListener callback) {
|
|
50
|
+
public static FFmpegSession trim(String inputFile, String outputFile, int videoDuration, long startMs, long endMs, boolean enableRotation, double rotationAngle, final VideoTrimListener callback) {
|
|
47
51
|
// Get the current date and time
|
|
48
52
|
Date currentDate = new Date();
|
|
49
53
|
|
|
@@ -55,26 +59,32 @@ public class VideoTrimmerUtil {
|
|
|
55
59
|
// Format the current date and time
|
|
56
60
|
String formattedDateTime = dateFormat.format(currentDate);
|
|
57
61
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"
|
|
67
|
-
"-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
62
|
+
// create list to store commands
|
|
63
|
+
List<String> cmds = new ArrayList<>();
|
|
64
|
+
cmds.add("-ss");
|
|
65
|
+
cmds.add(startMs + "ms");
|
|
66
|
+
cmds.add("-to");
|
|
67
|
+
cmds.add(endMs + "ms");
|
|
68
|
+
|
|
69
|
+
if (enableRotation) {
|
|
70
|
+
// add "-display_rotation" and rotation angle to the command, contact, not creating new
|
|
71
|
+
cmds.add("-display_rotation");
|
|
72
|
+
cmds.add(String.valueOf(rotationAngle));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
cmds.add("-i");
|
|
76
|
+
cmds.add(inputFile);
|
|
77
|
+
cmds.add("-c");
|
|
78
|
+
cmds.add("copy");
|
|
79
|
+
cmds.add("-metadata");
|
|
80
|
+
cmds.add("creation_time=" + formattedDateTime);
|
|
81
|
+
cmds.add(outputFile);
|
|
82
|
+
|
|
83
|
+
String[] command = cmds.toArray(new String[0]);
|
|
84
|
+
|
|
85
|
+
Log.d(TAG,"Command: " + String.join(",", command));
|
|
86
|
+
|
|
87
|
+
return FFmpegKit.executeWithArgumentsAsync(command, session -> {
|
|
78
88
|
SessionState state = session.getState();
|
|
79
89
|
ReturnCode returnCode = session.getReturnCode();
|
|
80
90
|
if (ReturnCode.isSuccess(session.getReturnCode())) {
|
|
@@ -91,11 +101,11 @@ public class VideoTrimmerUtil {
|
|
|
91
101
|
}, log -> {
|
|
92
102
|
Log.d(TAG, "FFmpeg process started with log " + log.getMessage());
|
|
93
103
|
|
|
94
|
-
|
|
95
|
-
map.
|
|
96
|
-
map.
|
|
97
|
-
map.
|
|
98
|
-
map.
|
|
104
|
+
WritableMap map = Arguments.createMap();
|
|
105
|
+
map.putInt("level", log.getLevel().getValue());
|
|
106
|
+
map.putString("message", log.getMessage());
|
|
107
|
+
map.putDouble("sessionId", log.getSessionId());
|
|
108
|
+
map.putString("logStr", log.toString());
|
|
99
109
|
|
|
100
110
|
callback.onLog(map);
|
|
101
111
|
}, statistics -> {
|
|
@@ -106,16 +116,16 @@ public class VideoTrimmerUtil {
|
|
|
106
116
|
callback.onTrimmingProgress(Math.min(Math.max(completePercentage, 0), 100));
|
|
107
117
|
}
|
|
108
118
|
|
|
109
|
-
|
|
110
|
-
map.
|
|
111
|
-
map.
|
|
112
|
-
map.
|
|
113
|
-
map.
|
|
114
|
-
map.
|
|
115
|
-
map.
|
|
116
|
-
map.
|
|
117
|
-
map.
|
|
118
|
-
map.
|
|
119
|
+
WritableMap map = Arguments.createMap();
|
|
120
|
+
map.putDouble("sessionId", statistics.getSessionId());
|
|
121
|
+
map.putInt("videoFrameNumber", statistics.getVideoFrameNumber());
|
|
122
|
+
map.putDouble("videoFps", statistics.getVideoFps());
|
|
123
|
+
map.putDouble("videoQuality", statistics.getVideoQuality());
|
|
124
|
+
map.putDouble("size", statistics.getSize());
|
|
125
|
+
map.putDouble("time", statistics.getTime());
|
|
126
|
+
map.putDouble("bitrate", statistics.getBitrate());
|
|
127
|
+
map.putDouble("speed", statistics.getSpeed());
|
|
128
|
+
map.putString("statisticsStr", statistics.toString());
|
|
119
129
|
callback.onStatistics(map);
|
|
120
130
|
});
|
|
121
131
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
package com.
|
|
1
|
+
package com.videotrim.widgets;
|
|
2
2
|
|
|
3
3
|
import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;
|
|
4
|
-
import static com.
|
|
5
|
-
import static com.
|
|
4
|
+
import static com.videotrim.utils.VideoTrimmerUtil.RECYCLER_VIEW_PADDING;
|
|
5
|
+
import static com.videotrim.utils.VideoTrimmerUtil.VIDEO_FRAMES_WIDTH;
|
|
6
6
|
|
|
7
7
|
import android.content.Context;
|
|
8
8
|
import android.content.pm.ActivityInfo;
|
|
9
9
|
import android.content.res.Configuration;
|
|
10
10
|
import android.graphics.Bitmap;
|
|
11
|
+
import android.graphics.Color;
|
|
11
12
|
import android.graphics.drawable.GradientDrawable;
|
|
12
13
|
import android.media.MediaMetadataRetriever;
|
|
13
14
|
import android.media.MediaPlayer;
|
|
@@ -34,17 +35,18 @@ import androidx.appcompat.app.AlertDialog;
|
|
|
34
35
|
|
|
35
36
|
import com.arthenica.ffmpegkit.FFmpegSession;
|
|
36
37
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
37
|
-
import com.
|
|
38
|
-
import com.
|
|
39
|
-
import com.
|
|
40
|
-
import com.
|
|
41
|
-
import com.
|
|
42
|
-
import com.
|
|
43
|
-
import com.
|
|
44
|
-
import com.
|
|
38
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
39
|
+
import com.videotrim.R;
|
|
40
|
+
import com.videotrim.enums.ErrorCode;
|
|
41
|
+
import com.videotrim.interfaces.IVideoTrimmerView;
|
|
42
|
+
import com.videotrim.interfaces.VideoTrimListener;
|
|
43
|
+
import com.videotrim.utils.MediaMetadataUtil;
|
|
44
|
+
import com.videotrim.utils.StorageUtil;
|
|
45
|
+
import com.videotrim.utils.VideoTrimmerUtil;
|
|
45
46
|
|
|
46
47
|
import java.io.IOException;
|
|
47
48
|
import java.util.Locale;
|
|
49
|
+
import java.util.Objects;
|
|
48
50
|
|
|
49
51
|
import iknow.android.utils.DeviceUtil;
|
|
50
52
|
import iknow.android.utils.thread.BackgroundExecutor;
|
|
@@ -87,6 +89,9 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
87
89
|
private RelativeLayout trimmerContainerWrapper;
|
|
88
90
|
|
|
89
91
|
private long startTime = 0, endTime = 0;
|
|
92
|
+
private boolean enableRotation = false;
|
|
93
|
+
private double rotationAngle = 0.0;
|
|
94
|
+
|
|
90
95
|
private Vibrator vibrator;
|
|
91
96
|
private boolean didClampWhilePanning = false;
|
|
92
97
|
|
|
@@ -116,16 +121,16 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
116
121
|
private String alertOnFailCloseText = "Close";
|
|
117
122
|
private View currentSelectedhandle;
|
|
118
123
|
|
|
119
|
-
public VideoTrimmerView(ReactApplicationContext context,
|
|
124
|
+
public VideoTrimmerView(ReactApplicationContext context, ReadableMap config, AttributeSet attrs) {
|
|
120
125
|
this(context, attrs, 0, config);
|
|
121
126
|
}
|
|
122
127
|
|
|
123
|
-
public VideoTrimmerView(ReactApplicationContext context, AttributeSet attrs, int defStyleAttr,
|
|
128
|
+
public VideoTrimmerView(ReactApplicationContext context, AttributeSet attrs, int defStyleAttr, ReadableMap config) {
|
|
124
129
|
super(context, attrs, defStyleAttr);
|
|
125
130
|
init(context, config);
|
|
126
131
|
}
|
|
127
132
|
|
|
128
|
-
private void init(ReactApplicationContext context,
|
|
133
|
+
private void init(ReactApplicationContext context, ReadableMap config) {
|
|
129
134
|
this.mContext = context;
|
|
130
135
|
|
|
131
136
|
context.getCurrentActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
|
@@ -374,6 +379,8 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
374
379
|
mDuration,
|
|
375
380
|
startTime,
|
|
376
381
|
endTime,
|
|
382
|
+
enableRotation,
|
|
383
|
+
rotationAngle,
|
|
377
384
|
mOnTrimVideoListener
|
|
378
385
|
);
|
|
379
386
|
}
|
|
@@ -445,51 +452,47 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
445
452
|
return screenWidth;
|
|
446
453
|
}
|
|
447
454
|
|
|
448
|
-
private void configure(
|
|
449
|
-
if (config.
|
|
450
|
-
mMaxDuration = (long) Math.max(0, config.
|
|
455
|
+
private void configure(ReadableMap config) {
|
|
456
|
+
if (config.hasKey("maxDuration") && config.getDouble("maxDuration") > 0) {
|
|
457
|
+
mMaxDuration = (long) Math.max(0, config.getDouble("maxDuration") * 1000L);
|
|
451
458
|
}
|
|
452
459
|
|
|
453
|
-
if (config.
|
|
454
|
-
mMinDuration = (long) Math.max(1000L, config.
|
|
460
|
+
if (config.hasKey("minDuration") && config.getDouble("minDuration") > 0) {
|
|
461
|
+
mMinDuration = (long) Math.max(1000L, config.getDouble("minDuration") * 1000L);
|
|
455
462
|
}
|
|
456
463
|
|
|
457
|
-
cancelBtn.setText(config.
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
isVideoType = config.getType().equals("video");
|
|
464
|
+
cancelBtn.setText(config.getString("cancelButtonText"));
|
|
465
|
+
saveBtn.setText(config.getString("saveButtonText"));
|
|
466
|
+
isVideoType = config.hasKey("type") && Objects.equals(config.getString("type"), "video");
|
|
467
|
+
System.out.println("1111 isVideoType: " + isVideoType);
|
|
462
468
|
|
|
463
|
-
mOutputExt = config.
|
|
469
|
+
mOutputExt = config.hasKey("outputExt") ? config.getString("outputExt") : "mp4";
|
|
464
470
|
if (!isVideoType) {
|
|
465
471
|
mOutputExt = "wav";
|
|
466
472
|
}
|
|
473
|
+
enableHapticFeedback = config.hasKey("enableHapticFeedback") && config.getBoolean("enableHapticFeedback");
|
|
474
|
+
autoplay = config.hasKey("autoplay") && config.getBoolean("autoplay");
|
|
467
475
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
if (config.getJumpToPositionOnLoad() > 0) {
|
|
472
|
-
jumpToPositionOnLoad = (long) config.getJumpToPositionOnLoad();
|
|
476
|
+
if (config.hasKey("jumpToPositionOnLoad") && config.getDouble("jumpToPositionOnLoad") > 0) {
|
|
477
|
+
jumpToPositionOnLoad = (long) Math.max(0, config.getDouble("jumpToPositionOnLoad") * 1000L);
|
|
473
478
|
}
|
|
479
|
+
headerText.setText(config.hasKey("headerText") ? config.getString("headerText") : "");
|
|
474
480
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
textSize = 16;
|
|
480
|
-
}
|
|
481
|
-
headerText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSize);
|
|
482
|
-
headerText.setTextColor((int) config.getHeaderTextColor());
|
|
483
|
-
|
|
484
|
-
headerView.setVisibility(View.VISIBLE);
|
|
485
|
-
|
|
486
|
-
alertOnFailToLoad = config.getAlertOnFailToLoad();
|
|
487
|
-
|
|
488
|
-
alertOnFailTitle = config.getAlertOnFailTitle();
|
|
481
|
+
int textSize = config.hasKey("headerTextSize") ? config.getInt("headerTextSize") : 16;
|
|
482
|
+
if (textSize < 0) {
|
|
483
|
+
textSize = 16;
|
|
484
|
+
}
|
|
489
485
|
|
|
490
|
-
|
|
486
|
+
headerText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSize);
|
|
487
|
+
headerText.setTextColor(config.hasKey("headerTextColor") ? config.getInt("headerTextColor") : Color.BLACK);
|
|
491
488
|
|
|
492
|
-
|
|
489
|
+
headerView.setVisibility(View.VISIBLE);
|
|
490
|
+
alertOnFailToLoad = config.hasKey("alertOnFailToLoad") && config.getBoolean("alertOnFailToLoad");
|
|
491
|
+
alertOnFailTitle = config.hasKey("alertOnFailTitle") ? config.getString("alertOnFailTitle") : "Error";
|
|
492
|
+
alertOnFailMessage = config.hasKey("alertOnFailMessage") ? config.getString("alertOnFailMessage") : "Fail to load media. Possibly invalid file or no network connection";
|
|
493
|
+
alertOnFailCloseText = config.hasKey("alertOnFailCloseText") ? config.getString("alertOnFailCloseText") : "Close";
|
|
494
|
+
enableRotation = config.hasKey("enableRotation") && config.getBoolean("enableRotation");
|
|
495
|
+
rotationAngle = config.hasKey("rotationAngle") ? config.getDouble("rotationAngle") : 0.0;
|
|
493
496
|
}
|
|
494
497
|
|
|
495
498
|
private void startTimingRunnable() {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
#import <AVFoundation/AVFoundation.h>
|
|
3
|
+
|
|
4
|
+
@class AssetLoader;
|
|
5
|
+
|
|
6
|
+
@protocol AssetLoaderDelegate <NSObject>
|
|
7
|
+
@optional
|
|
8
|
+
- (void)assetLoaderDidSucceed:(AssetLoader *)assetLoader;
|
|
9
|
+
- (void)assetLoader:(AssetLoader *)assetLoader didFailWithError:(NSError *)error forKey:(NSString *)key;
|
|
10
|
+
@end
|
|
11
|
+
|
|
12
|
+
@interface AssetLoader : NSObject
|
|
13
|
+
|
|
14
|
+
@property (nonatomic, weak) id<AssetLoaderDelegate> delegate;
|
|
15
|
+
@property (nonatomic, strong, readonly) AVURLAsset *asset;
|
|
16
|
+
|
|
17
|
+
- (void)loadAssetWithURL:(NSURL *)url isVideoType:(BOOL)isVideoType;
|
|
18
|
+
|
|
19
|
+
@end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#import "AssetLoader.h"
|
|
2
|
+
|
|
3
|
+
@interface AssetLoader ()
|
|
4
|
+
@property (nonatomic, strong) AVURLAsset *asset;
|
|
5
|
+
@end
|
|
6
|
+
|
|
7
|
+
@implementation AssetLoader
|
|
8
|
+
|
|
9
|
+
- (void)loadAssetWithURL:(NSURL *)url isVideoType:(BOOL)isVideoType {
|
|
10
|
+
NSDictionary *options = @{ AVURLAssetPreferPreciseDurationAndTimingKey: @YES };
|
|
11
|
+
self.asset = [AVURLAsset URLAssetWithURL:url options:options];
|
|
12
|
+
NSArray *keys = @[ @"duration", @"tracks" ];
|
|
13
|
+
[self.asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
|
|
14
|
+
[self assetLoaded:isVideoType];
|
|
15
|
+
}];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
- (void)assetLoaded:(BOOL)isVideoType {
|
|
19
|
+
NSArray *keys = @[ @"duration", @"tracks" ];
|
|
20
|
+
for (NSString *key in keys) {
|
|
21
|
+
NSError *error = nil;
|
|
22
|
+
AVKeyValueStatus status = [self.asset statusOfValueForKey:key error:&error];
|
|
23
|
+
if (status == AVKeyValueStatusFailed) {
|
|
24
|
+
if ([self.delegate respondsToSelector:@selector(assetLoader:didFailWithError:forKey:)]) {
|
|
25
|
+
[self.delegate assetLoader:self didFailWithError:error forKey:key];
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
} else if (status == AVKeyValueStatusCancelled) {
|
|
29
|
+
NSError *cancelError = [NSError errorWithDomain:@"AssetLoader" code:-1 userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"%@ loading was cancelled", key] }];
|
|
30
|
+
if ([self.delegate respondsToSelector:@selector(assetLoader:didFailWithError:forKey:)]) {
|
|
31
|
+
[self.delegate assetLoader:self didFailWithError:cancelError forKey:key];
|
|
32
|
+
}
|
|
33
|
+
return;
|
|
34
|
+
} else if (status != AVKeyValueStatusLoaded) {
|
|
35
|
+
NSError *unknownError = [NSError errorWithDomain:@"AssetLoader" code:-1 userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"%@ is in an unknown state", key] }];
|
|
36
|
+
if ([self.delegate respondsToSelector:@selector(assetLoader:didFailWithError:forKey:)]) {
|
|
37
|
+
[self.delegate assetLoader:self didFailWithError:unknownError forKey:key];
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (isVideoType) {
|
|
43
|
+
[self processAssetTracks];
|
|
44
|
+
} else {
|
|
45
|
+
if ([self.delegate respondsToSelector:@selector(assetLoaderDidSucceed:)]) {
|
|
46
|
+
[self.delegate assetLoaderDidSucceed:self];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
- (void)processAssetTracks {
|
|
52
|
+
NSArray *videoTracks = [self.asset tracksWithMediaType:AVMediaTypeVideo];
|
|
53
|
+
AVAssetTrack *videoTrack = videoTracks.firstObject;
|
|
54
|
+
if (!videoTrack) {
|
|
55
|
+
NSError *error = [NSError errorWithDomain:@"AssetLoader" code:-1 userInfo:@{ NSLocalizedDescriptionKey: @"No video tracks found" }];
|
|
56
|
+
if ([self.delegate respondsToSelector:@selector(assetLoader:didFailWithError:forKey:)]) {
|
|
57
|
+
[self.delegate assetLoader:self didFailWithError:error forKey:@"tracks"];
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
NSArray *trackKeys = @[ @"naturalSize", @"preferredTransform" ];
|
|
62
|
+
[videoTrack loadValuesAsynchronouslyForKeys:trackKeys completionHandler:^{
|
|
63
|
+
[self trackPropertiesLoaded:videoTrack];
|
|
64
|
+
}];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
- (void)trackPropertiesLoaded:(AVAssetTrack *)track {
|
|
68
|
+
NSError *error = nil;
|
|
69
|
+
AVKeyValueStatus naturalSizeStatus = [track statusOfValueForKey:@"naturalSize" error:&error];
|
|
70
|
+
AVKeyValueStatus preferredTransformStatus = [track statusOfValueForKey:@"preferredTransform" error:&error];
|
|
71
|
+
if (naturalSizeStatus == AVKeyValueStatusLoaded && preferredTransformStatus == AVKeyValueStatusLoaded) {
|
|
72
|
+
CGSize naturalSize = track.naturalSize;
|
|
73
|
+
CGAffineTransform preferredTransform = track.preferredTransform;
|
|
74
|
+
NSLog(@"Natural size: %@", NSStringFromCGSize(naturalSize));
|
|
75
|
+
NSLog(@"Preferred transform: %@", NSStringFromCGAffineTransform(preferredTransform));
|
|
76
|
+
if ([self.delegate respondsToSelector:@selector(assetLoaderDidSucceed:)]) {
|
|
77
|
+
[self.delegate assetLoaderDidSucceed:self];
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
if ([self.delegate respondsToSelector:@selector(assetLoader:didFailWithError:forKey:)]) {
|
|
81
|
+
NSString *failedKey = (naturalSizeStatus != AVKeyValueStatusLoaded) ? @"naturalSize" : @"preferredTransform";
|
|
82
|
+
[self.delegate assetLoader:self didFailWithError:error forKey:failedKey];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@end
|
package/ios/ErrorCode.h
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#import <UIKit/UIKit.h>
|
|
2
|
+
|
|
3
|
+
@interface ProgressAlertController : UIViewController
|
|
4
|
+
|
|
5
|
+
@property (nonatomic, copy) void (^onDismiss)(void);
|
|
6
|
+
@property (nonatomic, strong) UILabel *titleLabel;
|
|
7
|
+
@property (nonatomic, strong) UIProgressView *progressBar;
|
|
8
|
+
@property (nonatomic, strong) UIButton *actionButton;
|
|
9
|
+
|
|
10
|
+
- (void)setTitle:(NSString *)title;
|
|
11
|
+
- (void)setCancelTitle:(NSString *)title;
|
|
12
|
+
- (void)setProgress:(float)progress;
|
|
13
|
+
- (void)showCancelBtn;
|
|
14
|
+
|
|
15
|
+
@end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#import "ProgressAlertController.h"
|
|
2
|
+
|
|
3
|
+
@implementation ProgressAlertController
|
|
4
|
+
|
|
5
|
+
- (void)viewDidLoad {
|
|
6
|
+
[super viewDidLoad];
|
|
7
|
+
[self setupBackground];
|
|
8
|
+
[self setupAlertView];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
- (void)setupBackground {
|
|
12
|
+
self.view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
- (void)setupAlertView {
|
|
16
|
+
UIView *alertView = [[UIView alloc] init];
|
|
17
|
+
alertView.backgroundColor = [UIColor colorWithRed:28/255.0 green:28/255.0 blue:30/255.0 alpha:1.0];
|
|
18
|
+
alertView.layer.cornerRadius = 12;
|
|
19
|
+
alertView.translatesAutoresizingMaskIntoConstraints = NO;
|
|
20
|
+
[self.view addSubview:alertView];
|
|
21
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
22
|
+
[alertView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
|
|
23
|
+
[alertView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
|
|
24
|
+
[alertView.widthAnchor constraintEqualToConstant:270]
|
|
25
|
+
]];
|
|
26
|
+
self.titleLabel = [[UILabel alloc] init];
|
|
27
|
+
self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
|
28
|
+
self.titleLabel.textAlignment = NSTextAlignmentCenter;
|
|
29
|
+
self.titleLabel.font = [UIFont systemFontOfSize:18];
|
|
30
|
+
self.titleLabel.numberOfLines = 0;
|
|
31
|
+
self.titleLabel.textColor = [UIColor whiteColor];
|
|
32
|
+
[alertView addSubview:self.titleLabel];
|
|
33
|
+
self.progressBar = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
|
|
34
|
+
self.progressBar.translatesAutoresizingMaskIntoConstraints = NO;
|
|
35
|
+
[alertView addSubview:self.progressBar];
|
|
36
|
+
self.actionButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
|
37
|
+
[self.actionButton setTitle:@"Cancel" forState:UIControlStateNormal];
|
|
38
|
+
[self.actionButton setTitleColor:[UIColor systemPinkColor] forState:UIControlStateNormal];
|
|
39
|
+
self.actionButton.titleLabel.font = [UIFont systemFontOfSize:16];
|
|
40
|
+
[self.actionButton addTarget:self action:@selector(dismissAlert) forControlEvents:UIControlEventTouchUpInside];
|
|
41
|
+
self.actionButton.translatesAutoresizingMaskIntoConstraints = NO;
|
|
42
|
+
self.actionButton.hidden = YES;
|
|
43
|
+
[alertView addSubview:self.actionButton];
|
|
44
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
45
|
+
[self.titleLabel.topAnchor constraintEqualToAnchor:alertView.topAnchor constant:16],
|
|
46
|
+
[self.titleLabel.leadingAnchor constraintEqualToAnchor:alertView.leadingAnchor constant:16],
|
|
47
|
+
[self.titleLabel.trailingAnchor constraintEqualToAnchor:alertView.trailingAnchor constant:-16],
|
|
48
|
+
[self.progressBar.topAnchor constraintEqualToAnchor:self.titleLabel.bottomAnchor constant:16],
|
|
49
|
+
[self.progressBar.leadingAnchor constraintEqualToAnchor:alertView.leadingAnchor constant:16],
|
|
50
|
+
[self.progressBar.trailingAnchor constraintEqualToAnchor:alertView.trailingAnchor constant:-16],
|
|
51
|
+
[self.actionButton.topAnchor constraintEqualToAnchor:self.progressBar.bottomAnchor constant:16],
|
|
52
|
+
[self.actionButton.bottomAnchor constraintEqualToAnchor:alertView.bottomAnchor constant:-16],
|
|
53
|
+
[self.actionButton.centerXAnchor constraintEqualToAnchor:alertView.centerXAnchor]
|
|
54
|
+
]];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
- (void)dismissAlert {
|
|
58
|
+
if (self.onDismiss) self.onDismiss();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
- (void)setTitle:(NSString *)text {
|
|
62
|
+
self.titleLabel.text = text;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
- (void)setCancelTitle:(NSString *)text {
|
|
66
|
+
[self.actionButton setTitle:text forState:UIControlStateNormal];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
- (void)setProgress:(float)progress {
|
|
70
|
+
[self.progressBar setProgress:progress animated:YES];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
- (void)showCancelBtn {
|
|
74
|
+
[self.view layoutIfNeeded];
|
|
75
|
+
self.actionButton.hidden = NO;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@end
|
package/ios/VideoTrim.h
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#import <VideoTrimSpec/VideoTrimSpec.h>
|
|
2
|
+
#import <AVFoundation/AVFoundation.h>
|
|
3
|
+
#import <UIKit/UIKit.h>
|
|
4
|
+
#import "AssetLoader.h"
|
|
5
|
+
|
|
6
|
+
@class VideoTrimmerViewController;
|
|
7
|
+
@class ProgressAlertController;
|
|
8
|
+
|
|
9
|
+
@interface VideoTrim : NativeVideoTrimSpecBase <NativeVideoTrimSpec, AssetLoaderDelegate, UIDocumentPickerDelegate>
|
|
10
|
+
|
|
11
|
+
@property (nonatomic, strong) NSString *FILE_PREFIX;
|
|
12
|
+
@property (nonatomic, strong) NSString *BEFORE_TRIM_PREFIX;
|
|
13
|
+
@property (nonatomic, assign) BOOL isShowing;
|
|
14
|
+
@property (nonatomic, strong) VideoTrimmerViewController *vc;
|
|
15
|
+
@property (nonatomic, strong) NSURL *outputFile;
|
|
16
|
+
@property (nonatomic, assign) BOOL isVideoType;
|
|
17
|
+
//@property (nonatomic, assign) JS::NativeVideoTrim::EditorConfig editorConfig;
|
|
18
|
+
|
|
19
|
+
// Helper methods
|
|
20
|
+
- (NSURL *)renameFileAtURL:(NSURL *)url newName:(NSString *)newName;
|
|
21
|
+
- (void)trimWithViewController:(VideoTrimmerViewController *)viewController
|
|
22
|
+
inputFile:(NSURL *)inputFile
|
|
23
|
+
videoDuration:(double)videoDuration
|
|
24
|
+
startTime:(double)startTime
|
|
25
|
+
endTime:(double)endTime;
|
|
26
|
+
- (void)saveFileToFilesApp:(NSURL *)fileURL;
|
|
27
|
+
- (void)shareFile:(NSURL *)fileURL;
|
|
28
|
+
- (int)deleteFileAtURL:(NSURL *)url;
|
|
29
|
+
- (void)onError:(NSString *)message code:(NSString *)code;
|
|
30
|
+
|
|
31
|
+
@end
|