Camerax 打开指定摄像头id

安卓上开发摄像头预览应用,参考了谷歌的实现CameraX基本使用;具体使用可查看源码

复制一下关键代码:

private lateinit var viewFinder: PreviewView

private var camera: Camera? =null

private var cameraProvider: ProcessCameraProvider? =null

private var preview: Preview? =null

private var lensFacing: Int = CameraSelector.LENS_FACING_BACK

/** Initialize CameraX, and prepare to bind the camera use cases */

private fun setUpCamera() {

val cameraProviderFuture =       ProcessCameraProvider.getInstance(requireContext())

cameraProviderFuture.addListener(Runnable {

    // CameraProvider

    cameraProvider = cameraProviderFuture.get()

// Select lensFacing depending on the available cameras

    lensFacing =when {

        hasBackCamera() -> CameraSelector.LENS_FACING_BACK

        hasFrontCamera() -> CameraSelector.LENS_FACING_FRONT

        else ->throw IllegalStateException("Back and front camera are unavailable")

  }

// Build and bind the camera use cases

    bindCameraUseCases()

}, ContextCompat.getMainExecutor(requireContext()))

}

/** Declare and bind preview, capture and analysis use cases */

@SuppressLint("RestrictedApi")

private fun bindCameraUseCases() {

// Get screen metrics used to setup camera for full screen resolution

 val metrics = DisplayMetrics().also {         viewFinder.display.getRealMetrics(it)}

  val screenAspectRatio = aspectRatio(metrics.widthPixels,     metrics.heightPixels)

val rotation =viewFinder.display.rotation

val cameraProvider =cameraProvider

            ?:throw IllegalStateException("Camera initialization failed.")

    val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()

    preview = Preview.Builder()

            .setTargetAspectRatio(screenAspectRatio)

            .setTargetRotation(rotation)

            .build()

    // Must unbind the use-cases before rebinding them

    cameraProvider.unbindAll()

try {

        camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview)

        preview?.setSurfaceProvider(viewFinder.surfaceProvider)

    }catch (exc: Exception) {

    }

}



/** Returns true if the device has an available back camera. False otherwise */

  private fun hasBackCamera(): Boolean {

return cameraProvider?.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA) ?:false

}

/** Returns true if the device has an available front camera. False otherwise */

  private fun hasFrontCamera(): Boolean {

return cameraProvider?.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA) ?:false

}

其中选择预览的摄像头是通过

val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()

这个是CameraX 中默认的摄像头选择过滤的一个类,实现如下:

 public Builder requireLensFacing(@LensFacing int lensFacing) {

    mCameraFilterSet.add(new LensFacingCameraFilter(lensFacing));

     return this;
  }

继续看LensFacingCameraFilter类的实现,

public class LensFacingCameraFilterimplements CameraFilter {

@CameraSelector.LensFacing

private int mLensFacing;

public LensFacingCameraFilter(@CameraSelector.LensFacing int lensFacing) {

mLensFacing = lensFacing;

}

@NonNull

@Override

public List<CameraInfo>  filter(@NonNull List cameraInfos) {

List result =new ArrayList<CameraInfo>();

    for (CameraInfo cameraInfo : cameraInfos) {

      Preconditions.checkArgument(cameraInfoinstanceof CameraInfoInternal,

                "The camera info doesn't contain internal implementation.");

//拿到摄像头的前置或后置信息,和mLensFacing比较,相同的摄像头加入result返回

//mLensFacing就是requireLensFacing()传入的要打开前置还是后置

        Integer lensFacing = ((CameraInfoInternal) cameraInfo).getLensFacing();

        if (lensFacing !=null && lensFacing ==mLensFacing) {

            result.add(cameraInfo);      
          }

      }

      return result;
  }

}

主要是重写的filter()方法,其中的过滤方式是拿到 前后置和你设置的前后置一样的摄像头列表。

/** A camera on the device facing the same direction as the device's screen. */

public static final int LENS_FACING_FRONT =0;

/** A camera on the device facing the opposite direction as the device's screen. */

public static final int LENS_FACING_BACK =1;

当有多个摄像头都是前置或者后置是,默认就会打开列表的第一个;

先看下CameraSelector中的filter方法,

public List <CameraInfo>  filter(@NonNull List cameraInfos) {

List input =new ArrayList<CameraInfo>(cameraInfos);

List output =new ArrayList<CameraInfo>(cameraInfos);

for (CameraFilter filter :mCameraFilterSet) {

  //在这里调用了CameraFilter中的过滤方法filter,默认的是根据前后置来过滤,

//就是LensFacingCameraFilter类中filter的实现

output = filter.filter(Collections.unmodifiableList(output));

    if (output.isEmpty()) {

throw new IllegalArgumentException("No available camera can be found.");

    }else if (!input.containsAll(output)) {

throw new IllegalArgumentException("The output isn't contained in the input.");

    }

input.retainAll(output);

}

return output;

}

在绑定摄像头时,cameraProvider.bindToLifecycle()里的实现;


public CamerabindToLifecycle(

@NonNull LifecycleOwner lifecycleOwner,

        @NonNull CameraSelector cameraSelector,

        @Nullable ViewPort viewPort,

        @NonNull UseCase... useCases) {

...

CameraSelector.Builder selectorBuilder =

CameraSelector.Builder.fromSelector(cameraSelector);

// Append the camera filter required internally if there's any.

for (UseCase useCase : useCases) {

CameraSelector selector = useCase.getCurrentConfig().getCameraSelector(null);

    if (selector !=null) {

for (CameraFilter filter : selector.getCameraFilterSet()) {

selectorBuilder.addCameraFilter(filter);

        }

}

}

CameraSelector modifiedSelector = selectorBuilder.build();

/**在modifiedSelector.filter方法中会根据LensFacingCameraFilter的过滤规则把所有符合前置或后置的摄像头过滤出来*/

LinkedHashSet cameraInternals =

modifiedSelector.filter(mCameraX.getCameraRepository().getCameras());

...

// Try to get the camera before binding to the use case, and throw IllegalArgumentException

// if the camera not found.

//在这里 new CameraUseCaseAdapter()的时候会把都是前置或者后置的摄像头列表传进入,然后多个前置或后置时默认绑定第一个摄像头,无法指定第几个前置

if (lifecycleCameraToBind ==null) {

lifecycleCameraToBind =

mLifecycleCameraRepository.createLifecycleCamera(lifecycleOwner,

                    new CameraUseCaseAdapter(cameraInternals,

                            mCameraX.getCameraDeviceSurfaceManager(),

                            mCameraX.getDefaultConfigFactory()));

}

}

在CameraUseCaseAdapter初始化时默认选择列表的第一个摄像头:mCameraInternal = cameras.iterator().next();


public CameraUseCaseAdapter(@NonNull LinkedHashSet cameras,

        @NonNull CameraDeviceSurfaceManager cameraDeviceSurfaceManager,

        @NonNull UseCaseConfigFactory useCaseConfigFactory) {

mCameraInternal = cameras.iterator().next();

    mCameraInternals =new LinkedHashSet<>(cameras);

    mId =new CameraId(mCameraInternals);

    mCameraDeviceSurfaceManager = cameraDeviceSurfaceManager;

    mUseCaseConfigFactory = useCaseConfigFactory;

}

一开始使用CameraX,机器只有一个前置或后置时,通过前置或后置来选择摄像头没有问题;

后面遇到插入USB摄像头(我遇到的情况是插入多个USB摄像头时,和本地摄像头都被识别为前置摄像头)或者有多个后置或前置时,就只能打开默认的摄像头,没办法打开指定的摄像头了。

于是自定义自己的CameraFilter,通过摄像头的来过滤要打开的摄像头。

实现如下:


@ExperimentalCameraFilter

class MyCameraFilter(private val mId: String) : CameraFilter {

private val TAG ="CameraIdCameraFilter"

    @SuppressLint("RestrictedApi")

override fun filter(cameraInfos: MutableList): MutableList {

val result = ArrayList()

cameraInfos.forEach {

            Preconditions.checkArgument(

it is CameraInfoInternal,

                "The camera info doesn't contain internal implementation."

            )

it as CameraInfoInternal

val id =it.cameraId
//传入的mId通过CameraManager.cameraIdList获取就可以了
//但是有一个问题:外接的USB摄像头id在机器重启后,原来的摄像头对应的id会发生变化,
//这个时候想打开确定的摄像头的话,可以通过USB摄像头的pid,vid找到对应的摄像头对应
//的id,这个不同机型都不太一样,直接找系统工程师确认吧

      if (id==mId) {

            result.add(it)

            }

}

        return result

}

}

使用时初始化cameraSelector时改一下即可,其他不变:


private var mCameraId =0

val cameraSelector = CameraSelector.Builder()

.addCameraFilter(MyCameraFilter("$mCameraId")).build()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352