摄像头是游戏陪玩app源码进行视频连麦时的重要移动设备之一,在开发时,我们需要实现游戏陪玩app源码对摄像头的调用权限,这就涉及到相关接口的开发了,不过今天我们主要来了解一下在游戏陪玩app源码开发中,摄像头调用和视频处理的相关知识点。

打开摄像头步骤为:

1、检查摄像头;
2、打开摄像头;
3、设置摄像头参数;
4、设置预览界面。
  
在本文中使用一个单例CameraHolder来维持Camera相应的状态,也方便在其他地方得到Camera相关的参数。在CameraHolder中定义了Camera的三个状态:Init、Opened、Preview。三个状态的关系图如下所示:

一、检查摄像头

在游戏陪玩app源码使用摄像头之前先要检查摄像头服务是否可用,下面一段代码是检查摄像头服务是否可用的。

public static void checkCameraService(Context context)throws CameraDisabledException {// Check if device policy has disabled the camera.DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);if (dpm.getCameraDisabled(null)) {throw new CameraDisabledException();}
}

检查完摄像头服务后,还需要检查手机上摄像头的个数,如果个数为0,则说明手机上没有摄像头,这样的话也是不能实现视频的。在游戏陪玩app源码中,我将摄像头个数的检测和摄像头数据的初始化放在了一起,如下面代码所示:

public static List<CameraData> getAllCamerasData(boolean isBackFirst) {ArrayList<CameraData> cameraDatas = new ArrayList<>();Camera.CameraInfo cameraInfo = new Camera.CameraInfo();int numberOfCameras = Camera.getNumberOfCameras();for (int i = 0; i < numberOfCameras; i++) {Camera.getCameraInfo(i, cameraInfo);if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {CameraData cameraData = new CameraData(i, CameraData.FACING_FRONT);if(isBackFirst) {cameraDatas.add(cameraData);} else {cameraDatas.add(0, cameraData);}} else if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {CameraData cameraData = new CameraData(i, CameraData.FACING_BACK);if(isBackFirst) {cameraDatas.add(0, cameraData);} else {cameraDatas.add(cameraData);}}}return cameraDatas;
}

在上面的方法中,需要传入一个是否先开启背面摄像头的boolean变量,如果变量为true,则把背面摄像头放在列表第一个,之后在游戏陪玩app源码中打开摄像头的时候,直接获取列表中第一个摄像头相关参数,然后进行打开。
这样的设计使得游戏陪玩app源码切换摄像头变得十分简单,切换摄像头时,先关闭当前摄像头,然后变化摄像头列表中的顺序,然后再打开摄像头即可,也就是每次打开摄像头都打开摄像头列表中第一个摄像头参数所指向的摄像头。

二、打开摄像头

打开摄像头之前,先从摄像头列表中获取第一个摄像头参数,之后根据参数中的CameraId来打开摄像头,打开成功后改变相关状态。相关代码如下:

public synchronized Camera openCamera()throws CameraHardwareException, CameraNotSupportException {CameraData cameraData = mCameraDatas.get(0);if(mCameraDevice != null && mCameraData == cameraData) {return mCameraDevice;}if (mCameraDevice != null) {releaseCamera();}try {SopCastLog.d(TAG, "open camera " + cameraData.cameraID);mCameraDevice = Camera.open(cameraData.cameraID);} catch (RuntimeException e) {SopCastLog.e(TAG, "fail to connect Camera");throw new CameraHardwareException(e);}if(mCameraDevice == null) {throw new CameraNotSupportException();}mCameraData = cameraData;mState = State.OPENED;return mCameraDevice;
}

三、设置摄像头参数

在给摄像头设置参数后,需要记录这些参数,以方便游戏陪玩app源码其他地方使用。比如记录当前摄像头是否有闪光点,从而可以决定游戏陪玩app源码UI界面上是否显示打开闪光灯按钮。在游戏陪玩app源码中使用CameraData来记录这些参数,CameraData类如下所示:

public class CameraData {public static final int FACING_FRONT = 1;public static final int FACING_BACK = 2;public int cameraID;            //camera的idpublic int cameraFacing;        //区分前后摄像头public int cameraWidth;         //camera的采集宽度public int cameraHeight;        //camera的采集高度public boolean hasLight;        //camera是否有闪光灯public int orientation;         //camera旋转角度public boolean supportTouchFocus;   //camera是否支持手动对焦public boolean touchFocusMode;      //camera是否处在自动对焦模式public CameraData(int id, int facing, int width, int height){cameraID = id;cameraFacing = facing;cameraWidth = width;cameraHeight = height;}public CameraData(int id, int facing) {cameraID = id;cameraFacing = facing;}
}

给摄像头设置参数的时候,有一点需要注意:设置的参数不生效会抛出异常,因此需要每个参数单独设置,这样就避免一个参数不生效后抛出异常,导致之后游戏陪玩app源码所有的参数都没有设置。

设置预览界面

设置预览界面有两种方式:1、通过SurfaceView显示;2、通过GLSurfaceView显示。当为SurfaceView显示时,需要传给Camera这个SurfaceView的SurfaceHolder。当使用GLSurfaceView显示时,需要使用Renderer进行渲染,先通过OpenGL生成纹理,通过生成纹理的纹理id生成SurfaceTexture,将SurfaceTexture交给Camera,那么在Renderer中便可以使用这个纹理进行相应的渲染,最后通过GLSurfaceView显示。

通过GLSurfaceView显示流程图如下:

对摄像机设置SurfaceHolder和SurfaceTexture只需要调用游戏陪玩app源码相应的接口即可。

设置预览回调

首先需要设置预览回调的图片格式。

public static void setPreviewFormat(Camera camera, Camera.Parameters parameters) {//设置预览回调的图片格式try {parameters.setPreviewFormat(ImageFormat.NV21);camera.setParameters(parameters);} catch (Exception e) {e.printStackTrace();}
}

当设置预览好预览回调的图片格式后,需要设置预览回调的Callback。

Camera.PreviewCallback myCallback = new Camera.PreviewCallback() {@Overridepublic void onPreviewFrame(byte[] data, Camera camera) {//得到相应的图片数据//Do something}
};
public static void setPreviewCallback(Camera camera, Camera.PreviewCallback callback) {camera.setPreviewCallback(callback);
}

如果游戏陪玩app源码中音视频编码采用的是软编,由于H264支持I420的图片格式,因此需要将N21格式转为I420格式,然后交给x264编码库。如果是硬编的话,由于Android硬编编码器支持I420(COLOR_FormatYUV420Planar)和NV12(COLOR_FormatYUV420SemiPlanar),因此可以将N21的图片转为I420或者NV12,然后交给硬编编码器。

设置预览图像大小

在摄像头相关处理中,一个比较重要的是屏幕显示大小和摄像头预览大小比例不一致的处理。在游戏陪玩app源码中,摄像头有一系列的Preview Size,我们需要从中选出适合的Preview Size。选择合适的摄像头Preview Size的代码如下所示:

public static Camera.Size getOptimalPreviewSize(Camera camera, int width, int height) {Camera.Size optimalSize = null;double minHeightDiff = Double.MAX_VALUE;double minWidthDiff = Double.MAX_VALUE;List<Camera.Size> sizes = camera.getParameters().getSupportedPreviewSizes();if (sizes == null) return null;//找到宽度差距最小的for(Camera.Size size:sizes){if (Math.abs(size.width - width) < minWidthDiff) {minWidthDiff = Math.abs(size.width - width);}}//在宽度差距最小的里面,找到高度差距最小的for(Camera.Size size:sizes){if(Math.abs(size.width - width) == minWidthDiff) {if(Math.abs(size.height - height) < minHeightDiff) {optimalSize = size;minHeightDiff = Math.abs(size.height - height);}}}return optimalSize;
}public static void setPreviewSize(Camera camera, Camera.Size size, Camera.Parameters parameters) {try {    parameters.setPreviewSize(size.width, size.height);           camera.setParameters(parameters);} catch (Exception e) {    e.printStackTrace();}
}

在设置好最适合的Preview Size之后,将size信息存储在CameraData中。当选择了SurfaceView显示的方式,可以将SurfaceView放置在一个LinearLayout中,然后根据摄像头Preview Size的比例改变SurfaceView的大小,从而使得两者比例一致,确保图像正常。
当选择了GLSurfaceView显示的时候,可以通过裁剪纹理,使得纹理的大小比例和GLSurfaceView的大小比例保持一致,从而确保游戏陪玩app源码图像显示正常。

图像的旋转
在游戏陪玩app源码中摄像头出来的图像需要进行一定的旋转,然后才能交给屏幕显示,而且如果游戏陪玩app源码支持屏幕旋转的话,也需要根据旋转的状况实时调整摄像头的角度。
在Android中旋转摄像头图像同样有两种方法,一是通过摄像头的setDisplayOrientation(result)方法,一是通过OpenGL的矩阵进行旋转。下面是通过setDisplayOrientation(result)方法进行旋转的代码:

public static int getDisplayRotation(Activity activity) {int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();switch (rotation) {case Surface.ROTATION_0: return 0;case Surface.ROTATION_90: return 90;case Surface.ROTATION_180: return 180;case Surface.ROTATION_270: return 270;}return 0;
}public static void setCameraDisplayOrientation(Activity activity, int cameraId, Camera camera) {// See android.hardware.Camera.setCameraDisplayOrientation for// documentation.Camera.CameraInfo info = new Camera.CameraInfo();Camera.getCameraInfo(cameraId, info);int degrees = getDisplayRotation(activity);int result;if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {result = (info.orientation + degrees) % 360;result = (360 - result) % 360; // compensate the mirror} else { // back-facingresult = (info.orientation - degrees + 360) % 360;}camera.setDisplayOrientation(result);
}

通过OpenGL的矩阵进行旋转之后在OpenGL相关的文章中进行讲述。

设置预览帧率

通过Camera.Parameters中getSupportedPreviewFpsRange()可以获得摄像头支持的帧率变化范围,从中选取合适的设置给摄像头即可。相关的代码如下:

public static void setCameraFps(Camera camera, int fps) {Camera.Parameters params = camera.getParameters();int[] range = adaptPreviewFps(fps, params.getSupportedPreviewFpsRange());params.setPreviewFpsRange(range[0], range[1]);camera.setParameters(params);
}private static int[] adaptPreviewFps(int expectedFps, List<int[]> fpsRanges) {expectedFps *= 1000;int[] closestRange = fpsRanges.get(0);int measure = Math.abs(closestRange[0] - expectedFps) + Math.abs(closestRange[1] - expectedFps);for (int[] range : fpsRanges) {if (range[0] <= expectedFps && range[1] >= expectedFps) {int curMeasure = Math.abs(range[0] - expectedFps) + Math.abs(range[1] - expectedFps);if (curMeasure < measure) {closestRange = range;measure = curMeasure;}}}return closestRange;
}

设置对焦方式
一般摄像头对焦的方式有两种:手动对焦和触摸对焦。下面的代码分别是设置自动对焦和触摸对焦的模式:

public static void setAutoFocusMode(Camera camera) {try {Camera.Parameters parameters = camera.getParameters();List<String> focusModes = parameters.getSupportedFocusModes();if (focusModes.size() > 0 && focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);camera.setParameters(parameters);} else if (focusModes.size() > 0) {parameters.setFocusMode(focusModes.get(0));camera.setParameters(parameters);}} catch (Exception e) {e.printStackTrace();}
}public static void setTouchFocusMode(Camera camera) {try {Camera.Parameters parameters = camera.getParameters();List<String> focusModes = parameters.getSupportedFocusModes();if (focusModes.size() > 0 && focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);camera.setParameters(parameters);} else if (focusModes.size() > 0) {parameters.setFocusMode(focusModes.get(0));camera.setParameters(parameters);}} catch (Exception e) {e.printStackTrace();}
}

对于自动对焦这样设置后就完成了工作,但是对于触摸对焦则需要设置对应的对焦区域。
要准确地设置对焦区域,有三个步骤:
一、得到当前点击的坐标位置;
二、通过点击的坐标位置转换到摄像头预览界面坐标系统上的坐标;
三、根据坐标生成对焦区域并且设置给摄像头。
当游戏陪玩app源码中整个摄像头预览界面定义了如下的坐标系统,对焦区域也需要对应到这个坐标系统中。

如果摄像机预览界面是通过SurfaceView显示的则比较简单,由于要确保不变形,会将SurfaceView进行拉伸,从而使得SurfaceView和预览图像大小比例一致,因此整个SurfaceView相当于预览界面,只需要得到当前点击点在整个SurfaceView上对应的坐标,然后转化为相应的对焦区域即可。
如果摄像机预览界面是通过GLSurfaceView显示的则要复杂一些,由于纹理需要进行裁剪,才能使得显示不变形,这样的话,我们要还原出游戏陪玩app源码整个预览界面的大小,然后通过当前点击的位置换算成预览界面坐标系统上的坐标,然后得到相应的对焦区域,然后设置给摄像机。
当设置好对焦区域后,通过调用Camera的autoFocus()方法即可完成触摸对焦。

设置缩放
当检测到手势缩放的时候,我们往往希望摄像头也能进行相应的缩放,其实这个在游戏陪玩app源码开发中实现还是比较简单的。首先需要加入缩放的手势识别,当识别到缩放的手势的时候,根据缩放的大小来对摄像头进行缩放。代码如下所示:

/*** Handles the pinch-to-zoom gesture*/
private class ZoomGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {@Overridepublic boolean onScale(ScaleGestureDetector detector) {if (!mIsFocusing) {float progress = 0;if (detector.getScaleFactor() > 1.0f) {progress = CameraHolder.instance().cameraZoom(true);} else if (detector.getScaleFactor() < 1.0f) {progress = CameraHolder.instance().cameraZoom(false);} else {return false;}if(mZoomListener != null) {mZoomListener.onZoomProgress(progress);}}return true;}
}public float cameraZoom(boolean isBig) {if(mState != State.PREVIEW || mCameraDevice == null || mCameraData == null) {return -1;}Camera.Parameters params = mCameraDevice.getParameters();if(isBig) {params.setZoom(Math.min(params.getZoom() + 1, params.getMaxZoom()));} else {params.setZoom(Math.max(params.getZoom() - 1, 0));}mCameraDevice.setParameters(params);return (float) params.getZoom()/params.getMaxZoom();
}

闪光灯操作

一个摄像头可能有相应的闪光灯,也可能没有,因此在使用闪光灯功能的时候先要确认是否有相应的闪光灯。检测摄像头是否有闪光灯的代码如下:

public static boolean supportFlash(Camera camera){Camera.Parameters params = camera.getParameters();List<String> flashModes = params.getSupportedFlashModes();if(flashModes == null) {return false;}for(String flashMode : flashModes) {if(Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode)) {return true;}}return false;
}

切换闪光灯的代码如下:

public static void switchLight(Camera camera, Camera.Parameters cameraParameters) {if (cameraParameters.getFlashMode().equals(Camera.Parameters.FLASH_MODE_OFF)) {cameraParameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);} else {cameraParameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);}try {camera.setParameters(cameraParameters);}catch (Exception e) {e.printStackTrace();}
}

四、开始预览

当打开了摄像头,并且设置好了摄像头相关的参数后,便可以通过调用Camera的startPreview()方法开始预览。有一个需要说明,无论是SurfaceView还是GLSurfaceView,都可以设置SurfaceHolder.Callback,当游戏陪玩app源码界面开始显示的时候打开摄像头并且开始预览,当游戏陪玩app源码界面销毁的时候停止预览并且关闭摄像头,这样的话当程序退到后台,其他应用也能调用摄像头。

private SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {SopCastLog.d(SopCastConstant.TAG, "SurfaceView destroy");CameraHolder.instance().stopPreview();CameraHolder.instance().releaseCamera();}@TargetApi(Build.VERSION_CODES.GINGERBREAD)@Overridepublic void surfaceCreated(SurfaceHolder holder) {SopCastLog.d(SopCastConstant.TAG, "SurfaceView created");}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {SopCastLog.d(SopCastConstant.TAG, "SurfaceView width:" + width + " height:" + height);CameraHolder.instance().openCamera();CameraHolder.instance().startPreview();}
};

以上便是“游戏陪玩app源码开发中,摄像头的调用及视频处理”的全部内容了,希望对大家有帮助。

游戏陪玩app源码开发中,摄像头的调用及视频处理相关推荐

  1. 开发游戏陪玩app源码前,需要掌握的音视频格式知识

    为了让用户拥有更好的聊天体验,游戏陪玩app源码在开发时,采用了音视频连麦技术,通过语音或视频的方式快速拉近彼此的距离,哪怕在游戏过程中也不耽误彼此聊天.而且音视频连麦技术的应用还为游戏陪玩app源码 ...

  2. 游戏陪玩平台源码开发中阿里云云存储OSS功能介绍

    游戏陪玩平台源码开发中阿里云阿里云文件存储(Network Attached Storage,简称 NAS)是面向阿里云 ECS 实例.HPC 和 Docker 等计算节点的文件存储服务. 1.游戏陪 ...

  3. 游戏陪玩系统源码开发,实现登陆功能需要如何做?

    登录功能的实现,是用户使用游戏陪玩系统源码的第一步,如果登录功能都没有开发好,那就无法给用户留下好的第一印象,所以今天我们就一起来看看在游戏陪玩系统源码开发中,实现登陆功能需要如何做吧. Login. ...

  4. 游戏陪玩平台源码开发,依赖收集和触发的实现

    概述 在游戏陪玩平台源码开发中,依赖收集和触发比较复杂,不容易理解.今天咱们将它最简化的讲出来. 前期准备 1.声明一个Map做储蓄: 2.current用来传递数据: 3.data数据备用: con ...

  5. 游戏陪玩平台源码开发,语音通话中的噪音消除处理

    如果用户在游戏陪玩平台源码使用语音通话的过程中,出现大量噪音将会对用户的体验造成不良的影响,所以在开发时,要将噪音消除掉,具体的处理代码将在下方展现: // 初始化 降噪 Java_com_audio ...

  6. 游戏陪玩平台源码开发,如何快速实现搜索功能?

    现在的游戏陪玩平台源码中都有搜索功能.也就是说搜索栏下面有一个列表,列表中放的内容可能是游戏,也有可能是其他的内容.这时候,我们可以在搜索框中输入你想要搜索的内容,下面的列表就会出现你想要的内容. 别 ...

  7. 游戏陪玩平台源码开发,锁屏状态下消息提醒的实现

    当游戏陪玩平台源码在后台运行时,如果用户关闭了手机屏幕(手机进入灭屏休眠状态),这个时候有消息进来怎么办?这就需要在游戏陪玩平台源码开发时实现手机锁屏状态下消息提醒功能. 解决思路: 1.游戏陪玩平台 ...

  8. 游戏陪玩网站源码开发,搜索功能的实现与页面倒计时跳转

    在游戏陪玩网站源码开发时,首页会有一个搜索输入框和按钮区域,我们要做的就是来完善这部分功能.首先游戏陪玩网站源码搜索的逻辑,我们简单地设定为问题的title或者content包含搜索的关键字即可.假设 ...

  9. 如何提高游戏陪玩app源码的代码可读性?

    1.Why 对一线开发人员来说,每天工作内容大多是在已有游戏陪玩app源码的基础上继续堆代码.当游戏陪玩app源码实在堆不动时就需要寻找收益来重构代码.既然我们的大多数时间都花在坐在显示器前读写代码这 ...

最新文章

  1. IR21系列栅极驱动自举升压原理
  2. 小程序实践(三):九宫格实现及item跳转
  3. PLSQL存储过程中的内部存储过程
  4. Android测试——在Python中如何将Windows程序前台显示
  5. android获取有线、wifi、3G(4G)的IP
  6. 数据算法_数据结构和常用算法
  7. php strictbool,PHP 7 Bool类型提示不起作用
  8. 数据挖掘的9大成熟技术和应用
  9. python lmdb使用
  10. 免费云服务器获取方法 云服务器购买
  11. pixi 流星_流星语270—273
  12. PHP接入微信官方支付(native·APIv3)
  13. react初步学习(三)
  14. 解决谷歌浏览器,打开开发者工具后,页面突然变成手机模式
  15. 初学者C语言练习题-指针
  16. 计算机主机怎么连接显示器,一台主机两个显示器怎么连接 一台主机两个显示器如何设置...
  17. Linux内核如何输出中文字符
  18. Cordova iOS 项目中微信/qq/Apple ID插件的安装以及登录的实现
  19. 【转】什么是WTW Wi-Fi 6(802.11ax)解析18:TWT节能机制(Target Wake Time)
  20. 使用installshield在安装过程中任意步骤安装MS sql数据库

热门文章

  1. osg学习(二十八)有关kml和kmz
  2. 《Reinforcement Learning: An Introduction》 读书笔记 - 目录
  3. Linux 0.11-从内核态到用户态-23
  4. 计算时区函数gmt2local(time_t t)
  5. 软件测试中的“黑天鹅”
  6. Hopcroft-Karp 算法
  7. 2021年NBA季后赛第四轮总决赛晋级预测
  8. ValueError: Dimensions of labels and X must be compatible
  9. 莪自己做嘚~~ppp协议文档
  10. linux使用usb打印机驱动精灵,Qomo Linux上的“驱动精灵”