| 
 | 1 | +From 74dbf9bec330168803cc5f3417aa39bd9260e98e Mon Sep 17 00:00:00 2001  | 
 | 2 | +From: Cory Thompson <coryjthompson@gmail.com>  | 
 | 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