Android Camera設置顏色格式與Mediacodec編解碼顏色格式

前言

我們在前面學習的內容都是爲了如何通過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開發實現格式轉化;

發佈了38 篇原創文章 · 獲贊 45 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章