MediaMuxer video file size reduction (re-compression, resolution reduction)

I am looking for an effective way to reduce some weight of the video (like File , for download), and the obvious answer for this is: it allows me to reduce the resolution! (fullHD or 4K is not required, just enough for HD). I tried many ways that should work through many APIs (10 is needed), and the best way was to use android-ffmpeg-java , BUT ... on my pretty fast almost current flagship device the whole process lasts about length_of_video * 4 seconds, and also this lib weight is 9 MB, this amount increases the size of my application ... No, wai! (12 MB to 1 MB is a good result, but there are still too many shortcomings)

So, I decided to use Android’s own methods for this, MediaMuxer and MediaCodec - they are available from API18 and API16 (older device users: sorry, but they also often have “lower values”, camera). The below method almost works - MediaMuxer DOESN’t respect MediaFormat.KEY_WIDTH and MediaFormat.KEY_HEIGHT - the extracted File "re- MediaFormat.KEY_HEIGHT ", the weight is a little less, but the resolution is the same as in the original File video ..

So, the question is: how to compress and zoom / resize video using MediaMuxer and other related classes and methods?

 public File getCompressedFile(String videoPath) throws IOException{ MediaExtractor extractor = new MediaExtractor(); extractor.setDataSource(videoPath); int trackCount = extractor.getTrackCount(); String filePath = videoPath.substring(0, videoPath.lastIndexOf(File.separator)); String[] splitByDot = videoPath.split("\\."); String ext=""; if(splitByDot!=null && splitByDot.length>1) ext = splitByDot[splitByDot.length-1]; String fileName = videoPath.substring(videoPath.lastIndexOf(File.separator)+1, videoPath.length()); if(ext.length()>0) fileName=fileName.replace("."+ext, "_out."+ext); else fileName=fileName.concat("_out"); final File outFile = new File(filePath, fileName); if(!outFile.exists()) outFile.createNewFile(); MediaMuxer muxer = new MediaMuxer(outFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); HashMap<Integer, Integer> indexMap = new HashMap<Integer, Integer>(trackCount); for (int i = 0; i < trackCount; i++) { extractor.selectTrack(i); MediaFormat format = extractor.getTrackFormat(i); String mime = format.getString(MediaFormat.KEY_MIME); if(mime!=null && mime.startsWith("video")){ int currWidth = format.getInteger(MediaFormat.KEY_WIDTH); int currHeight = format.getInteger(MediaFormat.KEY_HEIGHT); format.setInteger(MediaFormat.KEY_WIDTH, currWidth>currHeight ? 960 : 540); format.setInteger(MediaFormat.KEY_HEIGHT, currWidth>currHeight ? 540 : 960); //API19 MediaFormat.KEY_MAX_WIDTH and KEY_MAX_HEIGHT format.setInteger("max-width", format.getInteger(MediaFormat.KEY_WIDTH)); format.setInteger("max-height", format.getInteger(MediaFormat.KEY_HEIGHT)); } int dstIndex = muxer.addTrack(format); indexMap.put(i, dstIndex); } boolean sawEOS = false; int bufferSize = 256 * 1024; int offset = 100; ByteBuffer dstBuf = ByteBuffer.allocate(bufferSize); MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); muxer.start(); while (!sawEOS) { bufferInfo.offset = offset; bufferInfo.size = extractor.readSampleData(dstBuf, offset); if (bufferInfo.size < 0) { sawEOS = true; bufferInfo.size = 0; } else { bufferInfo.presentationTimeUs = extractor.getSampleTime(); bufferInfo.flags = extractor.getSampleFlags(); int trackIndex = extractor.getSampleTrackIndex(); muxer.writeSampleData(indexMap.get(trackIndex), dstBuf, bufferInfo); extractor.advance(); } } muxer.stop(); muxer.release(); return outFile; } 

PS. a lot of useful material about muxer here , over code bases on MediaMuxerTest.java , method cloneMediaUsingMuxer

+5
android video video-processing mediacodec
source share
3 answers

Based on bigflake.com/mediacodec/ (an amazing source of knowledge about media classes) I tried several ways and finally ExtractDecodeEditEncodeMuxTest turned out to be very useful. This test was not described in an article on bigflake, but it can be found HERE next to the other classes mentioned in the text.

So, I copied most of the code from the above class ExtractDecodeEditEncodeMuxTest and here it is: VideoResolutionChanger . This gives me 2 MB of high definition video with 16 MB fullHD. Nice! And fast! On my device, the whole process is slightly longer than the duration of the input video, for example. 10 sec video input → 11-12 seconds of processing. With ffmpeg-java this will be approximately 40 seconds or more (and another 9 MB for the application).

Here we go:

VideoResolutionChanger:

 @TargetApi(18) public class VideoResolutionChanger { private static final int TIMEOUT_USEC = 10000; private static final String OUTPUT_VIDEO_MIME_TYPE = "video/avc"; private static final int OUTPUT_VIDEO_BIT_RATE = 2048 * 1024; private static final int OUTPUT_VIDEO_FRAME_RATE = 30; private static final int OUTPUT_VIDEO_IFRAME_INTERVAL = 10; private static final int OUTPUT_VIDEO_COLOR_FORMAT = MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface; private static final String OUTPUT_AUDIO_MIME_TYPE = "audio/mp4a-latm"; private static final int OUTPUT_AUDIO_CHANNEL_COUNT = 2; private static final int OUTPUT_AUDIO_BIT_RATE = 128 * 1024; private static final int OUTPUT_AUDIO_AAC_PROFILE = MediaCodecInfo.CodecProfileLevel.AACObjectHE; private static final int OUTPUT_AUDIO_SAMPLE_RATE_HZ = 44100; private int mWidth = 1280; private int mHeight = 720; private String mOutputFile, mInputFile; public String changeResolution(File f) throws Throwable { mInputFile=f.getAbsolutePath(); String filePath = mInputFile.substring(0, mInputFile.lastIndexOf(File.separator)); String[] splitByDot = mInputFile.split("\\."); String ext=""; if(splitByDot!=null && splitByDot.length>1) ext = splitByDot[splitByDot.length-1]; String fileName = mInputFile.substring(mInputFile.lastIndexOf(File.separator)+1, mInputFile.length()); if(ext.length()>0) fileName=fileName.replace("."+ext, "_out.mp4"); else fileName=fileName.concat("_out.mp4"); final File outFile = new File(Environment.getExternalStorageDirectory(), fileName); if(!outFile.exists()) outFile.createNewFile(); mOutputFile=outFile.getAbsolutePath(); ChangerWrapper.changeResolutionInSeparatedThread(this); return mOutputFile; } private static class ChangerWrapper implements Runnable { private Throwable mThrowable; private VideoResolutionChanger mChanger; private ChangerWrapper(VideoResolutionChanger changer) { mChanger = changer; } @Override public void run() { try { mChanger.prepareAndChangeResolution(); } catch (Throwable th) { mThrowable = th; } } public static void changeResolutionInSeparatedThread(VideoResolutionChanger changer) throws Throwable { ChangerWrapper wrapper = new ChangerWrapper(changer); Thread th = new Thread(wrapper, ChangerWrapper.class.getSimpleName()); th.start(); th.join(); if (wrapper.mThrowable != null) throw wrapper.mThrowable; } } private void prepareAndChangeResolution() throws Exception { Exception exception = null; MediaCodecInfo videoCodecInfo = selectCodec(OUTPUT_VIDEO_MIME_TYPE); if (videoCodecInfo == null) return; MediaCodecInfo audioCodecInfo = selectCodec(OUTPUT_AUDIO_MIME_TYPE); if (audioCodecInfo == null) return; MediaExtractor videoExtractor = null; MediaExtractor audioExtractor = null; OutputSurface outputSurface = null; MediaCodec videoDecoder = null; MediaCodec audioDecoder = null; MediaCodec videoEncoder = null; MediaCodec audioEncoder = null; MediaMuxer muxer = null; InputSurface inputSurface = null; try { videoExtractor = createExtractor(); int videoInputTrack = getAndSelectVideoTrackIndex(videoExtractor); MediaFormat inputFormat = videoExtractor.getTrackFormat(videoInputTrack); MediaMetadataRetriever m = new MediaMetadataRetriever(); m.setDataSource(mInputFile); int inputWidth, inputHeight; try { inputWidth = Integer.parseInt(m.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)); inputHeight = Integer.parseInt(m.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)); } catch (Exception e) { Bitmap thumbnail = m.getFrameAtTime(); inputWidth = thumbnail.getWidth(); inputHeight = thumbnail.getHeight(); thumbnail.recycle(); } if(inputWidth>inputHeight){ if(mWidth<mHeight){ int w = mWidth; mWidth=mHeight; mHeight=w; } } else{ if(mWidth>mHeight){ int w = mWidth; mWidth=mHeight; mHeight=w; } } MediaFormat outputVideoFormat = MediaFormat.createVideoFormat(OUTPUT_VIDEO_MIME_TYPE, mWidth, mHeight); outputVideoFormat.setInteger( MediaFormat.KEY_COLOR_FORMAT, OUTPUT_VIDEO_COLOR_FORMAT); outputVideoFormat.setInteger(MediaFormat.KEY_BIT_RATE, OUTPUT_VIDEO_BIT_RATE); outputVideoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, OUTPUT_VIDEO_FRAME_RATE); outputVideoFormat.setInteger( MediaFormat.KEY_I_FRAME_INTERVAL, OUTPUT_VIDEO_IFRAME_INTERVAL); AtomicReference<Surface> inputSurfaceReference = new AtomicReference<Surface>(); videoEncoder = createVideoEncoder( videoCodecInfo, outputVideoFormat, inputSurfaceReference); inputSurface = new InputSurface(inputSurfaceReference.get()); inputSurface.makeCurrent(); outputSurface = new OutputSurface(); videoDecoder = createVideoDecoder(inputFormat, outputSurface.getSurface()); audioExtractor = createExtractor(); int audioInputTrack = getAndSelectAudioTrackIndex(audioExtractor); MediaFormat inputAudioFormat = audioExtractor.getTrackFormat(audioInputTrack); MediaFormat outputAudioFormat = MediaFormat.createAudioFormat(inputAudioFormat.getString(MediaFormat.KEY_MIME), inputAudioFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE), inputAudioFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)); outputAudioFormat.setInteger(MediaFormat.KEY_BIT_RATE, OUTPUT_AUDIO_BIT_RATE); outputAudioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, OUTPUT_AUDIO_AAC_PROFILE); audioEncoder = createAudioEncoder(audioCodecInfo, outputAudioFormat); audioDecoder = createAudioDecoder(inputAudioFormat); muxer = new MediaMuxer(mOutputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); changeResolution(videoExtractor, audioExtractor, videoDecoder, videoEncoder, audioDecoder, audioEncoder, muxer, inputSurface, outputSurface); } finally { try { if (videoExtractor != null) videoExtractor.release(); } catch(Exception e) { if (exception == null) exception = e; } try { if (audioExtractor != null) audioExtractor.release(); } catch(Exception e) { if (exception == null) exception = e; } try { if (videoDecoder != null) { videoDecoder.stop(); videoDecoder.release(); } } catch(Exception e) { if (exception == null) exception = e; } try { if (outputSurface != null) { outputSurface.release(); } } catch(Exception e) { if (exception == null) exception = e; } try { if (videoEncoder != null) { videoEncoder.stop(); videoEncoder.release(); } } catch(Exception e) { if (exception == null) exception = e; } try { if (audioDecoder != null) { audioDecoder.stop(); audioDecoder.release(); } } catch(Exception e) { if (exception == null) exception = e; } try { if (audioEncoder != null) { audioEncoder.stop(); audioEncoder.release(); } } catch(Exception e) { if (exception == null) exception = e; } try { if (muxer != null) { muxer.stop(); muxer.release(); } } catch(Exception e) { if (exception == null) exception = e; } try { if (inputSurface != null) inputSurface.release(); } catch(Exception e) { if (exception == null) exception = e; } } if (exception != null) throw exception; } private MediaExtractor createExtractor() throws IOException { MediaExtractor extractor; extractor = new MediaExtractor(); extractor.setDataSource(mInputFile); return extractor; } private MediaCodec createVideoDecoder(MediaFormat inputFormat, Surface surface) throws IOException { MediaCodec decoder = MediaCodec.createDecoderByType(getMimeTypeFor(inputFormat)); decoder.configure(inputFormat, surface, null, 0); decoder.start(); return decoder; } private MediaCodec createVideoEncoder(MediaCodecInfo codecInfo, MediaFormat format, AtomicReference<Surface> surfaceReference) throws IOException { MediaCodec encoder = MediaCodec.createByCodecName(codecInfo.getName()); encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); surfaceReference.set(encoder.createInputSurface()); encoder.start(); return encoder; } private MediaCodec createAudioDecoder(MediaFormat inputFormat) throws IOException { MediaCodec decoder = MediaCodec.createDecoderByType(getMimeTypeFor(inputFormat)); decoder.configure(inputFormat, null, null, 0); decoder.start(); return decoder; } private MediaCodec createAudioEncoder(MediaCodecInfo codecInfo, MediaFormat format) throws IOException { MediaCodec encoder = MediaCodec.createByCodecName(codecInfo.getName()); encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); encoder.start(); return encoder; } private int getAndSelectVideoTrackIndex(MediaExtractor extractor) { for (int index = 0; index < extractor.getTrackCount(); ++index) { if (isVideoFormat(extractor.getTrackFormat(index))) { extractor.selectTrack(index); return index; } } return -1; } private int getAndSelectAudioTrackIndex(MediaExtractor extractor) { for (int index = 0; index < extractor.getTrackCount(); ++index) { if (isAudioFormat(extractor.getTrackFormat(index))) { extractor.selectTrack(index); return index; } } return -1; } private void changeResolution(MediaExtractor videoExtractor, MediaExtractor audioExtractor, MediaCodec videoDecoder, MediaCodec videoEncoder, MediaCodec audioDecoder, MediaCodec audioEncoder, MediaMuxer muxer, InputSurface inputSurface, OutputSurface outputSurface) { ByteBuffer[] videoDecoderInputBuffers = null; ByteBuffer[] videoDecoderOutputBuffers = null; ByteBuffer[] videoEncoderOutputBuffers = null; MediaCodec.BufferInfo videoDecoderOutputBufferInfo = null; MediaCodec.BufferInfo videoEncoderOutputBufferInfo = null; videoDecoderInputBuffers = videoDecoder.getInputBuffers(); videoDecoderOutputBuffers = videoDecoder.getOutputBuffers(); videoEncoderOutputBuffers = videoEncoder.getOutputBuffers(); videoDecoderOutputBufferInfo = new MediaCodec.BufferInfo(); videoEncoderOutputBufferInfo = new MediaCodec.BufferInfo(); ByteBuffer[] audioDecoderInputBuffers = null; ByteBuffer[] audioDecoderOutputBuffers = null; ByteBuffer[] audioEncoderInputBuffers = null; ByteBuffer[] audioEncoderOutputBuffers = null; MediaCodec.BufferInfo audioDecoderOutputBufferInfo = null; MediaCodec.BufferInfo audioEncoderOutputBufferInfo = null; audioDecoderInputBuffers = audioDecoder.getInputBuffers(); audioDecoderOutputBuffers = audioDecoder.getOutputBuffers(); audioEncoderInputBuffers = audioEncoder.getInputBuffers(); audioEncoderOutputBuffers = audioEncoder.getOutputBuffers(); audioDecoderOutputBufferInfo = new MediaCodec.BufferInfo(); audioEncoderOutputBufferInfo = new MediaCodec.BufferInfo(); MediaFormat decoderOutputVideoFormat = null; MediaFormat decoderOutputAudioFormat = null; MediaFormat encoderOutputVideoFormat = null; MediaFormat encoderOutputAudioFormat = null; int outputVideoTrack = -1; int outputAudioTrack = -1; boolean videoExtractorDone = false; boolean videoDecoderDone = false; boolean videoEncoderDone = false; boolean audioExtractorDone = false; boolean audioDecoderDone = false; boolean audioEncoderDone = false; int pendingAudioDecoderOutputBufferIndex = -1; boolean muxing = false; while ((!videoEncoderDone) || (!audioEncoderDone)) { while (!videoExtractorDone && (encoderOutputVideoFormat == null || muxing)) { int decoderInputBufferIndex = videoDecoder.dequeueInputBuffer(TIMEOUT_USEC); if (decoderInputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) break; ByteBuffer decoderInputBuffer = videoDecoderInputBuffers[decoderInputBufferIndex]; int size = videoExtractor.readSampleData(decoderInputBuffer, 0); long presentationTime = videoExtractor.getSampleTime(); if (size >= 0) { videoDecoder.queueInputBuffer( decoderInputBufferIndex, 0, size, presentationTime, videoExtractor.getSampleFlags()); } videoExtractorDone = !videoExtractor.advance(); if (videoExtractorDone) videoDecoder.queueInputBuffer(decoderInputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); break; } while (!audioExtractorDone && (encoderOutputAudioFormat == null || muxing)) { int decoderInputBufferIndex = audioDecoder.dequeueInputBuffer(TIMEOUT_USEC); if (decoderInputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) break; ByteBuffer decoderInputBuffer = audioDecoderInputBuffers[decoderInputBufferIndex]; int size = audioExtractor.readSampleData(decoderInputBuffer, 0); long presentationTime = audioExtractor.getSampleTime(); if (size >= 0) audioDecoder.queueInputBuffer(decoderInputBufferIndex, 0, size, presentationTime, audioExtractor.getSampleFlags()); audioExtractorDone = !audioExtractor.advance(); if (audioExtractorDone) audioDecoder.queueInputBuffer(decoderInputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); break; } while (!videoDecoderDone && (encoderOutputVideoFormat == null || muxing)) { int decoderOutputBufferIndex = videoDecoder.dequeueOutputBuffer( videoDecoderOutputBufferInfo, TIMEOUT_USEC); if (decoderOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) break; if (decoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { videoDecoderOutputBuffers = videoDecoder.getOutputBuffers(); break; } if (decoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { decoderOutputVideoFormat = videoDecoder.getOutputFormat(); break; } ByteBuffer decoderOutputBuffer = videoDecoderOutputBuffers[decoderOutputBufferIndex]; if ((videoDecoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { videoDecoder.releaseOutputBuffer(decoderOutputBufferIndex, false); break; } boolean render = videoDecoderOutputBufferInfo.size != 0; videoDecoder.releaseOutputBuffer(decoderOutputBufferIndex, render); if (render) { outputSurface.awaitNewImage(); outputSurface.drawImage(); inputSurface.setPresentationTime( videoDecoderOutputBufferInfo.presentationTimeUs * 1000); inputSurface.swapBuffers(); } if ((videoDecoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { videoDecoderDone = true; videoEncoder.signalEndOfInputStream(); } break; } while (!audioDecoderDone && pendingAudioDecoderOutputBufferIndex == -1 && (encoderOutputAudioFormat == null || muxing)) { int decoderOutputBufferIndex = audioDecoder.dequeueOutputBuffer( audioDecoderOutputBufferInfo, TIMEOUT_USEC); if (decoderOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) break; if (decoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { audioDecoderOutputBuffers = audioDecoder.getOutputBuffers(); break; } if (decoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { decoderOutputAudioFormat = audioDecoder.getOutputFormat(); break; } ByteBuffer decoderOutputBuffer = audioDecoderOutputBuffers[decoderOutputBufferIndex]; if ((audioDecoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { audioDecoder.releaseOutputBuffer(decoderOutputBufferIndex, false); break; } pendingAudioDecoderOutputBufferIndex = decoderOutputBufferIndex; break; } while (pendingAudioDecoderOutputBufferIndex != -1) { int encoderInputBufferIndex = audioEncoder.dequeueInputBuffer(TIMEOUT_USEC); ByteBuffer encoderInputBuffer = audioEncoderInputBuffers[encoderInputBufferIndex]; int size = audioDecoderOutputBufferInfo.size; long presentationTime = audioDecoderOutputBufferInfo.presentationTimeUs; if (size >= 0) { ByteBuffer decoderOutputBuffer = audioDecoderOutputBuffers[pendingAudioDecoderOutputBufferIndex] .duplicate(); decoderOutputBuffer.position(audioDecoderOutputBufferInfo.offset); decoderOutputBuffer.limit(audioDecoderOutputBufferInfo.offset + size); encoderInputBuffer.position(0); encoderInputBuffer.put(decoderOutputBuffer); audioEncoder.queueInputBuffer( encoderInputBufferIndex, 0, size, presentationTime, audioDecoderOutputBufferInfo.flags); } audioDecoder.releaseOutputBuffer(pendingAudioDecoderOutputBufferIndex, false); pendingAudioDecoderOutputBufferIndex = -1; if ((audioDecoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) audioDecoderDone = true; break; } while (!videoEncoderDone && (encoderOutputVideoFormat == null || muxing)) { int encoderOutputBufferIndex = videoEncoder.dequeueOutputBuffer( videoEncoderOutputBufferInfo, TIMEOUT_USEC); if (encoderOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) break; if (encoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { videoEncoderOutputBuffers = videoEncoder.getOutputBuffers(); break; } if (encoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { encoderOutputVideoFormat = videoEncoder.getOutputFormat(); break; } ByteBuffer encoderOutputBuffer = videoEncoderOutputBuffers[encoderOutputBufferIndex]; if ((videoEncoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { videoEncoder.releaseOutputBuffer(encoderOutputBufferIndex, false); break; } if (videoEncoderOutputBufferInfo.size != 0) { muxer.writeSampleData( outputVideoTrack, encoderOutputBuffer, videoEncoderOutputBufferInfo); } if ((videoEncoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { videoEncoderDone = true; } videoEncoder.releaseOutputBuffer(encoderOutputBufferIndex, false); break; } while (!audioEncoderDone && (encoderOutputAudioFormat == null || muxing)) { int encoderOutputBufferIndex = audioEncoder.dequeueOutputBuffer( audioEncoderOutputBufferInfo, TIMEOUT_USEC); if (encoderOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { break; } if (encoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { audioEncoderOutputBuffers = audioEncoder.getOutputBuffers(); break; } if (encoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { encoderOutputAudioFormat = audioEncoder.getOutputFormat(); break; } ByteBuffer encoderOutputBuffer = audioEncoderOutputBuffers[encoderOutputBufferIndex]; if ((audioEncoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { audioEncoder.releaseOutputBuffer(encoderOutputBufferIndex, false); break; } if (audioEncoderOutputBufferInfo.size != 0) muxer.writeSampleData( outputAudioTrack, encoderOutputBuffer, audioEncoderOutputBufferInfo); if ((audioEncoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) audioEncoderDone = true; audioEncoder.releaseOutputBuffer(encoderOutputBufferIndex, false); break; } if (!muxing && (encoderOutputAudioFormat != null) && (encoderOutputVideoFormat != null)) { outputVideoTrack = muxer.addTrack(encoderOutputVideoFormat); outputAudioTrack = muxer.addTrack(encoderOutputAudioFormat); muxer.start(); muxing = true; } } } private static boolean isVideoFormat(MediaFormat format) { return getMimeTypeFor(format).startsWith("video/"); } private static boolean isAudioFormat(MediaFormat format) { return getMimeTypeFor(format).startsWith("audio/"); } private static String getMimeTypeFor(MediaFormat format) { return format.getString(MediaFormat.KEY_MIME); } private static MediaCodecInfo selectCodec(String mimeType) { int numCodecs = MediaCodecList.getCodecCount(); for (int i = 0; i < numCodecs; i++) { MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); if (!codecInfo.isEncoder()) { continue; } String[] types = codecInfo.getSupportedTypes(); for (int j = 0; j < types.length; j++) { if (types[j].equalsIgnoreCase(mimeType)) { return codecInfo; } } } return null; } } 

he also needs InputSurface , OutputSurface and TextureRender , which are located next to ExtractDecodeEditEncodeMuxTest (above HERE link). Put these three in one package with VideoResolutionChanger and use it as follows:

 try{ String pathToReEncodedFile = new VideoResolutionChanger().changeResolution(videoFilePath); }catch(Throwable t){/* smth wrong :( */} 

Where videoFilePath can be obtained from File using file.getAbsolutePath() .

I know that this is not the cleanest and probably not the most efficient / effective way, but I searched for similar code in the last two days and found many topics that redirected me to INDE, ffmpeg or jcodec the most, others were left without a proper answer. So I leave it here, use it wisely!

LIMITATION:

  • above use-it-like-this fragment cannot be launched in the thread using a looper, for example. right inside the Activity . the best way is to create an IntentService and pass the path of the String input file in the Intent extra Bundle . Then you can run changeResolution stright inside onHandleIntent ;
  • API18 and higher (introduced by MediaMuxer );
  • API18, of course, WRITE_EXTERNAL_STORAGE , API19 and above have this "built-in";

@fadden THANKS YOU for your work and support! :)

+15
source share

MediaMuxer is not involved in compressing or scaling videos. All he does is take the H.264 output from MediaCodec and wrap it in a .mp4 wrapper.

Looking at your code, you retrieve NAL units with MediaExtractor and immediately rewind them with MediaMuxer. This should be extremely fast and not affect the video itself, as you are simply rebooting H.264.

To scale the video, you need to decode the video using the MediaCodec decoder, feeding the NAL modules from MediaExtractor into it and transcode it using the MediaCodec encoder, transferring frames to MediaMuxer.

You found bigflake.com ; see also Grafika . None of them have exactly what you are looking for, but there are different parts.

It is best to decode a surface rather than a ByteBuffer. This requires API 18, but for sanity, it's best to forget that MediaCodec existed before. And you will need API 18 for MediaMuxer.

+4
source share

You can try Intel INDE Media for Mobile, tutorials at https://software.intel.com/en-us/articles/intel-inde-media-pack-for-android-tutorials . It has a sample that shows how to use it for transcoding = recount video files.

You can set lower resolution and / or bit rate for output to get a smaller file https://github.com/INDExOS/media-for-mobile/blob/master/Android/samples/apps/src/com/intel/inde/ mp / samples / ComposerTranscodeCoreActivity.java

0
source share

All Articles