Camera API2使用流程分析

news2024/11/19 13:42:01

1.首先看下framework层CameraService服务初始化流程分析

<1>. main_cameraserver.cpp的路径

/frameworks/av/camera/cameraserver/

 CameraService::instantiate()实际调用的是BinderService中的函数,因为CameraService是继承BinderService<CameraService>

 <2>. BinderService的详细介绍

 从CameraService中可以看出service的名称是“media.camera”.因为CameraService是继承的RefBase,只要初始化该对象就会调用onFirstRef。

<3>. CameraService的onFirstRef分析

主要是enumerateProviders函数初始化CameraProviderManager和CameraFlashlight,

2. hal层的camerahalserver服务初始化流程分析

 

 <1>. camerahalserver.rc所在的路径

vendor/mediatek/proprietary/hardware/mtkcam3/main/hal/service/

<2>. ICameraProvider服务的注册

     1). 在service.cpp中,调用 registerPassthroughServiceImplementation进行注册,服务名称是"internal/0" :

     2). registerPassthroughServiceImplementation分析,函数实现在LegacySupport.h中

 3). Interface::getService实现服务的加载,详细可以参考out目录下面的CameraProviderAll.cpp类

4). :details::getServiceInternal调用的是system下面的HidlTransportSupport.h

getRawServiceInternal的实现是在system下面的ServiceManagement.cpp中,最后调用的是PassthroughServiceManager的get函数:

 PassthroughServiceManager的get函数主要是加载"internal/0"服务

 instance.cpp(mtkcam3),来看下源码:

#include "MyUtils.h"
//
#include <string>
//
#include <android/hardware/camera/provider/2.4/ICameraProvider.h>

/******************************************************************************
 *
 ******************************************************************************/
using namespace android::hardware::camera::provider::V2_4;

/******************************************************************************
 *
 ******************************************************************************/
#define MY_LOGV(fmt, arg...)        CAM_LOGV("[%s] " fmt, __FUNCTION__, ##arg)
#define MY_LOGD(fmt, arg...)        CAM_LOGD("[%s] " fmt, __FUNCTION__, ##arg)
#define MY_LOGI(fmt, arg...)        CAM_LOGI("[%s] " fmt, __FUNCTION__, ##arg)
#define MY_LOGW(fmt, arg...)        CAM_LOGW("[%s] " fmt, __FUNCTION__, ##arg)
#define MY_LOGE(fmt, arg...)        CAM_LOGE("[%s] " fmt, __FUNCTION__, ##arg)
#define MY_LOGA(fmt, arg...)        CAM_LOGA("[%s] " fmt, __FUNCTION__, ##arg)
#define MY_LOGF(fmt, arg...)        CAM_LOGF("[%s] " fmt, __FUNCTION__, ##arg)
//
#define MY_LOGV_IF(cond, ...)       do { if ( (cond) ) { MY_LOGV(__VA_ARGS__); } }while(0)
#define MY_LOGD_IF(cond, ...)       do { if ( (cond) ) { MY_LOGD(__VA_ARGS__); } }while(0)
#define MY_LOGI_IF(cond, ...)       do { if ( (cond) ) { MY_LOGI(__VA_ARGS__); } }while(0)
#define MY_LOGW_IF(cond, ...)       do { if ( (cond) ) { MY_LOGW(__VA_ARGS__); } }while(0)
#define MY_LOGE_IF(cond, ...)       do { if ( (cond) ) { MY_LOGE(__VA_ARGS__); } }while(0)
#define MY_LOGA_IF(cond, ...)       do { if ( (cond) ) { MY_LOGA(__VA_ARGS__); } }while(0)
#define MY_LOGF_IF(cond, ...)       do { if ( (cond) ) { MY_LOGF(__VA_ARGS__); } }while(0)


/******************************************************************************
 *
 ******************************************************************************/
static
std::string const&
getProviderType()
{
    struct T
    {
        char const* kDefaultType =
#if 1
            "internal";     //  "internal" for binderized mode
#else
            "legacy";       //  "legacy" for passthrough mode
#endif
        std::string mType;
        T()
        {
            mType = kDefaultType;
        }
    };
    static T singleton;
    return singleton.mType;
}

/******************************************************************************
 *
 ******************************************************************************/
extern "C"
NSCam::ICameraDeviceManager*
getCameraDeviceManager()
{
    static NSCam::CameraDeviceManagerImpl singleton(getProviderType().c_str());
    static bool init = singleton.initialize();
    if ( ! init ) {
        MY_LOGE("CameraDeviceManagerImpl::initialize fail %p", &singleton);
        return nullptr;
    }
    return &singleton;
}

/******************************************************************************
 *
 ******************************************************************************/
extern "C"
NSCam::ICameraDeviceManager*
getCameraDeviceManager()
{
    static NSCam::CameraDeviceManagerImpl singleton(getProviderType().c_str());
    static bool init = singleton.initialize();
    if ( ! init ) {
MY_LOGE("CameraDeviceManagerImpl::initialize fail %p", &singleton);
        return nullptr;
    }
    return &singleton;
}
/******************************************************************************
 *
 ******************************************************************************/
extern "C"
ICameraProvider*
createICameraProvider_V2_4(const char* providerName, NSCam::ICameraDeviceManager* manager);


extern "C"
ICameraProvider*
HIDL_FETCH_ICameraProvider(const char* name)
{
    //  name must be either "internal/<id>" or "legacy/<id>".
    std::string const strProviderName(name);
    size_t const pos = strProviderName.find('/');
    if ( 0 == pos || std::string::npos == pos ) {
        MY_LOGE("provider name (%s) with bad \'/\' at position %zu", name, pos);
        return nullptr;
    }
    //
    if ( 0 != strProviderName.compare(0, pos, getProviderType()) ) {
        MY_LOGW("provider name (%s) with mismatched type(%s) and \'/\' at position %zu",
            name, getProviderType().c_str(), pos);
        return nullptr;
    }
    //
return createICameraProvider_V2_4(name, getCameraDeviceManager());
}


可以看出初始化了CameraDeviceManagerImpl.cpp,并且调用了它的initialize函数。因为CameraDeviceManagerImpl继承CameraDeviceManagerBase,所以initialize实际是调用CameraDeviceManagerBase.cpp的initialize。createICameraProvider_V2_4的实现是在CameraDeviceManagerImpl中。

5). CameraDeviceManagerBase的initialize函数分析

主要是加载libmtkcam_device3.so库函数,并且用mCreateVirtualCameraDevice保存createVirtualCameraDevice函数。然后调用enumerateDevicesLocked函数加载mVirtEnumDeviceMap 和mPhysEnumDeviceMap。

6). enumerateDevicesLocked函数分析

 enumerateDevicesLocked实现在CameraDeviceManagerBase_camera.cpp中,onEnumerateDevicesLocked的实现在CameraDeviceManagerImpl.cpp中。来看下onEnumerateDevicesLocked

 MAKE_HalLogicalDeviceList获取的是HalLogicalDeviceList.cpp。addVirtualDevicesLocked的实现在CameraDeviceManagerBase_camera.cpp 中

 create_device实际就是createVirtualCameraDevice函数。createVirtualCameraDevice的实现是在mtkcam3的CameraDevice3Factory.cpp中,new_device是CameraDevice3Impl,也就是pInfo->mVirtDevice = pVirtualDevice保存的mVirtDevice就是CameraDevice3Impl对象。

extern "C"
NSCam::ICameraDeviceManager::IVirtualDevice*
createVirtualCameraDevice(CreateVirtualCameraDeviceParams* params)
{
    if ( ! params || ! params->pDeviceManager || ! params->pMetadataProvider ) {
        MY_LOGE("Bad params");
        return nullptr;
    }

    auto pDevice = new CameraDevice3Impl(
        params->pDeviceManager,
        params->pMetadataProvider,
        params->deviceType,
        params->instanceId
    );

    if ( ! pDevice ) {
        MY_LOGE("Fail to new CameraDevice3Impl");
        return nullptr;
    }

    NSCam::ICameraDevice3Session::CreationInfo const info = {
        .mDeviceManager     = params->pDeviceManager,
        .mStaticDeviceInfo  = pDevice->getStaticDeviceInfo(),
        .mMetadataProvider  = params->pMetadataProvider,
        .mMetadataConverter = pDevice->getMetadataConverter(),
    };

    bool bInitSuccess = pDevice->initialize(createCameraDevice3Session(info));
    if ( ! bInitSuccess ) {
        delete pDevice;
        pDevice = nullptr;
    }

    return pDevice;
}

7). MAKE_HalLogicalDeviceList分析

MAKE_HalLogicalDeviceList的实现在mtkcam里面,说明mtkcam3用到了mtkcam里面的部分内容,MTKCAM_MODULE_ID_UTILS_LOGICALDEV这个module_id很重要,后面会根据这个id找到HalLogicalDeviceList。

 MAKE_MTKCAM_MODULE实现在module.h

 getMtkcamModuleFactory 在helper.cpp中

 

 

查看register_utils.cpp可以看到 MTKCAM_MODULE_ID_UTILS_LOGICALDEV这个id对应的是NSCam::IHalLogicalDeviceList::get。

 下面来看下NSCam::IHalLogicalDeviceList::get实现,得到的就是HalLogicalDeviceList对象:

/vendor/mediatek/proprietary/hardware/mtkcam/utils/LogicalCam/HalLogicalDeviceList.cpp

 8).继续分析IHalLogicalDeviceList的searchDevices函数

        a.由上面分析可知,最终会调用到HalLogicalDeviceList的searchDevices函数

MINT32
HalLogicalDeviceList::
createDeviceMap()
{
    IHalSensorList* const pHalSensorList = MAKE_HalSensorList();
    size_t const sensorNum = pHalSensorList->searchSensors();
    SensorInfo_t vTempInfo;
    TempSensorInfo TempInfo;
    SensorStaticInfo sensorStaticInfo;
    unsigned int i = 0;
    std::vector<struct LogicalSensorStruct> CustomDevList;

    MY_LOGD("sensorNum : %zu", sensorNum);

    CustomDevList = get_LogicalDevice_CustomizeData();
    MY_LOGD("manual device count = %zu", CustomDevList.size());
    for(i = 0; i < sensorNum; i++)
    {
        memset(&sensorStaticInfo, 0, sizeof(SensorStaticInfo));
        int sendorDevIndex = pHalSensorList->querySensorDevIdx(i);
        pHalSensorList->querySensorStaticInfo(sendorDevIndex, &sensorStaticInfo);

        TempInfo.SensorId = i;
        TempInfo.RawType = sensorStaticInfo.rawFmtType;
        TempInfo.Facing = sensorStaticInfo.facingDirection;
        TempInfo.CaptureModeWidth = sensorStaticInfo.captureWidth;
        strncpy(TempInfo.Name, pHalSensorList->queryDriverName(i), MAX_SENSOR_NAME_SIZE - 1);
        vTempInfo.add(TempInfo.Name, TempInfo);
        MY_LOGD("i : %d, facing : %d", i, sensorStaticInfo.facingDirection);
MY_LOGD("i : %d, Name : %s", i, TempInfo.Name);
        MY_LOGD("i : %d, vTempInfo Name : %s", i, vTempInfo.valueFor(TempInfo.Name).Name);

        sp<CamDeviceInfo> Info = new CamDeviceInfo();
        Info->Sensors.push_back(i);
        Info->DualFeature = 0;
        Info->RawType = TempInfo.RawType;
        strncpy(Info->Name, TempInfo.Name, sizeof(Info->Name));
        // add physical sensor static metadata
        Info->sensorStaticMetadata = MAKE_HalSensorList()->queryStaticInfo(Info->Sensors[0]);
        MY_LOGD("i : %d, Info Name : %s, %p", i, Info->Name, Info->Name);

        mDeviceSensorMap.add(i, Info);
    }

    // manually add logical device from custom
    for(i = 0; i < CustomDevList.size(); i++)
    {
        for(int j = 0; j < CustomDevList[i].NumofDefinition; j++)
        {
            addLogicalDevice(vTempInfo, &CustomDevList[i], j);
        }
    }
    dumpDebugInfo();
    return 0;
}


 MAKE_HalSensorList调用的是IHalSensor.h的函数,流程跟MAKE_HalLogicalDeviceList类似,最终是拿到HalSensorList.cpp

 /vendor/mediatek/proprietary/hardware/mtkcam/include/mtkcam/drv/IHalSensor.h

 /vendor/mediatek/proprietary/hardware/mtkcam/drv/src/sensor/common/v1_1/HalSensorList.cpp

 b. 来看看HalSensorList的searchSensors函数

MUINT
HalSensorList::
searchSensors()
{
    Mutex::Autolock _l(mEnumSensorMutex);

    MY_LOGD("searchSensors");

    char value[PROPERTY_VALUE_MAX] = {'\0'};
    property_get("service.instantcam.running", value, "0");
    bool camRunning = ::atoi(value) != 0;
    if(camRunning)
        MY_LOGW("instantcam is running!");

    SeninfDrv *const pSeninfDrv = SeninfDrv::getInstance();

    if(pSeninfDrv && pSeninfDrv->init() < 0) {
        MY_LOGE("pSeninfDrv->init() fail");
        return 0;
    }

#ifdef MTK_MAIN3_IMGSENSOR
    MY_LOGD("impSearchSensor search to main_3\n");
    for (MUINT i = IMGSENSOR_SENSOR_IDX_MIN_NUM; i <= IMGSENSOR_SENSOR_IDX_MAIN3; i++) {
#else
#ifdef MTK_SUB2_IMGSENSOR
    MY_LOGD("impSearchSensor search to sub2\n");
    for (MUINT i = IMGSENSOR_SENSOR_IDX_MIN_NUM; i <= IMGSENSOR_SENSOR_IDX_SUB2; i++) {
#else
#ifdef MTK_MAIN2_IMGSENSOR
    MY_LOGD("impSearchSensor search to main_2\n");
    for (MUINT i = IMGSENSOR_SENSOR_IDX_MIN_NUM; i <= IMGSENSOR_SENSOR_IDX_MAIN2; i++) {
#else
#ifdef MTK_SUB_IMGSENSOR
    MY_LOGD("impSearchSensor search to sub\n");
    for (MUINT i = IMGSENSOR_SENSOR_IDX_MIN_NUM; i <= IMGSENSOR_SENSOR_IDX_SUB; i++) {
#else
    MY_LOGD("impSearchSensor search to main\n");
    for (MUINT i = IMGSENSOR_SENSOR_IDX_MIN_NUM; i < IMGSENSOR_SENSOR_IDX_SUB; i++) {
#endif
#endif
#endif
#endif
#ifndef MTK_SUB_IMGSENSOR
        if (i == IMGSENSOR_SENSOR_IDX_SUB)
            continue;
#endif
        ImgSensorDrv *const pSensorDrv = ImgSensorDrv::getInstance((IMGSENSOR_SENSOR_IDX)i);
        if(pSensorDrv->init((IMGSENSOR_SENSOR_IDX)i) != SENSOR_NO_ERROR)
            continue;

        MUINT32 mclkSrc;
pSensorDrv->sendCommand(CMD_SENSOR_GET_MCLK_CONNECTION, (MUINTPTR)&mclkSrc);
        pSeninfDrv->setMclkIODrivingCurrent(mclkSrc, ISP_DRIVING_8MA);
        if(!camRunning)
        pSeninfDrv->setMclk(mclkSrc, true, 24);

        if(pSensorDrv->searchSensor() == SENSOR_NO_ERROR) {
            //query sensorinfo
            querySensorInfo((IMGSENSOR_SENSOR_IDX)i);
            //fill in metadata
            buildSensorMetadata((IMGSENSOR_SENSOR_IDX)i);
            addAndInitSensorEnumInfo_Locked(
                (IMGSENSOR_SENSOR_IDX)i,
                mapToSensorType(pSensorDrv->getType()),
                (char *)pSensorDrv->getName());
        }

        if(!camRunning)
        pSeninfDrv->setMclk(mclkSrc, false, 24);
        pSensorDrv->uninit();
    }

    if(pSeninfDrv->uninit() < 0) {
        MY_LOGE("pSeninfDrv->uninit() fail");
        return 0;
    }

    return  mEnumSensorList.size();
}

c. SeninfDrv的初始化:

 init函数:

int SeninfDrvImp::init()
{
    KD_SENINF_REG reg;
    LOG_MSG("[init]: Entry count %d \n", mUser);
    Mutex::Autolock lock(mLock);
    if (mUser > 0) {
        LOG_MSG("Already inited \n");
        android_atomic_inc(&mUser);
        return 0;
    }
    // Open seninf driver
    mfd = open(SENINF_DEV_NAME, O_RDWR);
    if (mfd < 0) {
        LOG_ERR("error open kernel driver, %d, %s\n", errno, strerror(errno));
        return -1;
    }
    if (ioctl(mfd, KDSENINFIOC_X_GET_REG_ADDR, &reg) < 0) {
       LOG_ERR("ERROR:KDSENINFIOC_X_GET_REG_ADDR\n");
       return -2;
    }
    // mmap seninf reg
    mpSeninfHwRegAddr = (unsigned char *) mmap(0, reg.seninf.map_length, (PROT_READ|PROT_WRITE|PROT_NOCACHE), MAP_SHARED, mfd, reg.seninf.map_addr);
    if (mpSeninfHwRegAddr == MAP_FAILED) {
        LOG_ERR("mmap err(1), %d, %s \n", errno, strerror(errno));
        return -5;
    }

    // mipi rx analog address
mpCSI2RxAnalogRegStartAddrAlign = (unsigned char *) mmap(0, reg.ana.map_length, (PROT_READ|PROT_WRITE), MAP_SHARED, mfd, reg.ana.map_addr);
    if (mpCSI2RxAnalogRegStartAddrAlign == MAP_FAILED) {
        LOG_ERR("mmap err(5), %d, %s \n", errno, strerror(errno));
        return -9;
    }
    //gpio
    mpGpioHwRegAddr = (unsigned char *) mmap(0, reg.gpio.map_length, (PROT_READ|PROT_WRITE), MAP_SHARED, mfd, reg.gpio.map_addr);
    if (mpGpioHwRegAddr == MAP_FAILED) {
        LOG_ERR("mmap err(6), %d, %s \n", errno, strerror(errno));
        return -10;
    }

    /*CSI2 Base address*/
    //MMAP only support Page alignment(0x1000)
    mpCSI2RxAnaBaseAddr[CUSTOM_CFG_CSI_PORT_0]  = mpCSI2RxAnalogRegStartAddrAlign;
    mpCSI2RxAnaBaseAddr[CUSTOM_CFG_CSI_PORT_0A] = mpCSI2RxAnaBaseAddr[CUSTOM_CFG_CSI_PORT_0];
    mpCSI2RxAnaBaseAddr[CUSTOM_CFG_CSI_PORT_0B] = mpCSI2RxAnaBaseAddr[CUSTOM_CFG_CSI_PORT_0] + 0x1000;
    mpCSI2RxAnaBaseAddr[CUSTOM_CFG_CSI_PORT_1]  = mpCSI2RxAnaBaseAddr[CUSTOM_CFG_CSI_PORT_0] + 0x2000;
#if 0
    mpCSI2RxAnaBaseAddr[CUSTOM_CFG_CSI_PORT_1A] = mpCSI2RxAnaBaseAddr[CUSTOM_CFG_CSI_PORT_0] + 0x2000;
    mpCSI2RxAnaBaseAddr[CUSTOM_CFG_CSI_PORT_1B] = mpCSI2RxAnaBaseAddr[CUSTOM_CFG_CSI_PORT_0] + 0x3000;
#endif
    mpCSI2RxAnaBaseAddr[CUSTOM_CFG_CSI_PORT_2]  = mpCSI2RxAnaBaseAddr[CUSTOM_CFG_CSI_PORT_0] + 0x4000;
    /*SenINF base address*/
    mpSeninfCtrlRegAddr[SENINF_1] = mpSeninfHwRegAddr;
    mpSeninfCtrlRegAddr[SENINF_2] = mpSeninfHwRegAddr + 0x1000;
    mpSeninfCtrlRegAddr[SENINF_3] = mpSeninfHwRegAddr + 0x2000;
    mpSeninfCtrlRegAddr[SENINF_4] = mpSeninfHwRegAddr + 0x3000;
mpSeninfCtrlRegAddr[SENINF_5] = mpSeninfHwRegAddr + 0x4000;
    /*SenINF Mux Base address*/
    mpSeninfMuxBaseAddr[SENINF_MUX1] = mpSeninfHwRegAddr + 0x0d00;
    mpSeninfMuxBaseAddr[SENINF_MUX2] = mpSeninfHwRegAddr + 0x1d00;
    mpSeninfMuxBaseAddr[SENINF_MUX3] = mpSeninfHwRegAddr + 0x2d00;
    mpSeninfMuxBaseAddr[SENINF_MUX4] = mpSeninfHwRegAddr + 0x3d00;
    mpSeninfMuxBaseAddr[SENINF_MUX5] = mpSeninfHwRegAddr + 0x4d00;
    mpSeninfMuxBaseAddr[SENINF_MUX6] = mpSeninfHwRegAddr + 0x5d00;

    mpSeninfCSIRxConfBaseAddr[SENINF_1] = mpSeninfHwRegAddr + 0x0824;//18040800
    mpSeninfCSIRxConfBaseAddr[SENINF_2] = mpSeninfHwRegAddr + 0x1824;//18041800
    mpSeninfCSIRxConfBaseAddr[SENINF_3] = mpSeninfHwRegAddr + 0x2824;//18042800
    mpSeninfCSIRxConfBaseAddr[SENINF_4] = mpSeninfHwRegAddr + 0x3824;//18043800
    mpSeninfCSIRxConfBaseAddr[SENINF_5] = mpSeninfHwRegAddr + 0x4824;//18044800
#ifdef CSI2_EFUSE_SET
    mCSI[0] = 0; /* initial CSI value*/
    mCSI[1] = 0; /* initial CSI value*/
    mCSI[2] = 0; /* initial CSI value*/
    mCSI[3] = 0; /* initial CSI value*/
    mCSI[4] = 0; /* initial CSI value*/
    typedef struct {
        unsigned int entry_num;
        unsigned int data[200];
    } DEVINFO_S;
    int fd = 0;
    int ret = 0;
    DEVINFO_S devinfo;
    fd = open("/proc/device-tree/chosen/atag,devinfo", O_RDONLY); /* v2 device node */
    if (fd < 0) { /* Use v2 device node if v1 device node is removed */
        LOG_ERR("/proc/device-tree/chosen/atag,devinfo kernel open fail, errno(%d):%s",errno,strerror(errno));
} else {
        ret = read(fd, (void *)&devinfo, sizeof(DEVINFO_S));
        if (ret < 0) {
            LOG_ERR("Get Devinfo data fail, errno(%d):%s",errno,strerror(errno));
        } else {
            mCSI[0] = devinfo.data[103];//0x11F1018C
            mCSI[1] = devinfo.data[104];//0x11F10190
            mCSI[2] = devinfo.data[115];//0x11F101BC
            mCSI[3] = devinfo.data[116];//0x11F101C0
        }
        LOG_MSG("Efuse Data:0x1045018c= 0x%x, 0x10450190= 0x%x, 0x104501bc= 0x%x, 0x104501c0= 0x%x\n", mCSI[0], mCSI[1], mCSI[2], mCSI[3]);
        close(fd);
    }
#endif
    android_atomic_inc(&mUser);
    LOG_MSG("[init]: Exit count %d \n", mUser);
    return 0;
}

 SENINF_DEV_NAME是

#define SENINF_DEV_NAME     "/dev/seninf"

ioctl(mfd, KDSENINFIOC_X_GET_REG_ADDR, &reg)这个就已经调用到驱动seninf.c里面去了,在驱动中搜索KDSENINFIOC_X_GET_REG_ADDR即可找到位置。

kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1_1/seninf.c

 d. ImgSensorDrv的初始化:

路径:/vendor/mediatek/proprietary/hardware/mtkcam/drv/src/sensor/common/v1_1/imgsensor_drv.cpp 

 CAMERA_HW_DEVNAME的定义: 

/device/mediatek/common/kernel-headers/kd_imgsensor.h:265:#define CAMERA_HW_DEVNAME "kd_camera_hw"

 看下featureControl函数,最终调用到imgsensor.c驱动文件中去了。

 /kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1_1/imgsensor.c

其他的参考时序图,到目前为止可以看到从hal层跟踪到kernel里面去了。 

参考:

 1.Mtk Camera MtkCam3架构学习_liujun3512159的博客-CSDN博客_camera mtk

 2. Camera 服务启动流程简析_来自南陵县的平头哥的博客-CSDN博客_cameraservice启动流程
 3. Android P之Camera HAL3流程分析(3)_Vincentywj的博客-CSDN博客

 4. Android P之Camera HAL3流程分析(1)_Vincentywj的博客-CSDN博客

5. Android Camera2 Api 实现预览和拍照 - 知乎

6. 相机  |  Android 开源项目  |  Android Open Source Project

7. (1214条消息) Android P HIDL 之 CameraProvider_liujun3512159的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/62780.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[附源码]计算机毕业设计JAVA疫情状况下生活物资集体团购系统

[附源码]计算机毕业设计JAVA疫情状况下生活物资集体团购系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a…

学习ITIL4又给数字人带来哪些优势

2019新的ITIL 指南将被称为ITIL 4。这个名称反映了ITIL 4 在支持个人和组织应对第四次工业革命方面的作用。 IT 是当今世界每个企业的核心。该更新将使ITIL 能够反映我们所处的快节奏和复杂的环境&#xff0c;以及新的工作方式和新兴实践&#xff0c;所有这些都不仅对于ITSM 专…

鹿蜀:一个基于日常开发任务体现开发人员工作状况的系统

一、背景 为了能够更方便、更直观的让Boss&#xff0c;让同伴&#xff0c;让自己了解到自己近期的工作情况&#xff0c;我拉了一个小队&#xff0c;利用农闲的时间&#xff0c;结合已有的项目管理系统中的数据&#xff0c;开发了一个新系统&#xff0c;名叫 “鹿蜀”。 二、系…

K8s 实战 动态PV StorageClass 存储类

前面我们学习了 PV 和 PVC 的使用方法&#xff0c;但是前面的 PV 都是静态的&#xff0c;什么意思&#xff1f;就是我要使用的一个 PVC 的话就必须手动去创建一个 PV&#xff0c;我们也说过这种方式在很大程度上并不能满足我们的需求&#xff0c;比如我们有一个应用需要对存储的…

说说你对slot的理解?

首先&#xff0c;大概了解一下插槽&#xff1a; 插槽是什么 ![在这里插入图片描述](https://img-blog.csdnimg.cn/90b04660769e49c286ee2e1821d2a2bb.png 插槽&#xff1a;在HTML中 slot 元素 &#xff0c;作为 Web Components 技术套件的一部分&#xff0c;是Web组件内的一…

【蓝桥杯选拔赛真题33】python回文数升级 青少年组蓝桥杯python 选拔赛STEMA比赛真题解析

目录 ​编辑 python回文数升级 一、题目要求 1、编程实现 2、输入输出

Spring 框架介绍和使用

微信公众号&#xff1a;运维开发故事&#xff0c;作者&#xff1a;老郑 历史的选择 Spring 作为一个基础的框架&#xff0c;是在 Java EE 开发历史中&#xff0c;是成千上万公司选择。单独使用 Spring 的非常少了&#xff0c;很多都是用 Spring-Boot/Spring-Cloud 来开发&…

文件系统监视器

QFileSystemWatcher类通过监视指定路径的列表来监视文件系统对文件和目录的更改。 构造函数&#xff1a; 常用函数&#xff1a; addPath&#xff08;&#xff09;添加路径addPaths&#xff08;&#xff09;添加多个路径directory&#xff08;&#xff09;返回正在监视的目录的…

【JS】数据结构之队列

文章目录基本概念代码实现基本概念 队列&#xff1a;它是一种运算受限的线性表&#xff0c;FIFO&#xff08;先进先出&#xff09;特性&#xff1a;它只运行表的前端进行删除操作&#xff0c;在表的后端进行添加操作面对无法同时处理多个请求的场景&#xff0c;我们通常就会使…

HTML小游戏16 —— 消除游戏《魔法石》源码(附完整源码)

&#x1f482; 网站推荐:【神级源码资源网】【摸鱼小游戏】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 想寻找共同学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】&#x1f4ac; 免费且实用的计…

计算机组成原理(一)计算机系统概论

目录前言计算机硬件的组成和分类冯.诺依曼结构的基本思想运算器存储器控制器程序和指令控制器的基本任务指令流和数据流适配器与I/O设备输入设备计算机软件的组成与分类计算机系统的层次结构计算机系统的性能指标软硬件逻辑的等价性前言 计算机组成原理这门课可以说是计算机专…

b2b2c o2o 多商家入驻商城 直播带货商城 电子商务

一个好的SpringCloudSpringBoot b2b2c 电子商务平台涉及哪些技术、运营方案&#xff1f;以下是我结合公司的产品做的总结&#xff0c;希望可以帮助到大家&#xff01; 搜索体验小程序&#xff1a;海哇 1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买…

Aqwa 带你掌握船舶与海洋工程水动力理论与工程应用

作者&#xff1a;高巍&#xff0c;仿真秀科普作者 一、写在前面 20世纪60年代欧洲北海发现了储量丰富的油气资源&#xff0c;随着1973年第二次石油危机的到来&#xff0c;欧洲国家能源自主的迫切需要推进了北海油气资源的开发进程&#xff0c;推动了海洋工程技术发展。 为了…

【多线程(三)】生产者和消费者模式

文章目录3.生产者和消费者模式前言3.1生产者和消费者模式概述3.2生产者和消费者案例3.3 阻塞队列基本使用3.4 阻塞队列实现等待唤醒机制总结3.生产者和消费者模式 前言 在线程世界里&#xff0c;生产者就是生产数据的线程&#xff0c;消费者就是消费数据的线程。在多线 程开发…

SQL SERVER数据库修复之REPAIR_ALLOW_DATA_LOSS级别简介和实例

目录 &#xff08;一&#xff09;前言 &#xff08;二&#xff09;关于DBCC CHECKxxx系列命令 1. DBCC CHECKALLOC 2. DBCC CHECKCATALOG 3. DBCC CHECKDB &#xff08;1&#xff09;基本概念 &#xff08;2&#xff09;基本语法 &#xff08;3&#xff09;参数说明 &…

分析常见限流算法及手写三种(计数器、漏斗、令牌桶)代码实现

常见的限流算法分析 限流在我们日常生活中经常见到&#xff0c;如火车站门口的栏杆、一些景点的门票只出售一定的数量 等等。在我们的开发中也用到了这种思想。 为什么要限流 在保证可用的情况下尽可能多增加进入的人数,其余的人在排队等待,或者返回友好提示,保证里面的进行…

机器如何快速学习数据采集

很多人都在思考如何利用机器学习&#xff08;ML&#xff09;算法来提高产品或服务的质量。 如果你正在考虑采用ML&#xff0c;以正确的格式收集正确的数据&#xff0c;将会降低你的数据清理工作以及数据浪费。 要收集所有数据 收集所有数据是非常重要的。除非你真正训练一个…

Excel基于分隔符拆分列

1、示例数据 id name describe 1 张三 学生 2 李四 老师 3 王五 学生 2、将数据复制到Excel中 数据目前都在A列中 3、将数据一次拆分到多个列 Excel基于分隔符拆分列&#xff0c;将数据一次拆分到多个列。 选中数据&#xff0c;数据-分列-分列 设置分隔符 点击完成后&…

【Python自然语言处理】使用逻辑回归(logistic)对电影评论情感分析实战(超详细 附源码)

需要源码和数据集请点赞关注收藏后评论区留言私信~~~ 一、舆情分析 舆情分析很多情况下涉及到用户的情感分析&#xff0c;或者亦称为观点挖掘&#xff0c;是指用自然语言处理技术、文本挖掘以及计算机语言学等方法来正确识别和提取文本素材中的主观信息&#xff0c;通过对带有…

使用Visual Studio Code 进行Python编程(一)

1、下载Visual Studio Code 到微软的Visual Studio Code官方主页下载Visual Studio Code: Visual Studio: 面向软件开发人员和 Teams 的 IDE 和代码编辑器Visual Studio 开发工具和服务让任何开发人员在任何平台和语言的应用开发都更加轻松。 随时随地免费使用代码编辑器或 I…