Android音視頻—YUV格式深入淺出

文章參考:

  1. 圖文詳解YUV420數(shù)據(jù)格式
  2. YUV主要采樣格式理解
  3. YUV格式詳解
  4. 百度百科和維基百科

概述

本文基于Android音視頻開發(fā)時(shí)需要的,對(duì)基礎(chǔ)視頻流YUV格式的認(rèn)識(shí)。主要描述對(duì)YUV的基本認(rèn)識(shí)YUV格式的區(qū)別,Android音視頻開發(fā)時(shí)常用到的YUV格式處理,轉(zhuǎn)換,顯示方法等。YUV格式的認(rèn)識(shí)很多引用和參考上述博文,做了一些總結(jié),也包括一些個(gè)人的理解,還有許多開發(fā)時(shí)遇到的功能或者問(wèn)題的總結(jié)。

一、什么是YUV?

YUV是一種顏色編碼格式,可以說(shuō)YUV流媒體是原始流數(shù)據(jù),大部分的視頻領(lǐng)域都在使用。他與RGB類似,但RGB更多的用于渲染時(shí),而YUV則用在數(shù)據(jù)傳輸,因?yàn)樗加酶俚念l寬。當(dāng)然,實(shí)時(shí)通訊為了降低帶寬都會(huì)采用H264/H265編碼。從字面意思理解,YUV的含義:Y代表亮度信息(灰度),UV分別代表色彩信息。YUV的常用名稱有許多,如YUV422這是大部分鏡頭出來(lái)的數(shù)據(jù),還有許多(yuv420,yuv444等)。


YUV的 planar和packed的差別?####

這是yuv格式的兩大類

  • planar格式:連續(xù)存儲(chǔ)所有像素點(diǎn)Y,然后是所有像素點(diǎn)U,接著是V

  • packed格式:所有像素點(diǎn)的YUV信息連續(xù)交錯(cuò)存儲(chǔ)

      比如:
          YUV420P:YYYYYYYY UU VV 
          YUV420: YUV YUV YUV 
    

YUV,YCbCr,YPbPr寫法的含義

它們分別代表在不同領(lǐng)域時(shí)使用的名稱,總的大類都是一致的。主流上所說(shuō)的YUV即是YCbCr

  • YCbCr:其中Y是指亮度分量,Cb指藍(lán)色色度分量,而Cr指紅色色度分量
  • YPbPr:他和YCbCr的區(qū)別在于YCbCr是數(shù)字系統(tǒng)的標(biāo)識(shí),YPbPr是模擬系統(tǒng)的標(biāo)識(shí)。

怎么理解YUV后面的三個(gè)數(shù)字呢?

數(shù)字代表yuv信息在像素點(diǎn)中的分布狀況,為了維持人的肉眼觀感,通常需要每個(gè)像素點(diǎn)保存8bit的亮度,每2x2個(gè)點(diǎn)保存至少一個(gè)Cb和Cr值,如下所示(要理解它的排列就要知道,它在量化8bit之后,每個(gè)像素占用大小,可以參考文章:圖文詳解YUV420數(shù)據(jù)格式,它里面的描述圖很好理解):

  1. YUV444采樣,每個(gè)Y對(duì)應(yīng)一組UV,8bit量化,每個(gè)像素占用3個(gè)字節(jié)。

    • 四個(gè)像素點(diǎn): [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
    • 存放碼流: Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3
  2. YUV422采樣,每2個(gè)Y對(duì)應(yīng)一組UV,由兩個(gè)水平方向相鄰的像素組成的宏像素需要占用4字節(jié)內(nèi)存,亮度2個(gè)字節(jié),兩個(gè)色度各1個(gè)字節(jié)。

    • 四個(gè)像素點(diǎn): [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
    • 存放碼流: Y0 U0 Y1 V1 Y2 U2 Y3 V3
  3. YUV411采樣,每4個(gè)Y對(duì)應(yīng)一組UV,由4個(gè)水平方向相鄰的像素組成的宏像素需要占用6字節(jié)內(nèi)存,亮度4個(gè)字節(jié),兩個(gè)色度各1個(gè)字節(jié)。

    • 四個(gè)像素點(diǎn): [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
    • 存放碼流: Y0 U0 Y1 Y2 V2 Y3
  4. YUV420采樣,每4個(gè)Y對(duì)應(yīng)一組UV,每個(gè)由2x2個(gè)2行2列相鄰的像素組成的宏像素需要占用6字節(jié)內(nèi)存。,亮度4個(gè)字節(jié),兩個(gè)色度各1個(gè)字節(jié)。

    • 四個(gè)像素點(diǎn): [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
      [Y5 U5 V5] [Y6 U6 V6] [Y7 U7 V7] [Y8 U8 V8]
    • 存放碼流: Y0 U0 Y1 Y2 U2 Y3 Y5 V5 Y6 Y7 V7 Y8

YUV中stride跨距的含義?

跨距的由來(lái),因?yàn)镃PU存儲(chǔ)和讀取必須是2的密次方,故而很多分辨率的yuv格式通常會(huì)有一個(gè)stride,比如某個(gè)720*536的YUV420SP視頻,它的stride是768,那么中間48就是跨距。通常如果自己去解析,可以通過(guò)偏移裁取,如果采用第三方庫(kù),一般都會(huì)有傳入跨距的值。

  • 在Android中,setPreviewFormat中就有標(biāo)注YV12的跨距計(jì)算方式:
    {@link android.graphics.ImageFormat#YV12}. For camera callback data,
     * it can be assumed that the stride of the Y and UV data is the
     * smallest possible that meets the alignment requirements. That is, if
     * the preview size is <var>width x height</var>, then the following
     * equations describe the buffer index for the beginning of row
     * <var>y</var> for the Y plane and row <var>c</var> for the U and V
     * planes:
     *
     * <pre>{@code
     * yStride   = (int) ceil(width / 16.0) * 16;
     * uvStride  = (int) ceil( (yStride / 2) / 16.0) * 16;
     * ySize     = yStride * height;
     * uvSize    = uvStride * height / 2;
     * yRowIndex = yStride * y;
     * uRowIndex = ySize + uvSize + uvStride * c;
     * vRowIndex = ySize + uvStride * c;
     * size      = ySize + uvSize * 2;
     * }

二、一些常見YUV格式的區(qū)別

1. YUV422—包含如:YUYV、UYVY、YUV422P

YUV422,大多數(shù)的Android機(jī) sensor出來(lái)的視頻流都是這個(gè)格式

  • YUYV:

    image
  • YVYU:

    image
  • YUV422P:2個(gè)Y對(duì)應(yīng)一對(duì)UV,即Y00 Y01對(duì)應(yīng)U00 V00

    image

2. YUV420—包含如:YV12,YU12、NV12、NV21、YUV420SP、I420

在Android Camera框架中,setPreviewFormat中可傳入的格式,API給出的2個(gè)可供選擇的格式分別是ImageFormat.NV21和ImageFormat.YV12

  • YV12YU12都屬于YUV420p,其中Y\U\V分別對(duì)應(yīng)一個(gè)plane,區(qū)別在于UV的位置對(duì)調(diào),下面是YU12的存儲(chǔ)示意圖:

    image
  • NV12和NV21,其中NV12就是我們Android常見的YUV420SP,他們不像上一個(gè)YV12,有3個(gè)plane,而是由Y和UV分別兩個(gè)Plane組成,UV交替排列,U在前的是NV12,V在前為NV21.

    image
  • I420:或表示為IYUV,數(shù)碼攝像機(jī)專用表示法.

    • 一般來(lái)說(shuō),直接采集到的視頻數(shù)據(jù)是RGB24的格式,RGB24一幀的大小size=width×heigth×3 Byte,RGB32的size=width×heigth×4 Byte,如果是I420(即YUV標(biāo)準(zhǔn)格式4:2:0)的數(shù)據(jù)量是 size=width×heigth×1.5 Byte。 在采集到RGB24數(shù)據(jù)后,需要對(duì)這個(gè)格式的數(shù)據(jù)進(jìn)行第一次壓縮。即將圖像的顏色空間由RGB24轉(zhuǎn)化為IYUV。因?yàn)?,X264在進(jìn)行編碼的時(shí)候需要標(biāo)準(zhǔn)的YUV(4:2:0)。但是這里需要注意的是,雖然YV12也是(4:2:0),但是YV12和I420的卻是不同的,在存儲(chǔ)空間上面有些區(qū)別。如下:
    • YV12 : 亮度(行×列) + V(行×列/4) + U(行×列/4)
    • I420 : 亮度(行×列) +U(行×列/4) + V(行×列/4)
      可以看出,YV12和I420基本上是一樣的,就是UV的順序不同。(摘自百度百科I420)

三、Android中常用YUV格式認(rèn)識(shí)和處理

1. android Camera中用到的yuv格式。

  • setPreviewFormat中可以指定通常是NV12和YV12

    • YV12toYUV420SP:

        public static byte[] YV12toYUV420SP(final byte[] input, final byte[] output, final int width, final int height) {
            final int frameSize = width * height;
            final int qFrameSize = frameSize / 4;
            System.arraycopy(input, 0, output, 0, frameSize); // Y
            for (int i = 0; i < qFrameSize; i++) {
                output[frameSize + i * 2 + 1] = input[frameSize + i + qFrameSize]; // Cr (V)
                output[frameSize + i * 2] = input[frameSize + i]; // Cb (U)
            }
            return output;
        }
      
    • rotateYUV240SP:

        public static void rotateYUV240SP(byte[] src, byte[] des, int width, int height) {
            int wh = width * height;
            //旋轉(zhuǎn)Y
            int k = 0;
            for (int i = 0; i < width; i++) {
                for (int j = 0; j < height; j++) {
                    des[k] = src[width * j + i];
                    k++;
                }
            }
            for (int i = 0; i < width / 2; i++) {
                for (int j = 0; j < height / 2; j++) {
                    des[k] = src[wh + width / 2 * j + i];
                    des[k + width * height / 4] = src[wh * 5 / 4 + width / 2 * j + i];
                    k++;
                }
            }
        }
      

2. Android硬編碼對(duì)YUV格式的要求

  • 部分Android硬件平臺(tái)對(duì)stride也有要求,比如MTK

      // Note: the stride of resolution must be set as 16x for hard encoding with some chip like MTK
      // Since Y component is quadruple size as U and V component, the stride must be set as 32x
      if (!useSoftEncoder && vOutWidth % 32 != 0 || vOutHeight % 32 != 0) {
          if (vmci.getName().contains("MTK")) {
              throw new AssertionError("MTK encoding revolution stride must be 32x");
          }
      }
    
  • AndroidH264銀編碼時(shí)傳入的MediaFormat.KEY_COLOR_FORMAT注意事項(xiàng)

    • 首先時(shí)寫法,不同的CPU支持編碼的yuv格式不同,需要主動(dòng)查詢符合支持的列表

        // choose the video encoder by name.
        private MediaCodecInfo chooseVideoEncoder(String name) {
            int nbCodecs = MediaCodecList.getCodecCount();
            for (int i = 0; i < nbCodecs; i++) {
                MediaCodecInfo mci = MediaCodecList.getCodecInfoAt(i);
                if (!mci.isEncoder()) {
                    continue;
                }
                String[] types = mci.getSupportedTypes();
                for (int j = 0; j < types.length; j++) {
                    if (types[j].equalsIgnoreCase(VCODEC)) {
                        LogUtils.i(TAG, String.format("vencoder %s types: %s", mci.getName(), types[j]));
                        if (name == null) {
                            return mci;
                        }
                        if (mci.getName().contains(name)) {
                            return mci;
                        }
                    }
                }
            }
            return null;
        }
      

3. Android MediaFormat.KEY_COLOR_FORMAT代表的含義

通過(guò)查看android.media.MediaCodecInfo.CodecCapabilities,常用的YUV ColorFormat項(xiàng)如下,我們能發(fā)現(xiàn)大部分是隱藏API,反而推薦我們?nèi)ナ褂肅OLOR_FormatYUV420Flexible, COLOR_FormatYUV422Flexible等,接下來(lái)解釋下為什么會(huì)這樣

/** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */
    public static final int COLOR_FormatYUV411Planar            = 17;
    /** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */
    public static final int COLOR_FormatYUV411PackedPlanar      = 18;
    /** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */
    public static final int COLOR_FormatYUV420Planar            = 19;
    /** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */
    public static final int COLOR_FormatYUV420PackedPlanar      = 20;
    /** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */
    public static final int COLOR_FormatYUV420SemiPlanar        = 21;

    /** @deprecated Use {@link #COLOR_FormatYUV422Flexible}. */
    public static final int COLOR_FormatYUV422Planar            = 22;
    /** @deprecated Use {@link #COLOR_FormatYUV422Flexible}. */
    public static final int COLOR_FormatYUV422PackedPlanar      = 23;
    /** @deprecated Use {@link #COLOR_FormatYUV422Flexible}. */
    public static final int COLOR_FormatYUV422SemiPlanar        = 24;

    /** @deprecated Use {@link #COLOR_FormatYUV422Flexible}. */
    public static final int COLOR_FormatYCbYCr                  = 25;
    /** @deprecated Use {@link #COLOR_FormatYUV422Flexible}. */
    public static final int COLOR_FormatYCrYCb                  = 26;
    /** @deprecated Use {@link #COLOR_FormatYUV422Flexible}. */
    public static final int COLOR_FormatCbYCrY                  = 27;
    /** @deprecated Use {@link #COLOR_FormatYUV422Flexible}. */
    public static final int COLOR_FormatCrYCbY                  = 28;

    /** @deprecated Use {@link #COLOR_FormatYUV444Flexible}. */
    public static final int COLOR_FormatYUV444Interleaved       = 29;
  • COLOR_FormatYUV420Flexible

首先看注解:

    /**
     * Flexible 12 bits per pixel, subsampled YUV color format with 8-bit chroma and luma
     * components.
     * <p>
     * Chroma planes are subsampled by 2 both horizontally and vertically.
     * Use this format with {@link Image}.
     * This format corresponds to {@link android.graphics.ImageFormat#YUV_420_888},
     * and can represent the {@link #COLOR_FormatYUV411Planar},
     * {@link #COLOR_FormatYUV411PackedPlanar}, {@link #COLOR_FormatYUV420Planar},
     * {@link #COLOR_FormatYUV420PackedPlanar}, {@link #COLOR_FormatYUV420SemiPlanar}
     * and {@link #COLOR_FormatYUV420PackedSemiPlanar} formats.
     *
     * @see Image#getFormat
     */
    
    它大體意思是,哥們,我是個(gè)萬(wàn)能鑰匙,對(duì)應(yīng)了ImageFormat中的YUV_420_888,可以代替
    COLOR_FormatYUV411PackedPlanar,COLOR_FormatYUV420Planar,COLOR_FormatYUV420PackedPlanar
    以及COLOR_FormatYUV420SemiPlanar,COLOR_FormatYUV420PackedSemiPlanar使用

好吧,通常編碼時(shí)查看支持列表有它都可以傳入,那我們來(lái)看看它可替代的這些format的含義:

  1. COLOR_FormatYUV411PackedPlanar: YUV411,每4個(gè)連續(xù)的Y分量公用一個(gè)UV分量,并且Y分量和UV分量打包到同一個(gè)平面,用的不多。
  2. COLOR_FormatYUV420Planar:YUV420P,每2x2像素公用一個(gè)UV空間,Y分量空間-->U分量平面-->V分量平面
  3. COLOR_FormatYUV420PackedPlanar:YUV420 packet每2X2像素公用一個(gè)UV分量,并且將YUV打包到一個(gè)平面
  4. COLOR_FormatYUV420SemiPlanar:YUV420SP,即上述的NV12
  5. COLOR_FormatYUV420PackedSemiPlanar:Y分量空間-->V分量平面-->U分量平面,與COLOR_FormatYUV420Planar uv相反
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容