文章目录
- 资料
- 用到的接口
- 获取所有的能够解析的编/解码器
- MediaCodec在异步模式下的用法如下:(使用Buffer异步处理)
- MediaCodec在同步模式下的用法如下:(使用Buffers同步处理)
- 使用Buffer Arrays同步处理(deprecated)
- MediaExtractor API介绍
- 使用MediaCodec的编解码流程
资料
MediaCodec的使用介绍MediaCodec基本原理及使用
Android MediaCodec实现多段音视频的截取与拼接
安卓解码器MediaCodec解析Android直播开发之旅(8):Android硬编解码接口MediaCodec原理剖析
Android MediaCodec编解码详解及demoAndroid 音视频开发(六): MediaCodec API 详解
Android 音视频开发(五):使用 MediaExtractor 和 MediaMuxer API 解析和封装 mp4 文件
Android硬编解码接口MediaCodec使用完全解析(一)
Android分离合成音视频(用MediaExtractor和MediaMuxer)
Android音视频开发-入门(四):使用 MediaExtractor 和 MediaMuxer API 解析和封装 mp4 文件android中MediaCodec类解析
Android MediaCodec stuffAndroid 原生 MediaPlayer 和 MediaCodec 的区别和联系(二)
MediaCodec 官方API
用到的接口
获取所有的能够解析的编/解码器
// 获取所有支持的编码器数量
val codecCount: Int = MediaCodecList.getCodecCount()
for (i in 0 until codecCount) {
val codecInfoAt: MediaCodecInfo = MediaCodecList.getCodecInfoAt(i)
// 判断是否为编码器
if (!codecInfoAt.isEncoder) {
continue
}
// 获取编码器支持的MIME类型,并进行匹配
val types = codecInfoAt.supportedTypes
types.forEach { type: String ->
log("type = $type")
}
}
MediaCodec在异步模式下的用法如下:(使用Buffer异步处理)
MediaCodec codec = MediaCodec.createByCodecName(name);
MediaFormat mOutputFormat; // member variable
codec.setCallback(new MediaCodec.Callback() {
@Override
void onInputBufferAvailable(MediaCodec mc, int inputBufferId) {
ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId);
// fill inputBuffer with valid data
…
codec.queueInputBuffer(inputBufferId, …);
}
@Override
void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) {
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
// bufferFormat is equivalent to mOutputFormat
// outputBuffer is ready to be processed or rendered.
…
codec.releaseOutputBuffer(outputBufferId, …);
}
@Override
void onOutputFormatChanged(MediaCodec mc, MediaFormat format) {
// Subsequent data will conform to new format.
// Can ignore if using getOutputFormat(outputBufferId)
mOutputFormat = format; // option B
}
@Override
void onError(…) {
…
}
});
codec.configure(format, …);
mOutputFormat = codec.getOutputFormat(); // option B
codec.start();
// wait for processing to complete
codec.stop();
codec.release();
MediaCodec在同步模式下的用法如下:(使用Buffers同步处理)
MediaCodec codec = MediaCodec.createByCodecName(name);
codec.configure(format, …);
MediaFormat outputFormat = codec.getOutputFormat(); // option B
codec.start();
for (;;) {
int inputBufferId = codec.dequeueInputBuffer(timeoutUs);
if (inputBufferId >= 0) {
ByteBuffer inputBuffer = codec.getInputBuffer(…);
// fill inputBuffer with valid data
…
codec.queueInputBuffer(inputBufferId, …);
}
int outputBufferId = codec.dequeueOutputBuffer(…);
if (outputBufferId >= 0) {
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
// bufferFormat is identical to outputFormat
// outputBuffer is ready to be processed or rendered.
…
codec.releaseOutputBuffer(outputBufferId, …);
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// Subsequent data will conform to new format.
// Can ignore if using getOutputFormat(outputBufferId)
outputFormat = codec.getOutputFormat(); // option B
}
}
codec.stop();
codec.release();
使用Buffer Arrays同步处理(deprecated)
MediaCodec codec = MediaCodec.createByCodecName(name);
codec.configure(format, …);
codec.start();
ByteBuffer[] inputBuffers = codec.getInputBuffers();
ByteBuffer[] outputBuffers = codec.getOutputBuffers();
for (;;) {
int inputBufferId = codec.dequeueInputBuffer(…);
if (inputBufferId >= 0) {
// fill inputBuffers[inputBufferId] with valid data
…
codec.queueInputBuffer(inputBufferId, …);
}
int outputBufferId = codec.dequeueOutputBuffer(…);
if (outputBufferId >= 0) {
// outputBuffers[outputBufferId] is ready to be processed or rendered.
…
codec.releaseOutputBuffer(outputBufferId, …);
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
outputBuffers = codec.getOutputBuffers();
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// Subsequent data will conform to new format.
MediaFormat format = codec.getOutputFormat();
}
}
codec.stop();
codec.release();
MediaExtractor API介绍
MediaExtractor的作用是把音频和视频的数据进行分离。
主要API介绍:
setDataSource(String path):即可以设置本地文件又可以设置网络文件
getTrackCount():得到源文件通道数
getTrackFormat(int index):获取指定(index)的通道格式
getSampleTime():返回当前的时间戳
readSampleData(ByteBuffer byteBuf, int offset):把指定通道中的数据按偏移量读取到ByteBuffer中;
advance():读取下一帧数据
release(): 读取结束后释放资源
MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(...);
int numTracks = extractor.getTrackCount();
for (int i = 0; i < numTracks; ++i) {
MediaFormat format = extractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
if (weAreInterestedInThisTrack) {
extractor.selectTrack(i);
}
}
ByteBuffer inputBuffer = ByteBuffer.allocate(...)
while (extractor.readSampleData(inputBuffer, ...) >= 0) {
int trackIndex = extractor.getSampleTrackIndex();
long presentationTimeUs = extractor.getSampleTime();
...
extractor.advance();
}
extractor.release();
extractor = null;
使用MediaCodec的编解码流程
一些编解码器对于它们的buffer要求是比较特殊的,比如内存对齐或是有特定的最小最大限制,为了适应广泛的可能性,buffer分配是由编解码器实现的。
- 这看起来和“零拷贝”原则是相悖的,但大部分情况发生拷贝的几率是比较小的,因为编解码器并不需要复制或调整这些数据来满足要求,而且大多数情况可以直接使用buffer,比如直接从磁盘或网络读取数据到buffer中,不需要复制。
MediaCodec采用异步方式处理数据,并且使用了一组输入输出的buffer(ByteBuffer)。
- 使用者从MediacCodec请求一个空的输入buffer(ByteBuffer),填充满数据后将它传递给MediaCodec处理。
- MediaCodec处理完这些数据并将处理结果输出至一个空的输出buffer(ByteBuffer)中。
- 使用者从MediaCodec获取输出buffer的数据,消耗掉里面的数据,使用完输出buffer的数据之后,将其释放回编解码器。