方式1、 直接保存視頻預覽的數據幀
在預覽方法中imgData數據就是視頻幀
android默認的視頻採集格式是NV21,(屬於YUV420)
在onPreviewFrame中傳進來的byte[] data即爲NV21格式。
NV21 的存儲格式是,以4 X 4 圖片爲例子
佔用內存爲 4 X 4 X 3 / 2 = 24 個字節
Y Y Y Y
Y Y Y Y
Y Y Y Y
Y Y Y Y
V U V U
V U V U
NV12 的存儲格式是,以4 X 4 圖片爲例子
Y Y Y Y
Y Y Y Y
Y Y Y Y
Y Y Y Y
U V U V
U V U V
————————————————
public void onPreviewFrame(final byte[] imgData, final Camera camera) {
int width = camera.getParameters().getPreviewSize().width;//獲取視頻的寬度
int height = camera.getParameters().getPreviewSize().height;//獲取照片的高度
//得到寬和高 就知道了數組大小
//imgdata數組長度和 width height 關係 => imgdata.length = width * height *3/2
//byte[] outdata;
//outdata = rotateYUV420Degree180(data, width, height); //進行180旋轉
//outdata = rotateYUV420Degree90(data, width, height); //進行90旋轉
//outdata = rotateYUV420Degree270(data, width, height); //進行270旋轉
//如果保存數據幀 需要將nv21進行轉換
YuvImage yuvImage = new YuvImage(outdata, camera.getParameters()
.getPreviewFormat(), width, height, null);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
yuvImage.compressToJpeg(new Rect(0, 0, width, height), 80,
byteArrayOutputStream);
byte[] jpegData = byteArrayOutputStream.toByteArray();
File file = new File("/sdcard"+ "/"+i+".png");
i++;
FileOutputStream fos = new FileOutputStream(file);
fos.write(jpegData, 0, jpegData.length);
fos.flush();
fos.close();
}
不同的android手機 ,可能存在多種情況,比如預覽正常,保存的數據幀出現了90度 180度 270度旋轉。
對NV21進行順時針旋轉90度,180度和270度算法。
旋轉90度
privatebyte[] rotateYUV420Degree90(byte[] data, int imageWidth, int imageHeight){
byte[] yuv =newbyte[imageWidth*imageHeight*3/2];
// Rotate the Y luma
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++;}
}
// Rotate the U and V color components
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;}
用法:
//clockwise90:IplImage.create(480, 640) && new NewFFmpegFrameRecorder(480, 640)順時針旋轉90度,
將IplImage.create和new NewFFmpegFrameRecorder處源圖像的寬高640x480對換成旋轉後的真實寬高480x640
byte[] outdata;
outdata = rotateYUV420Degree90(data, 640, 480);
旋轉180度
privatebyte[] rotateYUV420Degree180(byte[] data, int imageWidth, int imageHeight){
byte[] yuv =newbyte[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;}
用法:
//clockwise180:IplImage.create(640, 480) && new NewFFmpegFrameRecorder(640, 480)上述2處無需改動
byte[] outdata;
outdata = rotateYUV420Degree180(data, 640, 480);
旋轉270度
private byte[] rotateYUV420Degree270(byte[] data, int imageWidth, int imageHeight){
byte[] yuv =new byte[imageWidth*imageHeight*3/2];
// Rotate the Y luma
int i =0;
for(int x = imageWidth-1;x >=0;x--){
for(int y =0;y < imageHeight;y++){
yuv[i]= data[y*imageWidth+x];
i++;
}
}// Rotate the U and V color components
i = imageWidth*imageHeight;
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-1)];
i++;
yuv[i]= data[(imageWidth*imageHeight)+(y*imageWidth)+x];
i++;
}
}
return yuv;
}
用法:
//clockwise270:IplImage.create(480, 640) && new NewFFmpegFrameRecorder(480, 640),設置與旋轉90度相同
byte[] outdata;
outdata = rotateYUV420Degree270(data, 640, 480);
裁剪NV21
publicbyte[] cropYUV420(byte[] data,int imageW,int imageH,int newImageH){int cropH;int i,j,count,tmp;byte[] yuv =newbyte[imageW*newImageH*3/2];
cropH =(imageH - newImageH)/2;
count =0;for(j=cropH;j<cropH+newImageH;j++){for(i=0;i<imageW;i++){
yuv[count++]= data[j*imageW+i];}}
//Cr Cb
tmp = imageH+cropH/2;for(j=tmp;j<tmp + newImageH/2;j++){for(i=0;i<imageW;i++){
yuv[count++]= data[j*imageW+i];}}
return yuv;}
用法:
將640x480裁剪成480x480時用法如下:
在onPreviewFrame(byte[] data, Camera camera)中調用
byte[] outdata2;
byte[] outdata;
outdata2 = rotateYUV420Degree90(data, 640, 480);//將640x480旋轉成480x640
outdata = cropYUV420(outdata2, 480, 640,480);//將480x640裁剪成480x480
在initVideoRecorder中
videoRecorder = new NewFFmpegFrameRecorder(strVideoPath, 480, 480, 1);
在handleSurfaceChanged中
yuvIplImage = IplImage.create(480, 480, IPL_DEPTH_8U, 2);
————————————————
版權聲明:本文爲CSDN博主「github.com/starRTC」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/elesos/article/details/53220309
這樣 就可以獲取到數據幀並完成旋轉保存或者裁剪保存。
不要嘗試將文件保存到本地之後,再對照片進行讀取旋轉,本人嘗試過通過
android.graphics.Matrix matrix = new android.graphics.Matrix(); matrix.setRotate(degrees, bitmap.getWidth() / 2, bitmap.getHeight() / 2); Bitmap bmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); bitmap.recycle();
方式旋轉,但是Bitmap 是獲取不到的。而且此方式不是常規做法
方式2、 使用照相機拍照功能
//照相機拍照後的回調
private final Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
@Override
public void onPictureTaken(final byte[] data, Camera camera) {
System.out.println(Thread.currentThread());
new Thread(new Runnable() {
@Override
public void run() {
File file = new File("/sdcard"+ "/facenew"+i+".png");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
fos.write(data, 0, data.length);
fos.flush();
fos.close();
i++;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
camera.startPreview();//重新開始預覽
}
};
@Override
public void onPreviewFrame(final byte[] imgData, final Camera camera) {
camera.takePicture(null, null, mPictureCallback);//執行拍照功能,拍照成功後執行回調
}
網上有網友提到 預覽是正向的,保存照片是旋轉的,可以按照方式1進行照片旋轉。
兩種方式的對比:
相機拍照功能 照片出現模糊的概率會小很多,但是每次執行takePicture的時候會推出預覽。保存之後重新開始預覽,會導致頁面出現頓卡。
直接保存數據幀流暢度會好很多,不過照片的清晰度不如拍照的好 ,(實測)原因不清楚