MediaCodec 的整体流程如上图所示,从input 输入数据,从output 输出数据,编码的时候输入的是原始数据,输出的是编码后的数据。
初始化
configure 编码器的时候要将flag 设置为encode。
public void startEncode(MediaFormat format) {
mFormat = format;
final String mimeType = format.getString(MediaFormat.KEY_MIME);
// Check to see if this is actually a video mime type. If it is, then create
// a codec that can decode this mime type.
try {
mCodec = MediaCodec.createEncoderByType(mimeType);
} catch (IOException e) {
throw new RuntimeException(e);
}
mCodec.configure(format,null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mCodec.start();
}
写数据
将原始数据送给 MediaCodec。
public void sendFrame(byte[] input, long pts, int flags) {
int size = input.length;
int inputBufferId = mCodec.dequeueInputBuffer(timeOut);
if (inputBufferId >= 0) {
// fill inputBuffers[inputBufferId] with valid data
ByteBuffer buffer = mCodec.getInputBuffer(inputBufferId);
buffer.put(input);
mCodec.queueInputBuffer(inputBufferId, 0, size, pts, flags);
}
}
取数据
从 MediaCodec 中取出编码后的数据,这里要注意返回的outputBufferId,会有不同的处理。
public VEAVPacket receivePacket() {
VEAVPacket packet = new VEAVPacket();
int outputBufferId = mCodec.dequeueOutputBuffer(mBufferInfo, timeOut);
if (outputBufferId == MediaCodec.INFO_TRY_AGAIN_LATER) {
Log.d(TAG, "INFO_TRY_AGAIN_LATER");
return packet;
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// Subsequent data will conform to new format.
packet.format = mCodec.getOutputFormat();
return packet;
} else if (outputBufferId < 0) {
Log.d(TAG, "receivePacket:" + outputBufferId);
return packet;
}
// outputBuffers[outputBufferId] is ready to be processed or rendered.
ByteBuffer outputBuffer = mCodec.getOutputBuffer(outputBufferId);
if (outputBuffer == null) {
return packet;
}
byte[] outData = new byte[mBufferInfo.size];
outputBuffer.get(outData);
packet.buffer = ByteBuffer.wrap(outData);
packet.bufferInfo = mBufferInfo;
mCodec.releaseOutputBuffer(outputBufferId, timeOut);
return packet;
}
结束
public void stopEncode() {
mCodec.stop();
mCodec.release();
}
注意事项
封装的时候可能提前需要获取 sps 和 pps,然而编码器接收到一定数据才会吐出这些信息,因此,我们可以先送入一些空数据,将这个信息给挤出来。
public MediaFormat getOutputFormat() {
if (mOutFormat != null) {
return mOutFormat;
}
byte [] data = new byte[1];
long time = System.currentTimeMillis();
sendFrame(data, time, 0);
VEAVPacket videoPacket = receivePacket();
if (videoPacket.format != null) {
mOutFormat = videoPacket.format;
}
return mOutFormat;
}
网友评论