Android Multimedia实战(五)MediaCodec编码解码实例解析

来源:互联网 发布:复利的威力 知乎 编辑:程序博客网 时间:2024/06/05 10:55

MediaCodec概述

MediaCodec是一个Android原生的编解码器。
简单的来说,MediaCodec可以把摄像头采集的数据流编码为H264格式,这个过程是压缩过程。也可以把H264格式解码在surface类的控件上显示。

我们先来看一下Android系统中解码器的命名,软解码器通常是以OMX.google开头的。硬解码器通常是以OMX.[hardware_vendor]开头的,比如TI的解码器是以OMX.TI开头的。当然还有一些不遵守这个命名规范的,不以OMX.开头的,那也会被认为是软解码器。

判断规则见frameworks/av/media/libstagefright/OMXCodec.cpp:

static bool IsSoftwareCodec(const char *componentName) {    if (!strncmp("OMX.google.", componentName, 11)) {        return true;    }    if (!strncmp("OMX.", componentName, 4)) {        return false;    }    return true;}

其实MediaCodec调用的是在系统中注册的解码器,系统中存在的解码器可以很多,但能够被应用使用的解码器是根据配置来的,即/system/etc/media_codecc.xml。这个文件一般由硬件或者系统的生产厂家在build整个系统的时候提供,一般是保存在代码的device/[company]/[codename]目录下的,例如device/samsung/tuna/media_codecs.xml。这个文件配置了系统中有哪些可用的codec以及,这些codec对应的媒体文件类型。在这个文件里面,系统里面提供的软硬codec都需要被列出来。

也就是说,如果系统里面实际上包含了某个codec,但是并没有被配置在这个文件里,那么应用程序也无法使用到。

在这个配置文件里面,如果出现多个codec对应同样类型的媒体格式的时候,这些codec都会被保留起来。当系统使用的时候,将会选择第一个匹配的codec。除非是指明了要软解码还是硬解码,但是Android的framework层为上层提供服务的AwesomePlayer中在处理音频和视频的时候,对到底是选择软解还是硬解的参数没有设置。所以虽然底层是支持选择的,但是对于上层使用MediaPlayer的Java程序来说,还是只能接受默认的codec选取规则。

但是Android提供的命令行程序/system/bin/stagefright在播放音频文件的时候,倒是可以根据参数来选择到底使用软解码还是硬解码,但是该工具只支持播放音频,不支持播放视频。

一般来说,如果系统里面有对应的媒体硬件解码器的话,系统开发人员应该是会配置在media_codecs.xml中,所以大多数情况下,如果有硬件解码器,那么我们总是会使用到硬件解码器。极少数情况下,硬件解码器存在,但不配置,我猜只可能是这个硬解码器还有bug,暂时还不适合发布,所以不用使用。

下面我们以摄像头采集数据通过MediaCodec编码为H264存入内存卡,在从内存卡读出数据显示为例子说明MediaCodec:

开始采集数据


1.编码过程:

初始化编码器:

private static final String MIME_TYPE = "video/avc"; // H.264的mime类型MediaCodecInfo codecInfo = selectCodec(MIME_TYPE);//选择系统用于编码H264的编码器信息,固定的调用mColorFormat = selectColorFormat(codecInfo, MIME_TYPE);//根据MIME格式,选择颜色格式,固定的调用MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE,        this.mWidth, this.mHeight);//根据MIME创建MediaFormat,固定//以下参数的设置,尽量固定.当然,如果你非常了解,也可以自行修改mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);//设置比特率mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);//设置帧率mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, mColorFormat);//设置颜色格式mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);//设置关键帧的时间try {    mMediaCodec = MediaCodec.createByCodecName(codecInfo.getName());//这里就是根据上面拿到的编码器创建一个MediaCodec了;//MediaCodec还有一个方法可以直接用MIME类型,创建} catch (IOException e) {    e.printStackTrace();}//第二个参数用于播放MP4文件,显示图像的Surface;//第四个参数,编码H264的时候,固定CONFIGURE_FLAG_ENCODE, 播放的时候传入0即可;API文档有解释mMediaCodec.configure(mediaFormat, null, null,        MediaCodec.CONFIGURE_FLAG_ENCODE);//关键方法mMediaCodec.start();//必须

把摄像头采集的数据传给编码器编码,把返回的H264数据写进文件。进入文件管理器查看已经写入

@Override    public void onPreviewFrame(byte[] data, Camera camera)     {        long newTime = System.currentTimeMillis();        long diff = newTime - lastTime;        lastTime = newTime;        //把摄像头的数据传给编码器        int ret = avcCodec.offerEncoder(data,h264);        if(ret > 0)        {            try {                       byte[] length_bytes = intToBytes(ret);                file.write(length_bytes);                file.write(h264, 0, ret);            } catch (IOException e)            {                Log.d("ws", "exception: " + e.toString());            }        }    }

对于有些机器上报错,修改 MediaFormat.KEY_COLOR_FORMAT值即可,有以下参考

 /*    case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:        case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:        case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:        case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:        case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:*/

2.解码
初始化解码器,读取之前文件的数据,把数据传给surface类的控件上显示

MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", width, width);            mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 2500000);            mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 20);            try {//初始化解码器                decoder = MediaCodec.createDecoderByType("video/avc");            } catch (IOException e) {                Log.d("Fuck", "Fail to create MediaCodec: " + e.toString());            }            //将解码出来的数据传给 surface进行显示            decoder.configure(mediaFormat, surface, null, 0);            //decoder.configure(mediaFormat, null, null, 0);            decoder.start();            // new BufferInfo();            ByteBuffer[] inputBuffers = decoder.getInputBuffers();            ByteBuffer[] outputBuffers = decoder.getOutputBuffers();            if (null == inputBuffers) {                Log.d("Fuck", "null == inputBuffers");            }            if (null == outputBuffers) {                Log.d("Fuck", "null == outbputBuffers 111");            }            FileInputStream file = null;            try {                file = new FileInputStream(fileString);            } catch (FileNotFoundException e) {                Log.d("Fuck", "open file error: " + e.toString());                return;            }            int read_size = -1;            int mCount = 0;            for (;;) {                byte[] h264 = null;                try {                    byte[] length_bytes = new byte[4];                    read_size = file.read(length_bytes);                    if (read_size < 0) {                        Log.d("Fuck", "read_size<0 pos1");                        break;                    }                    int byteCount = bytesToInt(length_bytes, 0);                    Log.d("Fuck", "byteCount: " + byteCount);                    h264 = new byte[byteCount];                    read_size = file.read(h264, 0, byteCount);                    // Log.d("Fuck", "read_size: " + read_size);                    if (read_size < 0) {                        Log.d("Fuck", "read_size<0 pos2");                        break;                    }                    // Log.d("Fuck", "pos: " + file.)                } catch (IOException e) {                    Log.d("Fuck", "read_size 2: " + read_size);                    Log.d("Fuck", "e.toStrinig(): " + e.toString());                    break;                }                int inputBufferIndex = decoder.dequeueInputBuffer(-1);                if (inputBufferIndex >= 0) {                    ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];                    inputBuffer.clear();                    inputBuffer.put(h264);                    // long sample_time = ;                    decoder.queueInputBuffer(inputBufferIndex, 0, h264.length, mCount * 1000000 / 20, 0);                    ++mCount;                } else {                    Log.d("Fuck", "dequeueInputBuffer error");                }                ByteBuffer outputBuffer = null;                MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();                int outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);                while (outputBufferIndex >= 0) {                    outputBuffer = outputBuffers[outputBufferIndex];                    decoder.releaseOutputBuffer(outputBufferIndex, true);                    outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);                }...

demo下载:https://github.com/WangShuo1143368701/VideoView/tree/master/mediacodecdemo

1 0
原创粉丝点击