我正在使用纹理向Unity3d发送Android相机预览,并且它已被拉伸。预览不会渲染到android中的任何视图,而是直接渲染到纹理:
setPreviewTexture(texture);
然后,将字节发送到Unity3d并在每个onPreviewFrame的屏幕上绘制。
相机预览尺寸和纹理尺寸设置为1024x768,Unity3d中的纹理容器尺寸相同。该设备的分辨率为960x540,因此它的比例不同。我正在为相机选择支持的分辨率,原始尺寸的比例为4:3,因此不应该拉伸。 似乎android正在渲染纹理只能实际渲染到屏幕的部分 - 它需要16:9的图像并将其渲染为4:3纹理。 如果我错了,请纠正我,但在这个例子中它似乎如何工作。 这是一些代码:
public int startCamera(int idx, int width, int height) {
nativeTexturePointer = createExternalTexture();
texture = new SurfaceTexture(nativeTexturePointer);
mCamera = Camera.open(idx);
setupCamera(width, height);
try {
mCamera.setPreviewTexture(texture);
mCamera.setPreviewCallback(this);
mCamera.startPreview();
Log.i("Unity", "JAVA: camera started");
} catch (IOException ioe) {
Log.w("Unity", "JAVA: CAM LAUNCH FAILED");
}
return nativeTexturePointer;
}
public void stopCamera() {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
Log.i("Unity", "JAVA: Camera stopped");
}
private int createExternalTexture() {
int[] textureIdContainer = new int[1];
GLES20.glGenTextures(1, textureIdContainer, 0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureIdContainer[0]);
return textureIdContainer[0];
}
@SuppressLint("NewApi")
private void setupCamera(int width, int height) {
Camera.Parameters params = mCamera.getParameters();
params.setRecordingHint(true);
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
params.setPreviewFormat(17);
params.setZoom(0);
// 16 ~ NV16 ~ YCbCr
// 17 ~ NV21 ~ YCbCr ~ DEFAULT *
// 4 ~ RGB_565
// 256~ JPEG
// 20 ~ YUY2 ~ YcbCr ...
// 842094169 ~ YV12 ~ 4:2:0 YCrCb comprised of WXH Y plane, W/2xH/2 Cr & Cb. see documentation *
params.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
Camera.Size previewSize = getOptimalSize(width, height, mCamera.getParameters().getSupportedPreviewSizes());
Camera.Size picSize = getOptimalSize(width, height, mCamera.getParameters().getSupportedPictureSizes());
params.setPictureSize(picSize.width, picSize.height);
params.setPreviewSize(previewSize.width, previewSize.height);
params.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
params.setExposureCompensation(0);
try{
mCamera.setParameters(params);
} catch (Exception e){
Log.i("Unity", "ERROR: " + e.getMessage());
}
Camera.Size mCameraPreviewSize = params.getPreviewSize();
prevWidth = mCameraPreviewSize.width;
prevHeight = mCameraPreviewSize.height;
int[] fpsRange = new int[2];
params.getPreviewFpsRange(fpsRange);
}
private Camera.Size getOptimalSize(int width, int height, List<Camera.Size> sizes) {
if(mCamera == null)
return null;
final double ASPECT_TOLERANCE = 0.1;
double targetRatio=(double)width / height;
if (sizes == null)
return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetWidth = width;
for (Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
Log.i("Unity", "RES: size=" + size.width + "/" + size.height + "/ Aspect Ratio: " + ratio + "target width: " + width + "target height: " + height);
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.width - targetWidth) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.width - targetWidth);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.width - targetWidth) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.width - targetWidth);
}
}
}
Log.i("Unity", "optimal size=" + optimalSize.width + "/" + optimalSize.height + "/ Aspect Ratio: " + (double) optimalSize.width / optimalSize.height);
return optimalSize;
}
public int getPreviewSizeWidth() {
return prevWidth;
}
public int getPreviewSizeHeight() { return prevHeight; }
public byte[] bytes;
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
bytes = data;
//Log.i("Unity", "JAVA: " + Integer.toString(data.length));
UnityPlayer.UnitySendMessage(gameObjectTargetName, "GetBuffer", "");
}
要求发送到Unity3d的纹理是1024x768而不进行裁剪。有没有人对此有任何解决方法?
更新(添加C#代码)
此代码从java获取预览:
void CreateCameraTexture() {
_texWidth = nativeCameraObject.Call<int>("getPreviewSizeWidth");
_texHeight = nativeCameraObject.Call<int>("getPreviewSizeHeight");
_cameraPreview = new Texture2D(_texWidth, _texHeight, TextureFormat.Alpha8, false);
_converter = new YUVDecode(_texWidth, _texHeight);
shaderMat.SetFloat("_Width", _texWidth);
if (OnCameraTextureCreated != null)
OnCameraTextureCreated(_cameraPreview);
}
private byte[] _bytes;
public void GetBuffer(string str) {
_bytes = nativeCameraObject.Get<byte[]>("bytes");
SetColor(_bytes);
_cameraPreview.LoadRawTextureData(_bytes);
_cameraPreview.Apply();
UpdateLibFrame();
}
void SetColor(byte[] bytes) {
_converter.SetBytes(bytes);
shaderMat.SetTexture("_YUVTex", _converter.yuvtexture);
}
void UpdateLibFrame() {
if (OnFrameUpdate != null) OnFrameUpdate(_cameraPreview.GetRawTextureData());
}
另一个类中的代码显示数据:
private RectTransform previewRect {
get {
if (!_previewRect)
_previewRect = cameraPreview.GetComponent<RectTransform>();
return _previewRect;
}
}
private void GetPreviewFromCamera() {
NativeCamera.OnCameraTextureCreated += (Texture2D cameraTex) => {
if (cameraPreview != null && cameraPreview.texture != null)
DestroyImmediate(cameraPreview.texture);
previewRect.sizeDelta = new Vector2(1024, 768);
cameraPreview.texture = cameraTex;
cameraPreview.enabled = true;
};
}
更新:添加了示例项目的链接
项目在这里: https://www.dropbox.com/s/wid1qa9cmq3ck6w/CameraPreview.zip?dl=0
CameraJava是一个gradle项目。输出是一个.AAR文件,您必须将其复制到Assets / Plugins。
答案 0 :(得分:0)