private void prepareEncoder(File outputFile) throws IOException {
mBufferInfo = new MediaCodec.BufferInfo();
MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE,WIDTH,HEIGHT);
// set necessary properties for the format
format.setInteger(MediaFormat.KEY_COLOR_FORMAT,MediaCodecInfo.CodecCapabilities
.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE,BIT_RATE);
format.setInteger(MediaFormat.KEY_FRAME_RATE,FRAMES_PER_SECOND);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,IFRAME_INTERVAL);
if(VERBOSE) Log.d(TAG, "format:" + format);
// create a media encoder and configure it with our format. get a surface
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.
if(VERBOSE) Log.d(TAG, "output will go to " + outputFile);
mMuxer = new MediaMuxer(outputFile.getPath(),
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
mTrackIndex = -1;
mMuxerStarted = false;
}
private void drainEncoder(boolean endOfStream) {
//设置超时时间
final int TIMEOUT_USEC = 10000;
if(endOfStream) {
if(VERBOSE) Log.d(TAG, "sending eos to encoder");
mEncoder.signalEndOfInputStream();
}
ByteBuffer[] encoderOutputBuffers = mEncoder.getOutputBuffers();
while(true) {
//获取编码的状态,让输出流缓冲队列出列,dequeueOutputBuffer会返回一个int类型的值作为编码状态
int encodeStatus = mEncoder.dequeueOutputBuffer(mBufferInfo,TIMEOUT_USEC);
//处理各种可能出现的编码状态
if(encodeStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
// no output available yet
if(!endOfStream) {
break; // out of while
} else {
if(VERBOSE) Log.d(TAG, "no output available ,spinning to await EOS");
}
} else if (encodeStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
//not expected for an encoder
encoderOutputBuffers = mEncoder.getOutputBuffers();
} else if (encodeStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// should happen before receiving buffers ,and should only happen once
if(mMuxerStarted) {
throw new RuntimeException("format changed twice");
}
// the first time format change
//format改变 意味着要开始将得到的数据编码成mp4了,启动mMuxer
MediaFormat newFormat = mEncoder.getOutputFormat();
Log.d(TAG, "encoder output format changed : " + newFormat);
// now we have the magic goodies ,start the muxer
mTrackIndex = mMuxer.addTrack(newFormat);
mMuxer.start();
mMuxerStarted = true;
} else if (encodeStatus < 0) {
Log.w(TAG, "unexpected result from encoder dequeue output buffers" +
encodeStatus);
} else {
// 启动了mMuxer之后的逻辑 以Byte为单位来读取数据
ByteBuffer encodeData = encoderOutputBuffers[encodeStatus];
if(encodeData == null) {
throw new RuntimeException("encoder data " + encodeStatus + "is null");
}
if((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
// The codec config data was pulled out and fed to the muxer when we got the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it.
if (VERBOSE) Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG");
mBufferInfo.size = 0;
}
if(mBufferInfo.size != 0) {
if(!mMuxerStarted) {
throw new RuntimeException("muxer hasn't started");
}
//这段代码是定位我们要写入的数据,通过在dequeueOutputBuffer中传入的mBufferInfo来设置
encodeData.position(mBufferInfo.offset);
encodeData.limit(mBufferInfo.offset+mBufferInfo.size);
mBufferInfo.presentationTimeUs = mFakePts;
mFakePts += 1000000L /FRAMES_PER_SECOND;
//通过mMuer写入编码后的数据
mMuxer.writeSampleData(mTrackIndex,encodeData,mBufferInfo);
if(VERBOSE) Log.d(TAG, "send " + mBufferInfo.size + "bytes to muxer ");
}
//释放持有的OutputBuffer
mEncoder.releaseOutputBuffer(encodeStatus,false);
if((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
if(!endOfStream) {
Log.d(TAG, "reach end of stream unexpretly");
} else {
if(VERBOSE) Log.d(TAG, "reach end of stream");
}
break; // break of a while
}
}
}
}