Android系统中的MediaCodec架构提供了一种直接操作显卡解码器的方式。在Android 5.0之后,MediaCodec架构提供了更为底层的codec,即Codec2,可以方便地实现硬件加速的解码和编码,从而能够提高媒体文件的处理速度。
在Android中初始化Codec2的具体流程如下:
1.获取Codec2的列表
如下代码所示,可以通过MediaCodecList类获取Codec2的列表。然后通过iterate方法,获取系统中所有支持的codec信息。
MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) {
if (!codecInfo.isEncoder()) {
continue;
}
String[] types = codecInfo.getSupportedTypes();
for (String type : types) {
if (!type.equals(Mime. VIDEO_H264)) {
continue;
}
// Get the default capabilities for an H.264 encoder
CodecCapabilities capabilities =
codecInfo.getCapabilitiesForType(type);
// Check the resolution supported by hardware codec and your device
int width = 1280;
int height = 720;
if (capabilities.isFormatSupported(new MediaFormat().
setInteger(MediaFormat.KEY_WIDTH, width).
setInteger(MediaFormat.KEY_HEIGHT, height).
setString(MediaFormat.KEY_MIME, type))) {
Log.i(TAG, "Found " + codecInfo.getName() + " supporting " + type + " for "
+ width + "x" + height + " with default capabilities: " + capabilities);
}
}
}
2.创建Codec2
通过Codec2的名称创建MediaCodec对象,并将其配置为编码器或解码 器,如下所示:
MediaCodec codec = MediaCodec.createByCodecName(codecInfo.getName());
MediaFormat inputFormat = MediaFormat.createVideoFormat(Mime.VIDEO_H264,
1280, 720);
codec.configure(inputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
codec.start();
3.接收数据并编码
通过configure方法配置设置编码器的输入格式,然后通过MediaCodec中的encoderInputBuffer,从交换数组中获取有效的输入数据进行编码,最后将编码后的数据放入MediaCodec的输出缓冲区中,如下所示:
ByteBuffer[] encoderInputBuffer = codec.getInputBuffers();
ByteBuffer[] encoderOutputBuffer = codec.getOutputBuffers();
int timeoutUs = 10000;
while (true) {
int inputBufferIndex = codec.dequeueInputBuffer(timeoutUs);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = encoderInputBuffer[inputBufferIndex];
//Fill the ByteBuffer with your raw data
int rawBytesDataLength = getAudioDataLength();
inputBuffer.clear();
byte[] rawBytesData = getRawBytesData();
inputBuffer.put(rawBytesData, 0, rawBytesDataLength);
codec.queueInputBuffer(inputBufferIndex, 0, rawBytesDataLength, 0, 0);
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, timeoutUs);
if (outputBufferIndex >= 0) {
ByteBuffer outputBuffer = encoderOutputBuffer[outputBufferIndex];
// Output the encoded frame here to other sources
codec.releaseOutputBuffer(outputBufferIndex, false);
}
}
以上是初始化Codec2的具体流程。其中,第二步和第三步中的核心代码,就是通过configure方法来进行配置,然后在输入数据中获取有效数据,经过编码后输出到输出缓冲区中。
下面是两个示例:
示例一:实现将本地视频转换为h.264编码格式并保存到文件中:
MediaCodec codec = MediaCodec.createEncoderByType("video/avc");
int width = 640;
int height = 480;
int frameRate = 30;
int bitRate = 1024 * 1024;
int colorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
int iFrameInterval = 1;
MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height);
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval);
codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
codec.start();
ByteBuffer[] inputBuffers = codec.getInputBuffers();
ByteBuffer[] outputBuffers = codec.getOutputBuffers();
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
// Do video decoding here until we get EOS from the decoder
boolean done = false;
while (done) {
// Do video encoding here
//extract data from input buffer
int inputBufferIndex = codec.dequeueInputBuffer(10000);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
byte[] newInputData = getInputData();
inputBuffer.put(newInputData);
codec.queueInputBuffer(inputBufferIndex, 0, newInputData.length, 0, 0);
}
//continue dequeueing and encoding until we get an EOF
int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, 10000);
while (outputBufferIndex != -1) {
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
//output the encoded video data
outputToFile(outputBuffer);
codec.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, 0);
}
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
done = true;
}
}
codec.stop();
codec.release();
示例二:实现通过异步线程进行数据的编码:
private static class EncodingTask extends AsyncTask<Void, Void, Void> {
MediaCodec codec;
ByteBuffer[] inputBuffers;
ByteBuffer[] outputBuffers;
MediaCodec.BufferInfo bufferInfo;
public EncodingTask() {
codec = MediaCodec.createEncoderByType("video/avc");
int width = 640;
int height = 480;
int frameRate = 30;
int bitRate = 1024 * 1024;
int colorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
int iFrameInterval = 1;
MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height);
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval);
codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
codec.start();
int timeoutUs = 10000;
inputBuffers = codec.getInputBuffers();
outputBuffers = codec.getOutputBuffers();
bufferInfo = new MediaCodec.BufferInfo();
// Put data into our queue
offerEncoderInput(bufferInfo, true);
}
@Override
protected Void doInBackground(Void... params) {
while (true) {
if (isCancelled()) {
return null;
}
int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, timeoutUs);
if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
continue;
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
Log.d(TAG, "encoder output format changed: " + codec.getOutputFormat());
continue;
} else if (outputBufferIndex < 0) {
continue;
}
ByteBuffer outputBuffer = codec.getOutputBuffers()[outputBufferIndex];
// Put data in the buffer
codec.releaseOutputBuffer(outputBufferIndex, false);
offerEncoderInput(bufferInfo, false);
}
}
private void offerEncoderInput(MediaCodec.BufferInfo bufferInfo, boolean eos) {
while (true) {
int inputBufferIndex = codec.dequeueInputBuffer(timeoutUs);
if (inputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
continue;
}
if (eos) {
codec.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
break;
}
ByteBuffer inputBuffer = codec.getInputBuffers()[inputBufferIndex];
// Put data into input buffer
codec.queueInputBuffer(inputBufferIndex, bufferInfo.offset, bufferInfo.size,
bufferInfo.presentationTimeUs, bufferInfo.flags);
break;
}
}
}
以上两个示例,分别实现了将本地视频转换为h.264编码格式及通过异步线程进行数据编码,并通过设置各种参数进行优化处理,显著提高了视频编码效率。其中,初始化Codec2的流程,包括获取Codec2的列表、创建Codec2以及接收数据并进行编码,都是必不可少的步骤。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Android中初始化Codec2的具体流程 - Python技术站