Created
January 23, 2018 11:06
-
-
Save jinminghe/f16d17ab9f41090640e7cb25962faf58 to your computer and use it in GitHub Desktop.
AndroidTranscoder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.annotation.TargetApi; | |
import android.media.MediaCodec; | |
import android.media.MediaCodecInfo; | |
import android.media.MediaExtractor; | |
import android.media.MediaFormat; | |
import android.media.MediaMuxer; | |
import android.media.MediaMuxer.OutputFormat; | |
import android.os.Handler; | |
import android.os.HandlerThread; | |
import android.os.SystemClock; | |
import android.view.Surface; | |
import java.io.IOException; | |
import java.nio.ByteBuffer; | |
@TargetApi(23) | |
public class SimpleTranscoder { | |
private static final String TAG = "SimpleTranscoder"; | |
private long startTime; | |
private long endTime; | |
private MediaExtractor videoExtractor; | |
private MediaCodec videoDecoder; | |
private MediaCodec videoEncoder; | |
private MediaMuxer muxer; | |
private int outputVideoTrack = -1; | |
private HandlerThread videoDecodeThread; | |
private Handler videoDecodeThreadHandler; | |
private HandlerThread videoEncodeThread; | |
private Handler videoEncodeThreadHandler; | |
private boolean extractorDone = false; | |
public SimpleTranscoder() { | |
// Set up threads. | |
videoDecodeThread = new HandlerThread("videoDecodeThread"); | |
videoDecodeThread.start(); | |
videoDecodeThreadHandler = new Handler(videoDecodeThread.getLooper()); | |
videoEncodeThread = new HandlerThread("videoEncodeThread"); | |
videoEncodeThread.start(); | |
videoEncodeThreadHandler = new Handler(videoEncodeThread.getLooper()); | |
} | |
public void configure(String input, String output) throws IOException { | |
// Extractors | |
videoExtractor = new MediaExtractor(); | |
videoExtractor.setDataSource(input); | |
int videoTrack = getAndSelectVideoTrackIndex(videoExtractor); | |
MediaFormat videoFormat = videoExtractor.getTrackFormat(videoTrack); | |
String mime = videoFormat.getString(MediaFormat.KEY_MIME); | |
// Codecs | |
videoDecoder = MediaCodec.createDecoderByType(mime); | |
videoEncoder = MediaCodec.createEncoderByType(mime); | |
videoDecoder.setCallback(new VideoDecodeCallBack(), videoDecodeThreadHandler); | |
videoEncoder.setCallback(new VideoEncodeCallBack(), videoEncodeThreadHandler); | |
videoEncoder.configure(createOutputFormat(), null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); | |
Surface surface = videoEncoder.createInputSurface(); | |
videoDecoder.configure(videoFormat, surface, null, 0); | |
// Muxer | |
muxer = new MediaMuxer(output, OutputFormat.MUXER_OUTPUT_MPEG_4); | |
} | |
public void start() { | |
videoEncoder.start(); | |
videoDecoder.start(); | |
} | |
public void release() { | |
} | |
private class VideoDecodeCallBack extends MediaCodec.Callback { | |
@Override | |
public void onInputBufferAvailable(MediaCodec codec, int index) { | |
ByteBuffer buffer = codec.getInputBuffer(index); | |
if (extractorDone) { | |
return; | |
} | |
int size = videoExtractor.readSampleData(buffer, 0); | |
long pts = videoExtractor.getSampleTime(); | |
if (size >= 0) { | |
codec.queueInputBuffer(index, 0, size, pts, videoExtractor.getSampleFlags()); | |
} | |
extractorDone = !videoExtractor.advance(); | |
if (extractorDone) { | |
codec.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); | |
} | |
} | |
@Override | |
public void onOutputBufferAvailable( | |
MediaCodec codec, int index, MediaCodec.BufferInfo info) { | |
if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { | |
codec.releaseOutputBuffer(index, false); | |
return; | |
} | |
boolean render = info.size != 0; | |
codec.releaseOutputBuffer(index, render); | |
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { | |
videoEncoder.signalEndOfInputStream(); | |
} | |
} | |
@Override | |
public void onError( | |
MediaCodec codec, MediaCodec.CodecException e) { | |
} | |
@Override | |
public void onOutputFormatChanged( | |
MediaCodec codec, MediaFormat format) { | |
} | |
} | |
private class VideoEncodeCallBack extends MediaCodec.Callback { | |
@Override | |
public void onInputBufferAvailable(MediaCodec codec, int index) { | |
} | |
@Override | |
public void onOutputBufferAvailable( | |
MediaCodec codec, int index, MediaCodec.BufferInfo info) { | |
ByteBuffer buffer = codec.getOutputBuffer(index); | |
if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { | |
codec.releaseOutputBuffer(index, false); | |
return; | |
} | |
if (info.size != 0) { | |
muxer.writeSampleData(outputVideoTrack, buffer, info); | |
} | |
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { | |
muxer.stop(); | |
muxer.release(); | |
} | |
codec.releaseOutputBuffer(index, false); | |
} | |
@Override | |
public void onError( | |
MediaCodec codec, MediaCodec.CodecException e) { | |
} | |
@Override | |
public void onOutputFormatChanged( | |
MediaCodec codec, MediaFormat format) { | |
outputVideoTrack = muxer.addTrack(format); | |
muxer.start(); | |
} | |
} | |
private static MediaFormat createOutputFormat() { | |
MediaFormat result = MediaFormat.createVideoFormat("video/avc", 540, 960); | |
result.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); | |
result.setInteger(MediaFormat.KEY_BIT_RATE, 1300000); | |
result.setInteger(MediaFormat.KEY_FRAME_RATE, 30); | |
result.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); | |
return result; | |
} | |
private static int getAndSelectVideoTrackIndex(MediaExtractor extractor) { | |
for (int i = 0; i < extractor.getTrackCount(); ++i) { | |
if (isVideoFormat(extractor.getTrackFormat(i))) { | |
extractor.selectTrack(i); | |
return i; | |
} | |
} | |
return -1; | |
} | |
private static int getAndSelectAudioTrackIndex(MediaExtractor extractor) { | |
for (int i = 0; i < extractor.getTrackCount(); ++i) { | |
if (isAudioFormat(extractor.getTrackFormat(i))) { | |
extractor.selectTrack(i); | |
return i; | |
} | |
} | |
return -1; | |
} | |
private static boolean isAudioFormat(MediaFormat format) { | |
return format.getString(MediaFormat.KEY_MIME).startsWith("audio/"); | |
} | |
private static boolean isVideoFormat(MediaFormat format) { | |
return format.getString(MediaFormat.KEY_MIME).startsWith("video/"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
where are threads?