这个是ZYCamera项目的说明文档,gitee地址
目的是 帮助用户快速理清ZYCamera的实现逻辑,能够快速去修改工程。
这里只会介绍整体的架构和运行流程。
ZYCamera是一个相机SDK ,主要有预览,拍照,录像3大核心功能。
根据需要功能设计的结构如下,主要有3个Module。
public interface ICamera {
/**开始预览*/
void startPreview();
/**结束预览*/
void stopPreview();
/**关闭相机 释放资源*/
void closeCamera();
/**获取 相机相关参数*/
IParameter<?> getParameterCamera(@CameraParameter.Type int parameter);
/**获取 设备相关参数*/
IParameter<?> getParameterDevice(@DeviceParameter.Type int parameter);
/**获取 预览相关参数*/
IParameter<?> getParameterPreview(@PreviewParameter.Type int parameter);
/**获取 拍照相关参数*/
IParameter<?> getParameterPhoto(@PhotoParameter.Type int parameter);
/**获取 录像相关参数*/
IParameter<?> getParameterRecord(@RecordParameter.Type int parameter);
/**获取 特殊模式相关参数*/
IParameter<?> getParameterSpecialty(@SpecialtyParameter.Type int parameter);
}
目前只有一个实现类Camera
public class Camera implements ICamera {
/**
* 控制相机的接口
*/
private ICameraUser mCameraUser;
/**
* 和相机相关的参数
*/
private final CameraParameter mCameraParameter;
public Camera(Context context) {
mCameraParameter = new CameraParameter(context);
// 省略
}
// 省略
private void openCameraApi(int api, Context context) {
Zy.d("open Camera Api =" + api);
switch (api) {
case CAMERA_2:
mCameraUser = new Camera2Proxy(context, mCameraParameter);
break;
case CAMERA_1:
default:
mCameraUser = new Camera1Proxy(mCameraParameter);
break;
}
}
// 省略
}
起到一个逻辑层作用,内部会创建CameraParameter对象,然后根据需要创建ICameraUser相机的实现类 Camera2Proxy或Camera1Proxy ,并将CameraParameter对象传入。这样用户与相机持有同一个CameraParameter对象 就可以进行相互通信。
public interface IParameter<T> {
/**设置*/
void set(T t);
/**获取*/
T get();
/**设置最大和最小值*/
void setMinMax(T min, T max);
/**获取最大值*/
T getMax();
/**获取最小值*/
T getMin();
/**刷新一次回调*/
void updateT();
/**参数销毁,清除回调*/
void destroy();
/**添加监听*/
void addObserver(IObserver<T> observer);
/**移除监听*/
void removeObserver(IObserver<T> observer);
/** 回调 接口*/
interface IObserver<T> {
/**
* 参数更新
* @param t 参数值
* @param status 参数状态(可用,开关)
*/
void update(T t, int status);
}
}
一个抽象类BaseParameter。
public abstract class BaseParameter<T> implements IParameter<T> {
/** 参数 是否支持 默认支持*/
public static final byte SUPPORT = 1;
/** 参数 是否使用 默认打开*/
public static final byte TRIGGER = 2;
/** 当前值,最大值,最小值---要注意最小值是负数的情况 */
protected T mT, mMinT, mMaxT;
/** 回调集合,使用CopyOnWriteArrayList*/
protected List<IObserver<T>> mTtList = new CopyOnWriteArrayList<>();
/** 控制
* 第一位表示 此参数是否 支持,
* 第二位表示 此参数是否 生效
*/
protected byte mStatus = SUPPORT | TRIGGER ;
/**构造函数不给 客户端调用*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
public BaseParameter() {
}
public void setSupport(boolean support) {
setBit(support,SUPPORT);
}
public void setTrigger(boolean trigger) {
setBit(trigger,TRIGGER);
updateT();
}
//省略
}
根据不同功能和需要,定义了许多不同的参数。
比如CameraApi 控制相机的实现API是Camera1 或是 Camera2。 PreviewFlash 控制闪光灯 等等, 每个参数对象都有类注释 说明作用。有需要可以继承BaseParameter增加需要的参数。
/**
* @author zhangxi
* @company 桂林智神信息技术有限公司
* @date 2020/10/12 2:09 PM
* @description 闪光灯--0关闭 1打开 2自动 3常亮
*/
public class PreviewFlash extends BaseParameter<PreviewFlash.FlashType> {
public enum FlashType{
/**闪光灯关闭*/
FLASH_OFF (0),
/**闪光灯打开*/
FLASH_ON (1),
/**闪光灯自动*/
FLASH_AUTO (2),
/**闪光灯常亮*/
FLASH_TORCH (3);
public int value;
FlashType(int value) {
this.value = value;
}
@Override
public String toString() {
return "FlashType{ name=" + name()+
", value=" +value+
'}';
}
}
/** 当前模式下支持的闪光灯列表 */
private final List<FlashType> mSupportList;
@RestrictTo(RestrictTo.Scope.LIBRARY)
public PreviewFlash() {
mT= FlashType.FLASH_OFF;
mMinT= mT;
mMaxT= mT;
mSupportList = new ArrayList<>();
}
// 省略
}
相机功能
接口ICameraUser:
public interface ICameraUser {
/**
* 开始预览
*/
void startPreview();
/**
* 停止预览
*/
void stopPreview();
/**
* 销毁
*/
void destroy();
/**
* 获取所有的参数
*
* @param parameter 参数定义的常量
* @return 参数
*/
IParameter<?> getParameterAll(int parameter);
}
目前有3个实现类型可选择:
以后用户可以自行扩展。现在主要介绍Camera2的实现,其他2个实现整体流程也类似,只是在具体的某些功能实现上有所区别。
Camera2Proxy
public class Camera2Proxy implements ICameraUser {
private ICamera2Core mCameraCore;
private final CameraParameter mCameraParameter;
public Camera2Proxy(Context context, CameraParameter cameraParameter) {
mCameraParameter = cameraParameter;
openCameraMode(mCameraParameter.getCameraMode().get(), context);
mCameraParameter.getCameraMode().addObserver((integer, status) -> {
Zy.d("Camera2 mode change=" + integer);
destroy();
openCameraMode(integer,context);
startPreview();
});
}
// 省略
@Override
public void startPreview() {
if (mCameraCore != null) {
mCameraCore.startPreview();
}
}
@Override
public void stopPreview() {
if (mCameraCore != null) {
mCameraCore.stopPreview();
}
}
@Override
public void destroy() {
if (mCameraCore != null) {
mCameraCore.destroy();
mCameraCore = null;
}
}
@Override
public IParameter<?> getParameterAll(int parameter) {
return mCameraParameter.getSparseArray().get(parameter);
}
Camera2Proxy是代理类。具体的实现是在ICamera2Core的实现类中。这里会根据CameraMode参数 的值通过openCameraMode()选择一个实现。
这里可以看下CameraMode参数
public class CameraMode extends BaseParameter<CameraMode.ModeType> {
public enum ModeType{
/**拍照模式*/
MODE_PHOTO (1),
/**录像模式*/
MODE_VIDEO (2),
/**特殊模式*/
MODE_SPECIALTY (3),
/**全景*/
MODE_PANORAMIC(4),
/**慢动作,这个需要根据判断是否支持高帧捕获*/
MODE_SLOW_MOTION (5),
/**延时摄影*/
MODE_TIME_LAPSE (6),
/**直播模式*/
MODE_LIVE (7),
/**故事模式*/
MODE_STORY (8),
/**希区柯克*/
MODE_DOLLY_ZOOM (9),
// 华为API才支持以下 模式
/**夜景*/
MODE_NIGHT (10),
/**大光圈*/
MODE_BOKEH (11),
/**人像*/
MODE_PORTRAIT (12);
public int value;
ModeType(int value) {
this.value = value;
}
}
// 省略
}
可以分为3类
模式虽然很多,但是它们的实现都差不多。有些视频模式就是为了ZYCami需求才加上的。我们只需要搞清楚 MODE_PHOTO和MODE_VIDEO 2种模式的实现就行了。
Camera2Video
/**
* @author zhangxi
* @company 桂林智神信息技术有限公司
* @date 2020/11/2 4:29 PM
* @description Camera2 普通录像模式,只提供录像功能。故事 和希区柯克 是 此模式的衍生
*/
public class Camera2Video extends Camera2VideoBase {
private Camera2VideoParameter mCamera2VideoParameter;
public Camera2Video(Context context, CameraParameter cameraParameter) {
this(context,cameraParameter,true);
}
/**
* 构造函数,主要是第三个参数
* @param context 上下文
* @param cameraParameter 控制参数类
* @param init 是否初始化:主要是为了给继承的类使用,子类都是false
*/
public Camera2Video(Context context, CameraParameter cameraParameter, boolean init) {
super(context);
if (init) {
mCamera2VideoParameter=new Camera2VideoParameter(this,cameraParameter);
initCamera2(mCamera2VideoParameter);
}
}
// 省略
}
public class Camera2VideoBase extends Camera2Base {
protected Camera2VideoParameterBase mCamera2VideoParameterBase;
public Camera2VideoBase(Context context) {
mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
}
// 省略
}
这里为了减少重复代码,增加了2个继承。
1.希区柯克,直播录像等模式与普通录像的差别不大。所以提取父类Camera2VideoBase公共的录像功能实现代码。
2.不管是拍照模式还是录像模式都要先开启相机预览。所以提取父类Camera2Base公共的预览功能实现代码。
Camera2Base
public abstract class Camera2Base implements ICamera2Core {
public CameraCharacteristics mCameraCharacteristics;
public CameraManager mCameraManager;
public CameraDevice mCameraDevice;
public CameraCaptureSession mCameraSession;
public CameraConstrainedHighSpeedCaptureSession mCameraSessionHighSpeed;
private CaptureRequest.Builder mPreviewBuilder;
private Camera2BaseParameter mCamera2BaseParameter;
public void initCamera2(Camera2BaseParameter camera2BaseParameter) {
Zy.d("init Camera2Base");
this.mCamera2BaseParameter = camera2BaseParameter;
}
/**
* 当前是否 普通捕获模式
*/
public abstract boolean isSessionNormal();
/**
* 获取 创建 相机会话时 需要添加的surface
*
* @return surface列表
*/
public abstract List<Surface> getSessionSurfaceList();
/**
* 获取 预览时 需要添加的surface
*
* @return surface列表
*/
public abstract List<Surface> getPreviewSurfaceList();
/**
* 预览的捕获结果
*
* @param captureResult 此次捕获的 请求
* @param request 捕获的结果
*/
public void callbackCaptureResult(CaptureResult captureResult, CaptureRequest request) { }
@Override
public void startPreview() {
Zy.d("startPreview");
mCamera2BaseParameter.openDevice();
}
@Override
public void stopPreview() {
Zy.d("stopPreview");
mCamera2BaseParameter.closeDevice();
}
@Override
public void destroy() {
Zy.d("destroy");
closeDevice();
mCamera2BaseParameter.release();
mCameraManager = null;
}
// 省略
public void openDevice() {
Zy.d("open Device start");
if (getPreviewView().get() == null || getPreviewSurface() == null) {
Zy.d("current is preview view=null or preview surface=null, return");
return;
}
Zy.d("openDevice ,Current DeviceStatus=" + getDeviceStatus().get());
if (getDeviceStatus().isDeviceOpenIng()) {
Zy.d("current is open ing , return");
return;
}
acquireLock();
getDeviceStatus().set(DEVICE_OPEN_ING);
// 省略
}
// 省略
}
此类负责相机预览的开关。需要注意的点 :人脸坐标,高帧率会话,异步操作注意加锁,捕获结果的处理。
持有Camera2BaseParameter :相机参数的控制和响应。
public abstract class Camera2BaseParameter {
/**
* 预览的最大曝光时间,即 预览时 sec的最大值 十分之一秒=100_000_000,如果再大 就无法预览了
*/
private static final long MAX_PREVIEW_EXPOSURE_TIME = 1_000_000_000L / 10;
private static final int FOCUS_AREA_WEIGHT = 1000;
public CameraParameter mCameraParameter;
public Camera2Base mCamera2Base;
protected CameraCharacteristics mCameraCharacteristics;
protected ImageReader mAnalysisImageReader;
private boolean mLastFaceTrigger = false;
private boolean mLastDataTrigger = false;
public static HashMap<PreviewWbMode.WbMode, Integer> sWbModes = new HashMap<>();
// 省略
/**
* 重置各个模式通用的参数
*
* @param add true 添加监听,false 取消监听
*/
private void resetPreviewListener(boolean add) {
addListener(add, getPreviewAe(), aeListener);
addListener(add, getPreviewAeMode(), aeModeListener);
addListener(add, getPreviewWt(), wtListener);
addListener(add, getPreviewFace(), faceListener);
addListener(add, getPreviewData(), dataListener);
addListener(add, getPreviewSize(), previewSizeListener);
addListener(add, getPreviewRect(), rectListener);
addListener(add, getPreviewFlash(), flashListener);
addListener(add, getPreviewAfMode(), afModeListener);
addListener(add, getPreviewWbMode(), WbModeListener);
addListener(add, getPreviewId(), idListener);
addListener(add, getSpecialtyManual(), manualListener);
previewAfListener(add);
previewSecListener(add);
previewIsoListener(add);
previewWbListener(add);
}
// 省略
/**
* 更新参数的值--但是要先更新id
*
* @param characteristics 属性
*/
protected void setCameraCharacteristics(CameraCharacteristics characteristics) {
Zy.d("setCameraCharacteristics start");
this.mCameraCharacteristics = characteristics;
resetFps();
resetCameraMode();
resetParameter();
resetAe();
resetAeMode();
resetAfMode();
resetWbMode();
resetFlash();
resetWt();
resetRatio();
resetDeviceOrientation();
resetDeviceLevel();
resetAf();
resetIso();
resetSec();
resetWb();
resetManual();
getCameraUpdate().set(CameraUpdate.UpdateType.UPDATE_OTHER);
}
// 省略
}
此类的作用。1.相机打开前setCameraCharacteristics()重置各个预览参数值。2.resetPreviewListener()添加或者移除各预览参数的监听。3.具体参数设置的实现。
这样Camera2Base与Camera2BaseParameter 组合实现了预览功能, 预览控制,预览参数的响应。
子类 Camera2VideoBase与Camera2VideoParameterBase 组合实现 预览+录像功能。
子类 Camera2PhotoBase与Camera2PhotoParameterBase 组合实现 预览+拍照功能。
录像功能
IVideoRecorder接口
/**
* @author zhangxi
* @company 桂林智神信息技术有限公司
* @date 7/14/21 10:08 AM
* @description 录像接口。
*/
public interface IVideoRecorder {
/**准备*/
boolean prepare(VideoProfile videoProfile);
/**开始*/
boolean start();
/**暂停*/
boolean pause();
/**恢复继续*/
boolean resume();
/**停止*/
boolean stop();
/**释放资源*/
void release();
/**获取录像时 音量大小*/
int getVolume();
/**是否正在录制*/
boolean isRecording();
/**由外界提供 pcm音频数据,当使用MediaCodec才有用*/
void sendAudioData(byte[] pcm);
}
VideoProfile定义了录像所需要的参数。
/**
* @author zhangxi
* @company 桂林智神信息技术有限公司
* @date 7/14/21 10:14 AM
* @description 创建录像实现类
*/
public class MediaRecordFactory {
/**使用系统MediaRecorder*/
public static final int MEDIA_RECORDER=1;
/**使用 相机中的 录像*/
public static final int MEDIA_CODEC_CAMERA=2;
public static IVideoRecorder createMediaRecord(@Type int type) {
Zy.d("createMediaRecord 3type="+type);
// 默认使用系统的MediaRecorder
switch (type) {
case MEDIA_CODEC_CAMERA:
return new MediaCodecCamera();
case MEDIA_RECORDER:
default:
return new MediaRecordZy();
}
}
@IntDef({MEDIA_RECORDER,MEDIA_CODEC_CAMERA})
@Retention(RetentionPolicy.SOURCE)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@interface Type {}
}
目前录像的实现主要有2种实现方式都是硬编码。1使用系统的MediaRecorder 。2使用系统的MediaCodec 。区别就是MediaCodec更加灵活功能更多。 可以控制到每一帧画面,但是使用复杂会有一些兼容性问题。
相比CameraY_camera 要简单一些。
定义了5个接口,从上往下依次是美颜功能,相机功能,Mask辅助,录像,纹理渲染。2个实现类CameraView和CameraYun 。CameraView是一个FrameLayout 是对外接口。
/**
* @author zhangxi
* @company 桂林智神信息技术有限公司
* @date 2020/11/3 10:14 AM
* @description 外界使用的View---主要是方便可以直接在XML文件中使用.主要的实现逻辑是{@link CameraYun}。
* 客户端可以通过此类拿到需要的参数对象进行设置,监听等操作。
*/
public class CameraView extends FrameLayout implements SubCamera, SubBeauty, SubMask, SubTexture, SubRecord {
private CameraYun mCameraYun;
public CameraView(@NonNull Context context) {
this(context, null);
}
public CameraView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CameraView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
mCameraYun = new CameraYun(context, this, attrs);
}
@Override
protected void onAttachedToWindow() {
Zy.d(" CameraView onAttachedToWindow");
super.onAttachedToWindow();
}
@Override
protected void onDetachedFromWindow() {
Zy.d(" CameraView onDetachedFromWindow");
mCameraYun.release();
super.onDetachedFromWindow();
}
// 省略
}
具体的实现在CameraYun中。
/**
* @author zhangxi
* @company 桂林智神信息技术有限公司
* @date 2020/10/2 5:54 PM
* @description 这个是相机SDK的核心逻辑层,相机API 预览组件API 美颜API等等,都是在此类中完成逻辑交互。配合实现上层需求。
* 由于 适配的特殊性。目前有2个 不同的项目实现StaCam 与(ZYPlay,ZYCami)
*/
public class CameraYun implements SubCamera, SubBeauty, SubMask, SubTexture, SubRecord {
private Context mContext;
private ICamera mCamera;
private IBeauty mBeauty;
private IMaskZy mMaskZy;
private ITexture<DisplayRender> mTexture;
private IOrientation mOrientation;
private LocationListen mLocationListen;
private SoundHelper mSoundHelper;
/**
* 以下变量在开启美颜后,使用频率太高,为避免反复进行强制转换而声明
*/
private Size mPreviewSize;
private PreviewId mPreviewId;
private ImageView mBlurView;
private DisplayRender mDisplayRender;
private float[] mMvpMatrix;
private CameraType mCameraType = CameraType.CAMERA_TWO;
private Lifecycle mLifecycle;
public CameraYun(Context context, ViewGroup viewGroup, AttributeSet attrs) {
Zy.init(context);
Zy.d("init Camera Yun");
this.mContext = context;
initView(context, viewGroup, attrs);
initData(context, viewGroup);
}
private void initView(Context context, ViewGroup viewGroup, AttributeSet attrs) {
// 解析美颜属性
final TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.CameraView);
int beautyMode = attributes.getInt(R.styleable.CameraView_beauty_mode, 0);
attributes.recycle();
//美颜类型 0无,1美颜,2滤镜,3同时2种
Zy.d("init texture zy ,beautyMode=" + beautyMode);
mTexture = new TextureViewImpl(context, beautyMode);
// mTexture = new SurfaceViewImpl(context, beautyMode);
// mTexture = new TextureViewSys(context ,beautyMode);
// mTexture = new SurfaceViewSys(context ,beautyMode);
// mTexture = new GLSurfaceViewSys(context);
viewGroup.addView(mTexture.getView());
mDisplayRender = mTexture.getRender();
createTextureListener();
Zy.d("init blur view");
mBlurView = new ImageView(context);
mBlurView.setScaleType(ImageView.ScaleType.FIT_XY);
mBlurView.setBackgroundColor(Color.BLACK);
viewGroup.addView(mBlurView);
}
//省略
private void createCameraListener() {
Zy.d("create camera listener");
createStatusListener();
createPreviewListener();
createPhotoListener();
createRecordListener();
}
//省略
}
会createCameraListener()创建一些参数的监听,用于处理逻辑。Texture是渲染环境,Camera是画面生产 CameraYun就是将2着粘合起来 ,保证功能的正常。
到目前为止估计还是有些懵,下面通过对 预览,拍照,录像 3个流程简单讲解,让用户明白 ZYCamera整体的运行流程。
预览是最基础,最重要的流程。主要涉及画面的产生和处理显示。基本流程是:
在项目中 需要添加滤镜 ,所以Surface 是渲染框架返回的SurfaceTexture纹理去创建的。具体在代码中的基本流程:
详细流程 考虑篇幅就不贴代码,用户可以打开工程对着看: