diff --git a/demo/src/main/java/com/otaliastudios/transcoder/demo/TranscoderActivity.java b/demo/src/main/java/com/otaliastudios/transcoder/demo/TranscoderActivity.java
index 478fd41e..40b7f995 100644
--- a/demo/src/main/java/com/otaliastudios/transcoder/demo/TranscoderActivity.java
+++ b/demo/src/main/java/com/otaliastudios/transcoder/demo/TranscoderActivity.java
@@ -3,10 +3,12 @@
import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.Intent;
-import android.media.MediaMuxer;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.RadioGroup;
import android.widget.TextView;
@@ -61,6 +63,8 @@ public class TranscoderActivity extends AppCompatActivity implements
private ProgressBar mProgressView;
private TextView mButtonView;
+ private EditText mTrimStartView;
+ private EditText mTrimEndView;
private TextView mAudioReplaceView;
private boolean mIsTranscoding;
@@ -74,6 +78,52 @@ public class TranscoderActivity extends AppCompatActivity implements
private long mTranscodeStartTime;
private TrackStrategy mTranscodeVideoStrategy;
private TrackStrategy mTranscodeAudioStrategy;
+ private long mTrimStartUs = 0;
+ private long mTrimEndUs = 0;
+
+ private TextWatcher mTrimStartTextWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.length() > 0) {
+ try {
+ mTrimStartUs = Long.valueOf(s.toString()) * 1000000;
+ } catch (NumberFormatException e) {
+ mTrimStartUs = 0;
+ LOG.w("Failed to read trimStart value.");
+ }
+ }
+ }
+ };
+ private TextWatcher mTrimEndTextWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.length() > 0) {
+ try {
+ mTrimEndUs = Long.valueOf(s.toString()) * 1000000;
+ } catch (NumberFormatException e) {
+ mTrimEndUs = 0;
+ LOG.w("Failed to read trimEnd value.");
+ }
+ }
+ }
+ };
+
@SuppressLint("SetTextI18n")
@Override
@@ -97,6 +147,8 @@ protected void onCreate(Bundle savedInstanceState) {
mProgressView = findViewById(R.id.progress);
mProgressView.setMax(PROGRESS_BAR_MAX);
+ mTrimStartView = findViewById(R.id.trim_start);
+ mTrimEndView = findViewById(R.id.trim_end);
mAudioReplaceView = findViewById(R.id.replace_info);
mAudioChannelsGroup = findViewById(R.id.channels);
@@ -113,6 +165,8 @@ protected void onCreate(Bundle savedInstanceState) {
mVideoResolutionGroup.setOnCheckedChangeListener(this);
mVideoAspectGroup.setOnCheckedChangeListener(this);
mAudioSampleRateGroup.setOnCheckedChangeListener(this);
+ mTrimStartView.addTextChangedListener(mTrimStartTextWatcher);
+ mTrimEndView.addTextChangedListener(mTrimEndTextWatcher);
syncParameters();
mAudioReplaceGroup.setOnCheckedChangeListener((group, checkedId) -> {
@@ -257,13 +311,27 @@ private void transcode() {
DataSink sink = new DefaultDataSink(mTranscodeOutputFile.getAbsolutePath());
TranscoderOptions.Builder builder = Transcoder.into(sink);
if (mAudioReplacementUri == null) {
- if (mTranscodeInputUri1 != null) builder.addDataSource(this, mTranscodeInputUri1);
- if (mTranscodeInputUri2 != null) builder.addDataSource(this, mTranscodeInputUri2);
- if (mTranscodeInputUri3 != null) builder.addDataSource(this, mTranscodeInputUri3);
+ if (mTrimStartUs > 0 || mTrimEndUs > 0) {
+ if (mTranscodeInputUri1 != null) builder.addDataSource(this, mTranscodeInputUri1, mTrimStartUs, mTrimEndUs);
+ if (mTranscodeInputUri2 != null) builder.addDataSource(this, mTranscodeInputUri2, mTrimStartUs, mTrimEndUs);
+ if (mTranscodeInputUri3 != null) builder.addDataSource(this, mTranscodeInputUri3, mTrimStartUs, mTrimEndUs);
+ }
+ else {
+ if (mTranscodeInputUri1 != null) builder.addDataSource(this, mTranscodeInputUri1);
+ if (mTranscodeInputUri2 != null) builder.addDataSource(this, mTranscodeInputUri2);
+ if (mTranscodeInputUri3 != null) builder.addDataSource(this, mTranscodeInputUri3);
+ }
} else {
- if (mTranscodeInputUri1 != null) builder.addDataSource(TrackType.VIDEO, this, mTranscodeInputUri1);
- if (mTranscodeInputUri2 != null) builder.addDataSource(TrackType.VIDEO, this, mTranscodeInputUri2);
- if (mTranscodeInputUri3 != null) builder.addDataSource(TrackType.VIDEO, this, mTranscodeInputUri3);
+ if (mTrimStartUs > 0 || mTrimEndUs > 0) {
+ if (mTranscodeInputUri1 != null) builder.addDataSource(TrackType.VIDEO, this, mTranscodeInputUri1, mTrimStartUs, mTrimEndUs);
+ if (mTranscodeInputUri2 != null) builder.addDataSource(TrackType.VIDEO, this, mTranscodeInputUri2, mTrimStartUs, mTrimEndUs);
+ if (mTranscodeInputUri3 != null) builder.addDataSource(TrackType.VIDEO, this, mTranscodeInputUri3, mTrimStartUs, mTrimEndUs);
+ }
+ else {
+ if (mTranscodeInputUri1 != null) builder.addDataSource(TrackType.VIDEO, this, mTranscodeInputUri1);
+ if (mTranscodeInputUri2 != null) builder.addDataSource(TrackType.VIDEO, this, mTranscodeInputUri2);
+ if (mTranscodeInputUri3 != null) builder.addDataSource(TrackType.VIDEO, this, mTranscodeInputUri3);
+ }
builder.addDataSource(TrackType.AUDIO, this, mAudioReplacementUri);
}
mTranscodeFuture = builder.setListener(this)
diff --git a/demo/src/main/res/layout/activity_transcoder.xml b/demo/src/main/res/layout/activity_transcoder.xml
index e142c221..9cbcafb7 100644
--- a/demo/src/main/res/layout/activity_transcoder.xml
+++ b/demo/src/main/res/layout/activity_transcoder.xml
@@ -285,6 +285,55 @@
android:layout_height="wrap_content" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
duration) {
+ throw new IllegalArgumentException("Trim values cannot be greater than media duration.");
+ }
+ return duration - trimStart - trimEnd;
+ }
+
+ @Override
+ public int getOrientation() {
+ return source.getOrientation();
+ }
+
+ @Nullable
+ @Override
+ public double[] getLocation() {
+ return source.getLocation();
+ }
+
+ @Override
+ public long getDurationUs() {
+ return trimDurationUs;
+ }
+
+ @Nullable
+ @Override
+ public MediaFormat getTrackFormat(@NonNull TrackType type) {
+ return source.getTrackFormat(type);
+ }
+
+ @Override
+ public void selectTrack(@NonNull TrackType type) {
+ source.selectTrack(type);
+ }
+
+ @Override
+ public long seekBy(long durationUs) {
+ return source.seekBy(durationUs);
+ }
+
+ @Override
+ public boolean canReadTrack(@NonNull TrackType type) {
+ if (!didSeekTracks) {
+ final long sampleTimeUs = seekBy(trimStartUs);
+ updateTrimValues(sampleTimeUs);
+ didSeekTracks = true;
+ }
+ return source.canReadTrack(type);
+ }
+
+ private void updateTrimValues(long timestampUs) {
+ trimDurationUs += trimStartUs - timestampUs;
+ trimStartUs = timestampUs;
+ }
+
+ @Override
+ public void readTrack(@NonNull Chunk chunk) {
+ source.readTrack(chunk);
+ }
+
+ @Override
+ public long getReadUs() {
+ return source.getReadUs();
+ }
+
+ @Override
+ public boolean isDrained() {
+ return source.isDrained() || getReadUs() >= getDurationUs();
+ }
+
+ @Override
+ public void releaseTrack(@NonNull TrackType type) {
+ source.releaseTrack(type);
+ }
+
+ @Override
+ public void rewind() {
+ didSeekTracks = false;
+ source.rewind();
+ }
+}