Skip to content

Commit 6f154fe

Browse files
committed
patch ypresto#32 Allow video with no audio stream present to be transcoded ypresto#32
1 parent 8bbf3ba commit 6f154fe

File tree

7 files changed

+249
-16
lines changed

7 files changed

+249
-16
lines changed

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

+12-3
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,12 @@ private void setupTrackTranscoders(MediaFormatStrategy formatStrategy) {
160160
@Override
161161
public void onDetermineOutputFormat() {
162162
MediaFormatValidator.validateVideoOutputFormat(mVideoTrackTranscoder.getDeterminedFormat());
163-
MediaFormatValidator.validateAudioOutputFormat(mAudioTrackTranscoder.getDeterminedFormat());
163+
164+
// If there is an audio track, validate the output is correct.
165+
MediaFormat audioFormat = mAudioTrackTranscoder.getDeterminedFormat();
166+
if (audioFormat != null) {
167+
MediaFormatValidator.validateAudioOutputFormat(audioFormat);
168+
}
164169
}
165170
});
166171

@@ -170,14 +175,18 @@ public void onDetermineOutputFormat() {
170175
mVideoTrackTranscoder = new VideoTrackTranscoder(mExtractor, trackResult.mVideoTrackIndex, videoOutputFormat, queuedMuxer);
171176
}
172177
mVideoTrackTranscoder.setup();
178+
mExtractor.selectTrack(trackResult.mVideoTrackIndex);
179+
173180
if (audioOutputFormat == null) {
174181
mAudioTrackTranscoder = new PassThroughTrackTranscoder(mExtractor, trackResult.mAudioTrackIndex, queuedMuxer, QueuedMuxer.SampleType.AUDIO);
175182
} else {
176183
mAudioTrackTranscoder = new AudioTrackTranscoder(mExtractor, trackResult.mAudioTrackIndex, audioOutputFormat, queuedMuxer);
177184
}
185+
186+
if (trackResult.mAudioTrackIndex >= 0) {
187+
mExtractor.selectTrack(trackResult.mAudioTrackIndex);
188+
}
178189
mAudioTrackTranscoder.setup();
179-
mExtractor.selectTrack(trackResult.mVideoTrackIndex);
180-
mExtractor.selectTrack(trackResult.mAudioTrackIndex);
181190
}
182191

183192
private void runPipelines() {

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

+13-4
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,19 @@ public PassThroughTrackTranscoder(MediaExtractor extractor, int trackIndex,
4242
mMuxer = muxer;
4343
mSampleType = sampleType;
4444

45-
mActualOutputFormat = mExtractor.getTrackFormat(mTrackIndex);
46-
mMuxer.setOutputFormat(mSampleType, mActualOutputFormat);
47-
mBufferSize = mActualOutputFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
48-
mBuffer = ByteBuffer.allocateDirect(mBufferSize).order(ByteOrder.nativeOrder());
45+
if (trackIndex >= 0) {
46+
mActualOutputFormat = mExtractor.getTrackFormat(mTrackIndex);
47+
mMuxer.setOutputFormat(mSampleType, mActualOutputFormat);
48+
mBufferSize = mActualOutputFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
49+
mBuffer = ByteBuffer.allocateDirect(mBufferSize).order(ByteOrder.nativeOrder());
50+
} else {
51+
// track has no audio. Passthrough should also exclude the track.
52+
mMuxer.setOutputFormat(mSampleType, null);
53+
54+
// Nothing to do. EOS and report it took us 0 ms.
55+
mIsEOS = true;
56+
mWrittenPresentationTimeUs = 0;
57+
}
4958
}
5059

5160
@Override

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

+13-3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
*/
3131
public class QueuedMuxer {
3232
private static final String TAG = "QueuedMuxer";
33+
private static final int EXCLUDE_TRACK_INDEX = -1;
3334
private static final int BUFFER_SIZE = 64 * 1024; // I have no idea whether this value is appropriate or not...
3435
private final MediaMuxer mMuxer;
3536
private final Listener mListener;
@@ -54,6 +55,11 @@ public void setOutputFormat(SampleType sampleType, MediaFormat format) {
5455
break;
5556
case AUDIO:
5657
mAudioFormat = format;
58+
59+
if(format == null) {
60+
// Tell the muxer we do not require audio.
61+
mAudioTrackIndex = EXCLUDE_TRACK_INDEX;
62+
}
5763
break;
5864
default:
5965
throw new AssertionError();
@@ -62,13 +68,17 @@ public void setOutputFormat(SampleType sampleType, MediaFormat format) {
6268
}
6369

6470
private void onSetOutputFormat() {
65-
if (mVideoFormat == null || mAudioFormat == null) return;
71+
if (mVideoFormat == null || (mAudioFormat == null && mAudioTrackIndex != EXCLUDE_TRACK_INDEX)) return;
6672
mListener.onDetermineOutputFormat();
6773

6874
mVideoTrackIndex = mMuxer.addTrack(mVideoFormat);
6975
Log.v(TAG, "Added track #" + mVideoTrackIndex + " with " + mVideoFormat.getString(MediaFormat.KEY_MIME) + " to muxer");
70-
mAudioTrackIndex = mMuxer.addTrack(mAudioFormat);
71-
Log.v(TAG, "Added track #" + mAudioTrackIndex + " with " + mAudioFormat.getString(MediaFormat.KEY_MIME) + " to muxer");
76+
77+
if(mAudioFormat != null) {
78+
mAudioTrackIndex = mMuxer.addTrack(mAudioFormat);
79+
Log.v(TAG, "Added track #" + mAudioTrackIndex + " with " + mAudioFormat.getString(MediaFormat.KEY_MIME) + " to muxer");
80+
}
81+
7282
mMuxer.start();
7383
mStarted = true;
7484

lib/src/main/java/net/ypresto/androidtranscoder/format/Android16By9FormatStrategy.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,14 @@ public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
6161
if (longer * 9 != shorter * 16) {
6262
throw new OutputFormatUnavailableException("This video is not 16:9, and is not able to transcode. (" + width + "x" + height + ")");
6363
}
64+
65+
/*
66+
I've commented this out because its unsafe to assume the user wants to bypass compression if resolution is equal.
6467
if (shorter <= targetShorter) {
6568
Log.d(TAG, "This video's height is less or equal to " + targetShorter + ", pass-through. (" + width + "x" + height + ")");
6669
return null;
67-
}
70+
}*/
71+
6872
MediaFormat format = MediaFormat.createVideoFormat("video/avc", outWidth, outHeight);
6973
// From Nexus 4 Camera in 720p
7074
format.setInteger(MediaFormat.KEY_BIT_RATE, mVideoBitrate);
@@ -76,7 +80,7 @@ public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
7680

7781
@Override
7882
public MediaFormat createAudioOutputFormat(MediaFormat inputFormat) {
79-
if (mAudioBitrate == AUDIO_BITRATE_AS_IS || mAudioChannels == AUDIO_CHANNELS_AS_IS) return null;
83+
if (inputFormat == null || mAudioBitrate == AUDIO_BITRATE_AS_IS || mAudioChannels == AUDIO_CHANNELS_AS_IS) return null;
8084

8185
// Use original sample rate, as resampling is not supported yet.
8286
final MediaFormat format = MediaFormat.createAudioFormat(MediaFormatExtraConstants.MIMETYPE_AUDIO_AAC,

lib/src/main/java/net/ypresto/androidtranscoder/format/Android720pFormatStrategy.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,13 @@ public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
6363
if (longer * 9 != shorter * 16) {
6464
throw new OutputFormatUnavailableException("This video is not 16:9, and is not able to transcode. (" + width + "x" + height + ")");
6565
}
66+
67+
/*
68+
I've commented this out because its unsafe to assume the user wants to bypass compression if resolution is equal.
6669
if (shorter <= SHORTER_LENGTH) {
6770
Log.d(TAG, "This video is less or equal to 720p, pass-through. (" + width + "x" + height + ")");
6871
return null;
69-
}
72+
}*/
7073
MediaFormat format = MediaFormat.createVideoFormat("video/avc", outWidth, outHeight);
7174
// From Nexus 4 Camera in 720p
7275
format.setInteger(MediaFormat.KEY_BIT_RATE, mVideoBitrate);
@@ -78,7 +81,7 @@ public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
7881

7982
@Override
8083
public MediaFormat createAudioOutputFormat(MediaFormat inputFormat) {
81-
if (mAudioBitrate == AUDIO_BITRATE_AS_IS || mAudioChannels == AUDIO_CHANNELS_AS_IS) return null;
84+
if (inputFormat == null || mAudioBitrate == AUDIO_BITRATE_AS_IS || mAudioChannels == AUDIO_CHANNELS_AS_IS) return null;
8285

8386
// Use original sample rate, as resampling is not supported yet.
8487
final MediaFormat format = MediaFormat.createAudioFormat(MediaFormatExtraConstants.MIMETYPE_AUDIO_AAC,

lib/src/main/java/net/ypresto/androidtranscoder/utils/MediaExtractorUtils.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ public static TrackResult getFirstVideoAndAudioTrack(MediaExtractor extractor) {
5555
}
5656
if (trackResult.mVideoTrackIndex >= 0 && trackResult.mAudioTrackIndex >= 0) break;
5757
}
58-
if (trackResult.mVideoTrackIndex < 0 || trackResult.mAudioTrackIndex < 0) {
59-
throw new IllegalArgumentException("extractor does not contain video and/or audio tracks.");
58+
if (trackResult.mVideoTrackIndex < 0) {
59+
throw new IllegalArgumentException("extractor does not contain video tracks.");
6060
}
6161
return trackResult;
6262
}

patch/android-transcode-audio.patch

+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
From 74dbf9bec330168803cc5f3417aa39bd9260e98e Mon Sep 17 00:00:00 2001
2+
From: Cory Thompson <[email protected]>
3+
Date: Thu, 11 Aug 2016 15:21:12 +1000
4+
Subject: [PATCH] Allow video with no audio stream present to be transcoded
5+
6+
---
7+
.../androidtranscoder/engine/MediaTranscoderEngine.java | 15 ++++++++++++---
8+
.../engine/PassThroughTrackTranscoder.java | 17 +++++++++++++----
9+
.../ypresto/androidtranscoder/engine/QueuedMuxer.java | 16 +++++++++++++---
10+
.../format/Android16By9FormatStrategy.java | 8 ++++++--
11+
.../format/Android720pFormatStrategy.java | 7 +++++--
12+
.../androidtranscoder/utils/MediaExtractorUtils.java | 4 ++--
13+
6 files changed, 51 insertions(+), 16 deletions(-)
14+
15+
diff --git a/lib/src/main/java/net/ypresto/androidtranscoder/engine/MediaTranscoderEngine.java b/lib/src/main/java/net/ypresto/androidtranscoder/engine/MediaTranscoderEngine.java
16+
index 64a1ede..c883979 100644
17+
--- a/lib/src/main/java/net/ypresto/androidtranscoder/engine/MediaTranscoderEngine.java
18+
+++ b/lib/src/main/java/net/ypresto/androidtranscoder/engine/MediaTranscoderEngine.java
19+
@@ -160,7 +160,12 @@ private void setupTrackTranscoders(MediaFormatStrategy formatStrategy) {
20+
@Override
21+
public void onDetermineOutputFormat() {
22+
MediaFormatValidator.validateVideoOutputFormat(mVideoTrackTranscoder.getDeterminedFormat());
23+
- MediaFormatValidator.validateAudioOutputFormat(mAudioTrackTranscoder.getDeterminedFormat());
24+
+
25+
+ // If there is an audio track, validate the output is correct.
26+
+ MediaFormat audioFormat = mAudioTrackTranscoder.getDeterminedFormat();
27+
+ if (audioFormat != null) {
28+
+ MediaFormatValidator.validateAudioOutputFormat(audioFormat);
29+
+ }
30+
}
31+
});
32+
33+
@@ -170,14 +175,18 @@ public void onDetermineOutputFormat() {
34+
mVideoTrackTranscoder = new VideoTrackTranscoder(mExtractor, trackResult.mVideoTrackIndex, videoOutputFormat, queuedMuxer);
35+
}
36+
mVideoTrackTranscoder.setup();
37+
+ mExtractor.selectTrack(trackResult.mVideoTrackIndex);
38+
+
39+
if (audioOutputFormat == null) {
40+
mAudioTrackTranscoder = new PassThroughTrackTranscoder(mExtractor, trackResult.mAudioTrackIndex, queuedMuxer, QueuedMuxer.SampleType.AUDIO);
41+
} else {
42+
mAudioTrackTranscoder = new AudioTrackTranscoder(mExtractor, trackResult.mAudioTrackIndex, audioOutputFormat, queuedMuxer);
43+
}
44+
+
45+
+ if (trackResult.mAudioTrackIndex >= 0) {
46+
+ mExtractor.selectTrack(trackResult.mAudioTrackIndex);
47+
+ }
48+
mAudioTrackTranscoder.setup();
49+
- mExtractor.selectTrack(trackResult.mVideoTrackIndex);
50+
- mExtractor.selectTrack(trackResult.mAudioTrackIndex);
51+
}
52+
53+
private void runPipelines() {
54+
diff --git a/lib/src/main/java/net/ypresto/androidtranscoder/engine/PassThroughTrackTranscoder.java b/lib/src/main/java/net/ypresto/androidtranscoder/engine/PassThroughTrackTranscoder.java
55+
index 7608dac..5813b8b 100644
56+
--- a/lib/src/main/java/net/ypresto/androidtranscoder/engine/PassThroughTrackTranscoder.java
57+
+++ b/lib/src/main/java/net/ypresto/androidtranscoder/engine/PassThroughTrackTranscoder.java
58+
@@ -42,10 +42,19 @@ public PassThroughTrackTranscoder(MediaExtractor extractor, int trackIndex,
59+
mMuxer = muxer;
60+
mSampleType = sampleType;
61+
62+
- mActualOutputFormat = mExtractor.getTrackFormat(mTrackIndex);
63+
- mMuxer.setOutputFormat(mSampleType, mActualOutputFormat);
64+
- mBufferSize = mActualOutputFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
65+
- mBuffer = ByteBuffer.allocateDirect(mBufferSize).order(ByteOrder.nativeOrder());
66+
+ if (trackIndex >= 0) {
67+
+ mActualOutputFormat = mExtractor.getTrackFormat(mTrackIndex);
68+
+ mMuxer.setOutputFormat(mSampleType, mActualOutputFormat);
69+
+ mBufferSize = mActualOutputFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
70+
+ mBuffer = ByteBuffer.allocateDirect(mBufferSize).order(ByteOrder.nativeOrder());
71+
+ } else {
72+
+ // track has no audio. Passthrough should also exclude the track.
73+
+ mMuxer.setOutputFormat(mSampleType, null);
74+
+
75+
+ // Nothing to do. EOS and report it took us 0 ms.
76+
+ mIsEOS = true;
77+
+ mWrittenPresentationTimeUs = 0;
78+
+ }
79+
}
80+
81+
@Override
82+
diff --git a/lib/src/main/java/net/ypresto/androidtranscoder/engine/QueuedMuxer.java b/lib/src/main/java/net/ypresto/androidtranscoder/engine/QueuedMuxer.java
83+
index df58e99..bc17a74 100644
84+
--- a/lib/src/main/java/net/ypresto/androidtranscoder/engine/QueuedMuxer.java
85+
+++ b/lib/src/main/java/net/ypresto/androidtranscoder/engine/QueuedMuxer.java
86+
@@ -30,6 +30,7 @@
87+
*/
88+
public class QueuedMuxer {
89+
private static final String TAG = "QueuedMuxer";
90+
+ private static final int EXCLUDE_TRACK_INDEX = -1;
91+
private static final int BUFFER_SIZE = 64 * 1024; // I have no idea whether this value is appropriate or not...
92+
private final MediaMuxer mMuxer;
93+
private final Listener mListener;
94+
@@ -54,6 +55,11 @@ public void setOutputFormat(SampleType sampleType, MediaFormat format) {
95+
break;
96+
case AUDIO:
97+
mAudioFormat = format;
98+
+
99+
+ if(format == null) {
100+
+ // Tell the muxer we do not require audio.
101+
+ mAudioTrackIndex = EXCLUDE_TRACK_INDEX;
102+
+ }
103+
break;
104+
default:
105+
throw new AssertionError();
106+
@@ -62,13 +68,17 @@ public void setOutputFormat(SampleType sampleType, MediaFormat format) {
107+
}
108+
109+
private void onSetOutputFormat() {
110+
- if (mVideoFormat == null || mAudioFormat == null) return;
111+
+ if (mVideoFormat == null || (mAudioFormat == null && mAudioTrackIndex != EXCLUDE_TRACK_INDEX)) return;
112+
mListener.onDetermineOutputFormat();
113+
114+
mVideoTrackIndex = mMuxer.addTrack(mVideoFormat);
115+
Log.v(TAG, "Added track #" + mVideoTrackIndex + " with " + mVideoFormat.getString(MediaFormat.KEY_MIME) + " to muxer");
116+
- mAudioTrackIndex = mMuxer.addTrack(mAudioFormat);
117+
- Log.v(TAG, "Added track #" + mAudioTrackIndex + " with " + mAudioFormat.getString(MediaFormat.KEY_MIME) + " to muxer");
118+
+
119+
+ if(mAudioFormat != null) {
120+
+ mAudioTrackIndex = mMuxer.addTrack(mAudioFormat);
121+
+ Log.v(TAG, "Added track #" + mAudioTrackIndex + " with " + mAudioFormat.getString(MediaFormat.KEY_MIME) + " to muxer");
122+
+ }
123+
+
124+
mMuxer.start();
125+
mStarted = true;
126+
127+
diff --git a/lib/src/main/java/net/ypresto/androidtranscoder/format/Android16By9FormatStrategy.java b/lib/src/main/java/net/ypresto/androidtranscoder/format/Android16By9FormatStrategy.java
128+
index faf3031..41d8fd6 100644
129+
--- a/lib/src/main/java/net/ypresto/androidtranscoder/format/Android16By9FormatStrategy.java
130+
+++ b/lib/src/main/java/net/ypresto/androidtranscoder/format/Android16By9FormatStrategy.java
131+
@@ -61,10 +61,14 @@ public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
132+
if (longer * 9 != shorter * 16) {
133+
throw new OutputFormatUnavailableException("This video is not 16:9, and is not able to transcode. (" + width + "x" + height + ")");
134+
}
135+
+
136+
+ /*
137+
+ I've commented this out because its unsafe to assume the user wants to bypass compression if resolution is equal.
138+
if (shorter <= targetShorter) {
139+
Log.d(TAG, "This video's height is less or equal to " + targetShorter + ", pass-through. (" + width + "x" + height + ")");
140+
return null;
141+
- }
142+
+ }*/
143+
+
144+
MediaFormat format = MediaFormat.createVideoFormat("video/avc", outWidth, outHeight);
145+
// From Nexus 4 Camera in 720p
146+
format.setInteger(MediaFormat.KEY_BIT_RATE, mVideoBitrate);
147+
@@ -76,7 +80,7 @@ public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
148+
149+
@Override
150+
public MediaFormat createAudioOutputFormat(MediaFormat inputFormat) {
151+
- if (mAudioBitrate == AUDIO_BITRATE_AS_IS || mAudioChannels == AUDIO_CHANNELS_AS_IS) return null;
152+
+ if (inputFormat == null || mAudioBitrate == AUDIO_BITRATE_AS_IS || mAudioChannels == AUDIO_CHANNELS_AS_IS) return null;
153+
154+
// Use original sample rate, as resampling is not supported yet.
155+
final MediaFormat format = MediaFormat.createAudioFormat(MediaFormatExtraConstants.MIMETYPE_AUDIO_AAC,
156+
diff --git a/lib/src/main/java/net/ypresto/androidtranscoder/format/Android720pFormatStrategy.java b/lib/src/main/java/net/ypresto/androidtranscoder/format/Android720pFormatStrategy.java
157+
index dc59caa..c910055 100644
158+
--- a/lib/src/main/java/net/ypresto/androidtranscoder/format/Android720pFormatStrategy.java
159+
+++ b/lib/src/main/java/net/ypresto/androidtranscoder/format/Android720pFormatStrategy.java
160+
@@ -63,10 +63,13 @@ public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
161+
if (longer * 9 != shorter * 16) {
162+
throw new OutputFormatUnavailableException("This video is not 16:9, and is not able to transcode. (" + width + "x" + height + ")");
163+
}
164+
+
165+
+ /*
166+
+ I've commented this out because its unsafe to assume the user wants to bypass compression if resolution is equal.
167+
if (shorter <= SHORTER_LENGTH) {
168+
Log.d(TAG, "This video is less or equal to 720p, pass-through. (" + width + "x" + height + ")");
169+
return null;
170+
- }
171+
+ }*/
172+
MediaFormat format = MediaFormat.createVideoFormat("video/avc", outWidth, outHeight);
173+
// From Nexus 4 Camera in 720p
174+
format.setInteger(MediaFormat.KEY_BIT_RATE, mVideoBitrate);
175+
@@ -78,7 +81,7 @@ public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
176+
177+
@Override
178+
public MediaFormat createAudioOutputFormat(MediaFormat inputFormat) {
179+
- if (mAudioBitrate == AUDIO_BITRATE_AS_IS || mAudioChannels == AUDIO_CHANNELS_AS_IS) return null;
180+
+ if (inputFormat == null || mAudioBitrate == AUDIO_BITRATE_AS_IS || mAudioChannels == AUDIO_CHANNELS_AS_IS) return null;
181+
182+
// Use original sample rate, as resampling is not supported yet.
183+
final MediaFormat format = MediaFormat.createAudioFormat(MediaFormatExtraConstants.MIMETYPE_AUDIO_AAC,
184+
diff --git a/lib/src/main/java/net/ypresto/androidtranscoder/utils/MediaExtractorUtils.java b/lib/src/main/java/net/ypresto/androidtranscoder/utils/MediaExtractorUtils.java
185+
index b973d2f..a6607db 100644
186+
--- a/lib/src/main/java/net/ypresto/androidtranscoder/utils/MediaExtractorUtils.java
187+
+++ b/lib/src/main/java/net/ypresto/androidtranscoder/utils/MediaExtractorUtils.java
188+
@@ -55,8 +55,8 @@ public static TrackResult getFirstVideoAndAudioTrack(MediaExtractor extractor) {
189+
}
190+
if (trackResult.mVideoTrackIndex >= 0 && trackResult.mAudioTrackIndex >= 0) break;
191+
}
192+
- if (trackResult.mVideoTrackIndex < 0 || trackResult.mAudioTrackIndex < 0) {
193+
- throw new IllegalArgumentException("extractor does not contain video and/or audio tracks.");
194+
+ if (trackResult.mVideoTrackIndex < 0) {
195+
+ throw new IllegalArgumentException("extractor does not contain video tracks.");
196+
}
197+
return trackResult;
198+
}

0 commit comments

Comments
 (0)