【Android音視訊】Android—YUV格式深入淺出
Android音視訊—YUV格式深入淺出
文章參考:
概述
本文基於Android音視訊開發時需要的,對基礎視訊流YUV格式的認識。主要描述對YUV的基本認識,YUV格式的區別,Android音視訊開發時常用到的YUV格式處理,轉換,顯示方法等。YUV格式的認識很多引用和參考上述博文,做了一些總結,也包括一些個人的理解,還有許多開發時遇到的功能或者問題的總結。
一、什麼是YUV?
YUV是一種顏色編碼格式,可以說YUV流媒體是原始流資料,大部分的視訊領域都在使用。他與RGB類似,但RGB更多的用於渲染時,而YUV則用在資料傳輸,因為它佔用更少的頻寬。當然,實時通訊為了降低頻寬都會採用H264/H265編碼。從字面意思理解,YUV的含義:Y代表亮度資訊(灰度),UV分別代表色彩信息。YUV的常用名稱有許多,如YUV422這是大部分鏡頭出來的資料,還有許多(yuv420,yuv444等)。
YUV的 planar和packed的差別?
這是yuv格式的兩大類
-
planar格式:連續儲存所有畫素點Y,然後是所有畫素點U,接著是V
-
packed格式:所有畫素點的YUV資訊連續交錯儲存
比如: YUV420P:YYYYYYYY UU VV YUV420: YUV YUV YUV
YUV,YCbCr,YPbPr寫法的含義
它們分別代表在不同領域時使用的名稱,總的大類都是一致的。主流上所說的YUV即是YCbCr
- YCbCr:其中Y是指亮度分量,Cb指藍色色度分量,而Cr指紅色色度分量
- YPbPr:他和YCbCr的區別在於YCbCr是數字系統的標識,YPbPr是模擬系統的標識。
怎麼理解YUV後面的三個數字呢?
數字代表yuv資訊在畫素點中的分佈狀況,為了維持人的肉眼觀感,通常需要每個畫素點儲存8bit的亮度,每2x2個點儲存至少一個Cb和Cr值,如下所示(要理解它的排列就要知道,它在量化8bit之後,每個畫素佔用大小,可以參考文章:圖文詳解YUV420資料格式,它裡面的描述圖很好理解):
-
YUV444取樣,每個Y對應一組UV,8bit量化,每個畫素佔用3個位元組。
- 四個畫素點: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
- 存放碼流: Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3
-
YUV422取樣,每2個Y對應一組UV,由兩個水平方向相鄰的畫素組成的巨集畫素需要佔用4位元組記憶體,亮度2個位元組,兩個色度各1個位元組。
- 四個畫素點: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
- 存放碼流: Y0 U0 Y1 V1 Y2 U2 Y3 V3
-
YUV411取樣,每4個Y對應一組UV,由4個水平方向相鄰的畫素組成的巨集畫素需要佔用6位元組記憶體,亮度4個位元組,兩個色度各1個位元組。
- 四個畫素點: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
- 存放碼流: Y0 U0 Y1 Y2 V2 Y3
-
YUV420取樣,每4個Y對應一組UV,每個由2x2個2行2列相鄰的畫素組成的巨集畫素需要佔用6位元組記憶體。,亮度4個位元組,兩個色度各1個位元組。
- 四個畫素點: [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
- 四個畫素點: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3]
YUV中stride跨距的含義?
跨距的由來,因為CPU儲存和讀取必須是2的密次方,故而很多解析度的yuv格式通常會有一個stride,比如某個720*536的YUV420SP視訊,它的stride是768,那麼中間48就是跨距。通常如果自己去解析,可以通過偏移裁取,如果採用第三方庫,一般都會有傳入跨距的值。
-
在Android中,setPreviewFormat中就有標註YV12的跨距計算方式:
{@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格式的區別
1. YUV422—包含如:YUYV、UYVY、YUV422P
YUV422,大多數的Android機 sensor出來的視訊流都是這個格式
-
YUYV:
-
YVYU:
-
YUV422P:2個Y對應一對UV,即Y00 Y01對應U00 V00
2. YUV420—包含如:YV12,YU12、NV12、NV21、YUV420SP、I420
在Android Camera框架中,setPreviewFormat中可傳入的格式,API給出的2個可供選擇的格式分別是ImageFormat.NV21和ImageFormat.YV12
-
YV12和YU12都屬於YUV420p,其中Y\U\V分別對應一個plane,區別在於UV的位置對調,下面是YU12的儲存示意圖:
-
NV12和NV21,其中NV12就是我們Android常見的YUV420SP,他們不像上一個YV12,有3個plane,而是由Y和UV分別兩個Plane組成,UV交替排列,U在前的是NV12,V在前為NV21.
-
I420:或表示為IYUV,數碼攝像機專用表示法.
- 一般來說,直接採集到的視訊資料是RGB24的格式,RGB24一幀的大小size=width×heigth×3 Byte,RGB32的size=width×heigth×4 Byte,如果是I420(即YUV標準格式4:2:0)的資料量是 size=width×heigth×1.5 Byte。 在採集到RGB24資料後,需要對這個格式的資料進行第一次壓縮。即將影象的顏色空間由RGB24轉化為IYUV。因為,X264在進行編碼的時候需要標準的YUV(4:2:0)。但是這裡需要注意的是,雖然YV12也是(4:2:0),但是YV12和I420的卻是不同的,在儲存空間上面有些區別。如下:
- YV12 : 亮度(行×列) + V(行×列/4) + U(行×列/4)
- I420 : 亮度(行×列) +U(行×列/4) + V(行×列/4)
可以看出,YV12和I420基本上是一樣的,就是UV的順序不同。(摘自百度百科I420)
三、Android中常用YUV格式認識和處理
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; //旋轉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硬編碼對YUV格式的要求
-
部分Android硬體平臺對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銀編碼時傳入的MediaFormat.KEY_COLOR_FORMAT注意事項
-
首先時寫法,不同的CPU支援編碼的yuv格式不同,需要主動查詢符合支援的列表
// 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代表的含義
通過檢視android.media.MediaCodecInfo.CodecCapabilities,常用的YUV ColorFormat項如下,我們能發現大部分是隱藏API,反而推薦我們去使用COLOR_FormatYUV420Flexible, COLOR_FormatYUV422Flexible等,接下來解釋下為什麼會這樣
/** @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
*/
它大體意思是,哥們,我是個萬能鑰匙,對應了ImageFormat中的YUV_420_888,可以代替
COLOR_FormatYUV411PackedPlanar,COLOR_FormatYUV420Planar,COLOR_FormatYUV420PackedPlanar
以及COLOR_FormatYUV420SemiPlanar,COLOR_FormatYUV420PackedSemiPlanar使用
好吧,通常編碼時檢視支援列表有它都可以傳入,那我們來看看它可替代的這些format的含義:
- COLOR_FormatYUV411PackedPlanar: YUV411,每4個連續的Y分量公用一個UV分量,並且Y分量和UV分量打包到同一個平面,用的不多。
- COLOR_FormatYUV420Planar:YUV420P,每2x2畫素公用一個UV空間,Y分量空間–>U分量平面–>V分量平面
- COLOR_FormatYUV420PackedPlanar:YUV420 packet每2X2畫素公用一個UV分量,並且將YUV打包到一個平面
- COLOR_FormatYUV420SemiPlanar:YUV420SP,即上述的NV12
- COLOR_FormatYUV420PackedSemiPlanar:Y分量空間–>V分量平面–>U分量平面,與COLOR_FormatYUV420Planar uv相反