|
| 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