前言
我们在前面学习的内容都是为了如何通过Camera预览获得录制的视频,然后把音频和视频上传到服务器或把音频与视频合成本地文件保存打好基础;但我们在还需要学习Camera预览的格式以及MediaCodec编解码的颜色格式的联系,否则我们在合成视频、上传到服务器的视频播放时会有颜色异常;比如:颜色不对、彩色变成黑白等等。
知识结构
1、Camera预览格式
Camera预览格式:NV21、YV12
通过以下代码获取手机支持的camera格式:
List<Integer> previewFormats = mCamera.getParameters().getSupportedPreviewFormats();
基本上就是上面的两种
2、MediaCodec编解码颜色格式
手机MediaCodec编解码颜色格式一般为:YUV420Planar/I420SemiPlanner 它们中的一个;
我们可以通过以下代码获得手机支持的MediaCodec编解码颜色格式:
package com.example.inflaterviewtest;
import java.util.Arrays;
import android.annotation.SuppressLint;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.util.Log;
public class MyMediaCodec {
private static final String TAG = "MyMediaCodec";
@SuppressLint("NewApi")
public void getMediaCodecList(){
//获取解码器列表
int numCodecs = MediaCodecList.getCodecCount();
MediaCodecInfo codecInfo = null;
for(int i = 0; i < numCodecs && codecInfo == null ; i++){
MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
if(!info.isEncoder()){
continue;
}
String[] types = info.getSupportedTypes();
boolean found = false;
//轮训所要的解码器
for(int j=0; j<types.length && !found; j++){
if(types[j].equals("video/avc")){
System.out.println("found");
found = true;
}
}
if(!found){
continue;
}
codecInfo = info;
}
Log.d(TAG, "found"+codecInfo.getName() + "supporting" +" video/avc");
//检查所支持的colorspace
int colorFormat = 0;
MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType("video/avc");
System.out.println("length-"+capabilities.colorFormats.length + "==" + Arrays.toString(capabilities.colorFormats));
for(int i = 0; i < capabilities.colorFormats.length && colorFormat == 0 ; i++){
int format = capabilities.colorFormats[i];
switch (format) {
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
System.out.println("-");
break;
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
System.out.println("-");
break;
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
System.out.println("-");
break;
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
System.out.println("-");
break;
case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
colorFormat = format;
System.out.println("-");
break;
default:
Log.d(TAG, "Skipping unsupported color format "+format);
break;
}
}
Log.d(TAG, "color format "+colorFormat);
}
}
3、YUV420的颜色格式
YUV420 数据在内存中的长度是 width * hight * 3 / 2
YUV420P:YV12、I420
YUV420SP:NV12、NV21
YUV420P 和 YUV420的区别 :
它们在存储格式上有区别?
YUV420P:yyyyyyyy uuuuuuuu vvvvv yuv420: yuv yuv yuv
YUV420P,Y,U,V三个分量都是平面格式,分为I420和YV12。I420格式和YV12格式的不同处在U平面和V平面的位置不同。在I420格式中,U平面紧跟在Y平面之后,然后才是V平面(即:YUV);但YV12则是相反(即:YVU)。
YUV420SP, Y分量平面格式,UV打包格式, 即NV12。 NV12与NV21类似,U 和 V 交错排列,不同在于UV顺序。
I420: YYYYYYYY UU VV =>YUV420P
YV12: YYYYYYYY VV UU =>YUV420P
NV12: YYYYYYYY UVUV =>YUV420SP
NV21: YYYYYYYY VUVU =>YUV420SP
YUV420格式详细介绍请看:图文详解YUV420数据格式
4、颜色格式转化
通过上面的学习我们了解了,Camera预览的格式为NV21、YV12;MediaCodec的编解码格式为:YUV420Planar/I420SemiPlanner ;由于他们在存储格式的不同,会导致视频编解码后颜色异常;
例如:Camera预览设置为:NV21 MediaCodec的编解码格式为 : YUV420Planar
由于NV21、YUV420Planar 的存储方式不同,导致视频编解码后颜色异常;所以我们必须进行转码操作;
如:nv21ToI420
public byte[] nv21ToI420(byte[] data, int width, int height) {
byte[] ret = globalBuffer;
int total = width * height;
ByteBuffer bufferY = ByteBuffer.wrap(ret, 0, total);
ByteBuffer bufferU = ByteBuffer.wrap(ret, total, total / 4);
ByteBuffer bufferV = ByteBuffer.wrap(ret, total + total / 4, total / 4);
bufferY.put(data, 0, total);
for (int i=total; i<data.length; i+=2) {
bufferV.put(data[i]);
bufferU.put(data[i+1]);
}
return ret;
}
其他格式的转化不再列出;
注意:
由于格式的转化比较耗性能的,所以我们最好使用Jni开发实现格式转化;