Skip to content

Commit c3dc512

Browse files
committed
Apply ypresto#32
Allow video with no audio stream present to be transcoded
1 parent e9c411c commit c3dc512

File tree

6 files changed

+66
-29
lines changed

6 files changed

+66
-29
lines changed

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

+19-6
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,17 +175,21 @@ 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

183-
private void runPipelines() throws InterruptedException {
192+
private void runPipelines() {
184193
long loopCount = 0;
185194
if (mDurationUs <= 0) {
186195
double progress = PROGRESS_UNKNOWN;
@@ -199,7 +208,11 @@ private void runPipelines() throws InterruptedException {
199208
if (mProgressCallback != null) mProgressCallback.onProgress(progress);
200209
}
201210
if (!stepped) {
202-
Thread.sleep(SLEEP_TO_WAIT_TRACK_TRANSCODERS);
211+
try {
212+
Thread.sleep(SLEEP_TO_WAIT_TRACK_TRANSCODERS);
213+
} catch (InterruptedException e) {
214+
// nothing to do
215+
}
203216
}
204217
}
205218
}
@@ -212,4 +225,4 @@ public interface ProgressCallback {
212225
*/
213226
void onProgress(double progress);
214227
}
215-
}
228+
}

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

+15-6
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,25 @@ public class PassThroughTrackTranscoder implements TrackTranscoder {
3636
private long mWrittenPresentationTimeUs;
3737

3838
public PassThroughTrackTranscoder(MediaExtractor extractor, int trackIndex,
39-
QueuedMuxer muxer, QueuedMuxer.SampleType sampleType) {
39+
QueuedMuxer muxer, QueuedMuxer.SampleType sampleType) {
4040
mExtractor = extractor;
4141
mTrackIndex = 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
@@ -97,4 +106,4 @@ public boolean isFinished() {
97106
@Override
98107
public void release() {
99108
}
100-
}
109+
}

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

+14-4
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

@@ -137,4 +147,4 @@ private void writeToBufferInfo(MediaCodec.BufferInfo bufferInfo, int offset) {
137147
public interface Listener {
138148
void onDetermineOutputFormat();
139149
}
140-
}
150+
}

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

+8-5
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
import android.media.MediaCodecInfo;
1919
import android.media.MediaFormat;
20-
import android.util.Log;
2120

2221
class Android16By9FormatStrategy implements MediaFormatStrategy {
2322
public static final int AUDIO_BITRATE_AS_IS = -1;
@@ -61,10 +60,14 @@ public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
6160
if (longer * 9 != shorter * 16) {
6261
throw new OutputFormatUnavailableException("This video is not 16:9, and is not able to transcode. (" + width + "x" + height + ")");
6362
}
63+
64+
/*
65+
I've commented this out because its unsafe to assume the user wants to bypass compression if resolution is equal.
6466
if (shorter <= targetShorter) {
6567
Log.d(TAG, "This video's height is less or equal to " + targetShorter + ", pass-through. (" + width + "x" + height + ")");
6668
return null;
67-
}
69+
}*/
70+
6871
MediaFormat format = MediaFormat.createVideoFormat("video/avc", outWidth, outHeight);
6972
// From Nexus 4 Camera in 720p
7073
format.setInteger(MediaFormat.KEY_BIT_RATE, mVideoBitrate);
@@ -76,13 +79,13 @@ public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
7679

7780
@Override
7881
public MediaFormat createAudioOutputFormat(MediaFormat inputFormat) {
79-
if (mAudioBitrate == AUDIO_BITRATE_AS_IS || mAudioChannels == AUDIO_CHANNELS_AS_IS) return null;
82+
if (inputFormat == null || mAudioBitrate == AUDIO_BITRATE_AS_IS || mAudioChannels == AUDIO_CHANNELS_AS_IS) return null;
8083

8184
// Use original sample rate, as resampling is not supported yet.
8285
final MediaFormat format = MediaFormat.createAudioFormat(MediaFormatExtraConstants.MIMETYPE_AUDIO_AAC,
83-
inputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE), mAudioChannels);
86+
inputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE), mAudioChannels);
8487
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
8588
format.setInteger(MediaFormat.KEY_BIT_RATE, mAudioBitrate);
8689
return format;
8790
}
88-
}
91+
}

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

+7-5
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
import android.media.MediaCodecInfo;
1919
import android.media.MediaFormat;
20-
import android.util.Log;
2120

2221
class Android720pFormatStrategy implements MediaFormatStrategy {
2322
public static final int AUDIO_BITRATE_AS_IS = -1;
@@ -63,10 +62,13 @@ public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
6362
if (longer * 9 != shorter * 16) {
6463
throw new OutputFormatUnavailableException("This video is not 16:9, and is not able to transcode. (" + width + "x" + height + ")");
6564
}
65+
66+
/*
67+
I've commented this out because its unsafe to assume the user wants to bypass compression if resolution is equal.
6668
if (shorter <= SHORTER_LENGTH) {
6769
Log.d(TAG, "This video is less or equal to 720p, pass-through. (" + width + "x" + height + ")");
6870
return null;
69-
}
71+
}*/
7072
MediaFormat format = MediaFormat.createVideoFormat("video/avc", outWidth, outHeight);
7173
// From Nexus 4 Camera in 720p
7274
format.setInteger(MediaFormat.KEY_BIT_RATE, mVideoBitrate);
@@ -78,13 +80,13 @@ public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
7880

7981
@Override
8082
public MediaFormat createAudioOutputFormat(MediaFormat inputFormat) {
81-
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;
8284

8385
// Use original sample rate, as resampling is not supported yet.
8486
final MediaFormat format = MediaFormat.createAudioFormat(MediaFormatExtraConstants.MIMETYPE_AUDIO_AAC,
85-
inputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE), mAudioChannels);
87+
inputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE), mAudioChannels);
8688
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
8789
format.setInteger(MediaFormat.KEY_BIT_RATE, mAudioBitrate);
8890
return format;
8991
}
90-
}
92+
}

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ 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
}
63-
}
63+
}

0 commit comments

Comments
 (0)