Skip to content

Commit 3a897c8

Browse files
author
ehmm
committed
trim video on transcoding
1 parent 6a07d64 commit 3a897c8

File tree

5 files changed

+42
-28
lines changed

5 files changed

+42
-28
lines changed

lib/src/main/java/net/ypresto/androidtranscoder/MediaTranscoder.java

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,14 @@
1919
import android.os.Handler;
2020
import android.os.Looper;
2121
import android.util.Log;
22-
2322
import net.ypresto.androidtranscoder.engine.MediaTranscoderEngine;
2423
import net.ypresto.androidtranscoder.format.MediaFormatPresets;
2524
import net.ypresto.androidtranscoder.format.MediaFormatStrategy;
2625

2726
import java.io.FileDescriptor;
2827
import java.io.FileInputStream;
2928
import java.io.IOException;
30-
import java.util.concurrent.Callable;
31-
import java.util.concurrent.Future;
32-
import java.util.concurrent.LinkedBlockingQueue;
33-
import java.util.concurrent.ThreadFactory;
34-
import java.util.concurrent.ThreadPoolExecutor;
35-
import java.util.concurrent.TimeUnit;
29+
import java.util.concurrent.*;
3630
import java.util.concurrent.atomic.AtomicReference;
3731

3832
public class MediaTranscoder {
@@ -74,7 +68,7 @@ public static MediaTranscoder getInstance() {
7468
* @deprecated Use {@link #transcodeVideo(FileDescriptor, String, MediaFormatStrategy, MediaTranscoder.Listener)} which accepts output video format.
7569
*/
7670
@Deprecated
77-
public Future<Void> transcodeVideo(final FileDescriptor inFileDescriptor, final String outPath, final Listener listener) {
71+
public Future<Void> transcodeVideo(final FileDescriptor inFileDescriptor, final String outPath, final Listener listener, long maxVideoDuration) {
7872
return transcodeVideo(inFileDescriptor, outPath, new MediaFormatStrategy() {
7973
@Override
8074
public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
@@ -85,7 +79,7 @@ public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
8579
public MediaFormat createAudioOutputFormat(MediaFormat inputFormat) {
8680
return null;
8781
}
88-
}, listener);
82+
}, listener, maxVideoDuration);
8983
}
9084

9185
/**
@@ -98,7 +92,7 @@ public MediaFormat createAudioOutputFormat(MediaFormat inputFormat) {
9892
* @param listener Listener instance for callback.
9993
* @throws IOException if input file could not be read.
10094
*/
101-
public Future<Void> transcodeVideo(final String inPath, final String outPath, final MediaFormatStrategy outFormatStrategy, final Listener listener) throws IOException {
95+
public Future<Void> transcodeVideo(final String inPath, final String outPath, final MediaFormatStrategy outFormatStrategy, final Listener listener, long maxVideoDuration) throws IOException {
10296
FileInputStream fileInputStream = null;
10397
FileDescriptor inFileDescriptor;
10498
try {
@@ -146,7 +140,7 @@ private void closeStream() {
146140
Log.e(TAG, "Can't close input stream: ", e);
147141
}
148142
}
149-
});
143+
}, maxVideoDuration);
150144
}
151145

152146
/**
@@ -158,11 +152,11 @@ private void closeStream() {
158152
* @param outFormatStrategy Strategy for output video format.
159153
* @param listener Listener instance for callback.
160154
*/
161-
public Future<Void> transcodeVideo(final FileDescriptor inFileDescriptor, final String outPath, final MediaFormatStrategy outFormatStrategy, final Listener listener) {
155+
public Future<Void> transcodeVideo(final FileDescriptor inFileDescriptor, final String outPath, final MediaFormatStrategy outFormatStrategy, final Listener listener, final long maxVideoDuration) {
162156
Looper looper = Looper.myLooper();
163157
if (looper == null) looper = Looper.getMainLooper();
164158
final Handler handler = new Handler(looper);
165-
final AtomicReference<Future<Void>> futureReference = new AtomicReference<>();
159+
final AtomicReference<Future<Void>> futureReference = new AtomicReference<Future<Void>>();
166160
final Future<Void> createdFuture = mExecutor.submit(new Callable<Void>() {
167161
@Override
168162
public Void call() throws Exception {
@@ -181,6 +175,7 @@ public void run() {
181175
}
182176
});
183177
engine.setDataSource(inFileDescriptor);
178+
engine.setMaxVideoDuration(maxVideoDuration);
184179
engine.transcodeVideo(outPath, outFormatStrategy);
185180
} catch (IOException e) {
186181
Log.w(TAG, "Transcode failed: input file (fd: " + inFileDescriptor.toString() + ") not found"

lib/src/main/java/net/ypresto/androidtranscoder/engine/MediaTranscoderEngine.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import android.media.MediaMetadataRetriever;
2121
import android.media.MediaMuxer;
2222
import android.util.Log;
23-
2423
import net.ypresto.androidtranscoder.format.MediaFormatStrategy;
2524
import net.ypresto.androidtranscoder.utils.MediaExtractorUtils;
2625

@@ -44,6 +43,7 @@ public class MediaTranscoderEngine {
4443
private volatile double mProgress;
4544
private ProgressCallback mProgressCallback;
4645
private long mDurationUs;
46+
private long mMaxVideoDuration;
4747

4848
/**
4949
* Do not use this constructor unless you know what you are doing.
@@ -55,6 +55,8 @@ public void setDataSource(FileDescriptor fileDescriptor) {
5555
mInputFileDescriptor = fileDescriptor;
5656
}
5757

58+
public void setMaxVideoDuration(long maxVideoDuration) {mMaxVideoDuration = maxVideoDuration;}
59+
5860
public ProgressCallback getProgressCallback() {
5961
return mProgressCallback;
6062
}
@@ -143,6 +145,11 @@ private void setupMetadata() throws IOException {
143145

144146
try {
145147
mDurationUs = Long.parseLong(mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)) * 1000;
148+
149+
if(mMaxVideoDuration > 0 && mDurationUs > mMaxVideoDuration) {
150+
mDurationUs = mMaxVideoDuration;
151+
}
152+
146153
} catch (NumberFormatException e) {
147154
mDurationUs = -1;
148155
}
@@ -165,13 +172,13 @@ public void onDetermineOutputFormat() {
165172
});
166173

167174
if (videoOutputFormat == null) {
168-
mVideoTrackTranscoder = new PassThroughTrackTranscoder(mExtractor, trackResult.mVideoTrackIndex, queuedMuxer, QueuedMuxer.SampleType.VIDEO);
175+
mVideoTrackTranscoder = new PassThroughTrackTranscoder(mExtractor, trackResult.mVideoTrackIndex, queuedMuxer, QueuedMuxer.SampleType.VIDEO, mMaxVideoDuration);
169176
} else {
170-
mVideoTrackTranscoder = new VideoTrackTranscoder(mExtractor, trackResult.mVideoTrackIndex, videoOutputFormat, queuedMuxer);
177+
mVideoTrackTranscoder = new VideoTrackTranscoder(mExtractor, trackResult.mVideoTrackIndex, videoOutputFormat, queuedMuxer, mMaxVideoDuration);
171178
}
172179
mVideoTrackTranscoder.setup();
173180
if (audioOutputFormat == null) {
174-
mAudioTrackTranscoder = new PassThroughTrackTranscoder(mExtractor, trackResult.mAudioTrackIndex, queuedMuxer, QueuedMuxer.SampleType.AUDIO);
181+
mAudioTrackTranscoder = new PassThroughTrackTranscoder(mExtractor, trackResult.mAudioTrackIndex, queuedMuxer, QueuedMuxer.SampleType.AUDIO, mMaxVideoDuration);
175182
} else {
176183
throw new UnsupportedOperationException("Transcoding audio tracks currently not supported.");
177184
}
@@ -187,9 +194,10 @@ private void runPipelines() {
187194
mProgress = progress;
188195
if (mProgressCallback != null) mProgressCallback.onProgress(progress); // unknown
189196
}
190-
while (!(mVideoTrackTranscoder.isFinished() && mAudioTrackTranscoder.isFinished())) {
197+
while (!(mVideoTrackTranscoder.isFinished() || mAudioTrackTranscoder.isFinished())) {
191198
boolean stepped = mVideoTrackTranscoder.stepPipeline()
192199
|| mAudioTrackTranscoder.stepPipeline();
200+
193201
loopCount++;
194202
if (mDurationUs > 0 && loopCount % PROGRESS_INTERVAL_STEPS == 0) {
195203
double videoProgress = mVideoTrackTranscoder.isFinished() ? 1.0 : Math.min(1.0, (double) mVideoTrackTranscoder.getWrittenPresentationTimeUs() / mDurationUs);

lib/src/main/java/net/ypresto/androidtranscoder/engine/PassThroughTrackTranscoder.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,15 @@ public class PassThroughTrackTranscoder implements TrackTranscoder {
3434
private boolean mIsEOS;
3535
private MediaFormat mActualOutputFormat;
3636
private long mWrittenPresentationTimeUs;
37+
private long mMaxVideoDuration;
3738

3839
public PassThroughTrackTranscoder(MediaExtractor extractor, int trackIndex,
39-
QueuedMuxer muxer, QueuedMuxer.SampleType sampleType) {
40+
QueuedMuxer muxer, QueuedMuxer.SampleType sampleType, long maxVideoDuration) {
4041
mExtractor = extractor;
4142
mTrackIndex = trackIndex;
4243
mMuxer = muxer;
4344
mSampleType = sampleType;
45+
mMaxVideoDuration = maxVideoDuration;
4446

4547
mActualOutputFormat = mExtractor.getTrackFormat(mTrackIndex);
4648
mMuxer.setOutputFormat(mSampleType, mActualOutputFormat);
@@ -62,7 +64,7 @@ public MediaFormat getDeterminedFormat() {
6264
public boolean stepPipeline() {
6365
if (mIsEOS) return false;
6466
int trackIndex = mExtractor.getSampleTrackIndex();
65-
if (trackIndex < 0) {
67+
if (trackIndex < 0 || (mMaxVideoDuration > 0 && mWrittenPresentationTimeUs >= mMaxVideoDuration)) {
6668
mBuffer.clear();
6769
mBufferInfo.set(0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
6870
mMuxer.writeSampleData(mSampleType, mBuffer, mBufferInfo);

lib/src/main/java/net/ypresto/androidtranscoder/engine/QueuedMuxer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public class QueuedMuxer {
4444
public QueuedMuxer(MediaMuxer muxer, Listener listener) {
4545
mMuxer = muxer;
4646
mListener = listener;
47-
mSampleInfoList = new ArrayList<>();
47+
mSampleInfoList = new ArrayList<SampleInfo>();
4848
}
4949

5050
public void setOutputFormat(SampleType sampleType, MediaFormat format) {

lib/src/main/java/net/ypresto/androidtranscoder/engine/VideoTrackTranscoder.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import android.media.MediaCodec;
1919
import android.media.MediaExtractor;
2020
import android.media.MediaFormat;
21-
2221
import net.ypresto.androidtranscoder.format.MediaFormatExtraConstants;
2322

2423
import java.io.IOException;
@@ -49,13 +48,16 @@ public class VideoTrackTranscoder implements TrackTranscoder {
4948
private boolean mDecoderStarted;
5049
private boolean mEncoderStarted;
5150
private long mWrittenPresentationTimeUs;
51+
private boolean mDurationReached;
52+
private long mMaxVideoDuration;
5253

5354
public VideoTrackTranscoder(MediaExtractor extractor, int trackIndex,
54-
MediaFormat outputFormat, QueuedMuxer muxer) {
55+
MediaFormat outputFormat, QueuedMuxer muxer, long maxVideoDuration) {
5556
mExtractor = extractor;
5657
mTrackIndex = trackIndex;
5758
mOutputFormat = outputFormat;
5859
mMuxer = muxer;
60+
mMaxVideoDuration = maxVideoDuration;
5961
}
6062

6163
@Override
@@ -103,13 +105,13 @@ public boolean stepPipeline() {
103105

104106
int status;
105107
while (drainEncoder(0) != DRAIN_STATE_NONE) busy = true;
108+
106109
do {
107110
status = drainDecoder(0);
108111
if (status != DRAIN_STATE_NONE) busy = true;
109112
// NOTE: not repeating to keep from deadlock when encoder is full.
110113
} while (status == DRAIN_STATE_SHOULD_RETRY_IMMEDIATELY);
111114
while (drainExtractor(0) != DRAIN_STATE_NONE) busy = true;
112-
113115
return busy;
114116
}
115117

@@ -149,12 +151,14 @@ public void release() {
149151
private int drainExtractor(long timeoutUs) {
150152
if (mIsExtractorEOS) return DRAIN_STATE_NONE;
151153
int trackIndex = mExtractor.getSampleTrackIndex();
152-
if (trackIndex >= 0 && trackIndex != mTrackIndex) {
154+
155+
if ((trackIndex >= 0 && trackIndex != mTrackIndex)) {
153156
return DRAIN_STATE_NONE;
154157
}
155158
int result = mDecoder.dequeueInputBuffer(timeoutUs);
156159
if (result < 0) return DRAIN_STATE_NONE;
157-
if (trackIndex < 0) {
160+
161+
if (trackIndex < 0 || mDurationReached) {
158162
mIsExtractorEOS = true;
159163
mDecoder.queueInputBuffer(result, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
160164
return DRAIN_STATE_NONE;
@@ -176,7 +180,8 @@ private int drainDecoder(long timeoutUs) {
176180
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
177181
return DRAIN_STATE_SHOULD_RETRY_IMMEDIATELY;
178182
}
179-
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
183+
184+
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0 || mDurationReached) {
180185
mEncoder.signalEndOfInputStream();
181186
mIsDecoderEOS = true;
182187
mBufferInfo.size = 0;
@@ -214,7 +219,11 @@ private int drainEncoder(long timeoutUs) {
214219
throw new RuntimeException("Could not determine actual output format.");
215220
}
216221

217-
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0 || mWrittenPresentationTimeUs >= 3 * 60 * 1000) {
222+
if(mMaxVideoDuration > 0 && mBufferInfo.presentationTimeUs >= mMaxVideoDuration) {
223+
mDurationReached = true;
224+
}
225+
226+
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0 || mDurationReached) {
218227
mIsEncoderEOS = true;
219228
mBufferInfo.set(0, 0, 0, mBufferInfo.flags);
220229
}

0 commit comments

Comments
 (0)