mediacodec

MedaiCodec简介

MediaCodec是Android中提供的音视频编/解码工具。它主要是完成上层接口的封装,供给开发者使用,编解码功能实际是在native底层服务中完成的

MediaCodec工作流程

包括两个缓冲区队列

一个输入缓冲区队列,包含一组输入缓冲区(格式ByteBuffer);
一个输出缓冲区队列,包含一组输出缓冲区(格式ByteBuffer);


使用中,需要不断重复以下过程:

1.把原始数据放入输入缓冲区队列中一个空缓冲区上;
2.编/解码器从输入缓冲队列中获取缓冲区数据,进行编码处理,结果存放到输出缓冲区上一个空缓冲区上,处理完毕后,释放该输入缓冲区,它会被重新放回输入缓冲区队列,以便下次重复使用;
3.对输出缓冲区上的数据做自己需要的业务处理处理,处理完毕后,释放该输出缓冲区,它会被重新放回输出缓冲区队列,以便下次重复使用。

当对视频帧进行编/解码时,一般会用编码器创建一个输入Surface或为编码器设置一个输出Surface,在这里Surface充当着数据缓冲区的角色。使用Surface可以提高编/解码器的能,Surface直接使用native视频数据缓存,没有映射或复制它们到ByteBuffers,这种方式会更加高效。

 状态机,三类状态:

1.停止态(Stopped):
    1.1 未初始化状态(Uninitialized)
    1.2 配置状态(Configured)
    1.3 错误状态(Error)
2.执行态(Executing):
    2.1 刷新状态(Flushed)
    2.2 运行状态( Running)
    2.3 流结束状态(End-of-Stream)
3.释放态(Released)


使用流程:

1.创建编/解码器,此时处于未初始化状态(Uninitialized);
2.调用configure(…)方法对编解码器进行配置,使编解码器转入配置状态(Configured);
3.调用start()方法,使其转入执行刷新状态(Flushed);
4.此时编解码器已经拥有其输入/输出缓存,当第一个输入缓存区被移出队列,编解码器转入运行状态( Running);
5.在运行状态( Running)中,编解码器不断对输入缓冲区中数据做编解码操作,结果存到输出缓冲区;
6.当一个带有end-of-stream标记的输入缓存入队列时,编解码器将转入流结束状态(End-of-Stream)。在这种状态下,编解码器不再接收新的输入缓存,但它仍然产生输出缓存。直到
当输入缓存中所有数据都被处理完,带有end-of-stream标记的数据帧到达输出缓存后,转入释放态(Released)。
7.当我们处理完输出数据后,在此状态下可以调用release()进行相关资源的释放。


状态转化其它相关要点:

1.在执行状态(Executing)下的任何时候,通过调用flush()方法使编解码器重新返回到刷新子状态(Flushed);
2.通过调用stop()方法使编解码器返回到未初始化状态(Uninitialized),此时这个编解码器可以再次重新配置;
3.编解码器遇到错误时会进入错误状态(Error),此时调用reset()方法使编解码器再次可用。
4.任何状态下调用reset()方法使编解码器返回到未初始化状态(Uninitialized)

MediaCodec相关API介绍:

createDecoderByType:获取解码器对象
createEncoderBytype:获取编码器对象
configure:对编解码器进行配置,使编解码器转入配置状态
start:使编码器转入执行刷新状态
stop:结束并返回到未初始化状态
release:释放实例资源
createInputSurface:创建输入缓冲Surface
setOutputSurface:设置输出缓冲Surface
getInputBuffers:获取需要编码数据的输入流队列,返回的是一个ByteBuffer数组
queueInputBuffer:输入流入队列
dequeueInputBuffer:从输入流队列中取数据进行编码操作
getOutputBuffers:获取编解码之后的数据输出流队列,返回的是一个ByteBuffer数组
dequeueOutputBuffer:从输出队列中取出编码操作之后的数据
releaseOutputBuffer:处理完成,释放ByteBuffer数据


MediaCodec的两种工作方式

1.同步方式:数据输入和输出依次进行。
           要等待上一次数据输入后,才能数据输出;
           等待上一次数据输出后,才能再次进行数据输入。
2.异步方式:数据输入和数据输出操作顺序相互独立。
           是底层服务来判断何时输入/输出可以进行,然后进行相应回调,
           开发者在回调中进行数据输入/输出处理。

同步方式

        while (!Thread.interrupted()){//只要线程不中断

            if (!isEOF){
                //返回有效的buffer 索引,如果没有相关的Buffer可用,就返回-1
                //传入的timeoutUs为0表示立即返回
                //如果数据的buffer可用,将无限期等待timeUs的单位是纳秒
                //从 输入流中取数据进行编解码
                int index =mMediaCodec.dequeueInputBuffer(10000);
                if (index >= 0){
                    ByteBuffer byteBuffer=inputBuffers[index];
                    Log.d("lpf","bytebuffer is "+byteBuffer);
                    int sampleSize=mMediaExtractor.readSampleData(byteBuffer,0);
                    Log.d("lpf","sampleSize is "+sampleSize);
                    if (sampleSize < 0){
                        Log.d("lpf","inputBuffer is BUFFER_FLAG_END_OF_STREAMING");
                        //输入流入队列
                        mMediaCodec.queueInputBuffer(index,0,0,0,MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                        isEOF=true;
                    }else{
                        mMediaCodec.queueInputBuffer(index,0,sampleSize,mMediaExtractor.getSampleTime(),0);
                        mMediaExtractor.advance();  //下一帧数据
                    }
                }
            }
            //从输出流中取出编解码后的数据
            int outIndex=mMediaCodec.dequeueOutputBuffer(info,100000);
            switch (outIndex){
                case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                    //当buffer变化时,必须重新指向新的buffer
                    outputBuffers=mMediaCodec.getOutputBuffers();
                    break;
                case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                    //当Buffer的封装格式发生变化的时候,需重新指向新的buffer格式
                    Log.d("lpf","output  buffer changed");
                    break;
                case MediaCodec.INFO_TRY_AGAIN_LATER:
                    //dequeueOutputBuffer 超时的时候会到这个case
                    Log.d("lpf","dequeueOutputBuffer timeout");
                    break;
                default:
                    ByteBuffer buffer=outputBuffers[outIndex];
                    //由于配置的时候 将Surface 传进去了  所以解码的时候 将数据直接交给了Surface进行显示了
                    //使用简单的时钟的方式保持视频的fps(每秒显示的帧数),不然视频会播放的比较快
                    Log.d("lpf","解码之后的 buffer数据="+buffer);
                    while (info.presentationTimeUs/1000>System.currentTimeMillis()-startMs){
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //处理完成 释放buffer
                    mMediaCodec.releaseOutputBuffer(outIndex,true);
                    break;
            }

            if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0){
                Log.d("lpf","outputBuffer BUFFER_FLAG_END_OF_STREAM");
                break;
            }
        }

异步方式

public class VideoDecodeThread extends Thread {
Handler handler ;
HandlerThread mHandlerThread;
mHandlerThread = new HandlerThread("video");
mHandlerThread.start();
handler = new Handler(getLooper());
mMediaCodec.setCallback(mCallback,handler); //在子线程中执行回调
mCallback.setMediaExtractor(mMediaExtractor);
//调用Start 如果没有异常信息,表示成功构建组件
mMediaCodec.start();
public class MyMediaCallback extends MediaCodec.Callback {
    MediaExtractor mMediaExtractor ;

    public void setMediaExtractor(MediaExtractor mediaExtractor) {
        Log.e("TAG","setMediaExtractor");
        mMediaExtractor = mediaExtractor;
    }

    @Override
    public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
        Log.e("TAG","onInputBufferAvailable");
        ByteBuffer inputBuffer = codec.getInputBuffer(index);
        int sampleSize=mMediaExtractor.readSampleData(inputBuffer,0);
        if (sampleSize > 0){
            codec.queueInputBuffer(index,0,sampleSize,mMediaExtractor.getSampleTime(),0);
            mMediaExtractor.advance();
        }else {
            codec.queueInputBuffer(index,0,0,0,MediaCodec.BUFFER_FLAG_END_OF_STREAM);
        }
    }

    @Override
    public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index,
            @NonNull MediaCodec.BufferInfo info) {
        Log.e("TAG","onOutputBufferAvailable");
        try {
            Thread.sleep(30);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        codec.releaseOutputBuffer(index,true);
    }

    @Override
    public void onError(@NonNull MediaCodec codec, @NonNull MediaCodec.CodecException e) {

    }

    @Override
    public void onOutputFormatChanged(@NonNull MediaCodec codec, @NonNull MediaFormat format) {

    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaowang_lj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值