You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CameraX外接摄像头VideoCapture无法选择高分辨率问题解决

问题:CameraX 1.4.0-alpha03外接摄像头同时启用Preview和VideoCapture时分辨率异常降低

问题场景

使用CameraX 1.4.0-alpha03版本,配置Preview与VideoCapture并指定外接摄像头后,添加VideoCapture用例只能获取640x442低分辨率;移除该用例则可正常获取1080P分辨率。相关代码如下:

val preview = Preview.Builder()
             .setResolutionSelector(
                  ResolutionSelector.Builder()
                        .setAllowedResolutionMode(PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE)
                  .setAspectRatioStrategy(AspectRatioStrategy.RATIO_16_9_FALLBACK_AUTO_STRATEGY)
                        .setResolutionStrategy(
                            ResolutionStrategy(
                                Size(1920, 1080),
                                ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER
                            )
                        ).build()
             )

val cameraSelector = CameraSelector.Builder()
                        .addCameraFilter(ExternalCameraFilter(camera.id))
                        .build()
val qualitySelector = QualitySelector.fromOrderedList(
            listOf(Quality.FHD, Quality.HD, Quality.SD),
            FallbackStrategy.higherQualityOrLowerThan(
                Quality.SD
            )
        )
val recorder = Recorder.Builder().setQualitySelector(qualitySelector).build()
val videoCapture = VideoCapture.withOutput(recorder)
val useCaseGroup =
            UseCaseGroup.Builder()
                .addUseCase(preview!!)
                .addUseCase(videoCapture)
                .setViewPort(view!!.viewPort!!)
                .build()

val cameraProvider = provider.get()
cameraProvider.unbindAll()

preview?.setSurfaceProvider(view!!.surfaceProvider)

cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup)

ExternalCameraFilter实现:

@SuppressLint("UnsafeOptInUsageError")
override fun filter(cameraInfos: MutableList<CameraInfo>): MutableList<CameraInfo> {
    return cameraInfos.filter { camInfo ->
        isExternalCamera(camInfo)
    }.filter {
        try {
            Camera2CameraInfo.from(it).cameraId == cameraId
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    }.toMutableList()
}

@SuppressLint("UnsafeOptInUsageError")
private fun isExternalCamera(cameraInfo: CameraInfo): Boolean {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        try {
            Camera2CameraInfo.from(cameraInfo).getCameraCharacteristic(
                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL
            ) == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    } else {
        false
    }
}

日志信息

19:59:33.261 CapabilitiesByQuality    E  No supported EncoderProfiles
19:59:33.262 VideoConfigUtil          D  No EncoderProfiles present. May rely on fallback defaults to derive VIDEO settings [chosen mime type: video/avc, dynamic range: DynamicRange@2080ac3{encoding=SDR, bitDepth=8}]
19:59:33.262 UseCaseAttachState       D  Active and attached use case: [] for camera: 102
19:59:33.262 VidEncCfgDefaultRslvr    D  Default resolved frame rate: 30fps. [Expected operating range: <UNSPECIFIED>]
19:59:33.262                          D  Resolved VIDEO frame rate: 30fps
19:59:33.262                          D  Using fallback VIDEO bitrate
19:59:33.263 VideoConfigUtil          D  Base Bitrate(14000000bps) * Bit Depth Ratio (8 / 8) * Frame Rate Ratio(30 / 30) * Width Ratio(640 / 1280) * Height Ratio(442 / 720) = 4297222
19:59:33.263 Camera2CameraImpl        D  {Camera@4890b0c[id=102]} Use case androidx.camera.core.streamsharing.StreamSharing-73ab81ad-8aee-43c8-8758-02fc986545c579779905 ACTIVE
19:59:33.263 UseCaseAttachState       D  Active and attached use case: [] for camera: 102
19:59:33.263 VideoConfigUtil          W  Unsupported mime type video/avc or profile level -1. Data space is unspecified.
19:59:33.283 VideoCapabilities        W  Unrecognized profile/level 0/3 for video/mpeg2
19:59:33.295 CCodec                   D  allocate(c2.rk.avc.encoder)
19:59:33.296 Codec2Client             I  Available Codec2 services: "default" "software"
19:59:33.297 CCodec                   I  setting up 'default' as default (vendor) store
19:59:33.302                          I  Created component [c2.rk.avc.encoder]
19:59:33.302 CCodecConfig             D  read media type: video/avc
19:59:33.304 ReflectedParamUpdater    D  extent() != 1 for single value type: algo.buffers.max-count.values
19:59:33.304                          D  extent() != 1 for single value type: output.subscribed-indices.values
19:59:33.304                          D  extent() != 1 for single value type: input.buffers.allocator-ids.values
19:59:33.305                          D  extent() != 1 for single value type: output.buffers.allocator-ids.values
19:59:33.305                          D  extent() != 1 for single value type: algo.buffers.allocator-ids.values
19:59:33.305                          D  extent() != 1 for single value type: output.buffers.pool-ids.values
19:59:33.305                          D  extent() != 1 for single value type: algo.buffers.pool-ids.values
19:59:33.306                          D  ignored struct field coding.gop.values
19:59:33.306                          D  ignored struct field coding.qp.values
19:59:33.307 CCodecConfig             D  ignoring local param raw.color (0xc2001809) as it is already supported
19:59:33.308                          I  query failed after returning 14 values (BAD_INDEX)
19:59:33.309                          D  c2 config diff is Dict {
                                           c2::u32 algo.bitrate-mode.value = 3
                                           c2::u32 coded.bitrate.value = 64000
                                           c2::float coded.frame-rate.value = 1
                                           c2::u32 coded.pl.level = 20489
                                           c2::u32 coded.pl.profile = 20480
                                           c2::u32 coded.vui.color.matrix = 0
                                           c2::u32 coded.vui.color.primaries = 0
                                           c2::u32 coded.vui.color.range = 0
                                           c2::u32 coded.vui.color.transfer = 0
                                           c2::u32 coding.intra-refresh.mode = 0
                                           c2::float coding.intra-refresh.period = 0
                                           c2::u32 coding.request-sync-frame.value = 0
                                           c2::i64 coding.sync-frame-interval.value = 1000000
                                           Buffer coding.temporal-layering = {
                                             00000000:  10 00 00 00 07 20 01 52  00 00 00 00 00 00 00 00  ..... .R........
                                           }
                                           c2::u32 input.delay.value = 0
                                           string input.media-type.value = "video/raw"
                                           string output.media-type.value = "video/avc"
                                           c2::u32 raw.color.matrix = 0
                                           c2::u32 raw.color.primaries = 0
                                           c2::u32 raw.color.range = 0
                                           c2::u32 raw.color.transfer = 0
                                           c2::u32 raw.size.height = 144
                                           c2::u32 raw.size.width = 176
                                         }
19:59:33.309 ColorUtils               W  expected specified color aspects (0:0:0:0)
19:59:33.313 hw-BpHwBinder            I  onLastStrongRef automatically unlinking death recipients
19:59:33.314 CameraOrientationUtil    D  getRelativeImageRotation: destRotationDegrees=0, sourceRotationDegrees=0, isOppositeFacing=false, result=0
19:59:33.314 VideoCapture             D  camera timebase = UPTIME, processing timebase = UPTIME
19:59:33.314 DeferrableSurface        D  Surface created[total_surfaces=9, used_surfaces=3](androidx.camera.core.processing.SurfaceEdge$SettableSurface@497ae85}
19:59:33.315                          D  Surface created[total_surfaces=10, used_surfaces=3](androidx.camera.core.SurfaceRequest$2@129ae01}
19:59:33.316                          D  New surface in use[total_surfaces=10, used_surfaces=4](androidx.camera.core.SurfaceRequest$2@129ae01}
19:59:33.316                          D  use count+1, useCount=1 androidx.camera.core.SurfaceRequest$2@129ae01
19:59:33.316 Recorder                 D  Surface is requested in state: CONFIGURING, Current surface: 0
19:59:33.317 CameraOrientationUtil    D  getRelativeImageRotation: destRotationDegrees=0, sourceRotationDegrees=0, isOppositeFacing=false, result=0
19:59:33.317 CapabilitiesByQuality    E  No supported EncoderProfiles
19:59:33.318 Recorder                 D  Using supported quality of ConstantQuality{value=-1, name=NONE, typicalSizes=[]} for surface size 640x442
19:59:33.318                          D  Try to safely release video encoder: null
19:59:33.319                          D  Video source has transitioned to state: ACTIVE_NON_STREAMING

排查细节

通过CamcorderProfileResolutionQuirk查询到设备支持的分辨率包括:[640x480, 720x480, 720x576, 800x600, 1280x720, 1024x768, 1360x768, 1280x960, 1280x1024, 1920x1080]。

调用如下代码查询视频能力:

Recorder.getVideoCapabilities(
    this.cameraInfo,
    Recorder.VIDEO_CAPABILITIES_SOURCE_CAMCORDER_PROFILE
).apply {
    getSupportedQualities(DynamicRange.SDR).apply {
        Timber.d("getSupportedQualities ${this}")
    }
    Timber.d("supportedDynamicRanges ${supportedDynamicRanges}")
    Timber.d(
        "Profiles FHD: ${getProfiles(Quality.FHD, DynamicRange.SDR)?.videoProfiles}"
    )
}

返回结果为空:

D  getSupportedQualities []
 D  supportedDynamicRanges []
 D  Profiles FHD: null

问题定位与解决

补充遗漏的OverlayEffect代码后,确认是该Effect导致CameraX选择低分辨率,属于CameraX 1.4.0-alpha03版本的已知问题:

val overlayEffect = OverlayEffect(
            CameraEffect.PREVIEW or CameraEffect.VIDEO_CAPTURE or CameraEffect.IMAGE_CAPTURE,
            0,
            Handler(),
            {})

...

val useCaseGroup =
            UseCaseGroup.Builder()
                .addUseCase(preview!!)
                .addUseCase(videoCapture)
                .addEffect(overlayEffect)
                .setViewPort(view!!.viewPort!!)
                .build()

编译androidx最新源码构建自定义CameraX模块后,问题解决。

内容的提问来源于stack exchange,提问作者Kolyneh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 23:50:54