react-native-video-trim 3.0.2 → 3.0.3
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.
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
package com.videotrim.utils;
|
|
2
2
|
|
|
3
|
-
import android.annotation.SuppressLint;
|
|
4
|
-
import android.content.Context;
|
|
5
3
|
import android.graphics.Bitmap;
|
|
6
4
|
import android.media.MediaCodec;
|
|
7
5
|
import android.media.MediaMetadataRetriever;
|
|
@@ -9,21 +7,13 @@ import com.facebook.react.bridge.Arguments;
|
|
|
9
7
|
import com.facebook.react.bridge.WritableMap;
|
|
10
8
|
import com.videotrim.enums.ErrorCode;
|
|
11
9
|
import com.videotrim.interfaces.VideoTrimListener;
|
|
12
|
-
|
|
13
|
-
import java.text.SimpleDateFormat;
|
|
14
|
-
import java.util.Date;
|
|
15
|
-
import java.util.TimeZone;
|
|
16
|
-
|
|
17
10
|
import iknow.android.utils.DeviceUtil;
|
|
18
11
|
import iknow.android.utils.UnitConverter;
|
|
19
12
|
import iknow.android.utils.callback.SingleCallback;
|
|
20
13
|
import iknow.android.utils.thread.BackgroundExecutor;
|
|
21
|
-
|
|
22
14
|
import android.media.MediaExtractor;
|
|
23
15
|
import android.media.MediaFormat;
|
|
24
16
|
import android.media.MediaMuxer;
|
|
25
|
-
import android.os.Handler;
|
|
26
|
-
import android.os.Looper;
|
|
27
17
|
import java.io.IOException;
|
|
28
18
|
import java.nio.ByteBuffer;
|
|
29
19
|
|
|
@@ -64,36 +54,25 @@ public class VideoTrimmerUtil {
|
|
|
64
54
|
}
|
|
65
55
|
}
|
|
66
56
|
|
|
67
|
-
public boolean
|
|
57
|
+
public boolean isActive() {
|
|
68
58
|
return !isCancelled;
|
|
69
59
|
}
|
|
70
60
|
}
|
|
71
61
|
|
|
72
62
|
public static TrimSession trim(String inputFile, String outputFile, int videoDuration, long startMs, long endMs, final VideoTrimListener callback, float progressUpdateInterval) {
|
|
73
|
-
// Format creation time
|
|
74
|
-
@SuppressLint("SimpleDateFormat") SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
|
|
75
|
-
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
|
76
|
-
String formattedDateTime = dateFormat.format(new Date());
|
|
77
|
-
|
|
78
63
|
// Start trimming in a background thread
|
|
79
64
|
Thread trimThread = new Thread(() -> {
|
|
80
65
|
MediaExtractor extractor = null;
|
|
81
66
|
MediaMuxer muxer = null;
|
|
82
|
-
// Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
83
67
|
TrimSession session = new TrimSession(Thread.currentThread());
|
|
84
68
|
|
|
85
69
|
try {
|
|
86
70
|
// Get rotation metadata from input file
|
|
87
|
-
MediaMetadataRetriever retriever =
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
String rotationStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
|
|
93
|
-
rotation = rotationStr != null ? Integer.parseInt(rotationStr) : 0;
|
|
94
|
-
retriever.release();
|
|
95
|
-
}
|
|
96
|
-
|
|
71
|
+
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
|
72
|
+
retriever.setDataSource(inputFile);
|
|
73
|
+
String rotationStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
|
|
74
|
+
int rotation = rotationStr != null ? Integer.parseInt(rotationStr) : 0;
|
|
75
|
+
retriever.release();
|
|
97
76
|
|
|
98
77
|
extractor = new MediaExtractor();
|
|
99
78
|
extractor.setDataSource(inputFile);
|
|
@@ -103,10 +82,22 @@ public class VideoTrimmerUtil {
|
|
|
103
82
|
int[] trackIndices = new int[trackCount];
|
|
104
83
|
long[] trackStartTimes = new long[trackCount];
|
|
105
84
|
boolean[] tracksAdded = new boolean[trackCount];
|
|
85
|
+
int videoTrackIndex = -1;
|
|
86
|
+
|
|
87
|
+
// Calculate trimmed duration
|
|
88
|
+
long startUs = startMs * 1000; // e.g., 5s = 5000000us
|
|
89
|
+
long endUs = endMs * 1000; // e.g., 9s = 9000000us
|
|
90
|
+
long trimmedDurationUs = endUs - startUs; // e.g., 4s = 4000000us
|
|
106
91
|
|
|
107
|
-
//
|
|
92
|
+
// Add tracks with corrected duration
|
|
108
93
|
for (int i = 0; i < trackCount; i++) {
|
|
109
94
|
MediaFormat format = extractor.getTrackFormat(i);
|
|
95
|
+
String mime = format.getString(MediaFormat.KEY_MIME);
|
|
96
|
+
if (mime.startsWith("video/")) {
|
|
97
|
+
videoTrackIndex = i;
|
|
98
|
+
}
|
|
99
|
+
// Set the duration for each track to the trimmed duration
|
|
100
|
+
format.setLong(MediaFormat.KEY_DURATION, trimmedDurationUs);
|
|
110
101
|
trackIndices[i] = muxer.addTrack(format);
|
|
111
102
|
tracksAdded[i] = false;
|
|
112
103
|
extractor.selectTrack(i);
|
|
@@ -116,16 +107,15 @@ public class VideoTrimmerUtil {
|
|
|
116
107
|
muxer.setOrientationHint(rotation);
|
|
117
108
|
|
|
118
109
|
// Seek to start time
|
|
119
|
-
long startUs = startMs * 1000; // Convert ms to μs
|
|
120
|
-
long endUs = endMs * 1000;
|
|
121
110
|
extractor.seekTo(startUs, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
|
|
122
111
|
|
|
123
112
|
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
|
|
124
113
|
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
|
125
114
|
long lastProgressTime = System.currentTimeMillis();
|
|
115
|
+
boolean videoSampleWritten = false;
|
|
126
116
|
|
|
127
117
|
muxer.start();
|
|
128
|
-
while (session.
|
|
118
|
+
while (session.isActive()) {
|
|
129
119
|
bufferInfo.size = extractor.readSampleData(buffer, 0);
|
|
130
120
|
if (bufferInfo.size < 0) break; // EOS
|
|
131
121
|
|
|
@@ -133,7 +123,6 @@ public class VideoTrimmerUtil {
|
|
|
133
123
|
if (sampleTime > endUs) break;
|
|
134
124
|
|
|
135
125
|
bufferInfo.presentationTimeUs = sampleTime;
|
|
136
|
-
// Map MediaExtractor flags to MediaCodec flags
|
|
137
126
|
int extractorFlags = extractor.getSampleFlags();
|
|
138
127
|
bufferInfo.flags = (extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0 ? MediaCodec.BUFFER_FLAG_KEY_FRAME : 0;
|
|
139
128
|
int trackIndex = extractor.getSampleTrackIndex();
|
|
@@ -144,10 +133,18 @@ public class VideoTrimmerUtil {
|
|
|
144
133
|
}
|
|
145
134
|
bufferInfo.presentationTimeUs -= trackStartTimes[trackIndex]; // Adjust time to start at 0
|
|
146
135
|
|
|
136
|
+
// Ensure presentation time doesn't exceed trimmed duration
|
|
137
|
+
if (bufferInfo.presentationTimeUs >= trimmedDurationUs) {
|
|
138
|
+
extractor.advance();
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (trackIndex == videoTrackIndex) {
|
|
143
|
+
videoSampleWritten = true;
|
|
144
|
+
}
|
|
147
145
|
muxer.writeSampleData(trackIndices[trackIndex], buffer, bufferInfo);
|
|
148
146
|
extractor.advance();
|
|
149
147
|
|
|
150
|
-
// Progress update (every progressUpdateInterval * 1000 ms)
|
|
151
148
|
long currentTime = System.currentTimeMillis();
|
|
152
149
|
if (currentTime - lastProgressTime >= progressUpdateInterval * 1000) {
|
|
153
150
|
double progress = (double)(sampleTime - startUs) / (endUs - startUs);
|
|
@@ -158,46 +155,44 @@ public class VideoTrimmerUtil {
|
|
|
158
155
|
|
|
159
156
|
callback.onStatistics(statsMap);
|
|
160
157
|
callback.onTrimmingProgress((int)(progress * 100));
|
|
161
|
-
|
|
162
|
-
// mainHandler.post(() -> callback.onStatistics(statsMap));
|
|
163
|
-
// mainHandler.post(() -> callback.onTrimmingProgress((int)(progress * 100)));
|
|
164
|
-
|
|
165
158
|
}
|
|
166
159
|
lastProgressTime = currentTime;
|
|
167
160
|
}
|
|
168
161
|
}
|
|
169
162
|
|
|
170
|
-
if
|
|
171
|
-
|
|
163
|
+
// For static videos: ensure one video frame if none written
|
|
164
|
+
if (!videoSampleWritten && videoTrackIndex != -1) {
|
|
165
|
+
for (int i = 0; i < trackCount; i++) {
|
|
166
|
+
if (i != videoTrackIndex) {
|
|
167
|
+
extractor.unselectTrack(i);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
extractor.selectTrack(videoTrackIndex);
|
|
171
|
+
extractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
|
|
172
|
+
bufferInfo.size = extractor.readSampleData(buffer, 0);
|
|
173
|
+
if (bufferInfo.size >= 0) {
|
|
174
|
+
bufferInfo.presentationTimeUs = 0;
|
|
175
|
+
// Map MediaExtractor flags to MediaCodec flags
|
|
176
|
+
int extractorFlags = extractor.getSampleFlags();
|
|
177
|
+
bufferInfo.flags = (extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0 ? MediaCodec.BUFFER_FLAG_KEY_FRAME : 0;
|
|
178
|
+
muxer.writeSampleData(trackIndices[videoTrackIndex], buffer, bufferInfo);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
172
181
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
// android.net.Uri uri = android.provider.MediaStore.Files.getContentUri("external");
|
|
176
|
-
// android.content.ContentValues values = new android.content.ContentValues();
|
|
177
|
-
// values.put(android.provider.MediaStore.MediaColumns.DATA, outputFile);
|
|
178
|
-
// values.put(android.provider.MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
|
|
179
|
-
// values.put(android.provider.MediaStore.MediaColumns.DATE_MODIFIED, System.currentTimeMillis() / 1000);
|
|
180
|
-
// values.put(android.provider.MediaStore.MediaColumns.DATE_TAKEN, System.currentTimeMillis());
|
|
181
|
-
// contentResolver.insert(uri, values);
|
|
182
|
-
// android.content.ContentValues updateValues = new android.content.ContentValues();
|
|
183
|
-
// updateValues.put(android.provider.MediaStore.MediaColumns.DATE_TAKEN, System.currentTimeMillis());
|
|
184
|
-
// contentResolver.update(uri, updateValues, android.provider.MediaStore.MediaColumns.DATA + "=?", new String[]{outputFile});
|
|
185
|
-
|
|
186
|
-
// mainHandler.post(() -> callback.onFinishTrim(outputFile, startMs, endMs, videoDuration));
|
|
182
|
+
if (session.isActive()) {
|
|
183
|
+
muxer.stop();
|
|
187
184
|
callback.onFinishTrim(outputFile, startMs, endMs, videoDuration);
|
|
188
185
|
} else {
|
|
189
|
-
// mainHandler.post(callback::onCancelTrim);
|
|
190
186
|
callback.onCancelTrim();
|
|
191
187
|
}
|
|
192
188
|
|
|
193
189
|
} catch (IOException e) {
|
|
194
190
|
e.printStackTrace();
|
|
195
|
-
// mainHandler.post(() -> callback.onError("Trimming failed: " + e.getMessage(), ErrorCode.TRIMMING_FAILED));
|
|
196
191
|
callback.onError("Trimming failed: " + e.getMessage(), ErrorCode.TRIMMING_FAILED);
|
|
197
192
|
} finally {
|
|
198
193
|
try {
|
|
199
|
-
if (muxer != null) {
|
|
200
|
-
|
|
194
|
+
if (muxer != null && session.isActive()) {
|
|
195
|
+
muxer.release();
|
|
201
196
|
}
|
|
202
197
|
if (extractor != null) extractor.release();
|
|
203
198
|
} catch (Exception e) {
|