標籤:xss vfs 擷取 amr nal err aar oom 提取
這周做的一個android的camera開發,需要擷取到視訊框架資料,並且需要是nv21格式的byte數組,並且視訊框架的映像需要是正方向的。和android相機打過交道的都清楚,android的camera擷取到的圖片都是橫向的,因此,需要進行旋轉,對於映像的旋轉,其實bitmap這個類已經可以幫我們實現了,但是前提是你需要將你的資料格式轉換為Bitmap才行,但是我們如果通過setPreviewCallback來擷取視訊框架,擷取到的圖片都是nv21,如果裝換為bitmap後,又很難的轉換為nv21格式的資料。因此則需要面臨一個問題,如歌旋轉nv21格式的byte數組,首先先來講下nv21格式,講到nv21就也要說說yuv240和nv12:
NV12、NV21(屬於YUV420)
NV12和NV21屬於YUV420格式,是一種two-plane模式,即Y和UV分為兩個Plane,但是UV(CbCr)為交錯儲存,而不是分為三個plane。其提取方式與上一種類似,即Y‘00、Y‘01、Y‘10、Y‘11共用Cr00、Cb00
YUV420 planar資料存放區, 以720×488大小圖象YUV420 planar為例,
其儲存格式是: 共大小為(720×480×3>>1)位元組,
分為三個部分: Y分量: (720×480)個位元組 U(Cb)分量: (720×480>>2)個位元組 V(Cr)分量: (720×480>>2)個位元組
三個部分內部均是行優先儲存,三個部分之間是Y,U,V 順序儲存。
即YUV資料的0--720×480位元組是Y分量值, 720×480--720×480×5/4位元組是U分量 720×480×5/4 --720×480×3/2位元組是V分量。
4 :2: 2 和4:2:0 轉換:
最簡單的方式:
YUV4:2:2 ---> YUV4:2:0 Y不變,將U和V訊號值在行(垂直方向)在進行一次隔行抽樣。 YUV4:2:0 ---> YUV4:2:2 Y不變,將U和V訊號值的每一行分別拷貝一份形成連續兩行資料。
在YUV420中,一個像素點對應一個Y,一個4X4的小方塊對應一個U和V。對於所有 YUV420映像,它們的Y值排列是完全相同的,因為只有Y的映像就是灰階映像。YUV420sp與YUV420p的資料格式它們的UV排列在原理上是完 全不同的。420p它是先把U存放完後,再存放V,也就是說UV它們是連續的。而420sp它是UV、UV這樣交替存放的。(見) 有了上面的理論,我就可以準確的計算出一個YUV420在記憶體中存放的大小。 width * hight =Y(總和) U = Y / 4 V = Y / 4
所以YUV420 資料在記憶體中的長度是 width * hight * 3 / 2,
假設一個解析度為8X4的YUV映像,它們的格式如:
圖:YUV420sp格式
圖:YUV420p資料格式如
具體的旋轉代碼如下:
public static byte[] rotateYUV420Degree90(byte[] data, int imageWidth, int imageHeight) { byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2]; int i = 0; for (int x = 0; x < imageWidth; x++) { for (int y = imageHeight - 1; y >= 0; y--) { yuv[i] = data[y * imageWidth + x]; i++; } } i = imageWidth * imageHeight * 3 / 2 - 1; for (int x = imageWidth - 1; x > 0; x = x - 2) { for (int y = 0; y < imageHeight / 2; y++) { yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + x]; i--; yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + (x - 1)]; i--; } } return yuv;}private static byte[] rotateYUV420Degree180(byte[] data, int imageWidth, int imageHeight) { byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2]; int i = 0; int count = 0; for (i = imageWidth * imageHeight - 1; i >= 0; i--) { yuv[count] = data[i]; count++; } i = imageWidth * imageHeight * 3 / 2 - 1; for (i = imageWidth * imageHeight * 3 / 2 - 1; i >= imageWidth * imageHeight; i -= 2) { yuv[count++] = data[i - 1]; yuv[count++] = data[i]; } return yuv;}public static byte[] rotateYUV420Degree270(byte[] data, int imageWidth, int imageHeight) { byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2]; int nWidth = 0, nHeight = 0; int wh = 0; int uvHeight = 0; if (imageWidth != nWidth || imageHeight != nHeight) { nWidth = imageWidth; nHeight = imageHeight; wh = imageWidth * imageHeight; uvHeight = imageHeight >> 1;// uvHeight = height / 2 } int k = 0; for (int i = 0; i < imageWidth; i++) { int nPos = 0; for (int j = 0; j < imageHeight; j++) { yuv[k] = data[nPos + i]; k++; nPos += imageWidth; } } for (int i = 0; i < imageWidth; i += 2) { int nPos = wh; for (int j = 0; j < uvHeight; j++) { yuv[k] = data[nPos + i]; yuv[k + 1] = data[nPos + i + 1]; k += 2; nPos += imageWidth; } } return rotateYuv420Degree180(rotateYuv420Degree90(data, imageWidth, imageHeight), imageWidth, imageHeight);}
然後,如果你想要查看旋轉後的映像,則通過以下代碼即可:
YuvImage yuvimage = new YuvImage( data, ImageFormat.NV21, width, height, null); baos = new ByteArrayOutputStream(); yuvimage.compressToJpeg(new Rect(0, 0, width, height), 100, baos);// 80--JPG圖片的品質[0-100],100最高 rawImage = baos.toByteArray(); //將rawImage轉換成bitmap BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.RGB_565; bitmap = BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length, options);
在這裡邊我們需要注意的是我們的寬和高,需要用轉移後的,不可使用轉移前的,否則會出現看到的圖片有重影的現象。
android camera之nv21旋轉