下面列出了android.media.MediaCodec# createEncoderByType ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
private void initAudioCodec(String audioType, int sampleRate, int channels) {
try {
audioCodec = MediaCodec.createEncoderByType(audioType);
MediaFormat audioFormat = MediaFormat.createAudioFormat(audioType, sampleRate, channels);
int BIT_RATE = 96000;
audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE,
MediaCodecInfo.CodecProfileLevel.AACObjectLC);
int MAX_INOUT_SIZE = 8192;
audioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, MAX_INOUT_SIZE);
audioCodec.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
audioBuffer = new MediaCodec.BufferInfo();
} catch (IOException e) {
Log.e(TAG, "initAudioCodec: 音频类型无效");
}
}
@Override
public void setup() throws IOException {
mExtractor.selectTrack(mTrackIndex);
mEncoder = MediaCodec.createEncoderByType(mOutputFormat.getString(MediaFormat.KEY_MIME));
mEncoder.configure(mOutputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mEncoderInputSurfaceWrapper = new InputSurface(mEncoder.createInputSurface());
mEncoder.start();
mEncoderStarted = true;
MediaFormat inputFormat = mExtractor.getTrackFormat(mTrackIndex);
// if (inputFormat.containsKey(MediaUtil.KEY_ROTATION)) {
// // Decoded video is rotated automatically in Android 5.0 lollipop.
// // Turn off here because we don't want to encode rotated one.
// // refer: https://android.googlesource
// // .com/platform/frameworks/av/+blame/lollipop-release/media/libstagefright/Utils.cpp
// inputFormat.setInteger(MediaUtil.KEY_ROTATION, 0);
// }
mDecoderOutputSurfaceWrapper = new OutputSurface(mSurfaceRender);
mDecoder = MediaCodec.createDecoderByType(inputFormat.getString(MediaFormat.KEY_MIME));
mDecoder.configure(inputFormat, mDecoderOutputSurfaceWrapper.getSurface(), null, 0);
mDecoder.start();
mDecoderStarted = true;
}
private void prepareEncoder() {
MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, mWidth, mHeight);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
Log.d(TAG, "created video format: " + format);
try {
mEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
} catch (IOException e) {
e.printStackTrace();
}
mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mSurface = mEncoder.createInputSurface();
Log.d(TAG, "created input surface: " + mSurface);
mEncoder.start();
}
@Override
protected MediaCodec createEncoder() throws IOException {
LogUtil.logd(TAG, "createEncoder");
MediaFormat videoFormat = MediaFormat.createVideoFormat(MIME_TYPE, mVideoSize.getWidth(), mVideoSize.getHeight());
videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, mIFrameInterval);
LogUtil.logd(TAG, "format: " + videoFormat);
MediaCodec encoder = MediaCodec.createEncoderByType(MIME_TYPE);
encoder.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mInputSurface = encoder.createInputSurface();
encoder.start();
LogUtil.logd(TAG, "createEncoder finishing");
return encoder;
}
private MediaCodec createMediaCodec(int bufferSize) throws IOException {
MediaCodec mediaCodec = MediaCodec.createEncoderByType("audio/mp4a-latm");
MediaFormat mediaFormat = new MediaFormat();
mediaFormat.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
mediaFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, SAMPLE_RATE);
mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, CHANNELS);
mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, bufferSize);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
try {
mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
} catch (Exception e) {
Log.w(TAG, e);
mediaCodec.release();
throw new IOException(e);
}
return mediaCodec;
}
/**
* prepare the Encoder. call this before start the encoder.
*/
void prepare(int bitrate, int sampleRate, long startStreamingAt) {
int bufferSize = AudioRecord.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT);
MediaFormat audioFormat =
MediaFormat.createAudioFormat(AUDIO_MIME_TYPE, sampleRate, CHANNEL_COUNT);
audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE,
MediaCodecInfo.CodecProfileLevel.AACObjectLC);
audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
audioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, bufferSize);
startedEncodingAt = startStreamingAt;
try {
encoder = MediaCodec.createEncoderByType(AUDIO_MIME_TYPE);
encoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
} catch (IOException | IllegalStateException e) {
e.printStackTrace();
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
private void prepareVideoEncoder(int width, int height) {
MediaFormat format = MediaFormat.createVideoFormat(VIDEO_MIME_TYPE, width, height);
int frameRate = 30; // 30 fps
// Set some required properties. The media codec may fail if these aren't defined.
format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE, 6000000); // 6Mbps
format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
format.setInteger(MediaFormat.KEY_CAPTURE_RATE, frameRate);
format.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 1000000 / frameRate);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); // 1 seconds between I-frames
// Create a MediaCodec encoder and configure it. Get a Surface we can use for recording into.
try {
videoEncoder = MediaCodec.createEncoderByType(VIDEO_MIME_TYPE);
videoEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
inputSurface = videoEncoder.createInputSurface();
videoEncoder.setCallback(encoderCallback);
videoEncoder.start();
} catch (IOException e) {
releaseEncoders();
}
}
private void initVideoMediaCodec()throws Exception{
MediaFormat mediaFormat;
if(rotation==90 || rotation==270){
//设置视频宽高
mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, videoHeight, videoWidth);
}else{
mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, videoWidth, videoHeight);
}
//图像数据格式 YUV420
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
//码率
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, videoWidth*videoHeight*3);
//每秒30帧
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
//1秒一个关键帧
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
videoMediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
videoMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
videoMediaCodec.start();
}
public void startRecord() throws IOException {
synchronized (REC_LOCK){
isRecordStarted=true;
MediaFormat audioFormat=mConfig.getAudioFormat();
mAudioEncoder=MediaCodec.createEncoderByType(audioFormat.getString(MediaFormat.KEY_MIME));
mAudioEncoder.configure(audioFormat,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);
MediaFormat videoFormat=mConfig.getVideoFormat();
mVideoEncoder=MediaCodec.createEncoderByType(videoFormat.getString(MediaFormat.KEY_MIME));
//此处不能用mOutputSurface,会configure失败
mVideoEncoder.configure(videoFormat,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);
mEncodeSurface=mVideoEncoder.createInputSurface();
mAudioEncoder.start();
mVideoEncoder.start();
mMuxer=new MediaMuxer(mOutputPath,MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
mRecordBufferSize = AudioRecord.getMinBufferSize(mRecordSampleRate,
mRecordChannelConfig, mRecordAudioFormat)*2;
// buffer=new byte[bufferSize];
mAudioRecord=new AudioRecord(MediaRecorder.AudioSource.MIC,mRecordSampleRate,mRecordChannelConfig,
mRecordAudioFormat,mRecordBufferSize);
mAudioThread=new Thread(new Runnable() {
@Override
public void run() {
mAudioRecord.startRecording();
while (!audioEncodeStep(isTryStopAudio)){};
mAudioRecord.stop();
}
});
mAudioThread.start();
isRecordAudioStarted=true;
}
}
/**
* onClick handler for "start" button.
*
* We create as many codecs as we can and return without releasing them. The codecs
* will remain in use until the next GC.
*/
public void clickStart(@SuppressWarnings("unused") View unused) {
final String MIME_TYPE = "video/avc";
final int WIDTH = 320;
final int HEIGHT = 240;
final int BIT_RATE = 1000000;
final int FRAME_RATE = 15;
final int IFRAME_INTERVAL = 1;
final boolean START_CODEC = true;
MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
Log.d(TAG, "format: " + format);
MediaCodec[] codecs = new MediaCodec[MAX_OPEN];
int i;
for (i = 0; i < MAX_OPEN; i++) {
try {
codecs[i] = MediaCodec.createEncoderByType(MIME_TYPE);
codecs[i].configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
if (START_CODEC) {
codecs[i].createInputSurface();
codecs[i].start();
}
} catch (Exception ex) {
Log.i(TAG, "Failed on creation of codec #" + i, ex);
break;
}
}
showCountDialog(i);
}
public AvcEncoder(EncodeParameters encodeParameters, EncodedFrameListener listener) throws IOException {
mEncodeParameters = new EncodeParameters(encodeParameters);
mediaCodec = MediaCodec.createEncoderByType("video/avc");
MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", mEncodeParameters.getWidth(), mEncodeParameters.getHeight());
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, mEncodeParameters.getBitrate());
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, mEncodeParameters.getColor_format());
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
mFrameListener = listener;
mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
if(mEncodeParameters.getColor_format()== MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface) {
mInputSurface = mediaCodec.createInputSurface();
}
}
public void create(EncoderInfo info, MediaFormat format, File out) {
this.info = info;
try {
encoder = MediaCodec.createEncoderByType(format.getString(MediaFormat.KEY_MIME));
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
encoder.start();
muxer = new MediaMuxer(out.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 开始录制前的准备(目前由SohuMediaMuxerManager在主线程调用)
*
* @throws IOException
*/
@Override
public void prepare() throws IOException {
LogUtils.d(TAG, "---prepare---");
//
mTrackIndex = -1;
//
mMuxerStarted = mIsEndOfStream = false;
//-----------------MediaFormat-----------------------
// mediaCodeC采用的是H.264编码
final MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
// 数据来源自surface
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
// 视频码率
format.setInteger(MediaFormat.KEY_BIT_RATE, calcBitRate());
// fps
format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
//设置关键帧的时间
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
//-----------------Encoder-----------------------
mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
// get Surface for encoder input
// this method only can call between #configure and #start
mSurface = mMediaCodec.createInputSurface();
//
mMediaCodec.start();
//
LogUtils.i(TAG, "prepare finishing");
if (mMediaEncoderListener != null) {
try {
mMediaEncoderListener.onPrepared(this);
} catch (final Exception e) {
Log.e(TAG, "prepare:", e);
}
}
}
@Override
protected boolean internalPrepare() throws Exception {
// if (DEBUG) Log.v(TAG, "internalPrepare:");
mTrackIndex = -1;
mRecorderStarted = mIsEOS = false;
// 内蔵マイクから音声を取り込んでAACにエンコードするためのMediaCodecの準備
final MediaCodecInfo audioCodecInfo = MediaCodecUtils.selectAudioEncoder(MIME_TYPE);
if (audioCodecInfo == null) {
// Log.e(TAG, "Unable to find an appropriate codec for " + MIME_TYPE);
return true;
}
// if (DEBUG) Log.i(TAG, "selected codec: " + audioCodecInfo.getName());
final MediaFormat audioFormat = MediaFormat.createAudioFormat(MIME_TYPE, mSampleRate, mChannelCount);
audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
audioFormat.setInteger(MediaFormat.KEY_CHANNEL_MASK,
mChannelCount == 1 ? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO);
audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, AbstractAudioEncoder.DEFAULT_BIT_RATE);
audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
// audioFormat.setLong(MediaFormat.KEY_MAX_INPUT_SIZE, inputFile.length());
// audioFormat.setLong(MediaFormat.KEY_DURATION, (long)durationInMs );
// if (DEBUG) Log.i(TAG, "format: " + audioFormat);
mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
mMediaCodec.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mMediaCodec.start();
// if (DEBUG) Log.i(TAG, "internalPrepare:finished");
return false;
}
private MediaCodec createACCAudioDecoder() throws IOException {
MediaCodec codec = MediaCodec.createEncoderByType(AUDIO_MIME);
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, AUDIO_MIME);
format.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
format.setInteger(MediaFormat.KEY_AAC_PROFILE,
MediaCodecInfo.CodecProfileLevel.AACObjectLC);
codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
return codec;
}
/**
* Configures encoder and muxer state, and prepares the input Surface.
*/
public VideoEncoderCore(int width, int height, int bitRate, File outputFile)
throws IOException {
mBufferInfo = new MediaCodec.BufferInfo();
MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, width, height);
// Set some properties. Failing to specify some of these can cause the MediaCodec
// configure() call to throw an unhelpful exception.
format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
if (VERBOSE) Log.d(TAG, "format: " + format);
// Create a MediaCodec encoder, and configure it with our format. Get a Surface
// we can use for input and wrap it with a class that handles the EGL work.
mEncoder = MediaCodec.createEncoderByType(MIME_TYPE);
mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mInputSurface = mEncoder.createInputSurface();
mEncoder.start();
// Create a MediaMuxer. We can't add the video track and start() the muxer here,
// because our MediaFormat doesn't have the Magic Goodies. These can only be
// obtained from the encoder after it has started processing data.
//
// We're not actually interested in multiplexing audio. We just want to convert
// the raw H.264 elementary stream we get from MediaCodec into a .mp4 file.
mMuxer = new MediaMuxer(outputFile.toString(),
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
mTrackIndex = -1;
mMuxerStarted = false;
}
/**
* Prepares the video encoder, muxer, and an EGL input surface.
*/
protected void prepareEncoder(String mimeType, int width, int height, int bitRate,
int framesPerSecond, File outputFile) throws IOException {
mBufferInfo = new MediaCodec.BufferInfo();
MediaFormat format = MediaFormat.createVideoFormat(mimeType, width, height);
// Set some properties. Failing to specify some of these can cause the MediaCodec
// configure() call to throw an unhelpful exception.
format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
format.setInteger(MediaFormat.KEY_FRAME_RATE, framesPerSecond);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
if (VERBOSE) Log.d(TAG, "format: " + format);
// Create a MediaCodec encoder, and configure it with our format. Get a Surface
// we can use for input and wrap it with a class that handles the EGL work.
mEncoder = MediaCodec.createEncoderByType(mimeType);
mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
Log.v(TAG, "encoder is " + mEncoder.getCodecInfo().getName());
Surface surface;
try {
surface = mEncoder.createInputSurface();
} catch (IllegalStateException ise) {
// This is generally the first time we ever try to encode something through a
// Surface, so specialize the message a bit if we can guess at why it's failing.
// TODO: failure message should come out of strings.xml for i18n
if (isSoftwareCodec(mEncoder)) {
throw new RuntimeException("Can't use input surface with software codec: " +
mEncoder.getCodecInfo().getName(),
ise);
} else {
throw new RuntimeException("Failed to create input surface", ise);
}
}
mEglCore = new EglCore(null, EglCore.FLAG_RECORDABLE);
mInputSurface = new WindowSurface(mEglCore, surface, true);
mInputSurface.makeCurrent();
mEncoder.start();
// Create a MediaMuxer. We can't add the video track and start() the muxer here,
// because our MediaFormat doesn't have the Magic Goodies. These can only be
// obtained from the encoder after it has started processing data.
//
// We're not actually interested in multiplexing audio. We just want to convert
// the raw H.264 elementary stream we get from MediaCodec into a .mp4 file.
if (VERBOSE) Log.d(TAG, "output will go to " + outputFile);
mMuxer = new MediaMuxer(outputFile.toString(),
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
mTrackIndex = -1;
mMuxerStarted = false;
}
/**
* Edits a video file, saving the contents to a new file. This involves decoding and
* re-encoding, not to mention conversions between YUV and RGB, and so may be lossy.
* <p>
* If we recognize the decoded format we can do this in Java code using the ByteBuffer[]
* output, but it's not practical to support all OEM formats. By using a SurfaceTexture
* for output and a Surface for input, we can avoid issues with obscure formats and can
* use a fragment shader to do transformations.
*/
private VideoChunks editVideoFile(VideoChunks inputData) {
if (VERBOSE) Log.d(TAG, "editVideoFile " + mWidth + "x" + mHeight);
VideoChunks outputData = new VideoChunks();
MediaCodec decoder = null;
MediaCodec encoder = null;
InputSurface inputSurface = null;
OutputSurface outputSurface = null;
try {
MediaFormat inputFormat = inputData.getMediaFormat();
// Create an encoder format that matches the input format. (Might be able to just
// re-use the format used to generate the video, since we want it to be the same.)
MediaFormat outputFormat = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
outputFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
outputFormat.setInteger(MediaFormat.KEY_BIT_RATE,
inputFormat.getInteger(MediaFormat.KEY_BIT_RATE));
outputFormat.setInteger(MediaFormat.KEY_FRAME_RATE,
inputFormat.getInteger(MediaFormat.KEY_FRAME_RATE));
outputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,
inputFormat.getInteger(MediaFormat.KEY_I_FRAME_INTERVAL));
outputData.setMediaFormat(outputFormat);
encoder = MediaCodec.createEncoderByType(MIME_TYPE);
encoder.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
inputSurface = new InputSurface(encoder.createInputSurface());
inputSurface.makeCurrent();
encoder.start();
// OutputSurface uses the EGL context created by InputSurface.
decoder = MediaCodec.createDecoderByType(MIME_TYPE);
outputSurface = new OutputSurface();
outputSurface.changeFragmentShader(FRAGMENT_SHADER);
decoder.configure(inputFormat, outputSurface.getSurface(), null, 0);
decoder.start();
editVideoData(inputData, decoder, outputSurface, inputSurface, encoder, outputData);
} finally {
if (VERBOSE) Log.d(TAG, "shutting down encoder, decoder");
if (outputSurface != null) {
outputSurface.release();
}
if (inputSurface != null) {
inputSurface.release();
}
if (encoder != null) {
encoder.stop();
encoder.release();
}
if (decoder != null) {
decoder.stop();
decoder.release();
}
}
return outputData;
}
/**
* Prepares the video encoder, muxer, and an EGL input surface.
*/
protected void prepareEncoder(String mimeType, int width, int height, int bitRate,
int framesPerSecond, File outputFile) throws IOException {
mBufferInfo = new MediaCodec.BufferInfo();
MediaFormat format = MediaFormat.createVideoFormat(mimeType, width, height);
// Set some properties. Failing to specify some of these can cause the MediaCodec
// configure() call to throw an unhelpful exception.
format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
format.setInteger(MediaFormat.KEY_FRAME_RATE, framesPerSecond);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
if (VERBOSE) Log.d(TAG, "format: " + format);
// Create a MediaCodec encoder, and configure it with our format. Get a Surface
// we can use for input and wrap it with a class that handles the EGL work.
mEncoder = MediaCodec.createEncoderByType(mimeType);
mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
Log.v(TAG, "encoder is " + mEncoder.getCodecInfo().getName());
Surface surface;
try {
surface = mEncoder.createInputSurface();
} catch (IllegalStateException ise) {
// This is generally the first time we ever try to encode something through a
// Surface, so specialize the message a bit if we can guess at why it's failing.
// TODO: failure message should come out of strings.xml for i18n
if (isSoftwareCodec(mEncoder)) {
throw new RuntimeException("Can't use input surface with software codec: " +
mEncoder.getCodecInfo().getName(),
ise);
} else {
throw new RuntimeException("Failed to create input surface", ise);
}
}
mEglCore = new EglCore(null, EglCore.FLAG_RECORDABLE);
mInputSurface = new WindowSurface(mEglCore, surface, true);
mInputSurface.makeCurrent();
mEncoder.start();
// Create a MediaMuxer. We can't add the video track and start() the muxer here,
// because our MediaFormat doesn't have the Magic Goodies. These can only be
// obtained from the encoder after it has started processing data.
//
// We're not actually interested in multiplexing audio. We just want to convert
// the raw H.264 elementary stream we get from MediaCodec into a .mp4 file.
if (VERBOSE) Log.d(TAG, "output will go to " + outputFile);
mMuxer = new MediaMuxer(outputFile.toString(),
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
mTrackIndex = -1;
mMuxerStarted = false;
}
private void doEncode() throws IOException {
MediaFormat inputFormat = mExtractor.getTrackFormat(mVideoIndex);
//初始化编码器
int frameRate = inputFormat.containsKey(MediaFormat.KEY_FRAME_RATE) ? inputFormat.getInteger(inputFormat.KEY_FRAME_RATE) : DEFAULT_FRAME_RATE;
MediaFormat outputFormat = MediaFormat.createVideoFormat(VideoProcessor.OUTPUT_MIME_TYPE, mResultWidth, mResultHeight);
outputFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, mBitrate);
outputFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
outputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, mIFrameInterval);
mEncoder = MediaCodec.createEncoderByType(VideoProcessor.OUTPUT_MIME_TYPE);
mEncoder.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mSurface = mEncoder.createInputSurface();
// mInputSurface = new InputSurface(encodeSurface);
// mInputSurface.makeCurrent();
mEncoder.start();
mEglContextLatch.countDown();
boolean signalEncodeEnd = false;
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
int encodeTryAgainCount = 0;
//开始编码
//输出
while (true) {
if (mDecodeDone.get() && !signalEncodeEnd) {
signalEncodeEnd = true;
mEncoder.signalEndOfInputStream();
}
int outputBufferIndex = mEncoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
CL.i("encode outputBufferIndex = " + outputBufferIndex);
if (signalEncodeEnd && outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
encodeTryAgainCount++;
if (encodeTryAgainCount > 10) {
//三星S8上出现signalEndOfInputStream之后一直tryAgain的问题
CL.e("INFO_TRY_AGAIN_LATER 10 times,force End!");
break;
}
} else {
encodeTryAgainCount = 0;
}
if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
continue;
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
MediaFormat newFormat = mEncoder.getOutputFormat();
CL.i("encode newFormat = " + newFormat);
} else if (outputBufferIndex < 0) {
//ignore
CL.e("unexpected result from decoder.dequeueOutputBuffer: " + outputBufferIndex);
} else {
//编码数据可用
ByteBuffer outputBuffer = mEncoder.getOutputBuffer(outputBufferIndex);
info.presentationTimeUs += mBaseMuxerFrameTimeUs;
if (!mIsFirst && info.flags == MediaCodec.BUFFER_FLAG_CODEC_CONFIG) {
//非第一个片段跳过写入Config
continue;
}
if (!mIsLast && info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
//非最后一个片段不写入End
CL.i("encoderDone");
mEncoder.releaseOutputBuffer(outputBufferIndex, false);
break;
}
if (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM && info.presentationTimeUs < 0) {
info.presentationTimeUs = 0;
}
CL.i("writeSampleData,size:" + info.size + " time:" + info.presentationTimeUs / 1000 + " flag:" + info.flags);
mMuxer.writeSampleData(mMuxerVideoTrackIndex, outputBuffer, info);
if (mLastFrametimeUs < info.presentationTimeUs) {
mLastFrametimeUs = info.presentationTimeUs;
}
mEncoder.releaseOutputBuffer(outputBufferIndex, false);
if (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
CL.i("encoderDone");
break;
}
}
}
}