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
- Android Multimedia实战(五)MediaCodec编码解码实例解析
- Android Multimedia框架总结(二十七)MediaCodec回顾
- Android Multimedia框架总结(二十)MediaCodec状态周期及Codec与输入/输出Buffer过程(附实例)
- Android利用mediacodec进行视频H264编码解码播放
- Android 用MediaCodec实现编码camera再解码
- Android利用mediacodec进行视频H264编码解码播放
- Android利用mediacodec进行视频H264编码解码播放
- Android硬编解码接口MediaCodec使用完全解析(一)
- android下MediaCodec硬编码(转)
- Android MediaCodec 视频编码
- Android Camera2 Mediacodec编码
- Android MediaCodec解码aac,播放.
- android利用MediaCodec硬解码
- Android MediaCodec解码aac,播放.
- Android MediaCodec解码aac播放
- Android媒体解码MediaCodec,MediaExtractor
- Android Multimedia框架总结(二十三)MediaCodec补充及MediaMuxer引入(附案例)
- Android Multimedia框架总结(二十三)MediaCodec补充及MediaMuxer引入(附案例)
- Oracle 11g 删除归档日志
- AngularJS自定义服务
- ImageView的src和background的区别、padding的使用技巧、ImageView根据屏幕对缩放
- JavaScript巧学巧用
- android(体验一个项目天气预报开发)-2
- Android Multimedia实战(五)MediaCodec编码解码实例解析
- 《啊哈c语言》pdf
- ViewPager调用SetCurrentItem()方法,跨页面跳转时闪屏的问题
- strcpy和memcpy的区别
- 对Bitmap 进行水平或者垂直的镜面翻转
- 设计模式全面摘录笔记
- Sql order by 和 group BY一起使用时需要注意
- log4j2 日志配置实战
- 一张图助你分分钟掌握用photoshop将图片转化为背景透明的png技能-ps2017