react-native-video-trim 1.0.22 → 1.0.24
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/android/src/main/java/com/videotrim/VideoTrimModule.java +9 -1
- package/android/src/main/java/com/videotrim/interfaces/VideoTrimListener.java +1 -1
- package/android/src/main/java/com/videotrim/utils/VideoTrimmerUtil.java +18 -4
- package/ios/VideoTrim.swift +17 -5
- package/ios/VideoTrimmerViewController.swift +18 -7
- package/package.json +1 -1
|
@@ -164,6 +164,11 @@ public class VideoTrimModule extends ReactContextBaseJavaModule implements Video
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
@Override public void onTrimmingProgress(int percentage) {
|
|
167
|
+
// prevent onTrimmingProgress is called after onFinishTrim (some rare cases)
|
|
168
|
+
if (mProgressBar == null) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
167
172
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
168
173
|
mProgressBar.setProgress(percentage, true);
|
|
169
174
|
} else {
|
|
@@ -172,10 +177,13 @@ public class VideoTrimModule extends ReactContextBaseJavaModule implements Video
|
|
|
172
177
|
}
|
|
173
178
|
|
|
174
179
|
|
|
175
|
-
@Override public void onFinishTrim(String in) {
|
|
180
|
+
@Override public void onFinishTrim(String in, long startTime, long endTime, int duration) {
|
|
176
181
|
runOnUiThread(() -> {
|
|
177
182
|
WritableMap map = Arguments.createMap();
|
|
178
183
|
map.putString("outputPath", in);
|
|
184
|
+
map.putInt("duration", duration);
|
|
185
|
+
map.putDouble("startTime", (double) startTime);
|
|
186
|
+
map.putDouble("endTime", (double) endTime);
|
|
179
187
|
sendEvent(getReactApplicationContext(), "onFinishTrimming", map);
|
|
180
188
|
showEditorPromise.resolve(in);
|
|
181
189
|
});
|
|
@@ -3,7 +3,7 @@ package com.videotrim.interfaces;
|
|
|
3
3
|
public interface VideoTrimListener {
|
|
4
4
|
void onStartTrim();
|
|
5
5
|
void onTrimmingProgress(int percentage);
|
|
6
|
-
void onFinishTrim(String url);
|
|
6
|
+
void onFinishTrim(String url, long startMs, long endMs, int videoDuration);
|
|
7
7
|
void onError(String errorMessage);
|
|
8
8
|
void onCancel();
|
|
9
9
|
void onSave();
|
|
@@ -48,15 +48,29 @@ public class VideoTrimmerUtil {
|
|
|
48
48
|
// Format the current date and time
|
|
49
49
|
String formattedDateTime = dateFormat.format(currentDate);
|
|
50
50
|
|
|
51
|
-
String cmd = "-ss " + startMs + "ms" + " -to " + endMs + "ms -i " + inputFile + " -c copy -metadata creation_time=" + formattedDateTime + " " + outputFile;
|
|
52
51
|
callback.onStartTrim();
|
|
53
|
-
|
|
52
|
+
|
|
53
|
+
String[] cmds = {
|
|
54
|
+
"-ss",
|
|
55
|
+
startMs + "ms",
|
|
56
|
+
"-to",
|
|
57
|
+
endMs + "ms",
|
|
58
|
+
"-i",
|
|
59
|
+
inputFile,
|
|
60
|
+
"-c",
|
|
61
|
+
"copy",
|
|
62
|
+
"-metadata",
|
|
63
|
+
"creation_time=" + formattedDateTime,
|
|
64
|
+
outputFile
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
FFmpegKit.executeWithArgumentsAsync(cmds, session -> {
|
|
54
68
|
SessionState state = session.getState();
|
|
55
69
|
ReturnCode returnCode = session.getReturnCode();
|
|
56
70
|
|
|
57
71
|
if (ReturnCode.isSuccess(returnCode)) {
|
|
58
72
|
// SUCCESS
|
|
59
|
-
callback.onFinishTrim(outputFile);
|
|
73
|
+
callback.onFinishTrim(outputFile, startMs, endMs, videoDuration);
|
|
60
74
|
}
|
|
61
75
|
else {
|
|
62
76
|
// CANCEL + FAILURE
|
|
@@ -64,7 +78,7 @@ public class VideoTrimmerUtil {
|
|
|
64
78
|
callback.onError(errorMessage);
|
|
65
79
|
}
|
|
66
80
|
}, log -> {
|
|
67
|
-
|
|
81
|
+
System.out.println("FFmpeg process started with log " + log.getMessage());
|
|
68
82
|
}, statistics -> {
|
|
69
83
|
int timeInMilliseconds = (int) statistics.getTime();
|
|
70
84
|
if (timeInMilliseconds > 0) {
|
package/ios/VideoTrim.swift
CHANGED
|
@@ -281,8 +281,6 @@ class VideoTrim: RCTEventEmitter {
|
|
|
281
281
|
formatter.timeZone = TimeZone(identifier: "UTC")
|
|
282
282
|
let dateTime = formatter.string(from: Date())
|
|
283
283
|
|
|
284
|
-
let cmd = "-ss \(startTime * 1000)ms -to \(endTime * 1000)ms -i \(inputFile) -c copy -metadata creation_time=\(dateTime) \(outputFile)";
|
|
285
|
-
|
|
286
284
|
self.emitEventToJS("onStartTrimming", eventData: nil)
|
|
287
285
|
|
|
288
286
|
// Create Alert
|
|
@@ -304,14 +302,28 @@ class VideoTrim: RCTEventEmitter {
|
|
|
304
302
|
})
|
|
305
303
|
}
|
|
306
304
|
|
|
307
|
-
|
|
305
|
+
let cmds = [
|
|
306
|
+
"-ss",
|
|
307
|
+
"\(startTime * 1000)ms",
|
|
308
|
+
"-to",
|
|
309
|
+
"\(endTime * 1000)ms",
|
|
310
|
+
"-i",
|
|
311
|
+
"\(inputFile)",
|
|
312
|
+
"-c",
|
|
313
|
+
"copy",
|
|
314
|
+
"-metadata",
|
|
315
|
+
"creation_time=\(dateTime)",
|
|
316
|
+
outputFile
|
|
317
|
+
]
|
|
318
|
+
|
|
319
|
+
FFmpegKit.execute(withArgumentsAsync: cmds, withCompleteCallback: { session in
|
|
308
320
|
let _ = self.deleteFile(url: inputFile) // remove the file we just copied to document directory
|
|
309
321
|
|
|
310
322
|
let state = session?.getState()
|
|
311
323
|
let returnCode = session?.getReturnCode()
|
|
312
324
|
|
|
313
325
|
if ReturnCode.isSuccess(returnCode) {
|
|
314
|
-
let eventPayload: [String: Any] = ["outputPath": outputFile]
|
|
326
|
+
let eventPayload: [String: Any] = ["outputPath": outputFile, "startTime": startTime, "endTime": endTime, "duration": videoDuration]
|
|
315
327
|
self.emitEventToJS("onFinishTrimming", eventData: eventPayload)
|
|
316
328
|
|
|
317
329
|
if (self.saveToPhoto) {
|
|
@@ -356,7 +368,7 @@ class VideoTrim: RCTEventEmitter {
|
|
|
356
368
|
})
|
|
357
369
|
}
|
|
358
370
|
}, withLogCallback: { log in
|
|
359
|
-
|
|
371
|
+
print("FFmpeg process started with log " + (log?.getMessage() ?? ""));
|
|
360
372
|
}, withStatisticsCallback: { statistics in
|
|
361
373
|
let timeInMilliseconds = statistics?.getTime() ?? 0;
|
|
362
374
|
if timeInMilliseconds > 0 {
|
|
@@ -48,7 +48,6 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
48
48
|
var isSeekInProgress: Bool = false // Marker
|
|
49
49
|
private var chaseTime = CMTime.zero
|
|
50
50
|
private var preferredFrameRate: Float = 23.98
|
|
51
|
-
|
|
52
51
|
|
|
53
52
|
// MARK: - Input
|
|
54
53
|
@objc private func didBeginTrimmingFromStart(_ sender: VideoTrimmer) {
|
|
@@ -133,6 +132,9 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
133
132
|
player.removeTimeObserver(token)
|
|
134
133
|
timeObserverToken = nil
|
|
135
134
|
}
|
|
135
|
+
// Remove observer
|
|
136
|
+
NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
|
|
137
|
+
|
|
136
138
|
playerController.player = nil
|
|
137
139
|
playerController.dismiss(animated: false, completion: nil)
|
|
138
140
|
}
|
|
@@ -259,6 +261,15 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
259
261
|
playerController.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
|
|
260
262
|
playerController.view.bottomAnchor.constraint(equalTo: trimmer.topAnchor, constant: -16)
|
|
261
263
|
])
|
|
264
|
+
|
|
265
|
+
// Add observer for the end of playback
|
|
266
|
+
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying), name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
@objc private func playerDidFinishPlaying(note: NSNotification) {
|
|
270
|
+
// Directly set the play icon
|
|
271
|
+
// the reason in at this time player.timeControlStatus == .playing still returns true
|
|
272
|
+
playBtn.setImage(self.playIcon, for: .normal)
|
|
262
273
|
}
|
|
263
274
|
|
|
264
275
|
private func setupTimeObserver() {
|
|
@@ -292,29 +303,29 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
292
303
|
public func seek(to time: CMTime) {
|
|
293
304
|
seekSmoothlyToTime(newChaseTime: time)
|
|
294
305
|
}
|
|
295
|
-
|
|
306
|
+
|
|
296
307
|
private func seekSmoothlyToTime(newChaseTime: CMTime) {
|
|
297
308
|
if CMTimeCompare(newChaseTime, chaseTime) != 0 {
|
|
298
309
|
chaseTime = newChaseTime
|
|
299
|
-
|
|
310
|
+
|
|
300
311
|
if !isSeekInProgress {
|
|
301
312
|
trySeekToChaseTime()
|
|
302
313
|
}
|
|
303
314
|
}
|
|
304
315
|
}
|
|
305
|
-
|
|
316
|
+
|
|
306
317
|
private func trySeekToChaseTime() {
|
|
307
318
|
guard player?.status == .readyToPlay else { return }
|
|
308
319
|
actuallySeekToTime()
|
|
309
320
|
}
|
|
310
|
-
|
|
321
|
+
|
|
311
322
|
private func actuallySeekToTime() {
|
|
312
323
|
isSeekInProgress = true
|
|
313
324
|
let seekTimeInProgress = chaseTime
|
|
314
|
-
|
|
325
|
+
|
|
315
326
|
player?.seek(to: seekTimeInProgress, toleranceBefore: .zero, toleranceAfter: .zero) { [weak self] _ in
|
|
316
327
|
guard let `self` = self else { return }
|
|
317
|
-
|
|
328
|
+
|
|
318
329
|
if CMTimeCompare(seekTimeInProgress, self.chaseTime) == 0 {
|
|
319
330
|
self.isSeekInProgress = false
|
|
320
331
|
} else {
|