最近在做直播推流方面的工作,因为需要添加美白,滤镜,AR贴图等效果。所以不能简单的使用SufaceView加Camera的方式进行数据的采集,而是需要对Camera采集到的YUV数据进行相关的处理之后然后再进行推流的操作,YUV数据的返回接口。
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
}
当然,美白,滤镜,AR贴图等效果采用的是第三方的SDK了。除此之外,因为Android摄像头采集的数据都是有一定的旋转的。一般前置摄像头有270度的旋转,后置摄像头有90的旋转。所以要对YUV数据进行一定旋转操作,同时对于前置摄像头的数据还要进行镜像翻转的操作。网上一般比较多的算法是关于旋转的
private byte[] rotateYUVDegree90(byte[] data, int imageWidth, int imageHeight) {
byte[] yuv = new byte[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;
}
private byte[] rotateYUVDegree270(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;
}
上述两个算法分别用于90度旋转(后置摄像头)和270度旋转(前置摄像头),但是对于前置摄像头的YUV数据是需要镜像的,参照上面的算法,实现了前置摄像头的镜像算法。
private byte[] rotateYUVDegree270AndMirror(byte[] data, int imageWidth, int imageHeight) {
byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2];
// Rotate and mirror the Y luma
int i = 0;
int maxY = 0;
for (int x = imageWidth - 1; x >= 0; x--) {
maxY = imageWidth * (imageHeight - 1) + x * 2;
for (int y = 0; y < imageHeight; y++) {
yuv[i] = data[maxY - (y * imageWidth + x)];
i++;
}
}
// Rotate and mirror the U and V color components
int uvSize = imageWidth * imageHeight;
i = uvSize;
int maxUV = 0;
for (int x = imageWidth - 1; x > 0; x = x - 2) {
maxUV = imageWidth * (imageHeight / 2 - 1) + x * 2 + uvSize;
for (int y = 0; y < imageHeight / 2; y++) {
yuv[i] = data[maxUV - 2 - (y * imageWidth + x - 1)];
i++;
yuv[i] = data[maxUV - (y * imageWidth + x)];
i++;
}
}
return yuv;
}
至于更多关于YUV和推流的知识,目前我还不是很了解。这篇文章也主要是分享这三个算法。
网友评论
当然如果能直接使用opengl不经过yuv效率上更好
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
Log.e(TAG, "预览帧frame");
rotateYUVDegree90(data,640,480);
camera.addCallbackBuffer(gBuffer);
}
private int setCameraDisplayOrientation(int cameraId) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
int rotation = mContext.getCurrentActivity().getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
//前置摄像头需要镜像,转化后进行设置
mCamera.setDisplayOrientation((360 - result) % 360);
} else {
result = (info.orientation - degrees + 360) % 360;
//后置摄像头直接进行显示
mCamera.setDisplayOrientation(result);
}
return result;
}
这里面的几个方法是对摄像头获取的数据进行使用的