目录
final public class MediaCodec
MediaCodec类可以访问底层媒体编解码框架(Stage或OpenMAX),即编解码组件。通常与MediaExtractor、MediaSync、MediaMuxer、Image、Surface和AudioTrack一起使用。它本身并不是Codec,它通过调用底层编解码组件获得了Codec的能力。
创建MediaCodec(按格式创建):
创建MediaCodec(按Codec名字创建):

视频类型的Mediaformat
可以通过如下代码创建视频类型Mediaformat:
MediaFormat videoFormat = MediaFormat.createVideoFormat(videoType, width, height);
videoType常用的有两种:
以下配置是必须要指定的,否则会报错。
- MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);
- //色彩空间
- format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
- MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
- //码率
- format.setInteger(MediaFormat.KEY_BIT_RATE,500_000);
- //帧率fps
- format.setInteger(MediaFormat.KEY_FRAME_RATE,20);
- //关键帧间隔
- format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,2);
音频类型的Mediaformat
可以通过如下代码创建音频类型Mediaformat:
MediaFormat.createAudioFormat(audioType, sampleRate, channelCount);
以下配置是必须要指定的,否则会报错。
- //音频比特率(码率)
- audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, 96000);
配置解码器或者编码器configure函数
public void configure( @Nullable MediaFormat format,
@Nullable Surface surface,
@Nullable MediaCrypto crypto,@ConfigureFlag int flags)
函数的参数介绍:
使用示例:
- mediaCodec=MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
- mediaCodec.configure(format,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);
- mediaCodec.start();
创建混合器MediaMuxer函数
public MediaMuxer(@NonNull String path, @Format int format)
函数的参数介绍:
示例代码:
- mMuxer=new MediaMuxer(path,
- MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4
- );
- mMuxer.setOrientationHint(degress);
介绍MediaCodec编码用到的四个方法:
1. dequeueInputBuffer
返回用于填充有效数据的输入buffer的索引,如果当前没有可用的buffer,则返回-1。
public final int dequeueInputBuffer(long timeoutUs)
函数的参数介绍:
2. queueInputBuffer
在指定索引处填充输入buffer后,使用queueInputBuffer将buffer提交给组件。
public native final void queueInputBuffer(
int index,
int offset, int size, long presentationTimeUs, int flags)
3. dequeueOutputBuffer
从MediaCodec获取输出buffer。
public final int dequeueOutputBuffer(
@NonNull BufferInfo info, long timeoutUs)
4. releaseOutputBuffer
使用此方法将输出buffer返回给codec或将其渲染在输出surface。
public void releaseOutputBuffer (int index, boolean render)
示例代码:
- public void queueEncode(byte[] buffer){
- if(!isRecording){
- Log.e("xxx","没开始录制");
- return;
- }
- Log.e("xxx","开始录制");
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- //立即得到有效输入缓冲区
- int index=mediaCodec.dequeueInputBuffer(0);
- if(index>=0){
- ByteBuffer inputBuffer=mediaCodec.getInputBuffer(index);
- inputBuffer.clear();
- inputBuffer.put(buffer,0,buffer.length);
- //填充数据后再加入队列
- mediaCodec.queueInputBuffer(index,0,buffer.length,System.nanoTime()/1000,0);
- }
- while (true){
- //获取输出缓冲区(编码后的数据从缓冲区获取)
- MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
- int encoderStatus = mediaCodec.dequeueOutputBuffer(bufferInfo, 10_000);
- //稍后重试
- if(encoderStatus== MediaCodec.INFO_TRY_AGAIN_LATER){
- break;
- }else if(encoderStatus==MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
- //输出格式发生变化 第一次总会调用,所以在这里开启混合器
- MediaFormat newFormat=mediaCodec.getOutputFormat();
- videoTrack=mMuxer.addTrack(newFormat);
- mMuxer.start();
- }else if(encoderStatus==MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED){
- //可以忽略
- }else{
- //正常则encoderStatus 获取缓冲区下标
- ByteBuffer encodedData=mediaCodec.getOutputBuffer(encoderStatus);
- //如果当前的buffer是配置信息,不管它,不用写出去
- if((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG)!=0){
- bufferInfo.size=0;
- }
- if(bufferInfo.size!=0){
- //设置从哪里开始读数据(读出来就是编码后的数据)
- encodedData.position(bufferInfo.offset);
- //设置能读数据的总长度
- encodedData.limit(bufferInfo.offset+bufferInfo.size);
- //写出为MP4
- mMuxer.writeSampleData(videoTrack,encodedData,bufferInfo);
- }
- //释放这个缓冲区,后续可以存放新的编码后的数据
- mediaCodec.releaseOutputBuffer(encoderStatus,false);
- }
- }
- }
- });
- }
简单来说,可以看做是一个生产者-消费者模式,在MediaCodec里面有两个队列,一个是输入队列,一个是输出队列,数据放入输入队列,MediaCodec拿出数据进行编码,把编码完的数据放入输出队列。

在MediaCodec的生命周期内存在3种状态,即Stopped、Executing和Released。
Stopped状态还可处于三个状态:Uninitialized、Configures和Error。
Executing状态概念上的进展通过3个子状态进行:Flushed、Running和end-of-Stream。
MediaCodec状态周期图如下图:

使用缓冲区的异步处理:

在异步模式下,媒体编解码器通常像这样使用:
-
- 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(…) {
- …
- }
- @Override
- void onCryptoError(…) {
- …
- }
- });
- codec.configure(format, …);
- mOutputFormat = codec.getOutputFormat(); // option B
- codec.start();
- // wait for processing to complete
- codec.stop();
- codec.release();
使用缓冲区的同步处理:
媒体编解码器在同步模式下通常这样使用:
-
- 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();
功能介绍:
实现Camera1采集相机数据,设置预览画布TextureView显示采集的数据,然后MediaCode对图像数据编码,最后MediaMuxer把图像数据放入MP4盒子。
运行图:

这是我的项目代码:
(小白一个,代码只是能简单地实现功能)