之前自学了下ffmpeg,使用ffmpeg在ubuntu下编解码比较方便,但是到了Android,发现使用比较多的编解码类是MediaCodec,在工作之余,抽点时间,学习下这个类的使用,做点记录,以供后续查阅。
MediaCodec类可用于访问Android底层的媒体编解码器,它是Android为多媒体支持提供的底层接口的一部分(通常与MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface, 以及AudioTrack一起使用)—这是官网对MediaCodec类的简介。阅读了下API文档,还是水里雾里的,于是设计了一个实验,慢慢尝试MediaCodec类的使用。
我设计的实验非常简单,就是从摄像头拿到数据,然后使用MediaCodec将其压缩为h.264格式,压缩完成后写入一个文件中。
获取摄像头的数据的过程之前在 一文中已经说过了,是的,基于此,我们将拿到的数据进行编码。
先看一下结果吧,因为h.264文件是可以直接播放的:
从摄像头取出图像数据
首先,创建一个TextureView对象或者SurfaceView,二者都可以,这里使用TextureView,它直接在布局文件中使用,然后在Activity中使用findViewById来找到它。
其次,给TextureView设置监听器:textureView.setSurfaceTextureListener(this);Acitivity实现了这个监听器接口。
然后,在onSurfaceTextureAvailable函数中初始化摄像头,并打开,就像下面这样:
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Log.d("jw_liu","onPreviewFrame");
mCamera = Camera.open();
try {
mCamera.setPreviewTexture(surface);
mCamera.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
Log.d("jinwei","l:"+data.length);
if(MediaCodecManager.frame != null){
System.arraycopy(data,0,MediaCodecManager.frame,0,data.length);
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPictureFormat(PixelFormat.JPEG);
parameters.setPreviewFormat(PixelFormat.YCbCr_420_SP);
parameters.setPreviewSize(480, 320);
parameters.set("orientation", "portrait");
parameters.set("rotation", 180);
mCamera.setDisplayOrientation(180);
mCamera.setParameters(parameters);
mCamera.startPreview();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Log.d("jw_liu","onSurfaceTextureSizeChanged");
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
// Log.d("jw_liu","onSurfaceTextureDestroyed");
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// Log.d("jw_liu","onSurfaceTextureUpdated");
}
这个流程非常简单,我们需要关注的是摄像头的配置参数。
我们将摄像头的预览格式配置成 了YCbCr_420_SP,预览的大小为420*320,这两个参数非常重要,因为编码器也需要设置编码图像的大小和图片格式,如果这两者是一致的,那么你就省去了在这之间做转换的繁琐工作。
我们给camera设置了预览的回调函数,这样,当摄像头采集到数据以后,回调函数就会将预览的数据传给你,由此,我们获取到了摄像头的数据。
获取到摄像头的数据以后,我们便可以对其进行编码了。我把这部分逻辑放在一个单独的类MediaCodecManager中。这个类会创建一个MeidaCodec的实例,并使用它来对视屏编码。
编码的流程
首先,我们要获得一个编码器,这没什么好说的,我们可以通过类型来直接获取:
mediaCodec = MediaCodec.createEncoderByType(“video/avc”);
其次,这个编码器我们需要对他进行配置。官方文档中对配置的条目有明确的书名,很多条目都是必须要配置的,配置的不对,调用mediaCodec.start()方法就会失败。我对编码器做的配置如下:
try {
mediaCodec = MediaCodec.createEncoderByType("video/avc");
} catch (IOException e) {
e.printStackTrace();
}
MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", 480, 320);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 125000);
mediaFormat