http://www.360doc.com/content/14/0119/10/8122810_346350456.shtml
本文向你讲述如何用android标准的API (MediaCodec)实现视频的硬件编解码。例程将从摄像头采集视频开始,然后进行H264编码,再解码,然后显示。我将尽量讲得简短而清晰,不展示那些不相关的代码。但是,我不建议你读这篇文章,也不建议你开发这类应用,而应该转而开发一些戳鱼、打鸟、其乐融融的程序。好吧,下面的内容是写给那些执迷不悟的人的,看完之后也许你会同意我的说法:Android只是一个玩具,很难指望它来做靠谱的应用。
1、从摄像头采集视频
可以通过摄像头Preview的回调,来获取视频数据。
首先创建摄像头,并设置参数:
- cam = Camera.open();
- cam.setPreviewDisplay(holder);
- Camera.Parameters parameters = cam.getParameters();
- parameters.setFlashMode("off"); // 无闪光灯
- parameters.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
- parameters.setSceneMode(Camera.Parameters.SCENE_MODE_AUTO);
- parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
- parameters.setPreviewFormat(ImageFormat.YV12);
- parameters.setPictureSize(camWidth, camHeight);
- parameters.setPreviewSize(camWidth, camHeight);
- //这两个属性 如果这两个属性设置的和真实手机的不一样时,就会报错
- cam.setParameters(parameters);
- buf = new byte[camWidth * camHeight * 3 / 2];
- cam.addCallbackBuffer(buf);
- cam.setPreviewCallbackWithBuffer(this);
- cam.startPreview();
setPreviewCallbackWithBuffer是很有必要的,不然每次回调系统都重新分配缓冲区,效率会很低。
在onPreviewFrame中就可以获得原始的图片了(当然,this 肯定要 implements PreviewCallback了)。这里我们是把它传给编码器:
- public void onPreviewFrame(byte[] data, Camera camera) {
- if (frameListener != null) {
- frameListener.onFrame(data, 0, data.length, 0);
- }
- cam.addCallbackBuffer(buf);
- }
首先要初始化编码器:
- mediaCodec = MediaCodec.createEncoderByType("Video/AVC");
- MediaFormat mediaFormat = MediaFormat.createVideoFormat(type, width, height);
- mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 125000);
- mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
- mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
- mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
- mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
- mediaCodec.start();
然后就是给他喂数据了,这里的数据是来自摄像头的:
- public void onFrame(byte[] buf, int offset, int length, int flag) {
- ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
- ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
- int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
- if (inputBufferIndex >= 0)
- ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
- inputBuffer.clear();
- inputBuffer.put(buf, offset, length);
- mediaCodec.queueInputBuffer(inputBufferIndex, 0, length, 0, 0);
- }
- MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
- int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,0);
- while (outputBufferIndex >= 0) {
- ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
- if (frameListener != null)
- frameListener.onFrame(outputBuffer, 0, length, flag);
- mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
- outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
- }
3、解码和显示
首先初始化解码器:
- mediaCodec = MediaCodec.createDecoderByType("Video/AVC");
- MediaFormat mediaFormat = MediaFormat.createVideoFormat(mime, width, height);
- mediaCodec.configure(mediaFormat, surface, null, 0);
- mediaCodec.start();
这里通过给解码器一个surface,解码器就能直接显示画面。
然后就是处理数据了:
- public void onFrame(byte[] buf, int offset, int length, int flag) {
- ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
- int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
- if (inputBufferIndex >= 0) {
- ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
- inputBuffer.clear();
- inputBuffer.put(buf, offset, length);
- mediaCodec.queueInputBuffer(inputBufferIndex, 0, length, mCount * 1000000 / FRAME_RATE, 0);
- mCount++;
- }
- MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
- int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,0);
- while (outputBufferIndex >= 0) {
- mediaCodec.releaseOutputBuffer(outputBufferIndex, true);
- outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
- }
- }
好了,到现在,基本上就可以了。如果你运气够好,现在就能看到视频了,比如在我的三星手机上这样就可以了。但是,我试过几个其他平台,多数都不可以,总是有各种各样的问题,如果要开发一个不依赖平台的应用,还有很多的问题要解决。说说我遇到的一些情况:
1、视频尺寸
一般都能支持176X144/352X288这种尺寸,但是大一些的,640X480就有很多机子不行了,至于为什么,我也不知道。当然,这个尺寸必须和摄像头预览的尺寸一致,预览的尺寸可以枚举一下。
2、颜色空间
根据ANdroid SDK文档,确保所有硬件平台都支持的颜色,在摄像头预览输出是YUV12,在编码器输入是COLOR_FormatYUV420Planar,也就是前面代码中设置的那样。 不过,文档终究是文档,否则安卓就不是安卓。
在有的平台上,这两个颜色格式是一样的,摄像头的输出可以直接作为编码器的输入。也有的平台,两个是不一样的,前者就是YUV12,后者等于I420,需要把前者的UV分量颠倒一下。下面的代码效率不高,可供参考。
- byte[] i420bytes = null;
- private byte[] swapYV12toI420(byte[] yv12bytes, int width, int height) {
- if (i420bytes == null)
- i420bytes = new byte[yv12bytes.length];
- for (int i = 0; i < width*height; i++)
- i420bytes[i] = yv12bytes[i];
- for (int i = width*height; i < width*height + (width/2*height/2); i++)
- i420bytes[i] = yv12bytes[i + (width/2*height/2)];
- for (int i = width*height + (width/2*height/2); i < width*height + 2*(width/2*height/2); i++)
- i420bytes[i] = yv12bytes[i - (width/2*height/2)];
- return i420bytes;
- }
3、输入输出缓冲区的格式
SDK里并没有规定格式,但是,这种情况H264的格式基本上就是附录B。但是,也有比较有特色的,它就是不带那个StartCode,就是那个0x000001,搞得把他编码器编出来的东西送给他的解码器,他自己都解不出来。还好,我们可以自己加。
- ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
- byte[] outData = new byte[bufferInfo.size + 3];
- outputBuffer.get(outData, 3, bufferInfo.size);
- if (frameListener != null) {
- if ((outData[3]==0 && outData[4]==0 && outData[5]==1)
- || (outData[3]==0 && outData[4]==0 && outData[5]==0 && outData[6]==1))
- {
- frameListener.onFrame(outData, 3, outData.length-3, bufferInfo.flags);
- }
- else
- {
- outData[0] = 0;
- outData[1] = 0;
- outData[2] = 1;
- frameListener.onFrame(outData, 0, outData.length, bufferInfo.flags);
- }
- }
4、有时候会死在dequeueInputBuffer(-1)上面
根据SDK文档,dequeueInputBuffer 的参数表示等待的时间(毫秒),-1表示一直等,0表示不等。按常理传-1就行,但实际上在很多机子上会挂掉,没办法,还是传0吧,丢帧总比挂掉好。当然也可以传一个具体的毫秒数,不过没什么大意思
相关推荐
在Android平台上,MediaCodec是系统提供的一个核心API,用于实现媒体数据的编解码操作,特别是在处理音频和视频流时。硬编解码是指利用设备硬件进行编码和解码,通常比软件编解码更高效,能降低CPU的使用率并提高...
将Camera数据显示到OpenGL ES渲染的图像中,并通过MediaCodeC 将 OpenGL ES 缓冲数据,硬编码写入到 mp4 视频文件中,完成视频的录制。 文件介绍请见: https://xiaxl.blog.csdn.net/article/details/72530314 ...
MediaCodec 实现h264硬编解码全过程,视频数据从摄像头读出 yv12格式,转换为I420,投递给encoder,再从encoder取出编码后的h264数据投递给decoder后显示到surfaceView; 实现了udp将h264数据发送到指定主机,可通过...
本篇文章将深入探讨“Android硬解码MediaCodecDemo”,介绍如何利用MediaCodec进行硬解码操作,并处理在实际应用中可能遇到的crash、ANR(Application Not Responding)以及黑屏问题。 一、MediaCodec简介 ...
总的来说,"Android 采用MediaCodec实现Camera预览视频编解码"涉及了Android多媒体处理的核心技术,包括MediaCodec的使用、Camera2 API的集成以及视频编码和容器格式的相关知识。通过深入理解这些概念和实践,开发者...
Android使用MediaCodec将摄像头采集的视频编码为h264 Android平台上,MediaCodec是Android 4.1(Jelly ...MediaCodec提供了一个统一的接口来访问各种硬件编码器和解码器,使得开发者可以轻松地实现视频的编码和解码。
在本文中,我们将深入探讨如何使用MediaCodec进行H264视频的硬解码,这是一个高效的处理方式,因为它利用了设备硬件加速的能力。 1. **Android MediaCodec介绍** Android MediaCodec是系统服务的一部分,它为应用...
本篇文章将深入探讨在Android 6.0上使用MediaCodec进行H.264视频流的硬解码测试,以及如何在不同硬件平台上确保其兼容性和稳定性。 首先,我们要理解什么是H.264。H.264,也称为AVC(Advanced Video Coding),是一...
以下是一份简化的使用MediaCodec硬解码H264和AAC的步骤: 1. **初始化MediaCodec**: 首先,你需要根据视频的编码格式(H264)创建一个MediaCodec实例。使用`MediaCodec.createByCodecName()`或`MediaCodecList`...
Android系统下的视频硬解码demo,调用系统api,android 4.1.2+系统可用,根据github上的代码改的,增加选择文件功能,共享给大家,方便研究。MediaCodec。 Github链接 -> https://github.com/vecio/MediaCodecDemo ...
Android 中使用 MediaCodec 类实现视频文件的硬解码和硬编码是非常重要的技术。MediaCodec 是 Android 提供的一个多媒体编解码器,能够实现视频和音频的编解码。下面将详细介绍 MediaCodec 的编解码原理和使用示例...
综上所述,通过Android的Mediacodec API,开发者可以充分利用设备的硬件能力,实现高效且流畅的视频编解码。在实际应用中,如"CameraCodec"所示,我们能够实现同时在两个Surface上预览和解码图像,为用户提供更好的...
Android利用MediaCodec硬解码H264,AAC文件并播放Demo。相关博客:http://blog.csdn.net/a512337862/article/details/72629755和http://blog.csdn.net/a512337862/article/details/72629755
本项目深入分析了基于Android MediaCodec的硬编解码技术,源码由74个文件组成,涵盖了27个Java源文件、21个XML配置文件、10个PNG资源文件、3个Gradle构建脚本、3个Kotlin文件以及少量其他辅助文件。该设计旨在优化...
本文将深入探讨如何在Android上构建一个使用MediaCodec进行硬解码的RTSP H264播放器客户端。 MediaCodec是Android系统提供的一个低级别的硬件加速解码和编码API,它可以有效地处理各种视频和音频格式,包括H264。...
总之,Android的MediaCodec API为开发者提供了强大的多媒体处理能力,通过硬编解码,我们可以实现高效、低耗的图像和视频处理。在实际应用中,需要结合Camera2 API来完成预览和解码显示的功能,同时注意设备的兼容性...
基于Android平台音视频编解码项目+Kotlin+MediaCodeC+OpenGL高效解决音视频编解码问题+源码+开发文档,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 基于Android...
具有硬解的能力,但是需要android 4.1以上才能支持。 这个demo主要是测试硬解和软解h265裸流的能力,有一些手机可能不...硬解H265使用的是MediaCodec+ffmpeg方案,ffmepg负责拆包,MediaCodec负责解码和渲染,祝你愉快
综上所述,理解并掌握Android上的H265和H264解码技术,包括硬解码和相关API的使用,对于开发高效、流畅的视频应用至关重要。而解码库的选择则取决于项目需求,如性能、跨平台兼容性、开发效率等。开发者应当根据实际...